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