summaryrefslogtreecommitdiff
path: root/apps/plugins/minesweeper.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/minesweeper.c')
-rw-r--r--apps/plugins/minesweeper.c436
1 files changed, 436 insertions, 0 deletions
diff --git a/apps/plugins/minesweeper.c b/apps/plugins/minesweeper.c
new file mode 100644
index 0000000000..b155c3fbad
--- /dev/null
+++ b/apps/plugins/minesweeper.c
@@ -0,0 +1,436 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2004 dionoea (Antoine Cellerier)
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20/*****************************************************************************
21Mine Sweeper by dionoea
22
23use arrow keys to move cursor
24use ON or F2 to clear a tile
25use PLAY or F1 to put a flag on a tile
26use F3 to see how many mines are left (supposing all your flags are correct)
27
28*****************************************************************************/
29
30#include "plugin.h"
31#include "button.h"
32#include "lcd.h"
33
34#ifdef HAVE_LCD_BITMAP
35
36//what the minesweeper() function can return
37#define MINESWEEPER_QUIT 2
38#define MINESWEEPER_LOSE 1
39#define MINESWEEPER_WIN 0
40
41
42/* here is a global api struct pointer. while not strictly necessary,
43 it's nice not to have to pass the api pointer in all function calls
44 in the plugin */
45static struct plugin_api* rb;
46
47
48//define how numbers are displayed (that way we don't have to
49//worry about fonts)
50static unsigned char num[9][8] = {
51 /*reading the sprites:
52 on screen f123
53 4567
54 890a
55 bcde
56
57 in binary b84f
58 c951
59 d062
60 ea73
61 */
62
63 //0
64 {0x00, //........
65 0x00, //........
66 0x00, //........
67 0x00, //........
68 0x00, //........
69 0x00, //........
70 0x00, //........
71 0x00},//........
72 //1
73 {0x00, //........
74 0x00, //........
75 0x00, //...OO...
76 0x44, //....O...
77 0x7c, //....O...
78 0x40, //....O...
79 0x00, //...OOO..
80 0x00},//........
81 //2
82 {0x00, //........
83 0x00, //........
84 0x48, //...OO...
85 0x64, //..O..O..
86 0x54, //....O...
87 0x48, //...O....
88 0x00, //..OOOO..
89 0x00},//........
90 //3
91 {0x00, //........
92 0x00, //........
93 0x44, //..OOO...
94 0x54, //.....O..
95 0x54, //...OO...
96 0x28, //.....O..
97 0x00, //..OOO...
98 0x00},//........
99 //4
100 {0x00, //........
101 0x00, //........
102 0x1c, //..O.....
103 0x10, //..O.....
104 0x70, //..OOOO..
105 0x10, //....O...
106 0x00, //....O...
107 0x00},//........
108 //5
109 {0x00, //........
110 0x00, //........
111 0x5c, //..OOOO..
112 0x54, //..O.....
113 0x54, //..OOO...
114 0x24, //.....O..
115 0x00, //..OOO...
116 0x00},//........
117 //6
118 {0x00, //........
119 0x00, //........
120 0x38, //...OOO..
121 0x54, //..O.....
122 0x54, //..OOO...
123 0x24, //..O..O..
124 0x00, //...OO...
125 0x00},//........
126 //7
127 {0x00, //........
128 0x00, //........
129 0x44, //..OOOO..
130 0x24, //.....O..
131 0x14, //....O...
132 0x0c, //...O....
133 0x00, //..O.....
134 0x00},//........
135 //8
136 {0x00, //........
137 0x00, //........
138 0x28, //...OO...
139 0x54, //..O..O..
140 0x54, //...OO...
141 0x28, //..O..O..
142 0x00, //...OO...
143 0x00},//........
144};
145
146/* the tile struct
147if there is a mine, mine is true
148if tile is known by player, known is true
149if tile has a flag, flag is true
150neighbors is the total number of mines arround tile
151*/
152typedef struct tile {
153 unsigned char mine : 1;
154 unsigned char known : 1;
155 unsigned char flag : 1;
156 unsigned char neighbors : 4;
157} tile;
158
159//the height and width of the field
160//could be variable if malloc worked in the API :)
161const int height = LCD_HEIGHT/8;
162const int width = LCD_WIDTH/8;
163
164//the minefield
165tile minefield[LCD_HEIGHT/8][LCD_WIDTH/8];
166
167//total number of mines on the game
168int mine_num = 0;
169
170//discovers the tile when player clears one of them
171//a chain reaction (of discovery) occurs if tile has no mines
172//as neighbors
173void discover(int, int);
174void discover(int x, int y){
175
176 if(x<0) return;
177 if(y<0) return;
178 if(x>width-1) return;
179 if(y>height-1) return;
180 if(minefield[y][x].known) return;
181
182 minefield[y][x].known = 1;
183 if(minefield[y][x].neighbors == 0){
184 discover(x-1,y-1);
185 discover(x,y-1);
186 discover(x+1,y-1);
187 discover(x+1,y);
188 discover(x+1,y+1);
189 discover(x,y+1);
190 discover(x-1,y+1);
191 discover(x-1,y);
192 }
193 return;
194}
195
196
197//init not mine related elements of the mine field
198void minesweeper_init(void){
199 int i,j;
200
201 for(i=0;i<height;i++){
202 for(j=0;j<width;j++){
203 minefield[i][j].known = 0;
204 minefield[i][j].flag = 0;
205 }
206 }
207}
208
209
210//put mines on the mine field
211//there is p% chance that a tile is a mine
212//if the tile has coordinates (x,y), then it can't be a mine
213void minesweeper_putmines(int p, int x, int y){
214 int i,j;
215
216 for(i=0;i<height;i++){
217 for(j=0;j<width;j++){
218 if(rb->rand()%100<p && !(y==i && x==j)){
219 minefield[i][j].mine = 1;
220 mine_num++;
221 } else {
222 minefield[i][j].mine = 0;
223 }
224 minefield[i][j].neighbors = 0;
225 }
226 }
227
228 //we need to compute the neighbor element for each tile
229 for(i=0;i<height;i++){
230 for(j=0;j<width;j++){
231 if(i>0){
232 if(j>0) minefield[i][j].neighbors += minefield[i-1][j-1].mine;
233 minefield[i][j].neighbors += minefield[i-1][j].mine;
234 if(j<width-1) minefield[i][j].neighbors += minefield[i-1][j+1].mine;
235 }
236 if(j>0) minefield[i][j].neighbors += minefield[i][j-1].mine;
237 if(j<width-1) minefield[i][j].neighbors += minefield[i][j+1].mine;
238 if(i<height-1){
239 if(j>0) minefield[i][j].neighbors += minefield[i+1][j-1].mine;
240 minefield[i][j].neighbors += minefield[i+1][j].mine;
241 if(j<width-1) minefield[i][j].neighbors += minefield[i+1][j+1].mine;
242 }
243 }
244 }
245}
246
247//the big and ugly function that is the game
248int minesweeper(void){
249
250
251 int i,j;
252
253 //the cursor coordinates
254 int x=0,y=0;
255
256 //number of tiles left on the game
257 int tiles_left=width*height;
258
259 //percentage of mines on minefield used durring generation
260 int p=16;
261
262 //a usefull string for snprintf
263 char str[30];
264
265 //welcome screen where player can chose mine percentage
266 i = 0;
267 while(true){
268 rb->lcd_clear_display();
269
270 rb->lcd_putsxy(1,1,"Mine Sweeper");
271
272 rb->snprintf(str, 20, "%d%% mines", p);
273 rb->lcd_putsxy(1,19,str);
274 rb->lcd_putsxy(1,28,"down / up");
275 rb->lcd_putsxy(1,44,"ON to start");
276
277 rb->lcd_update();
278
279
280 switch(rb->button_get(true)){
281 case BUTTON_DOWN:
282 case BUTTON_LEFT:
283 p = (p + 98)%100;
284 break;
285
286 case BUTTON_UP:
287 case BUTTON_RIGHT:
288 p = (p + 2)%100;
289 break;
290
291 case BUTTON_ON://start playing
292 i = 1;
293 break;
294
295 case BUTTON_OFF://quit program
296 return MINESWEEPER_QUIT;
297 }
298 if(i==1) break;
299 }
300
301
302 /********************
303 * init *
304 ********************/
305
306 minesweeper_init();
307
308 /**********************
309 * play *
310 **********************/
311
312 while(true){
313
314 //clear the screen buffer
315 rb->lcd_clear_display();
316
317 //display the mine field
318 for(i=0;i<height;i++){
319 for(j=0;j<width;j++){
320 rb->lcd_drawrect(j*8,i*8,8,8);
321 if(minefield[i][j].known){
322 if(minefield[i][j].mine){
323 rb->lcd_putsxy(j*8+1,i*8+1,"b");
324 } else if(minefield[i][j].neighbors){
325 rb->lcd_bitmap(num[minefield[i][j].neighbors],j*8,i*8,8,8,false);
326 }
327 } else if(minefield[i][j].flag) {
328 rb->lcd_drawline(j*8+2,i*8+2,j*8+5,i*8+5);
329 rb->lcd_drawline(j*8+2,i*8+5,j*8+5,i*8+2);
330 } else {
331 rb->lcd_fillrect(j*8+2,i*8+2,4,4);
332 }
333 }
334 }
335
336 //display the cursor
337 rb->lcd_invertrect(x*8,y*8,8,8);
338
339 //update the screen
340 rb->lcd_update();
341
342 switch(rb->button_get(true)){
343 //quit minesweeper (you really shouldn't use this button ...)
344 case BUTTON_OFF:
345 return MINESWEEPER_QUIT;
346
347 //move cursor left
348 case BUTTON_LEFT:
349 x = (x + width - 1)%width;
350 break;
351
352 //move cursor right
353 case BUTTON_RIGHT:
354 x = (x + 1)%width;
355 break;
356
357 //move cursor down
358 case BUTTON_DOWN:
359 y = (y + 1)%height;
360 break;
361
362 //move cursor up
363 case BUTTON_UP:
364 y = (y + height - 1)%height;
365 break;
366
367 //discover a tile (and it's neighbors if .neighbors == 0)
368 case BUTTON_ON:
369 case BUTTON_F2:
370 if(minefield[y][x].flag) break;
371 //we put the mines on the first "click" so that you don't
372 //lose on the first "click"
373 if(tiles_left == width*height) minesweeper_putmines(p,x,y);
374 discover(x,y);
375 if(minefield[y][x].mine){
376 return MINESWEEPER_LOSE;
377 }
378 tiles_left = 0;
379 for(i=0;i<height;i++){
380 for(j=0;j<width;j++){
381 if(minefield[i][j].known == 0) tiles_left++;
382 }
383 }
384 if(tiles_left == mine_num){
385 return MINESWEEPER_WIN;
386 }
387 break;
388
389 //toggle flag under cursor
390 case BUTTON_PLAY:
391 case BUTTON_F1:
392 minefield[y][x].flag = (minefield[y][x].flag + 1)%2;
393 break;
394
395 //show how many mines you think you have found and how many
396 //there really are on the game
397 case BUTTON_F3:
398 tiles_left = 0;
399 for(i=0;i<height;i++){
400 for(j=0;j<width;j++){
401 if(minefield[i][j].flag) tiles_left++;
402 }
403 }
404 rb->splash(HZ*2, true, "You found %d mines out of %d", tiles_left, mine_num);
405 break;
406 }
407 }
408
409}
410
411//plugin entry point
412enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
413{
414 //plugin init
415 TEST_PLUGIN_API(api);
416 (void)parameter;
417 rb = api;
418 //end of plugin init
419
420 switch(minesweeper()){
421 case MINESWEEPER_WIN:
422 rb->splash(HZ*2, true, "You Win :)");
423 break;
424
425 case MINESWEEPER_LOSE:
426 rb->splash(HZ*2, true, "You Lost :(");
427 break;
428
429 default:
430 break;
431 }
432
433 return PLUGIN_OK;
434}
435
436#endif