Remove plugin-specific defines from the pdbox makefile, and add them to m_pd.h, which...
[kugel-rb.git] / apps / plugins / minesweeper.c
blob58e56de159c15d4aa358323997d47175d357978f
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 #elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
253 # define MINESWP_LEFT BUTTON_PREV
254 # define MINESWP_RIGHT BUTTON_NEXT
255 # define MINESWP_UP BUTTON_UP
256 # define MINESWP_DOWN BUTTON_DOWN
257 # define MINESWP_QUIT BUTTON_REC
258 # define MINESWP_TOGGLE BUTTON_PLAY
259 # define MINESWP_DISCOVER BUTTON_OK
260 # define MINESWP_INFO BUTTON_MENU
262 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
263 # define MINESWP_LEFT BUTTON_VOL_DOWN
264 # define MINESWP_RIGHT BUTTON_VOL_UP
265 # define MINESWP_UP BUTTON_PREV
266 # define MINESWP_DOWN BUTTON_NEXT
267 # define MINESWP_QUIT (BUTTON_REC|BUTTON_PLAY)
268 # define MINESWP_TOGGLE BUTTON_PLAY
269 # define MINESWP_DISCOVER BUTTON_SELECT
270 # define MINESWP_INFO BUTTON_REC
272 #else
273 #error No keymap defined!
274 #endif
276 #ifdef HAVE_TOUCHSCREEN
277 #ifndef MINESWP_QUIT
278 # define MINESWP_QUIT BUTTON_TOPLEFT
279 #endif
280 #ifndef MINESWP_LEFT
281 # define MINESWP_LEFT BUTTON_MIDLEFT
282 #endif
283 #ifndef MINESWP_RIGHT
284 # define MINESWP_RIGHT BUTTON_MIDRIGHT
285 #endif
286 #ifndef MINESWP_UP
287 # define MINESWP_UP BUTTON_TOPMIDDLE
288 #endif
289 #ifndef MINESWP_DOWN
290 # define MINESWP_DOWN BUTTON_BOTTOMMIDDLE
291 #endif
292 #ifndef MINESWP_TOGGLE
293 # define MINESWP_TOGGLE BUTTON_CENTER
294 #endif
295 #ifndef MINESWP_DISCOVER
296 # define MINESWP_DISCOVER BUTTON_BOTTOMLEFT
297 #endif
298 #ifndef MINESWP_INFO
299 # define MINESWP_INFO BUTTON_BOTTOMRIGHT
300 #endif
301 #endif
303 extern const fb_data minesweeper_tiles[];
305 #ifdef HAVE_LCD_COLOR
306 # if ( LCD_HEIGHT * LCD_WIDTH ) / ( 16 * 16 ) >= 130
307 /* We want to have at least 130 tiles on the screen */
308 # define TileSize 16
309 # elif ( LCD_HEIGHT * LCD_WIDTH ) / ( 12 * 12 ) >= 130
310 # define TileSize 12
311 # else
312 # define TileSize 10
313 # endif
314 # define BackgroundColor LCD_RGBPACK( 128, 128, 128 )
315 #elif LCD_DEPTH > 1
316 # define TileSize 12
317 #else
318 # define TileSize 8
319 #endif
321 #define Mine 9
322 #define Flag 10
323 #define Unknown 11
324 #define ExplodedMine 12
325 #define WrongFlag 13
326 #define CorrectFlag 14
328 #define draw_tile( num, x, y ) \
329 rb->lcd_bitmap_part( minesweeper_tiles, 0, num * TileSize, \
330 TileSize, left+x*TileSize, top+y*TileSize, \
331 TileSize, TileSize )
333 #define invert_tile( x, y ) \
334 rb->lcd_set_drawmode(DRMODE_COMPLEMENT); \
335 rb->lcd_fillrect( left+x*TileSize, top+y*TileSize, TileSize, TileSize ); \
336 rb->lcd_set_drawmode(DRMODE_SOLID);
339 /* the tile struct
340 * if there is a mine, mine is true
341 * if tile is known by player, known is true
342 * if tile has a flag, flag is true
343 * neighbors is the total number of mines arround tile
345 typedef struct tile
347 unsigned char mine : 1;
348 unsigned char known : 1;
349 unsigned char flag : 1;
350 unsigned char neighbors : 4;
351 } tile;
353 /* the height and width of the field */
354 #define MAX_HEIGHT (LCD_HEIGHT/TileSize)
355 #define MAX_WIDTH (LCD_WIDTH/TileSize)
356 int height = MAX_HEIGHT;
357 int width = MAX_WIDTH;
358 int top;
359 int left;
361 /* The Minefield. Caution it is defined as Y, X! Not the opposite. */
362 tile minefield[MAX_HEIGHT][MAX_WIDTH];
364 /* total number of mines on the game */
365 int mine_num = 0;
367 /* percentage of mines on minefield used during generation */
368 int percent = 16;
370 /* number of tiles left on the game */
371 int tiles_left;
373 /* Because mines are set after the first move... */
374 bool no_mines = true;
376 /* We need a stack (created on discover()) for the cascade algorithm. */
377 int stack_pos = 0;
379 #ifdef HAVE_TOUCHSCREEN
381 #include "lib/pluginlib_touchscreen.h"
382 static struct ts_raster mine_raster = { 0, 0, MAX_WIDTH, MAX_HEIGHT, TileSize, TileSize };
383 #endif
386 void push( int *stack, int y, int x )
388 if( stack_pos <= height*width )
390 stack[++stack_pos] = y;
391 stack[++stack_pos] = x;
395 /* Unveil tiles and push them to stack if they are empty. */
396 void unveil( int *stack, int y, int x )
398 if( x < 0 || y < 0 || x > width - 1 || y > height - 1
399 || minefield[y][x].known
400 || minefield[y][x].mine || minefield[y][x].flag ) return;
402 minefield[y][x].known = 1;
404 if( minefield[y][x].neighbors == 0 )
405 push( stack, y, x );
408 int is_flagged( int y, int x )
410 if( x >= 0 && y >= 0 && x < width && y < height && minefield[y][x].flag )
411 return 1;
412 return 0;
415 int neighbors_flagged( int y, int x )
417 return is_flagged( y-1, x-1 ) +
418 is_flagged( y-1, x ) +
419 is_flagged( y-1, x+1 ) +
420 is_flagged( y, x-1 ) +
421 is_flagged( y, x ) +
422 is_flagged( y, x+1 ) +
423 is_flagged( y+1, x-1 ) +
424 is_flagged( y+1, x ) +
425 is_flagged( y+1, x+1 );
428 bool discover( int y, int x, bool explore_neighbors )
430 /* Selected tile. */
431 if( x < 0 || y < 0 || x > width - 1 || y > height - 1)
432 return false;
434 if( minefield[y][x].known || minefield[y][x].mine || minefield[y][x].flag )
436 if( !minefield[y][x].flag && minefield[y][x].mine )
437 return true;
439 if( explore_neighbors && minefield[y][x].known &&
440 minefield[y][x].neighbors == neighbors_flagged( y, x ) )
442 return discover( y-1, x-1, false ) ||
443 discover( y-1, x, false ) ||
444 discover( y-1, x+1, false ) ||
445 discover( y, x-1, false ) ||
446 discover( y, x, false ) ||
447 discover( y, x+1, false ) ||
448 discover( y+1, x-1, false ) ||
449 discover( y+1, x, false ) ||
450 discover( y+1, x+1, false );
453 return false;
456 minefield[y][x].known = 1;
457 /* Exit if the tile is not empty. (no mines nearby) */
458 if( minefield[y][x].neighbors ) return false;
460 int stack[height*width];
462 push( stack, y, x );
464 /* Scan all nearby tiles. If we meet a tile with a number we just unveil
465 * it. If we meet an empty tile, we push the location in stack. For each
466 * location in stack we do the same thing. (scan again all nearby tiles)
468 while( stack_pos )
470 /* Pop x, y from stack. */
471 x = stack[stack_pos--];
472 y = stack[stack_pos--];
474 unveil( stack, y-1, x-1 );
475 unveil( stack, y-1, x );
476 unveil( stack, y-1, x+1 );
477 unveil( stack, y, x+1 );
478 unveil( stack, y+1, x+1 );
479 unveil( stack, y+1, x );
480 unveil( stack, y+1, x-1 );
481 unveil( stack, y, x-1 );
484 return false;
487 /* Reset the whole board for a new game. */
488 void minesweeper_init( void )
490 rb->memset(minefield, 0, sizeof(minefield));
491 no_mines = true;
492 tiles_left = width*height;
496 /* put mines on the mine field */
497 /* there is p% chance that a tile is a mine */
498 /* if the tile has coordinates (x,y), then it can't be a mine */
499 void minesweeper_putmines( int p, int x, int y )
501 int i,j;
503 mine_num = 0;
504 for( i = 0; i < height; i++ )
506 for( j = 0; j < width; j++ )
508 if( rb->rand()%100 < p && !( y==i && x==j ) )
510 minefield[i][j].mine = 1;
511 mine_num++;
513 else
515 minefield[i][j].mine = 0;
517 minefield[i][j].neighbors = 0;
521 /* we need to compute the neighbor element for each tile */
522 for( i = 0; i < height; i++ )
524 for( j = 0; j < width; j++ )
526 if( i > 0 )
528 if( j > 0 )
529 minefield[i][j].neighbors += minefield[i-1][j-1].mine;
530 minefield[i][j].neighbors += minefield[i-1][j].mine;
531 if( j < width - 1 )
532 minefield[i][j].neighbors += minefield[i-1][j+1].mine;
534 if( j > 0 )
535 minefield[i][j].neighbors += minefield[i][j-1].mine;
536 if( j < width - 1 )
537 minefield[i][j].neighbors += minefield[i][j+1].mine;
538 if( i < height - 1 )
540 if( j > 0 )
541 minefield[i][j].neighbors += minefield[i+1][j-1].mine;
542 minefield[i][j].neighbors += minefield[i+1][j].mine;
543 if( j < width - 1 )
544 minefield[i][j].neighbors += minefield[i+1][j+1].mine;
549 no_mines = false;
551 /* In case the user is lucky and there are no mines positioned. */
552 if( !mine_num && height*width != 1 )
554 minesweeper_putmines(p, x, y);
558 /* A function that will uncover all the board, when the user wins or loses.
559 can easily be expanded, (just a call assigned to a button) as a solver. */
560 void mine_show( void )
562 int i, j, button;
564 for( i = 0; i < height; i++ )
566 for( j = 0; j < width; j++ )
568 if( minefield[i][j].mine )
570 if( minefield[i][j].known )
571 draw_tile( ExplodedMine, j, i );
572 else if( minefield[i][j].flag )
573 draw_tile( CorrectFlag, j, i );
574 else
575 draw_tile( Mine, j, i );
577 else
579 if( minefield[i][j].flag )
580 draw_tile( WrongFlag, j, i );
581 else
582 draw_tile( minefield[i][j].neighbors, j, i );
586 rb->lcd_update();
589 button = rb->button_get(true);
590 while( ( button == BUTTON_NONE )
591 || ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
592 #ifdef HAVE_TOUCHSCREEN
593 button = BUTTON_NONE;
594 #endif
597 int count_tiles_left( void )
599 int tiles = 0;
600 int i, j;
601 for( i = 0; i < height; i++ )
602 for( j = 0; j < width; j++ )
603 if( minefield[i][j].known == 0 )
604 tiles++;
605 return tiles;
608 int count_flags( void )
610 int flags = 0;
611 int i, j;
612 for( i = 0; i < height; i++ )
613 for( j = 0; j < width; j++ )
614 if( minefield[i][j].flag == 1 )
615 flags++;
616 return flags;
619 /* welcome screen where player can chose mine percentage */
620 enum minesweeper_status menu( void )
622 int selection = 0, result = MINESWEEPER_QUIT;
623 bool menu_quit = false;
625 MENUITEM_STRINGLIST( menu, "Minesweeper Menu", NULL, "Play Minesweeper",
626 "Mine Percentage", "Number of Rows",
627 "Number of Columns", "Playback Control", "Quit" );
629 #ifdef HAVE_LCD_COLOR
630 rb->lcd_set_foreground( rb->global_settings->fg_color );
631 rb->lcd_set_background( rb->global_settings->bg_color );
632 #endif
634 while( !menu_quit )
636 switch( rb->do_menu( &menu, &selection, NULL, false ) )
638 case 0:
639 result = MINESWEEPER_WIN; /* start playing */
640 menu_quit = true;
641 break;
643 case 1:
644 rb->set_int( "Mine Percentage", "%", UNIT_INT, &percent, NULL,
645 1, 2, 98, NULL );
646 break;
648 case 2:
649 rb->set_int( "Number of Rows", "", UNIT_INT, &height, NULL,
650 1, 1, MAX_HEIGHT, NULL );
651 break;
653 case 3:
654 rb->set_int( "Number of Columns", "", UNIT_INT, &width, NULL,
655 1, 1, MAX_WIDTH, NULL );
656 break;
658 case 4:
659 playback_control( NULL );
660 break;
662 default:
663 result = MINESWEEPER_QUIT; /* quit program */
664 menu_quit = true;
665 break;
669 return result;
672 /* the big and ugly game function */
673 enum minesweeper_status minesweeper( void )
675 int i, j;
676 int button;
677 int lastbutton = BUTTON_NONE;
679 /* the cursor coordinates */
680 int x=0, y=0;
683 * Show the menu
685 if( ( i = menu() ) != MINESWEEPER_WIN ) return i;
688 * Init game
690 top = (LCD_HEIGHT-height*TileSize)/2;
691 left = (LCD_WIDTH-width*TileSize)/2;
693 #ifdef HAVE_TOUCHSCREEN
694 mine_raster.tl_x = left;
695 mine_raster.tl_y = top;
696 mine_raster.width = width*TileSize;
697 mine_raster.height = height*TileSize;
698 #endif
700 rb->srand( *rb->current_tick );
701 minesweeper_init();
702 x = 0;
703 y = 0;
706 * Play
708 while( true )
711 /* clear the screen buffer */
712 #ifdef HAVE_LCD_COLOR
713 rb->lcd_set_background( BackgroundColor );
714 #endif
715 rb->lcd_clear_display();
717 /* display the mine field */
718 for( i = 0; i < height; i++ )
720 for( j = 0; j < width; j++ )
722 if( minefield[i][j].known )
724 draw_tile( minefield[i][j].neighbors, j, i );
726 else if(minefield[i][j].flag)
728 draw_tile( Flag, j, i );
730 else
732 draw_tile( Unknown, j, i );
737 /* display the cursor */
738 invert_tile( x, y );
740 /* update the screen */
741 rb->lcd_update();
743 button = rb->button_get(true);
744 #ifdef HAVE_TOUCHSCREEN
745 if(button & BUTTON_TOUCHSCREEN)
747 struct ts_raster_result res;
748 if(touchscreen_map_raster(&mine_raster, rb->button_get_data() >> 16, rb->button_get_data() & 0xffff, &res) == 1)
750 button &= ~BUTTON_TOUCHSCREEN;
751 lastbutton &= ~BUTTON_TOUCHSCREEN;
753 if(button & BUTTON_REPEAT && lastbutton != MINESWP_TOGGLE && lastbutton ^ BUTTON_REPEAT)
754 button = MINESWP_TOGGLE;
755 else if(button == BUTTON_REL && lastbutton ^ BUTTON_REPEAT)
756 button = MINESWP_DISCOVER;
757 else
758 button |= BUTTON_TOUCHSCREEN;
760 x = res.x;
761 y = res.y;
764 #endif
765 switch(button)
767 /* quit minesweeper (you really shouldn't use this button ...) */
768 #ifdef MINESWP_RC_QUIT
769 case MINESWP_RC_QUIT:
770 #endif
771 case MINESWP_QUIT:
772 return MINESWEEPER_QUIT;
774 /* move cursor left */
775 case MINESWP_LEFT:
776 case MINESWP_LEFT|BUTTON_REPEAT:
777 if( --x < 0)
778 x += width;
779 break;
781 /* move cursor right */
782 case MINESWP_RIGHT:
783 case MINESWP_RIGHT|BUTTON_REPEAT:
784 if( ++x >= width )
785 x -= width;
786 break;
788 /* move cursor down */
789 case MINESWP_DOWN:
790 case MINESWP_DOWN|BUTTON_REPEAT:
791 if( ++y >= height )
792 y -= height;
793 break;
795 /* move cursor up */
796 case MINESWP_UP:
797 case MINESWP_UP|BUTTON_REPEAT:
798 if( --y < 0 )
799 y += height;
800 break;
802 /*move cursor though the entire field*/
803 #ifdef MINESWP_SCROLLWHEEL
804 case MINESWP_NEXT:
805 case MINESWP_NEXT|BUTTON_REPEAT:
806 if (x == width -1 ) {
807 if( ++y >= height )
808 y -= height;
810 if( ++x >= width )
811 x -= width;
812 break;
814 case MINESWP_PREV:
815 case MINESWP_PREV|BUTTON_REPEAT:
816 if (x == 0) {
817 if( --y < 0 )
818 y += height;
820 if( --x < 0 )
821 x += width;
822 break;
823 #endif
824 /* discover a tile (and it's neighbors if .neighbors == 0) */
825 case MINESWP_DISCOVER:
826 #ifdef MINESWP_DISCOVER2
827 case MINESWP_DISCOVER2:
828 #endif
829 if( minefield[y][x].flag ) break;
830 /* we put the mines on the first "click" so that you don't
831 * lose on the first "click" */
832 if( tiles_left == width*height && no_mines )
833 minesweeper_putmines(percent,x,y);
835 if( discover( y, x, true ) )
837 minefield[y][x].known = 1;
838 return MINESWEEPER_LOSE;
841 tiles_left = count_tiles_left();
842 if( tiles_left == mine_num )
844 return MINESWEEPER_WIN;
846 break;
848 /* toggle flag under cursor */
849 case MINESWP_TOGGLE:
850 #ifdef MINESWP_TOGGLE_PRE
851 if( lastbutton != MINESWP_TOGGLE_PRE )
852 break;
853 #endif
854 #ifdef MINESWP_TOGGLE2
855 case MINESWP_TOGGLE2:
856 #endif
857 if( !minefield[y][x].known )
858 minefield[y][x].flag = !minefield[y][x].flag;
859 break;
861 /* show how many mines you think you have found and how many
862 * there really are on the game */
863 case MINESWP_INFO:
864 if( no_mines )
865 break;
866 int flags_used = count_flags();
867 if (flags_used == 1) {
868 rb->splashf( HZ*2, "You marked 1 field. There are %d mines.",
869 mine_num );
871 else
873 rb->splashf( HZ*2, "You marked %d fields. There are %d mines.",
874 flags_used, mine_num );
876 break;
878 default:
879 if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
880 return MINESWEEPER_USB;
881 break;
883 if( button != BUTTON_NONE )
884 lastbutton = button;
889 /* plugin entry point */
890 enum plugin_status plugin_start(const void* parameter)
892 bool exit = false;
894 (void)parameter;
895 #if LCD_DEPTH > 1
896 rb->lcd_set_backdrop(NULL);
897 #endif
899 while( !exit )
901 switch( minesweeper() )
903 case MINESWEEPER_WIN:
904 rb->splash( HZ, "You Win!" );
905 rb->lcd_clear_display();
906 mine_show();
907 break;
909 case MINESWEEPER_LOSE:
910 rb->splash( HZ, "You Lose!" );
911 rb->lcd_clear_display();
912 mine_show();
913 break;
915 case MINESWEEPER_USB:
916 return PLUGIN_USB_CONNECTED;
918 case MINESWEEPER_QUIT:
919 exit = true;
920 break;
922 default:
923 break;
927 return PLUGIN_OK;
930 #endif