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
|
|
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
|
UNIT = 30,
|
28
|
};
|
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
|
|
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
|
|
74
|
void Bombs::UncoverAll()
|
75
|
{
|
76
|
for(int i = cx * cy - 1; i >= 0; i--)
|
77
|
field[i] &= ~HIDDEN;
|
78
|
Refresh();
|
79
|
}
|
80
|
|
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
|
Size tsz = GetTextSize(txt, Roman(2 * UNIT / 3));
|
111
|
w.DrawText(x * UNIT + (UNIT - tsz.cx) / 2, y * UNIT + (UNIT - tsz.cy) / 2,
|
112
|
txt, Roman(2 * UNIT / 3), ink);
|
113
|
if(f & EXPLODED)
|
114
|
cross = SLtBlue;
|
115
|
|
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
|
}
|
121
|
|
122
|
void Bombs::Uncover(int x, int y)
|
123
|
{
|
124
|
if(x >= 0 && x < cx && y >= 0 && y < cy) {
|
125
|
byte& f = Field(x, y);
|
126
|
if((f & (HIDDEN|MARK)) == HIDDEN) {
|
127
|
if(f & BOMB) {
|
128
|
f |= EXPLODED;
|
129
|
normal_cells = 0;
|
130
|
UncoverAll();
|
131
|
return;
|
132
|
}
|
133
|
if((f &= ~HIDDEN) == 0)
|
134
|
for(int xx = -1; xx <= 1; xx++)
|
135
|
for(int yy = -1; yy <= 1; yy++)
|
136
|
if(xx || yy)
|
137
|
Uncover(x + xx, y + yy);
|
138
|
normal_cells--;
|
139
|
if(normal_cells == 0) {
|
140
|
UncoverAll();
|
141
|
PromptOK("[*@4A6 Nice!]&You have found all the bombs!");
|
142
|
}
|
143
|
}
|
144
|
}
|
145
|
}
|
146
|
|
147
|
void Bombs::LeftDown(Point p, dword flags)
|
148
|
{
|
149
|
if(!normal_cells)
|
150
|
return;
|
151
|
p /= UNIT;
|
152
|
Uncover(p.x, p.y);
|
153
|
Refresh();
|
154
|
ShowStatus();
|
155
|
}
|
156
|
|
157
|
void Bombs::RightDown(Point p, dword flags)
|
158
|
{
|
159
|
if(!normal_cells)
|
160
|
return;
|
161
|
p /= UNIT;
|
162
|
if(Field(p.x, p.y) & HIDDEN) {
|
163
|
Field(p.x, p.y) ^= MARK;
|
164
|
Refresh();
|
165
|
}
|
166
|
}
|
167
|
|
168
|
void Bombs::ShowStatus()
|
169
|
{
|
170
|
status = Format("%d bombs, %d cells remaining", bombs, normal_cells);
|
171
|
}
|
172
|
|
173
|
void Bombs::Level(Size sz)
|
174
|
{
|
175
|
level = sz;
|
176
|
}
|
177
|
|
178
|
void Bombs::About()
|
179
|
{
|
180
|
PromptOK("[*A9/ uBombs]&[A5 Ultimate`+`+ example]");
|
181
|
}
|
182
|
|
183
|
void Bombs::File(Bar& menu)
|
184
|
{
|
185
|
menu.Add("Exit", Breaker(IDOK));
|
186
|
menu.Separator();
|
187
|
menu.Add("About..", THISBACK(About));
|
188
|
}
|
189
|
|
190
|
void Bombs::Game(Bar& menu)
|
191
|
{
|
192
|
menu.Add("Restart", THISBACK(Generate));
|
193
|
menu.Separator();
|
194
|
menu.Add("Easy", THISBACK1(Level, Size(10, 10)))
|
195
|
.Check(level.cx == 10);
|
196
|
menu.Add("Medium", THISBACK1(Level, Size(15, 15)))
|
197
|
.Check(level.cx == 15);
|
198
|
menu.Add("Difficult", THISBACK1(Level, Size(25, 20)))
|
199
|
.Check(level.cx == 25);
|
200
|
}
|
201
|
|
202
|
void Bombs::Menu(Bar& menu)
|
203
|
{
|
204
|
menu.Add("File", THISBACK(File));
|
205
|
menu.Add("Game", THISBACK(Game));
|
206
|
}
|
207
|
|
208
|
#define IMAGECLASS BombsImg
|
209
|
#define IMAGEFILE <Bombs/bombs.iml>
|
210
|
#include <Draw/iml.h>
|
211
|
|
212
|
Bombs::Bombs()
|
213
|
{
|
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
|
}
|