More iPod 3G work from Seven Le Mesle
[Rockbox.git] / apps / plugins / minesweeper.c
blob8ca85a4965bdf3d80821d7a5553a19fc24ee5967
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2004 dionoea (Antoine Cellerier)
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 /*****************************************************************************
21 Mine Sweeper by dionoea
23 use arrow keys to move cursor
24 use ON or F2 to clear a tile
25 use PLAY or F1 to put a flag on a tile
26 use F3 to see how many mines are left (supposing all your flags are correct)
28 *****************************************************************************/
30 #include "plugin.h"
32 #ifdef HAVE_LCD_BITMAP
34 PLUGIN_HEADER
36 //what the minesweeper() function can return
37 #define MINESWEEPER_USB 3
38 #define MINESWEEPER_QUIT 2
39 #define MINESWEEPER_LOSE 1
40 #define MINESWEEPER_WIN 0
42 /* variable button definitions */
43 #if CONFIG_KEYPAD == RECORDER_PAD
44 #define MINESWP_UP BUTTON_UP
45 #define MINESWP_DOWN BUTTON_DOWN
46 #define MINESWP_QUIT BUTTON_OFF
47 #define MINESWP_START BUTTON_ON
48 #define MINESWP_TOGGLE BUTTON_PLAY
49 #define MINESWP_TOGGLE2 BUTTON_F1
50 #define MINESWP_DISCOVER BUTTON_ON
51 #define MINESWP_DISCOVER2 BUTTON_F2
52 #define MINESWP_INFO BUTTON_F3
54 #elif CONFIG_KEYPAD == ONDIO_PAD
55 #define MINESWP_UP BUTTON_UP
56 #define MINESWP_DOWN BUTTON_DOWN
57 #define MINESWP_QUIT BUTTON_OFF
58 #define MINESWP_START BUTTON_MENU
59 #define MINESWP_TOGGLE_PRE BUTTON_MENU
60 #define MINESWP_TOGGLE (BUTTON_MENU | BUTTON_REL)
61 #define MINESWP_DISCOVER (BUTTON_MENU | BUTTON_REPEAT)
62 #define MINESWP_INFO (BUTTON_MENU | BUTTON_OFF)
64 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
65 (CONFIG_KEYPAD == IRIVER_H300_PAD)
66 #define MINESWP_UP BUTTON_UP
67 #define MINESWP_DOWN BUTTON_DOWN
68 #define MINESWP_QUIT BUTTON_OFF
69 #define MINESWP_START BUTTON_SELECT
70 #define MINESWP_TOGGLE BUTTON_SELECT
71 #define MINESWP_DISCOVER BUTTON_ON
72 #define MINESWP_INFO BUTTON_MODE
74 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
75 (CONFIG_KEYPAD == IPOD_3G_PAD)
76 #define MINESWP_UP BUTTON_SCROLL_BACK
77 #define MINESWP_DOWN BUTTON_SCROLL_FWD
78 #define MINESWP_QUIT BUTTON_MENU
79 #define MINESWP_START BUTTON_SELECT
80 #define MINESWP_TOGGLE BUTTON_PLAY
81 #define MINESWP_DISCOVER (BUTTON_SELECT | BUTTON_PLAY)
82 #define MINESWP_INFO (BUTTON_SELECT | BUTTON_MENU)
84 #elif (CONFIG_KEYPAD == IAUDIO_X5_PAD)
85 #define MINESWP_UP BUTTON_UP
86 #define MINESWP_DOWN BUTTON_DOWN
87 #define MINESWP_QUIT BUTTON_POWER
88 #define MINESWP_START BUTTON_REC
89 #define MINESWP_TOGGLE BUTTON_PLAY
90 #define MINESWP_DISCOVER BUTTON_SELECT
91 #define MINESWP_INFO (BUTTON_REC | BUTTON_PLAY)
93 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
94 #define MINESWP_UP BUTTON_UP
95 #define MINESWP_DOWN BUTTON_DOWN
96 #define MINESWP_QUIT BUTTON_A
97 #define MINESWP_START BUTTON_SELECT
98 #define MINESWP_TOGGLE BUTTON_SELECT
99 #define MINESWP_DISCOVER BUTTON_POWER
100 #define MINESWP_INFO BUTTON_MENU
102 #endif
104 /* here is a global api struct pointer. while not strictly necessary,
105 it's nice not to have to pass the api pointer in all function calls
106 in the plugin */
107 static struct plugin_api* rb;
110 /* define how numbers are displayed (that way we don't have to */
111 /* worry about fonts) */
112 static unsigned char num[9][8] = {
113 /*reading the sprites:
114 on screen f123
115 4567
116 890a
117 bcde
119 in binary b84f
120 c951
121 d062
122 ea73
125 /* 0 */
126 {0x00, /* ........ */
127 0x00, /* ........ */
128 0x00, /* ........ */
129 0x00, /* ........ */
130 0x00, /* ........ */
131 0x00, /* ........ */
132 0x00, /* ........ */
133 0x00},/* ........ */
134 /* 1 */
135 {0x00, /* ........ */
136 0x00, /* ........ */
137 0x00, /* ...OO... */
138 0x44, /* ....O... */
139 0x7c, /* ....O... */
140 0x40, /* ....O... */
141 0x00, /* ...OOO.. */
142 0x00},/* ........ */
143 /* 2 */
144 {0x00, /* ........ */
145 0x00, /* ........ */
146 0x48, /* ...OO... */
147 0x64, /* ..O..O.. */
148 0x54, /* ....O... */
149 0x48, /* ...O.... */
150 0x00, /* ..OOOO.. */
151 0x00},/* ........ */
152 /* 3 */
153 {0x00, /* ........ */
154 0x00, /* ........ */
155 0x44, /* ..OOO... */
156 0x54, /* .....O.. */
157 0x54, /* ...OO... */
158 0x28, /* .....O.. */
159 0x00, /* ..OOO... */
160 0x00},/* ........ */
161 /* 4 */
162 {0x00, /* ........ */
163 0x00, /* ........ */
164 0x1c, /* ..O..... */
165 0x10, /* ..O..... */
166 0x70, /* ..OOOO.. */
167 0x10, /* ....O... */
168 0x00, /* ....O... */
169 0x00},/* ........ */
170 /* 5 */
171 {0x00, /* ........ */
172 0x00, /* ........ */
173 0x5c, /* ..OOOO.. */
174 0x54, /* ..O..... */
175 0x54, /* ..OOO... */
176 0x24, /* .....O.. */
177 0x00, /* ..OOO... */
178 0x00},/* ........ */
179 /* 6 */
180 {0x00, /* ........ */
181 0x00, /* ........ */
182 0x38, /* ...OOO.. */
183 0x54, /* ..O..... */
184 0x54, /* ..OOO... */
185 0x24, /* ..O..O.. */
186 0x00, /* ...OO... */
187 0x00},/* ........ */
188 /* 7 */
189 {0x00, /* ........ */
190 0x00, /* ........ */
191 0x44, /* ..OOOO.. */
192 0x24, /* .....O.. */
193 0x14, /* ....O... */
194 0x0c, /* ...O.... */
195 0x00, /* ..O..... */
196 0x00},/* ........ */
197 /* 8 */
198 {0x00, /* ........ */
199 0x00, /* ........ */
200 0x28, /* ...OO... */
201 0x54, /* ..O..O.. */
202 0x54, /* ...OO... */
203 0x28, /* ..O..O.. */
204 0x00, /* ...OO... */
205 0x00},/* ........ */
208 /* the tile struct
209 if there is a mine, mine is true
210 if tile is known by player, known is true
211 if tile has a flag, flag is true
212 neighbors is the total number of mines arround tile
214 typedef struct tile {
215 unsigned char mine : 1;
216 unsigned char known : 1;
217 unsigned char flag : 1;
218 unsigned char neighbors : 4;
219 } tile;
221 /* the height and width of the field */
222 int height = LCD_HEIGHT/8;
223 int width = LCD_WIDTH/8;
225 /* the minefield */
226 tile minefield[LCD_HEIGHT/8][LCD_WIDTH/8];
228 /* total number of mines on the game */
229 int mine_num = 0;
231 /* discovers the tile when player clears one of them */
232 /* a chain reaction (of discovery) occurs if tile has no mines */
233 /* as neighbors */
234 void discover(int, int);
235 void discover(int x, int y){
237 if(x<0) return;
238 if(y<0) return;
239 if(x>width-1) return;
240 if(y>height-1) return;
241 if(minefield[y][x].known) return;
243 minefield[y][x].known = 1;
244 if(minefield[y][x].neighbors == 0){
245 discover(x-1,y-1);
246 discover(x,y-1);
247 discover(x+1,y-1);
248 discover(x+1,y);
249 discover(x+1,y+1);
250 discover(x,y+1);
251 discover(x-1,y+1);
252 discover(x-1,y);
254 return;
258 /* init not mine related elements of the mine field */
259 void minesweeper_init(void){
260 int i,j;
262 for(i=0;i<height;i++){
263 for(j=0;j<width;j++){
264 minefield[i][j].known = 0;
265 minefield[i][j].flag = 0;
271 /* put mines on the mine field */
272 /* there is p% chance that a tile is a mine */
273 /* if the tile has coordinates (x,y), then it can't be a mine */
274 void minesweeper_putmines(int p, int x, int y){
275 int i,j;
277 mine_num = 0;
278 for(i=0;i<height;i++){
279 for(j=0;j<width;j++){
280 if(rb->rand()%100<p && !(y==i && x==j)){
281 minefield[i][j].mine = 1;
282 mine_num++;
283 } else {
284 minefield[i][j].mine = 0;
286 minefield[i][j].neighbors = 0;
290 /* we need to compute the neighbor element for each tile */
291 for(i=0;i<height;i++){
292 for(j=0;j<width;j++){
293 if(i>0){
294 if(j>0)
295 minefield[i][j].neighbors += minefield[i-1][j-1].mine;
296 minefield[i][j].neighbors += minefield[i-1][j].mine;
297 if(j<width-1)
298 minefield[i][j].neighbors += minefield[i-1][j+1].mine;
300 if(j>0)
301 minefield[i][j].neighbors += minefield[i][j-1].mine;
302 if(j<width-1)
303 minefield[i][j].neighbors += minefield[i][j+1].mine;
304 if(i<height-1){
305 if(j>0)
306 minefield[i][j].neighbors += minefield[i+1][j-1].mine;
307 minefield[i][j].neighbors += minefield[i+1][j].mine;
308 if(j<width-1)
309 minefield[i][j].neighbors += minefield[i+1][j+1].mine;
315 /* the big and ugly function that is the game */
316 int minesweeper(void)
318 int i,j;
319 int button;
320 int lastbutton = BUTTON_NONE;
322 /* the cursor coordinates */
323 int x=0,y=0;
325 /* number of tiles left on the game */
326 int tiles_left=width*height;
328 /* percentage of mines on minefield used durring generation */
329 int p=16;
331 /* a usefull string for snprintf */
332 char str[30];
334 /* welcome screen where player can chose mine percentage */
335 i = 0;
336 while(true){
337 rb->lcd_clear_display();
339 rb->lcd_puts(0,0,"Mine Sweeper");
341 rb->snprintf(str, 20, "%d%% mines", p);
342 rb->lcd_puts(0,2,str);
343 rb->lcd_puts(0,3,"down / up");
344 rb->snprintf(str, 20, "%d cols x %d rows", width, height);
345 rb->lcd_puts(0,4,str);
346 rb->lcd_puts(0,5,"left x right ");
347 #if CONFIG_KEYPAD == RECORDER_PAD
348 rb->lcd_puts(0,6,"ON to start");
349 #elif CONFIG_KEYPAD == ONDIO_PAD
350 rb->lcd_puts(0,6,"MODE to start");
351 #elif CONFIG_KEYPAD == IRIVER_H100_PAD
352 rb->lcd_puts(0,6,"SELECT to start");
353 #endif
355 rb->lcd_update();
358 button = rb->button_get(true);
359 switch(button){
360 case MINESWP_DOWN:
361 p = (p + 98)%100;
362 break;
364 case MINESWP_UP:
365 p = (p + 2)%100;
366 break;
368 case BUTTON_RIGHT:
369 height = height%(LCD_HEIGHT/8)+1;
370 break;
372 case BUTTON_LEFT:
373 width = width%(LCD_WIDTH/8)+1;
374 break;
376 case MINESWP_START:/* start playing */
377 i = 1;
378 break;
380 case MINESWP_QUIT:/* quit program */
381 return MINESWEEPER_QUIT;
383 default:
384 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
385 return MINESWEEPER_USB;
386 break;
388 if(i==1)
389 break;
393 /********************
394 * init *
395 ********************/
397 minesweeper_init();
399 /**********************
400 * play *
401 **********************/
403 while(true){
405 //clear the screen buffer
406 rb->lcd_clear_display();
408 //display the mine field
409 for(i=0;i<height;i++){
410 for(j=0;j<width;j++){
411 #if LCD_DEPTH > 1
412 rb->lcd_set_foreground(LCD_DARKGRAY);
413 rb->lcd_drawrect(j*8,i*8,8,8);
414 rb->lcd_set_foreground(LCD_BLACK);
415 #else
416 rb->lcd_drawrect(j*8,i*8,8,8);
417 #endif
418 if(minefield[i][j].known){
419 if(minefield[i][j].mine){
420 rb->lcd_putsxy(j*8+1,i*8+1,"b");
421 } else if(minefield[i][j].neighbors){
422 rb->lcd_set_drawmode(DRMODE_FG);
423 rb->lcd_mono_bitmap(num[minefield[i][j].neighbors],j*8,i*8,8,8);
424 rb->lcd_set_drawmode(DRMODE_SOLID);
426 } else if(minefield[i][j].flag) {
427 rb->lcd_drawline(j*8+2,i*8+2,j*8+5,i*8+5);
428 rb->lcd_drawline(j*8+2,i*8+5,j*8+5,i*8+2);
429 } else {
430 #if LCD_DEPTH > 1
431 rb->lcd_set_foreground(LCD_LIGHTGRAY);
432 rb->lcd_fillrect(j*8+1,i*8+1,6,6);
433 rb->lcd_set_foreground(LCD_BLACK);
434 #else
435 rb->lcd_fillrect(j*8+2,i*8+2,4,4);
436 #endif
441 /* display the cursor */
442 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
443 rb->lcd_fillrect(x*8,y*8,8,8);
444 rb->lcd_set_drawmode(DRMODE_SOLID);
446 /* update the screen */
447 rb->lcd_update();
449 button = rb->button_get(true);
450 switch(button){
451 /* quit minesweeper (you really shouldn't use this button ...) */
452 case MINESWP_QUIT:
453 return MINESWEEPER_QUIT;
455 /* move cursor left */
456 case BUTTON_LEFT:
457 case (BUTTON_LEFT | BUTTON_REPEAT):
458 x = (x + width - 1)%width;
459 break;
461 /* move cursor right */
462 case BUTTON_RIGHT:
463 case (BUTTON_RIGHT | BUTTON_REPEAT):
464 x = (x + 1)%width;
465 break;
467 /* move cursor down */
468 case MINESWP_DOWN:
469 case (MINESWP_DOWN | BUTTON_REPEAT):
470 y = (y + 1)%height;
471 break;
473 /* move cursor up */
474 case MINESWP_UP:
475 case (MINESWP_UP | BUTTON_REPEAT):
476 y = (y + height - 1)%height;
477 break;
479 /* discover a tile (and it's neighbors if .neighbors == 0) */
480 case MINESWP_DISCOVER:
481 #ifdef MINESWP_DISCOVER2
482 case MINESWP_DISCOVER2:
483 #endif
484 if(minefield[y][x].flag) break;
485 /* we put the mines on the first "click" so that you don't */
486 /* lose on the first "click" */
487 if(tiles_left == width*height) minesweeper_putmines(p,x,y);
488 discover(x,y);
489 if(minefield[y][x].mine){
490 return MINESWEEPER_LOSE;
492 tiles_left = 0;
493 for(i=0;i<height;i++){
494 for(j=0;j<width;j++){
495 if(minefield[i][j].known == 0) tiles_left++;
498 if(tiles_left == mine_num){
499 return MINESWEEPER_WIN;
501 break;
503 /* toggle flag under cursor */
504 case MINESWP_TOGGLE:
505 #ifdef MINESWP_TOGGLE_PRE
506 if (lastbutton != MINESWP_TOGGLE_PRE)
507 break;
508 #endif
509 #ifdef MINESWP_TOGGLE2
510 case MINESWP_TOGGLE2:
511 #endif
512 minefield[y][x].flag = (minefield[y][x].flag + 1)%2;
513 break;
515 /* show how many mines you think you have found and how many */
516 /* there really are on the game */
517 case MINESWP_INFO:
518 tiles_left = 0;
519 for(i=0;i<height;i++){
520 for(j=0;j<width;j++){
521 if(minefield[i][j].flag) tiles_left++;
524 rb->splash(HZ*2, true, "You found %d mines out of %d", tiles_left, mine_num);
525 break;
527 default:
528 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
529 return MINESWEEPER_USB;
530 break;
532 if (button != BUTTON_NONE)
533 lastbutton = button;
538 /* plugin entry point */
539 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
541 bool exit = false;
542 /* plugin init */
543 (void)parameter;
544 rb = api;
545 /* end of plugin init */
547 while(!exit) {
548 switch(minesweeper()){
549 case MINESWEEPER_WIN:
550 rb->splash(HZ*2, true, "You Win :)");
551 break;
553 case MINESWEEPER_LOSE:
554 rb->splash(HZ*2, true, "You Lost :(");
555 break;
557 case MINESWEEPER_USB:
558 return PLUGIN_USB_CONNECTED;
560 case MINESWEEPER_QUIT:
561 exit = true;
562 break;
564 default:
565 break;
569 return PLUGIN_OK;
572 #endif