1 #include <CtrlLib/CtrlLib.h>
2
3 using namespace Upp;
4
5 class Bombs : public TopWindow {
6 public:
7 virtual void Paint(Draw& w);
8 virtual void LeftDown(Point p, dword flags);
9 virtual void RightDown(Point p, dword flags);
10
11 private:
12 Size level;
13 int cx, cy;
14 int normal_cells, bombs;
15 Buffer<byte> field;
16 MenuBar menu;
17 StatusBar status;
18
Field(int x,int y)19 byte& Field(int x, int y) { return field[x + y * cx]; }
20
21 enum {
22 HIDDEN = 16,
23 BOMB = 32,
24 MARK = 64,
25 EXPLODED = 128,
26 };
27
28 int UNIT = 30;
29
30 void About();
31
32 void File(Bar& menu);
33 void Game(Bar& menu);
34 void Menu(Bar& menu);
35
36 void ShowStatus();
37 void Level(Size sz);
38
39 void Uncover(int x, int y);
40 void Generate();
41 void UncoverAll();
42
43 public:
44 typedef Bombs CLASSNAME;
45 Bombs();
46 };
47
Generate()48 void Bombs::Generate()
49 {
50 cx = level.cx;
51 cy = level.cy;
52 field.Alloc(cx * cy);
53 for(int i = cx * cy - 1; i >= 0; i--)
54 field[i] = (rand() & 15) < 3 ? HIDDEN|BOMB : HIDDEN;
55 normal_cells = 0;
56 for(int x = 0; x < cx; x++)
57 for(int y = 0; y < cy; y++)
58 if((Field(x, y) & BOMB) == 0) {
59 normal_cells++;
60 for(int xx = -1; xx <= 1; xx++)
61 for(int yy = -1; yy <= 1; yy++)
62 if((xx || yy) && x + xx >= 0 && x + xx < cx && y + yy >= 0 && y + yy < cy &&
63 (Field(x + xx, y + yy) & BOMB))
64 Field(x, y)++;
65 }
66 bombs = cx * cy - normal_cells;
67 Rect r = GetRect();
68 r.SetSize(AddFrameSize(UNIT * cx, UNIT * cy));
69 SetRect(r);
70 ShowStatus();
71 Refresh();
72 }
73
UncoverAll()74 void Bombs::UncoverAll()
75 {
76 for(int i = cx * cy - 1; i >= 0; i--)
77 field[i] &= ~HIDDEN;
78 Refresh();
79 }
80
Paint(Draw & w)81 void Bombs::Paint(Draw& w)
82 {
83 for(int x = 0; x < cx; x++)
84 for(int y = 0; y < cy; y++) {
85 byte f = Field(x, y);
86 w.DrawRect(x * UNIT, y * UNIT + UNIT - 1, UNIT, 1, SBlack);
87 w.DrawRect(x * UNIT + UNIT - 1, y * UNIT, 1, UNIT, SBlack);
88 w.DrawRect(x * UNIT, y * UNIT, UNIT - 1, UNIT - 1,
89 (f & (HIDDEN|MARK)) ? SLtGray : f & BOMB ? SLtRed : SWhite);
90 String txt;
91 Color ink = SBlack;
92 Color cross = Null;
93 if(f & MARK) {
94 txt = "M";
95 ink = SLtRed;
96 if((f & (HIDDEN|BOMB)) == BOMB) {
97 ink = SLtBlue;
98 cross = SLtRed;
99 }
100 }
101 else
102 if(!(f & HIDDEN)) {
103 if(f & BOMB)
104 txt = "B";
105 else {
106 f = f & 15;
107 txt = String(f + '0', 1);
108 ink = f == 0 ? SLtGreen : f == 1 ? SLtBlue : SBlack;
109 }
110 }
111 Size tsz = GetTextSize(txt, Roman(2 * UNIT / 3));
112 w.DrawText(x * UNIT + (UNIT - tsz.cx) / 2, y * UNIT + (UNIT - tsz.cy) / 2,
113 txt, Roman(2 * UNIT / 3), ink);
114 if(f & EXPLODED)
115 cross = SLtBlue;
116 w.DrawLine(x * UNIT, y * UNIT, x * UNIT + UNIT - 1, y * UNIT + UNIT - 1, 1, cross);
117 w.DrawLine(x * UNIT, y * UNIT + UNIT - 1, x * UNIT + UNIT - 1, y * UNIT, 1, cross);
118 }
119 }
120
Uncover(int x,int y)121 void Bombs::Uncover(int x, int y)
122 {
123 if(x >= 0 && x < cx && y >= 0 && y < cy) {
124 byte& f = Field(x, y);
125 if((f & (HIDDEN|MARK)) == HIDDEN) {
126 if(f & BOMB) {
127 f |= EXPLODED;
128 normal_cells = 0;
129 UncoverAll();
130 return;
131 }
132 if((f &= ~HIDDEN) == 0)
133 for(int xx = -1; xx <= 1; xx++)
134 for(int yy = -1; yy <= 1; yy++)
135 if(xx || yy)
136 Uncover(x + xx, y + yy);
137 normal_cells--;
138 if(normal_cells == 0) {
139 UncoverAll();
140 PromptOK("[*@4A6 Nice!]&You have found all the bombs!");
141 }
142 }
143 }
144 }
145
LeftDown(Point p,dword flags)146 void Bombs::LeftDown(Point p, dword flags)
147 {
148 if(!normal_cells)
149 return;
150 p /= UNIT;
151 Uncover(p.x, p.y);
152 Refresh();
153 ShowStatus();
154 }
155
RightDown(Point p,dword flags)156 void Bombs::RightDown(Point p, dword flags)
157 {
158 if(!normal_cells)
159 return;
160 p /= UNIT;
161 if(Field(p.x, p.y) & HIDDEN) {
162 Field(p.x, p.y) ^= MARK;
163 Refresh();
164 }
165 }
166
ShowStatus()167 void Bombs::ShowStatus()
168 {
169 status = Format("%d bombs, %d cells remaining", bombs, normal_cells);
170 }
171
Level(Size sz)172 void Bombs::Level(Size sz)
173 {
174 level = sz;
175 }
176
About()177 void Bombs::About()
178 {
179 PromptOK("[*A9/ uBombs]&[A5 Ultimate`+`+ example]");
180 }
181
File(Bar & menu)182 void Bombs::File(Bar& menu)
183 {
184 menu.Add("Exit", Breaker(IDOK));
185 menu.Separator();
186 menu.Add("About..", THISBACK(About));
187 }
188
Game(Bar & menu)189 void Bombs::Game(Bar& menu)
190 {
191 menu.Add("Restart", THISBACK(Generate));
192 menu.Separator();
193 menu.Add("Easy", THISBACK1(Level, Size(10, 10)))
194 .Check(level.cx == 10);
195 menu.Add("Medium", THISBACK1(Level, Size(15, 15)))
196 .Check(level.cx == 15);
197 menu.Add("Difficult", THISBACK1(Level, Size(25, 20)))
198 .Check(level.cx == 25);
199 }
200
Menu(Bar & menu)201 void Bombs::Menu(Bar& menu)
202 {
203 menu.Add("File", THISBACK(File));
204 menu.Add("Game", THISBACK(Game));
205 }
206
207 #define IMAGECLASS BombsImg
208 #define IMAGEFILE <Bombs/bombs.iml>
209 #include <Draw/iml.h>
210
Bombs()211 Bombs::Bombs()
212 {
213 UNIT = DPI(30);
214 level = Size(10, 10);
215 AddFrame(menu);
216 menu.Set(THISBACK(Menu));
217 AddFrame(status);
218 AddFrame(InsetFrame());
219 Title("uBombs");
220 Icon(BombsImg::Small());
221 Generate();
222 }
223
224 GUI_APP_MAIN
225 {
226 Bombs().Run();
227 }
228