Some formatting fixes, reported by Alexander Levin
[kugel-rb.git] / apps / plugins / minesweeper.c
blob1805c2251abd8524171d71b3b66afa232eaedcb7
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2004-2006 Antoine Cellerier <dionoea -at- videolan -dot- org>
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include "plugin.h"
24 #ifdef HAVE_LCD_BITMAP
26 #include "lib/playback_control.h"
28 PLUGIN_HEADER
30 /* what the minesweeper() function can return */
31 enum minesweeper_status {
32 MINESWEEPER_WIN,
33 MINESWEEPER_LOSE,
34 MINESWEEPER_QUIT,
35 MINESWEEPER_USB
38 /* variable button definitions */
39 #if CONFIG_KEYPAD == RECORDER_PAD
40 # define MINESWP_LEFT BUTTON_LEFT
41 # define MINESWP_RIGHT BUTTON_RIGHT
42 # define MINESWP_UP BUTTON_UP
43 # define MINESWP_DOWN BUTTON_DOWN
44 # define MINESWP_QUIT BUTTON_OFF
45 # define MINESWP_TOGGLE BUTTON_ON
46 # define MINESWP_TOGGLE2 BUTTON_F1
47 # define MINESWP_DISCOVER BUTTON_PLAY
48 # define MINESWP_DISCOVER2 BUTTON_F2
49 # define MINESWP_INFO BUTTON_F3
51 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
52 # define MINESWP_LEFT BUTTON_LEFT
53 # define MINESWP_RIGHT BUTTON_RIGHT
54 # define MINESWP_UP BUTTON_UP
55 # define MINESWP_DOWN BUTTON_DOWN
56 # define MINESWP_QUIT BUTTON_OFF
57 # define MINESWP_TOGGLE BUTTON_ON
58 # define MINESWP_TOGGLE2 BUTTON_F1
59 # define MINESWP_DISCOVER BUTTON_SELECT
60 # define MINESWP_DISCOVER2 BUTTON_F2
61 # define MINESWP_INFO BUTTON_F3
63 #elif CONFIG_KEYPAD == ONDIO_PAD
64 # define MINESWP_LEFT BUTTON_LEFT
65 # define MINESWP_RIGHT BUTTON_RIGHT
66 # define MINESWP_UP BUTTON_UP
67 # define MINESWP_DOWN BUTTON_DOWN
68 # define MINESWP_QUIT BUTTON_OFF
69 # define MINESWP_TOGGLE_PRE BUTTON_MENU
70 # define MINESWP_TOGGLE (BUTTON_MENU | BUTTON_REL)
71 # define MINESWP_DISCOVER (BUTTON_MENU | BUTTON_REPEAT)
72 # define MINESWP_INFO (BUTTON_MENU | BUTTON_OFF)
74 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
75 (CONFIG_KEYPAD == IRIVER_H300_PAD)
76 # define MINESWP_LEFT BUTTON_LEFT
77 # define MINESWP_RIGHT BUTTON_RIGHT
78 # define MINESWP_UP BUTTON_UP
79 # define MINESWP_DOWN BUTTON_DOWN
80 # define MINESWP_QUIT BUTTON_OFF
81 # define MINESWP_TOGGLE BUTTON_ON
82 # define MINESWP_TOGGLE2 BUTTON_REC
83 # define MINESWP_DISCOVER BUTTON_SELECT
84 # define MINESWP_INFO BUTTON_MODE
86 # define MINESWP_RC_QUIT BUTTON_RC_STOP
88 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
89 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
90 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
91 # define MINESWP_SCROLLWHEEL
92 # define MINESWP_LEFT BUTTON_LEFT
93 # define MINESWP_RIGHT BUTTON_RIGHT
94 # define MINESWP_UP BUTTON_MENU
95 # define MINESWP_DOWN BUTTON_PLAY
96 # define MINESWP_NEXT BUTTON_SCROLL_FWD
97 # define MINESWP_PREV BUTTON_SCROLL_BACK
98 # define MINESWP_QUIT (BUTTON_SELECT | BUTTON_MENU)
99 # define MINESWP_TOGGLE_PRE BUTTON_SELECT
100 # define MINESWP_TOGGLE (BUTTON_SELECT | BUTTON_REL)
101 # define MINESWP_DISCOVER (BUTTON_SELECT | BUTTON_REPEAT)
102 # define MINESWP_INFO (BUTTON_SELECT | BUTTON_PLAY)
104 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
105 # define MINESWP_LEFT BUTTON_LEFT
106 # define MINESWP_RIGHT BUTTON_RIGHT
107 # define MINESWP_UP BUTTON_UP
108 # define MINESWP_DOWN BUTTON_DOWN
109 # define MINESWP_QUIT BUTTON_POWER
110 # define MINESWP_TOGGLE BUTTON_PLAY
111 # define MINESWP_DISCOVER BUTTON_SELECT
112 # define MINESWP_INFO BUTTON_REC
114 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
115 # define MINESWP_LEFT BUTTON_LEFT
116 # define MINESWP_RIGHT BUTTON_RIGHT
117 # define MINESWP_UP BUTTON_UP
118 # define MINESWP_DOWN BUTTON_DOWN
119 # define MINESWP_QUIT BUTTON_POWER
120 # define MINESWP_TOGGLE BUTTON_A
121 # define MINESWP_DISCOVER BUTTON_SELECT
122 # define MINESWP_INFO BUTTON_MENU
124 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
126 # define MINESWP_SCROLLWHEEL
127 # define MINESWP_LEFT BUTTON_LEFT
128 # define MINESWP_RIGHT BUTTON_RIGHT
129 # define MINESWP_UP BUTTON_UP
130 # define MINESWP_DOWN BUTTON_DOWN
131 # define MINESWP_QUIT BUTTON_POWER
132 # define MINESWP_NEXT BUTTON_SCROLL_FWD
133 # define MINESWP_PREV BUTTON_SCROLL_BACK
134 # define MINESWP_TOGGLE BUTTON_REC
135 # define MINESWP_DISCOVER BUTTON_SELECT
136 # define MINESWP_INFO (BUTTON_REC|BUTTON_REPEAT)
138 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
140 # define MINESWP_LEFT BUTTON_LEFT
141 # define MINESWP_RIGHT BUTTON_RIGHT
142 # define MINESWP_UP BUTTON_UP
143 # define MINESWP_DOWN BUTTON_DOWN
144 # define MINESWP_QUIT (BUTTON_HOME|BUTTON_REPEAT)
145 # define MINESWP_TOGGLE BUTTON_SCROLL_FWD
146 # define MINESWP_DISCOVER BUTTON_SELECT
147 # define MINESWP_INFO BUTTON_SCROLL_BACK
149 #elif (CONFIG_KEYPAD == SANSA_C200_PAD) || \
150 (CONFIG_KEYPAD == SANSA_CLIP_PAD) || \
151 (CONFIG_KEYPAD == SANSA_M200_PAD)
152 # define MINESWP_LEFT BUTTON_LEFT
153 # define MINESWP_RIGHT BUTTON_RIGHT
154 # define MINESWP_UP BUTTON_UP
155 # define MINESWP_DOWN BUTTON_DOWN
156 # define MINESWP_QUIT BUTTON_POWER
157 # define MINESWP_TOGGLE_PRE BUTTON_SELECT
158 # define MINESWP_TOGGLE (BUTTON_SELECT | BUTTON_REL)
159 # define MINESWP_TOGGLE2 BUTTON_VOL_DOWN
160 # define MINESWP_DISCOVER (BUTTON_SELECT | BUTTON_REPEAT)
161 # define MINESWP_DISCOVER2 BUTTON_VOL_UP
162 # define MINESWP_INFO (BUTTON_SELECT | BUTTON_UP)
164 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
165 # define MINESWP_LEFT BUTTON_LEFT
166 # define MINESWP_RIGHT BUTTON_RIGHT
167 # define MINESWP_UP BUTTON_SCROLL_UP
168 # define MINESWP_DOWN BUTTON_SCROLL_DOWN
169 # define MINESWP_QUIT BUTTON_POWER
170 # define MINESWP_TOGGLE BUTTON_PLAY
171 # define MINESWP_DISCOVER BUTTON_REW
172 # define MINESWP_INFO (BUTTON_REW | BUTTON_PLAY)
174 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
175 # define MINESWP_LEFT BUTTON_LEFT
176 # define MINESWP_RIGHT BUTTON_RIGHT
177 # define MINESWP_UP BUTTON_UP
178 # define MINESWP_DOWN BUTTON_DOWN
179 # define MINESWP_QUIT BUTTON_BACK
180 # define MINESWP_TOGGLE BUTTON_PLAY
181 # define MINESWP_DISCOVER BUTTON_SELECT
182 # define MINESWP_INFO BUTTON_MENU
184 #elif (CONFIG_KEYPAD == MROBE100_PAD)
185 # define MINESWP_LEFT BUTTON_LEFT
186 # define MINESWP_RIGHT BUTTON_RIGHT
187 # define MINESWP_UP BUTTON_UP
188 # define MINESWP_DOWN BUTTON_DOWN
189 # define MINESWP_QUIT BUTTON_POWER
190 # define MINESWP_TOGGLE BUTTON_DISPLAY
191 # define MINESWP_DISCOVER BUTTON_SELECT
192 # define MINESWP_INFO BUTTON_MENU
194 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
195 # define MINESWP_LEFT BUTTON_RC_REW
196 # define MINESWP_RIGHT BUTTON_RC_FF
197 # define MINESWP_UP BUTTON_RC_VOL_UP
198 # define MINESWP_DOWN BUTTON_RC_VOL_DOWN
199 # define MINESWP_QUIT BUTTON_RC_REC
200 # define MINESWP_TOGGLE BUTTON_RC_MODE
201 # define MINESWP_DISCOVER BUTTON_RC_PLAY
202 # define MINESWP_INFO BUTTON_RC_MENU
204 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
205 # define MINESWP_QUIT BUTTON_POWER
207 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
208 # define MINESWP_LEFT BUTTON_LEFT
209 # define MINESWP_RIGHT BUTTON_RIGHT
210 # define MINESWP_UP BUTTON_UP
211 # define MINESWP_DOWN BUTTON_DOWN
212 # define MINESWP_QUIT BUTTON_BACK
213 # define MINESWP_TOGGLE BUTTON_SELECT
214 # define MINESWP_DISCOVER BUTTON_PLAY
215 # define MINESWP_INFO BUTTON_MENU
217 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
218 # define MINESWP_LEFT BUTTON_LEFT
219 # define MINESWP_RIGHT BUTTON_RIGHT
220 # define MINESWP_UP BUTTON_UP
221 # define MINESWP_DOWN BUTTON_DOWN
222 # define MINESWP_QUIT BUTTON_POWER
223 # define MINESWP_TOGGLE BUTTON_VIEW
224 # define MINESWP_DISCOVER BUTTON_SELECT
225 # define MINESWP_INFO BUTTON_MENU
227 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
228 # define MINESWP_LEFT BUTTON_PREV
229 # define MINESWP_RIGHT BUTTON_NEXT
230 # define MINESWP_UP BUTTON_UP
231 # define MINESWP_DOWN BUTTON_DOWN
232 # define MINESWP_QUIT BUTTON_POWER
233 # define MINESWP_TOGGLE BUTTON_RIGHT
234 # define MINESWP_DISCOVER BUTTON_PLAY
235 # define MINESWP_INFO BUTTON_MENU
237 #elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \
238 CONFIG_KEYPAD == ONDAVX777_PAD || \
239 CONFIG_KEYPAD == MROBE500_PAD
240 # define MINESWP_QUIT BUTTON_POWER
242 #elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
243 # define MINESWP_LEFT BUTTON_LEFT
244 # define MINESWP_RIGHT BUTTON_RIGHT
245 # define MINESWP_UP BUTTON_UP
246 # define MINESWP_DOWN BUTTON_DOWN
247 # define MINESWP_QUIT BUTTON_REC
248 # define MINESWP_TOGGLE BUTTON_PLAY
249 # define MINESWP_DISCOVER BUTTON_REW
250 # define MINESWP_INFO BUTTON_FFWD
252 #else
253 #error No keymap defined!
254 #endif
256 #ifdef HAVE_TOUCHSCREEN
257 #ifndef MINESWP_QUIT
258 # define MINESWP_QUIT BUTTON_TOPLEFT
259 #endif
260 #ifndef MINESWP_LEFT
261 # define MINESWP_LEFT BUTTON_MIDLEFT
262 #endif
263 #ifndef MINESWP_RIGHT
264 # define MINESWP_RIGHT BUTTON_MIDRIGHT
265 #endif
266 #ifndef MINESWP_UP
267 # define MINESWP_UP BUTTON_TOPMIDDLE
268 #endif
269 #ifndef MINESWP_DOWN
270 # define MINESWP_DOWN BUTTON_BOTTOMMIDDLE
271 #endif
272 #ifndef MINESWP_TOGGLE
273 # define MINESWP_TOGGLE BUTTON_CENTER
274 #endif
275 #ifndef MINESWP_DISCOVER
276 # define MINESWP_DISCOVER BUTTON_BOTTOMLEFT
277 #endif
278 #ifndef MINESWP_INFO
279 # define MINESWP_INFO BUTTON_BOTTOMRIGHT
280 #endif
281 #endif
283 extern const fb_data minesweeper_tiles[];
285 #ifdef HAVE_LCD_COLOR
286 # if ( LCD_HEIGHT * LCD_WIDTH ) / ( 16 * 16 ) >= 130
287 /* We want to have at least 130 tiles on the screen */
288 # define TileSize 16
289 # elif ( LCD_HEIGHT * LCD_WIDTH ) / ( 12 * 12 ) >= 130
290 # define TileSize 12
291 # else
292 # define TileSize 10
293 # endif
294 # define BackgroundColor LCD_RGBPACK( 128, 128, 128 )
295 #elif LCD_DEPTH > 1
296 # define TileSize 12
297 #else
298 # define TileSize 8
299 #endif
301 #define Mine 9
302 #define Flag 10
303 #define Unknown 11
304 #define ExplodedMine 12
305 #define WrongFlag 13
306 #define CorrectFlag 14
308 #define draw_tile( num, x, y ) \
309 rb->lcd_bitmap_part( minesweeper_tiles, 0, num * TileSize, \
310 TileSize, left+x*TileSize, top+y*TileSize, \
311 TileSize, TileSize )
313 #define invert_tile( x, y ) \
314 rb->lcd_set_drawmode(DRMODE_COMPLEMENT); \
315 rb->lcd_fillrect( left+x*TileSize, top+y*TileSize, TileSize, TileSize ); \
316 rb->lcd_set_drawmode(DRMODE_SOLID);
319 /* the tile struct
320 * if there is a mine, mine is true
321 * if tile is known by player, known is true
322 * if tile has a flag, flag is true
323 * neighbors is the total number of mines arround tile
325 typedef struct tile
327 unsigned char mine : 1;
328 unsigned char known : 1;
329 unsigned char flag : 1;
330 unsigned char neighbors : 4;
331 } tile;
333 /* the height and width of the field */
334 #define MAX_HEIGHT (LCD_HEIGHT/TileSize)
335 #define MAX_WIDTH (LCD_WIDTH/TileSize)
336 int height = MAX_HEIGHT;
337 int width = MAX_WIDTH;
338 int top;
339 int left;
341 /* The Minefield. Caution it is defined as Y, X! Not the opposite. */
342 tile minefield[MAX_HEIGHT][MAX_WIDTH];
344 /* total number of mines on the game */
345 int mine_num = 0;
347 /* percentage of mines on minefield used during generation */
348 int p = 16;
350 /* number of tiles left on the game */
351 int tiles_left;
353 /* number of used flags on the game */
354 int flags_used;
356 /* Because mines are set after the first move... */
357 bool no_mines = true;
359 /* We need a stack (created on discover()) for the cascade algorithm. */
360 int stack_pos = 0;
362 /* a usefull string for snprintf */
363 char str[30];
365 #ifdef HAVE_TOUCHSCREEN
367 #include "lib/pluginlib_touchscreen.h"
368 static struct ts_raster mine_raster = { 0, 0, MAX_WIDTH, MAX_HEIGHT, TileSize, TileSize };
369 #endif
372 void push( int *stack, int y, int x )
374 if( stack_pos <= height*width )
376 stack[++stack_pos] = y;
377 stack[++stack_pos] = x;
381 /* Unveil tiles and push them to stack if they are empty. */
382 void unveil( int *stack, int y, int x )
384 if( x < 0 || y < 0 || x > width - 1 || y > height - 1
385 || minefield[y][x].known
386 || minefield[y][x].mine || minefield[y][x].flag ) return;
388 minefield[y][x].known = 1;
390 if( minefield[y][x].neighbors == 0 )
391 push( stack, y, x );
394 int is_flagged( int y, int x )
396 if( x >= 0 && y >= 0 && x < width && y < height && minefield[y][x].flag )
397 return 1;
398 return 0;
401 int neighbors_flagged( int y, int x )
403 return is_flagged( y-1, x-1 ) +
404 is_flagged( y-1, x ) +
405 is_flagged( y-1, x+1 ) +
406 is_flagged( y, x-1 ) +
407 is_flagged( y, x ) +
408 is_flagged( y, x+1 ) +
409 is_flagged( y+1, x-1 ) +
410 is_flagged( y+1, x ) +
411 is_flagged( y+1, x+1 );
414 bool discover( int y, int x, bool explore_neighbors )
416 /* Selected tile. */
417 if( x < 0 || y < 0 || x > width - 1 || y > height - 1
418 || minefield[y][x].known
419 || minefield[y][x].mine || minefield[y][x].flag )
421 if( !minefield[y][x].flag && minefield[y][x].mine )
422 return true;
424 if( explore_neighbors && minefield[y][x].known &&
425 minefield[y][x].neighbors == neighbors_flagged( y, x ) )
427 return discover( y-1, x-1, false ) ||
428 discover( y-1, x, false ) ||
429 discover( y-1, x+1, false ) ||
430 discover( y, x-1, false ) ||
431 discover( y, x, false ) ||
432 discover( y, x+1, false ) ||
433 discover( y+1, x-1, false ) ||
434 discover( y+1, x, false ) ||
435 discover( y+1, x+1, false );
438 return false;
441 minefield[y][x].known = 1;
442 /* Exit if the tile is not empty. (no mines nearby) */
443 if( minefield[y][x].neighbors ) return false;
445 int stack[height*width];
447 push( stack, y, x );
449 /* Scan all nearby tiles. If we meet a tile with a number we just unveil
450 * it. If we meet an empty tile, we push the location in stack. For each
451 * location in stack we do the same thing. (scan again all nearby tiles)
453 while( stack_pos )
455 /* Pop x, y from stack. */
456 x = stack[stack_pos--];
457 y = stack[stack_pos--];
459 unveil( stack, y-1, x-1 );
460 unveil( stack, y-1, x );
461 unveil( stack, y-1, x+1 );
462 unveil( stack, y, x+1 );
463 unveil( stack, y+1, x+1 );
464 unveil( stack, y+1, x );
465 unveil( stack, y+1, x-1 );
466 unveil( stack, y, x-1 );
469 return false;
472 /* Reset the whole board for a new game. */
473 void minesweeper_init( void )
475 int i,j;
477 for( i = 0; i < MAX_HEIGHT; i++ )
479 for( j = 0; j < MAX_WIDTH; j++ )
481 minefield[i][j].known = 0;
482 minefield[i][j].flag = 0;
483 minefield[i][j].mine = 0;
484 minefield[i][j].neighbors = 0;
487 no_mines = true;
488 tiles_left = width*height;
492 /* put mines on the mine field */
493 /* there is p% chance that a tile is a mine */
494 /* if the tile has coordinates (x,y), then it can't be a mine */
495 void minesweeper_putmines( int p, int x, int y )
497 int i,j;
499 mine_num = 0;
500 for( i = 0; i < height; i++ )
502 for( j = 0; j < width; j++ )
504 if( rb->rand()%100 < p && !( y==i && x==j ) )
506 minefield[i][j].mine = 1;
507 mine_num++;
509 else
511 minefield[i][j].mine = 0;
513 minefield[i][j].neighbors = 0;
517 /* we need to compute the neighbor element for each tile */
518 for( i = 0; i < height; i++ )
520 for( j = 0; j < width; j++ )
522 if( i > 0 )
524 if( j > 0 )
525 minefield[i][j].neighbors += minefield[i-1][j-1].mine;
526 minefield[i][j].neighbors += minefield[i-1][j].mine;
527 if( j < width - 1 )
528 minefield[i][j].neighbors += minefield[i-1][j+1].mine;
530 if( j > 0 )
531 minefield[i][j].neighbors += minefield[i][j-1].mine;
532 if( j < width - 1 )
533 minefield[i][j].neighbors += minefield[i][j+1].mine;
534 if( i < height - 1 )
536 if( j > 0 )
537 minefield[i][j].neighbors += minefield[i+1][j-1].mine;
538 minefield[i][j].neighbors += minefield[i+1][j].mine;
539 if( j < width - 1 )
540 minefield[i][j].neighbors += minefield[i+1][j+1].mine;
545 no_mines = false;
547 /* In case the user is lucky and there are no mines positioned. */
548 if( !mine_num && height*width != 1 )
550 minesweeper_putmines(p, x, y);
554 /* A function that will uncover all the board, when the user wins or loses.
555 can easily be expanded, (just a call assigned to a button) as a solver. */
556 void mine_show( void )
558 int i, j, button;
560 for( i = 0; i < height; i++ )
562 for( j = 0; j < width; j++ )
564 if( minefield[i][j].mine )
566 if( minefield[i][j].known )
567 draw_tile( ExplodedMine, j, i );
568 else if( minefield[i][j].flag )
569 draw_tile( CorrectFlag, j, i );
570 else
571 draw_tile( Mine, j, i );
573 else
575 if( minefield[i][j].flag )
576 draw_tile( WrongFlag, j, i );
577 else
578 draw_tile( minefield[i][j].neighbors, j, i );
582 rb->lcd_update();
585 button = rb->button_get(true);
586 while( ( button == BUTTON_NONE )
587 || ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
588 #ifdef HAVE_TOUCHSCREEN
589 button = BUTTON_NONE;
590 #endif
593 int count_tiles_left( void )
595 int tiles_left = 0;
596 int i, j;
597 for( i = 0; i < height; i++ )
598 for( j = 0; j < width; j++ )
599 if( minefield[i][j].known == 0 )
600 tiles_left++;
601 return tiles_left;
604 int count_flags( void )
606 int flags_used = 0;
607 int i, j;
608 for( i = 0; i < height; i++ )
609 for( j = 0; j < width; j++ )
610 if( minefield[i][j].flag == 1 )
611 flags_used++;
612 return flags_used;
615 /* welcome screen where player can chose mine percentage */
616 enum minesweeper_status menu( void )
618 int selection = 0, result = MINESWEEPER_QUIT;
619 bool menu_quit = false;
621 MENUITEM_STRINGLIST( menu, "Minesweeper Menu", NULL, "Play Minesweeper",
622 "Mine Percentage", "Number of Rows",
623 "Number of Columns", "Playback Control", "Quit" );
625 #ifdef HAVE_LCD_COLOR
626 rb->lcd_set_foreground( rb->global_settings->fg_color );
627 rb->lcd_set_background( rb->global_settings->bg_color );
628 #endif
630 while( !menu_quit )
632 switch( rb->do_menu( &menu, &selection, NULL, false ) )
634 case 0:
635 result = MINESWEEPER_WIN; /* start playing */
636 menu_quit = true;
637 break;
639 case 1:
640 rb->set_int( "Mine Percentage", "%", UNIT_INT, &p, NULL,
641 1, 2, 98, NULL );
642 break;
644 case 2:
645 rb->set_int( "Number of Rows", "", UNIT_INT, &height, NULL,
646 1, 1, MAX_HEIGHT, NULL );
647 break;
649 case 3:
650 rb->set_int( "Number of Columns", "", UNIT_INT, &width, NULL,
651 1, 1, MAX_WIDTH, NULL );
652 break;
654 case 4:
655 playback_control( NULL );
656 break;
658 default:
659 result = MINESWEEPER_QUIT; /* quit program */
660 menu_quit = true;
661 break;
665 return result;
668 /* the big and ugly game function */
669 enum minesweeper_status minesweeper( void )
671 int i, j;
672 int button;
673 int lastbutton = BUTTON_NONE;
675 /* the cursor coordinates */
676 int x=0, y=0;
679 * Show the menu
681 if( ( i = menu() ) != MINESWEEPER_WIN ) return i;
684 * Init game
686 top = (LCD_HEIGHT-height*TileSize)/2;
687 left = (LCD_WIDTH-width*TileSize)/2;
689 #ifdef HAVE_TOUCHSCREEN
690 mine_raster.tl_x = left;
691 mine_raster.tl_y = top;
692 mine_raster.width = width*TileSize;
693 mine_raster.height = height*TileSize;
694 #endif
696 rb->srand( *rb->current_tick );
697 minesweeper_init();
698 x = 0;
699 y = 0;
702 * Play
704 while( true )
707 /* clear the screen buffer */
708 #ifdef HAVE_LCD_COLOR
709 rb->lcd_set_background( BackgroundColor );
710 #endif
711 rb->lcd_clear_display();
713 /* display the mine field */
714 for( i = 0; i < height; i++ )
716 for( j = 0; j < width; j++ )
718 if( minefield[i][j].known )
720 draw_tile( minefield[i][j].neighbors, j, i );
722 else if(minefield[i][j].flag)
724 draw_tile( Flag, j, i );
726 else
728 draw_tile( Unknown, j, i );
733 /* display the cursor */
734 invert_tile( x, y );
736 /* update the screen */
737 rb->lcd_update();
739 button = rb->button_get(true);
740 #ifdef HAVE_TOUCHSCREEN
741 if(button & BUTTON_TOUCHSCREEN)
743 struct ts_raster_result res;
744 if(touchscreen_map_raster(&mine_raster, rb->button_get_data() >> 16, rb->button_get_data() & 0xffff, &res) == 1)
746 button &= ~BUTTON_TOUCHSCREEN;
747 lastbutton &= ~BUTTON_TOUCHSCREEN;
749 if(button & BUTTON_REPEAT && lastbutton != MINESWP_TOGGLE && lastbutton ^ BUTTON_REPEAT)
750 button = MINESWP_TOGGLE;
751 else if(button == BUTTON_REL && lastbutton ^ BUTTON_REPEAT)
752 button = MINESWP_DISCOVER;
753 else
754 button |= BUTTON_TOUCHSCREEN;
756 x = res.x;
757 y = res.y;
760 #endif
761 switch(button)
763 /* quit minesweeper (you really shouldn't use this button ...) */
764 #ifdef MINESWP_RC_QUIT
765 case MINESWP_RC_QUIT:
766 #endif
767 case MINESWP_QUIT:
768 return MINESWEEPER_QUIT;
770 /* move cursor left */
771 case MINESWP_LEFT:
772 case MINESWP_LEFT|BUTTON_REPEAT:
773 x = ( x + width - 1 )%width;
774 break;
776 /* move cursor right */
777 case MINESWP_RIGHT:
778 case MINESWP_RIGHT|BUTTON_REPEAT:
779 x = ( x + 1 )%width;
780 break;
782 /* move cursor down */
783 case MINESWP_DOWN:
784 case MINESWP_DOWN|BUTTON_REPEAT:
785 y = ( y + 1 )%height;
786 break;
788 /* move cursor up */
789 case MINESWP_UP:
790 case MINESWP_UP|BUTTON_REPEAT:
791 y = ( y + height - 1 )%height;
792 break;
794 /*move cursor though the entire field*/
795 #ifdef MINESWP_SCROLLWHEEL
796 case MINESWP_NEXT:
797 case MINESWP_NEXT|BUTTON_REPEAT:
798 if (x == width -1 ) {
799 y = ( y + 1 )%height;
801 x = ( x + 1 )%width;
802 break;
804 case MINESWP_PREV:
805 case MINESWP_PREV|BUTTON_REPEAT:
806 if (x == 0) {
807 y = ( y + height - 1 )%height;
809 x = ( x + width - 1 )%width;
810 break;
811 #endif
812 /* discover a tile (and it's neighbors if .neighbors == 0) */
813 case MINESWP_DISCOVER:
814 #ifdef MINESWP_DISCOVER2
815 case MINESWP_DISCOVER2:
816 #endif
817 if( minefield[y][x].flag ) break;
818 /* we put the mines on the first "click" so that you don't
819 * lose on the first "click" */
820 if( tiles_left == width*height && no_mines )
821 minesweeper_putmines(p,x,y);
823 if( discover( y, x, true ) )
825 minefield[y][x].known = 1;
826 return MINESWEEPER_LOSE;
829 tiles_left = count_tiles_left();
830 if( tiles_left == mine_num )
832 return MINESWEEPER_WIN;
834 break;
836 /* toggle flag under cursor */
837 case MINESWP_TOGGLE:
838 #ifdef MINESWP_TOGGLE_PRE
839 if( lastbutton != MINESWP_TOGGLE_PRE )
840 break;
841 #endif
842 #ifdef MINESWP_TOGGLE2
843 case MINESWP_TOGGLE2:
844 #endif
845 minefield[y][x].flag = ( minefield[y][x].flag + 1 )%2;
846 break;
848 /* show how many mines you think you have found and how many
849 * there really are on the game */
850 case MINESWP_INFO:
851 if( no_mines )
852 break;
853 flags_used = count_flags();
854 if (flags_used == 1) {
855 rb->splashf( HZ*2, "You marked 1 field. There are %d mines.",
856 mine_num );
858 else
860 rb->splashf( HZ*2, "You marked %d fields. There are %d mines.",
861 flags_used, mine_num );
863 break;
865 default:
866 if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
867 return MINESWEEPER_USB;
868 break;
870 if( button != BUTTON_NONE )
871 lastbutton = button;
876 /* plugin entry point */
877 enum plugin_status plugin_start(const void* parameter)
879 bool exit = false;
881 (void)parameter;
882 #if LCD_DEPTH > 1
883 rb->lcd_set_backdrop(NULL);
884 #endif
886 while( !exit )
888 switch( minesweeper() )
890 case MINESWEEPER_WIN:
891 rb->splash( HZ, "You Win!" );
892 rb->lcd_clear_display();
893 mine_show();
894 break;
896 case MINESWEEPER_LOSE:
897 rb->splash( HZ, "You Lose!" );
898 rb->lcd_clear_display();
899 mine_show();
900 break;
902 case MINESWEEPER_USB:
903 return PLUGIN_USB_CONNECTED;
905 case MINESWEEPER_QUIT:
906 exit = true;
907 break;
909 default:
910 break;
914 return PLUGIN_OK;
917 #endif