Prepare new maemo release
[maemo-rb.git] / apps / plugins / minesweeper.c
blob542b9c8cba5331ab27bf328c33e190cd97594359
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 #include "lib/playback_control.h"
28 /* what the minesweeper() function can return */
29 enum minesweeper_status {
30 MINESWEEPER_WIN,
31 MINESWEEPER_LOSE,
32 MINESWEEPER_QUIT,
33 MINESWEEPER_USB
36 /* variable button definitions */
37 #if CONFIG_KEYPAD == RECORDER_PAD
38 # define MINESWP_LEFT BUTTON_LEFT
39 # define MINESWP_RIGHT BUTTON_RIGHT
40 # define MINESWP_UP BUTTON_UP
41 # define MINESWP_DOWN BUTTON_DOWN
42 # define MINESWP_QUIT BUTTON_OFF
43 # define MINESWP_TOGGLE BUTTON_ON
44 # define MINESWP_TOGGLE2 BUTTON_F1
45 # define MINESWP_DISCOVER BUTTON_PLAY
46 # define MINESWP_DISCOVER2 BUTTON_F2
47 # define MINESWP_INFO BUTTON_F3
49 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
50 # define MINESWP_LEFT BUTTON_LEFT
51 # define MINESWP_RIGHT BUTTON_RIGHT
52 # define MINESWP_UP BUTTON_UP
53 # define MINESWP_DOWN BUTTON_DOWN
54 # define MINESWP_QUIT BUTTON_OFF
55 # define MINESWP_TOGGLE BUTTON_ON
56 # define MINESWP_TOGGLE2 BUTTON_F1
57 # define MINESWP_DISCOVER BUTTON_SELECT
58 # define MINESWP_DISCOVER2 BUTTON_F2
59 # define MINESWP_INFO BUTTON_F3
61 #elif CONFIG_KEYPAD == ONDIO_PAD
62 # define MINESWP_LEFT BUTTON_LEFT
63 # define MINESWP_RIGHT BUTTON_RIGHT
64 # define MINESWP_UP BUTTON_UP
65 # define MINESWP_DOWN BUTTON_DOWN
66 # define MINESWP_QUIT BUTTON_OFF
67 # define MINESWP_TOGGLE_PRE BUTTON_MENU
68 # define MINESWP_TOGGLE (BUTTON_MENU | BUTTON_REL)
69 # define MINESWP_DISCOVER (BUTTON_MENU | BUTTON_REPEAT)
70 # define MINESWP_INFO (BUTTON_MENU | BUTTON_OFF)
72 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
73 (CONFIG_KEYPAD == IRIVER_H300_PAD)
74 # define MINESWP_LEFT BUTTON_LEFT
75 # define MINESWP_RIGHT BUTTON_RIGHT
76 # define MINESWP_UP BUTTON_UP
77 # define MINESWP_DOWN BUTTON_DOWN
78 # define MINESWP_QUIT BUTTON_OFF
79 # define MINESWP_TOGGLE BUTTON_ON
80 # define MINESWP_TOGGLE2 BUTTON_REC
81 # define MINESWP_DISCOVER BUTTON_SELECT
82 # define MINESWP_INFO BUTTON_MODE
84 # define MINESWP_RC_QUIT BUTTON_RC_STOP
86 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
87 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
88 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
89 # define MINESWP_SCROLLWHEEL
90 # define MINESWP_LEFT BUTTON_LEFT
91 # define MINESWP_RIGHT BUTTON_RIGHT
92 # define MINESWP_UP BUTTON_MENU
93 # define MINESWP_DOWN BUTTON_PLAY
94 # define MINESWP_NEXT BUTTON_SCROLL_FWD
95 # define MINESWP_PREV BUTTON_SCROLL_BACK
96 # define MINESWP_QUIT (BUTTON_SELECT | BUTTON_MENU)
97 # define MINESWP_TOGGLE_PRE BUTTON_SELECT
98 # define MINESWP_TOGGLE (BUTTON_SELECT | BUTTON_REL)
99 # define MINESWP_DISCOVER (BUTTON_SELECT | BUTTON_REPEAT)
100 # define MINESWP_INFO (BUTTON_SELECT | BUTTON_PLAY)
102 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
103 # define MINESWP_LEFT BUTTON_LEFT
104 # define MINESWP_RIGHT BUTTON_RIGHT
105 # define MINESWP_UP BUTTON_UP
106 # define MINESWP_DOWN BUTTON_DOWN
107 # define MINESWP_QUIT BUTTON_POWER
108 # define MINESWP_TOGGLE BUTTON_PLAY
109 # define MINESWP_DISCOVER BUTTON_SELECT
110 # define MINESWP_INFO BUTTON_REC
112 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
113 # define MINESWP_LEFT BUTTON_LEFT
114 # define MINESWP_RIGHT BUTTON_RIGHT
115 # define MINESWP_UP BUTTON_UP
116 # define MINESWP_DOWN BUTTON_DOWN
117 # define MINESWP_QUIT BUTTON_POWER
118 # define MINESWP_TOGGLE BUTTON_A
119 # define MINESWP_DISCOVER BUTTON_SELECT
120 # define MINESWP_INFO BUTTON_MENU
122 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
124 # define MINESWP_SCROLLWHEEL
125 # define MINESWP_LEFT BUTTON_LEFT
126 # define MINESWP_RIGHT BUTTON_RIGHT
127 # define MINESWP_UP BUTTON_UP
128 # define MINESWP_DOWN BUTTON_DOWN
129 # define MINESWP_QUIT BUTTON_POWER
130 # define MINESWP_NEXT BUTTON_SCROLL_FWD
131 # define MINESWP_PREV BUTTON_SCROLL_BACK
132 # define MINESWP_TOGGLE BUTTON_REC
133 # define MINESWP_DISCOVER BUTTON_SELECT
134 # define MINESWP_INFO (BUTTON_REC|BUTTON_REPEAT)
136 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
138 # define MINESWP_LEFT BUTTON_LEFT
139 # define MINESWP_RIGHT BUTTON_RIGHT
140 # define MINESWP_UP BUTTON_UP
141 # define MINESWP_DOWN BUTTON_DOWN
142 # define MINESWP_QUIT (BUTTON_HOME|BUTTON_REPEAT)
143 # define MINESWP_TOGGLE BUTTON_SCROLL_FWD
144 # define MINESWP_DISCOVER BUTTON_SELECT
145 # define MINESWP_INFO BUTTON_SCROLL_BACK
147 #elif (CONFIG_KEYPAD == SANSA_C200_PAD) || \
148 (CONFIG_KEYPAD == SANSA_CLIP_PAD) || \
149 (CONFIG_KEYPAD == SANSA_M200_PAD)
150 # define MINESWP_LEFT BUTTON_LEFT
151 # define MINESWP_RIGHT BUTTON_RIGHT
152 # define MINESWP_UP BUTTON_UP
153 # define MINESWP_DOWN BUTTON_DOWN
154 # define MINESWP_QUIT BUTTON_POWER
155 # define MINESWP_TOGGLE_PRE BUTTON_SELECT
156 # define MINESWP_TOGGLE (BUTTON_SELECT | BUTTON_REL)
157 # define MINESWP_TOGGLE2 BUTTON_VOL_DOWN
158 # define MINESWP_DISCOVER (BUTTON_SELECT | BUTTON_REPEAT)
159 # define MINESWP_DISCOVER2 BUTTON_VOL_UP
160 # define MINESWP_INFO (BUTTON_SELECT | BUTTON_UP)
162 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
163 # define MINESWP_LEFT BUTTON_LEFT
164 # define MINESWP_RIGHT BUTTON_RIGHT
165 # define MINESWP_UP BUTTON_SCROLL_UP
166 # define MINESWP_DOWN BUTTON_SCROLL_DOWN
167 # define MINESWP_QUIT BUTTON_POWER
168 # define MINESWP_TOGGLE BUTTON_PLAY
169 # define MINESWP_DISCOVER BUTTON_REW
170 # define MINESWP_INFO (BUTTON_REW | BUTTON_PLAY)
172 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
173 # define MINESWP_LEFT BUTTON_LEFT
174 # define MINESWP_RIGHT BUTTON_RIGHT
175 # define MINESWP_UP BUTTON_UP
176 # define MINESWP_DOWN BUTTON_DOWN
177 # define MINESWP_QUIT BUTTON_BACK
178 # define MINESWP_TOGGLE BUTTON_PLAY
179 # define MINESWP_DISCOVER BUTTON_SELECT
180 # define MINESWP_INFO BUTTON_MENU
182 #elif (CONFIG_KEYPAD == MROBE100_PAD)
183 # define MINESWP_LEFT BUTTON_LEFT
184 # define MINESWP_RIGHT BUTTON_RIGHT
185 # define MINESWP_UP BUTTON_UP
186 # define MINESWP_DOWN BUTTON_DOWN
187 # define MINESWP_QUIT BUTTON_POWER
188 # define MINESWP_TOGGLE BUTTON_DISPLAY
189 # define MINESWP_DISCOVER BUTTON_SELECT
190 # define MINESWP_INFO BUTTON_MENU
192 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
193 # define MINESWP_LEFT BUTTON_RC_REW
194 # define MINESWP_RIGHT BUTTON_RC_FF
195 # define MINESWP_UP BUTTON_RC_VOL_UP
196 # define MINESWP_DOWN BUTTON_RC_VOL_DOWN
197 # define MINESWP_QUIT BUTTON_RC_REC
198 # define MINESWP_TOGGLE BUTTON_RC_MODE
199 # define MINESWP_DISCOVER BUTTON_RC_PLAY
200 # define MINESWP_INFO BUTTON_RC_MENU
202 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
203 # define MINESWP_QUIT BUTTON_POWER
205 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
206 # define MINESWP_LEFT BUTTON_LEFT
207 # define MINESWP_RIGHT BUTTON_RIGHT
208 # define MINESWP_UP BUTTON_UP
209 # define MINESWP_DOWN BUTTON_DOWN
210 # define MINESWP_QUIT BUTTON_BACK
211 # define MINESWP_TOGGLE BUTTON_SELECT
212 # define MINESWP_DISCOVER BUTTON_PLAY
213 # define MINESWP_INFO BUTTON_MENU
215 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
216 # define MINESWP_LEFT BUTTON_LEFT
217 # define MINESWP_RIGHT BUTTON_RIGHT
218 # define MINESWP_UP BUTTON_UP
219 # define MINESWP_DOWN BUTTON_DOWN
220 # define MINESWP_QUIT BUTTON_POWER
221 # define MINESWP_TOGGLE BUTTON_VIEW
222 # define MINESWP_DISCOVER BUTTON_SELECT
223 # define MINESWP_INFO BUTTON_MENU
225 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
226 # define MINESWP_LEFT BUTTON_LEFT
227 # define MINESWP_RIGHT BUTTON_RIGHT
228 # define MINESWP_UP BUTTON_UP
229 # define MINESWP_DOWN BUTTON_DOWN
230 # define MINESWP_QUIT BUTTON_POWER
231 # define MINESWP_TOGGLE BUTTON_NEXT
232 # define MINESWP_DISCOVER BUTTON_PLAY
233 # define MINESWP_INFO BUTTON_MENU
235 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
236 # define MINESWP_LEFT BUTTON_PREV
237 # define MINESWP_RIGHT BUTTON_NEXT
238 # define MINESWP_UP BUTTON_UP
239 # define MINESWP_DOWN BUTTON_DOWN
240 # define MINESWP_QUIT BUTTON_POWER
241 # define MINESWP_TOGGLE BUTTON_RIGHT
242 # define MINESWP_DISCOVER BUTTON_PLAY
243 # define MINESWP_INFO BUTTON_MENU
245 #elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \
246 CONFIG_KEYPAD == ONDAVX777_PAD || \
247 CONFIG_KEYPAD == MROBE500_PAD
248 # define MINESWP_QUIT BUTTON_POWER
250 #elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
251 # define MINESWP_LEFT BUTTON_LEFT
252 # define MINESWP_RIGHT BUTTON_RIGHT
253 # define MINESWP_UP BUTTON_UP
254 # define MINESWP_DOWN BUTTON_DOWN
255 # define MINESWP_QUIT BUTTON_REC
256 # define MINESWP_TOGGLE BUTTON_PLAY
257 # define MINESWP_DISCOVER BUTTON_REW
258 # define MINESWP_INFO BUTTON_FFWD
260 #elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
261 # define MINESWP_LEFT BUTTON_PREV
262 # define MINESWP_RIGHT BUTTON_NEXT
263 # define MINESWP_UP BUTTON_UP
264 # define MINESWP_DOWN BUTTON_DOWN
265 # define MINESWP_QUIT BUTTON_REC
266 # define MINESWP_TOGGLE BUTTON_PLAY
267 # define MINESWP_DISCOVER BUTTON_OK
268 # define MINESWP_INFO BUTTON_MENU
270 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
271 # define MINESWP_LEFT BUTTON_VOL_DOWN
272 # define MINESWP_RIGHT BUTTON_VOL_UP
273 # define MINESWP_UP BUTTON_REW
274 # define MINESWP_DOWN BUTTON_FF
275 # define MINESWP_QUIT (BUTTON_REC|BUTTON_PLAY)
276 # define MINESWP_TOGGLE BUTTON_PLAY
277 # define MINESWP_DISCOVER BUTTON_FUNC
278 # define MINESWP_INFO BUTTON_REC
280 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
281 # define MINESWP_LEFT BUTTON_REW
282 # define MINESWP_RIGHT BUTTON_FF
283 # define MINESWP_UP BUTTON_UP
284 # define MINESWP_DOWN BUTTON_DOWN
285 # define MINESWP_QUIT (BUTTON_MENU|BUTTON_REPEAT)
286 # define MINESWP_TOGGLE BUTTON_ENTER
287 # define MINESWP_DISCOVER BUTTON_PLAY
288 # define MINESWP_INFO (BUTTON_MENU|BUTTON_REL)
290 #elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
291 # define MINESWP_LEFT BUTTON_LEFT
292 # define MINESWP_RIGHT BUTTON_RIGHT
293 # define MINESWP_UP BUTTON_UP
294 # define MINESWP_DOWN BUTTON_DOWN
295 # define MINESWP_QUIT BUTTON_POWER
296 # define MINESWP_TOGGLE BUTTON_SELECT
297 # define MINESWP_DISCOVER BUTTON_PLAYPAUSE
298 # define MINESWP_NEXT BUTTON_BOTTOMRIGHT
299 # define MINESWP_PREV BUTTON_BOTTOMLEFT
300 # define MINESWP_INFO BUTTON_BACK
303 #elif (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
304 # define MINESWP_SCROLLWHEEL
305 # define MINESWP_LEFT BUTTON_LEFT
306 # define MINESWP_RIGHT BUTTON_RIGHT
307 # define MINESWP_UP BUTTON_UP
308 # define MINESWP_DOWN BUTTON_DOWN
309 # define MINESWP_QUIT BUTTON_POWER
310 # define MINESWP_NEXT BUTTON_SCROLL_FWD
311 # define MINESWP_PREV BUTTON_SCROLL_BACK
312 # define MINESWP_TOGGLE BUTTON_NEXT
313 # define MINESWP_DISCOVER BUTTON_SELECT
314 # define MINESWP_INFO BUTTON_PREV
316 #elif (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
317 # define MINESWP_LEFT BUTTON_LEFT
318 # define MINESWP_RIGHT BUTTON_RIGHT
319 # define MINESWP_UP BUTTON_UP
320 # define MINESWP_DOWN BUTTON_DOWN
321 # define MINESWP_QUIT BUTTON_BACK
322 # define MINESWP_TOGGLE BUTTON_USER
323 # define MINESWP_DISCOVER BUTTON_SELECT
324 # define MINESWP_INFO BUTTON_MENU
326 #elif (CONFIG_KEYPAD == HM60X_PAD)
328 # define MINESWP_LEFT BUTTON_LEFT
329 # define MINESWP_RIGHT BUTTON_RIGHT
330 # define MINESWP_UP BUTTON_UP
331 # define MINESWP_DOWN BUTTON_DOWN
332 # define MINESWP_QUIT BUTTON_POWER
333 # define MINESWP_TOGGLE (BUTTON_LEFT|BUTTON_POWER)
334 # define MINESWP_DISCOVER BUTTON_SELECT
335 # define MINESWP_INFO (BUTTON_UP|BUTTON_POWER)
337 #elif (CONFIG_KEYPAD == HM801_PAD)
339 # define MINESWP_LEFT BUTTON_LEFT
340 # define MINESWP_RIGHT BUTTON_RIGHT
341 # define MINESWP_UP BUTTON_UP
342 # define MINESWP_DOWN BUTTON_DOWN
343 # define MINESWP_QUIT BUTTON_POWER
344 # define MINESWP_TOGGLE BUTTON_PLAY
345 # define MINESWP_DISCOVER BUTTON_SELECT
346 # define MINESWP_INFO BUTTON_PREV
348 #else
349 #error No keymap defined!
350 #endif
352 #ifdef HAVE_TOUCHSCREEN
353 #ifndef MINESWP_QUIT
354 # define MINESWP_QUIT BUTTON_TOPLEFT
355 #endif
356 #ifndef MINESWP_LEFT
357 # define MINESWP_LEFT BUTTON_MIDLEFT
358 #endif
359 #ifndef MINESWP_RIGHT
360 # define MINESWP_RIGHT BUTTON_MIDRIGHT
361 #endif
362 #ifndef MINESWP_UP
363 # define MINESWP_UP BUTTON_TOPMIDDLE
364 #endif
365 #ifndef MINESWP_DOWN
366 # define MINESWP_DOWN BUTTON_BOTTOMMIDDLE
367 #endif
368 #ifndef MINESWP_TOGGLE
369 # define MINESWP_TOGGLE BUTTON_CENTER
370 #endif
371 #ifndef MINESWP_DISCOVER
372 # define MINESWP_DISCOVER BUTTON_BOTTOMLEFT
373 #endif
374 #ifndef MINESWP_INFO
375 # define MINESWP_INFO BUTTON_BOTTOMRIGHT
376 #endif
377 #endif
379 extern const fb_data minesweeper_tiles[];
381 #ifdef HAVE_LCD_COLOR
382 # if ( LCD_HEIGHT * LCD_WIDTH ) / ( 16 * 16 ) >= 130
383 /* We want to have at least 130 tiles on the screen */
384 # define TileSize 16
385 # elif ( LCD_HEIGHT * LCD_WIDTH ) / ( 12 * 12 ) >= 130
386 # define TileSize 12
387 # else
388 # define TileSize 10
389 # endif
390 # define BackgroundColor LCD_RGBPACK( 128, 128, 128 )
391 #elif LCD_DEPTH > 1
392 # define TileSize 12
393 #else
394 # define TileSize 8
395 #endif
397 #define Mine 9
398 #define Flag 10
399 #define Unknown 11
400 #define ExplodedMine 12
401 #define WrongFlag 13
402 #define CorrectFlag 14
404 #define draw_tile( num, x, y ) \
405 rb->lcd_bitmap_part( minesweeper_tiles, 0, num * TileSize, \
406 TileSize, left+x*TileSize, top+y*TileSize, \
407 TileSize, TileSize )
409 #define invert_tile( x, y ) \
410 rb->lcd_set_drawmode(DRMODE_COMPLEMENT); \
411 rb->lcd_fillrect( left+x*TileSize, top+y*TileSize, TileSize, TileSize ); \
412 rb->lcd_set_drawmode(DRMODE_SOLID);
415 /* the tile struct
416 * if there is a mine, mine is true
417 * if tile is known by player, known is true
418 * if tile has a flag, flag is true
419 * neighbors is the total number of mines arround tile
421 typedef struct tile
423 unsigned char mine : 1;
424 unsigned char known : 1;
425 unsigned char flag : 1;
426 unsigned char neighbors : 4;
427 } tile;
429 /* the height and width of the field */
430 #define MAX_HEIGHT (LCD_HEIGHT/TileSize)
431 #define MAX_WIDTH (LCD_WIDTH/TileSize)
432 static int height = MAX_HEIGHT;
433 static int width = MAX_WIDTH;
434 static int top;
435 static int left;
437 /* The Minefield. Caution it is defined as Y, X! Not the opposite. */
438 static tile minefield[MAX_HEIGHT][MAX_WIDTH];
440 /* total number of mines on the game */
441 static int mine_num = 0;
443 /* percentage of mines on minefield used during generation */
444 static int percent = 16;
446 /* number of tiles left on the game */
447 static int tiles_left;
449 /* Because mines are set after the first move... */
450 static bool no_mines = true;
452 /* We need a stack (created on discover()) for the cascade algorithm. */
453 static int stack_pos = 0;
455 #ifdef HAVE_TOUCHSCREEN
457 #include "lib/pluginlib_touchscreen.h"
458 static struct ts_raster mine_raster = { 0, 0, MAX_WIDTH, MAX_HEIGHT, TileSize, TileSize };
459 #endif
462 static void push( int *stack, int y, int x )
464 if( stack_pos <= height*width )
466 stack[++stack_pos] = y;
467 stack[++stack_pos] = x;
471 /* Unveil tiles and push them to stack if they are empty. */
472 static void unveil( int *stack, int y, int x )
474 if( x < 0 || y < 0 || x > width - 1 || y > height - 1
475 || minefield[y][x].known
476 || minefield[y][x].mine || minefield[y][x].flag ) return;
478 minefield[y][x].known = 1;
480 if( minefield[y][x].neighbors == 0 )
481 push( stack, y, x );
484 static int is_flagged( int y, int x )
486 if( x >= 0 && y >= 0 && x < width && y < height && minefield[y][x].flag )
487 return 1;
488 return 0;
491 static int neighbors_flagged( int y, int x )
493 return is_flagged( y-1, x-1 ) +
494 is_flagged( y-1, x ) +
495 is_flagged( y-1, x+1 ) +
496 is_flagged( y, x-1 ) +
497 is_flagged( y, x ) +
498 is_flagged( y, x+1 ) +
499 is_flagged( y+1, x-1 ) +
500 is_flagged( y+1, x ) +
501 is_flagged( y+1, x+1 );
504 static bool discover( int y, int x, bool explore_neighbors )
506 /* Selected tile. */
507 if( x < 0 || y < 0 || x > width - 1 || y > height - 1)
508 return false;
510 if( minefield[y][x].known || minefield[y][x].mine || minefield[y][x].flag )
512 if( !minefield[y][x].flag && minefield[y][x].mine )
513 return true;
515 if( explore_neighbors && minefield[y][x].known &&
516 minefield[y][x].neighbors == neighbors_flagged( y, x ) )
518 return discover( y-1, x-1, false ) ||
519 discover( y-1, x, false ) ||
520 discover( y-1, x+1, false ) ||
521 discover( y, x-1, false ) ||
522 discover( y, x, false ) ||
523 discover( y, x+1, false ) ||
524 discover( y+1, x-1, false ) ||
525 discover( y+1, x, false ) ||
526 discover( y+1, x+1, false );
529 return false;
532 minefield[y][x].known = 1;
533 /* Exit if the tile is not empty. (no mines nearby) */
534 if( minefield[y][x].neighbors ) return false;
536 int stack[height*width];
538 push( stack, y, x );
540 /* Scan all nearby tiles. If we meet a tile with a number we just unveil
541 * it. If we meet an empty tile, we push the location in stack. For each
542 * location in stack we do the same thing. (scan again all nearby tiles)
544 while( stack_pos )
546 /* Pop x, y from stack. */
547 x = stack[stack_pos--];
548 y = stack[stack_pos--];
550 unveil( stack, y-1, x-1 );
551 unveil( stack, y-1, x );
552 unveil( stack, y-1, x+1 );
553 unveil( stack, y, x+1 );
554 unveil( stack, y+1, x+1 );
555 unveil( stack, y+1, x );
556 unveil( stack, y+1, x-1 );
557 unveil( stack, y, x-1 );
560 return false;
563 /* Reset the whole board for a new game. */
564 static void minesweeper_init( void )
566 rb->memset(minefield, 0, sizeof(minefield));
567 no_mines = true;
568 tiles_left = width*height;
572 /* put mines on the mine field */
573 /* there is p% chance that a tile is a mine */
574 /* if the tile has coordinates (x,y), or is adjacent to those,
575 * then it can't be a mine because that would reduce the game
576 * from a logic game to a guessing game. */
577 static void minesweeper_putmines( int p, int x, int y )
579 int i,j;
581 mine_num = 0;
582 for( i = 0; i < height; i++ )
584 for( j = 0; j < width; j++ )
586 if( rb->rand()%100 < p
587 && !( i>=y-1 && i<=y+1 && j>=x-1 && j<=x+1 ) )
589 minefield[i][j].mine = 1;
590 mine_num++;
592 else
594 minefield[i][j].mine = 0;
596 minefield[i][j].neighbors = 0;
600 /* we need to compute the neighbor element for each tile */
601 for( i = 0; i < height; i++ )
603 for( j = 0; j < width; j++ )
605 if( i > 0 )
607 if( j > 0 )
608 minefield[i][j].neighbors += minefield[i-1][j-1].mine;
609 minefield[i][j].neighbors += minefield[i-1][j].mine;
610 if( j < width - 1 )
611 minefield[i][j].neighbors += minefield[i-1][j+1].mine;
613 if( j > 0 )
614 minefield[i][j].neighbors += minefield[i][j-1].mine;
615 if( j < width - 1 )
616 minefield[i][j].neighbors += minefield[i][j+1].mine;
617 if( i < height - 1 )
619 if( j > 0 )
620 minefield[i][j].neighbors += minefield[i+1][j-1].mine;
621 minefield[i][j].neighbors += minefield[i+1][j].mine;
622 if( j < width - 1 )
623 minefield[i][j].neighbors += minefield[i+1][j+1].mine;
628 no_mines = false;
630 /* In case the user is lucky and there are no mines positioned. */
631 if( !mine_num && height*width != 1 )
633 minesweeper_putmines(p, x, y);
637 /* A function that will uncover all the board, when the user wins or loses.
638 can easily be expanded, (just a call assigned to a button) as a solver. */
639 static void mine_show( void )
641 int i, j, button;
643 for( i = 0; i < height; i++ )
645 for( j = 0; j < width; j++ )
647 if( minefield[i][j].mine )
649 if( minefield[i][j].known )
650 draw_tile( ExplodedMine, j, i );
651 else if( minefield[i][j].flag )
652 draw_tile( CorrectFlag, j, i );
653 else
654 draw_tile( Mine, j, i );
656 else
658 if( minefield[i][j].flag )
659 draw_tile( WrongFlag, j, i );
660 else
661 draw_tile( minefield[i][j].neighbors, j, i );
665 rb->lcd_update();
668 button = rb->button_get(true);
669 while( ( button == BUTTON_NONE )
670 || ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
671 #ifdef HAVE_TOUCHSCREEN
672 button = BUTTON_NONE;
673 #endif
676 static int count_tiles_left( void )
678 int tiles = 0;
679 int i, j;
680 for( i = 0; i < height; i++ )
681 for( j = 0; j < width; j++ )
682 if( minefield[i][j].known == 0 )
683 tiles++;
684 return tiles;
687 static int count_flags( void )
689 int flags = 0;
690 int i, j;
691 for( i = 0; i < height; i++ )
692 for( j = 0; j < width; j++ )
693 if( minefield[i][j].flag == 1 )
694 flags++;
695 return flags;
698 /* welcome screen where player can chose mine percentage */
699 static enum minesweeper_status menu( void )
701 int selection = 0, result = MINESWEEPER_QUIT;
702 bool menu_quit = false;
704 MENUITEM_STRINGLIST( menu, "Minesweeper Menu", NULL, "Play Minesweeper",
705 "Mine Percentage", "Number of Rows",
706 "Number of Columns", "Playback Control", "Quit" );
708 #ifdef HAVE_LCD_COLOR
709 rb->lcd_set_foreground( rb->global_settings->fg_color );
710 rb->lcd_set_background( rb->global_settings->bg_color );
711 #endif
713 while( !menu_quit )
715 switch( rb->do_menu( &menu, &selection, NULL, false ) )
717 case 0:
718 result = MINESWEEPER_WIN; /* start playing */
719 menu_quit = true;
720 break;
722 case 1:
723 rb->set_int( "Mine Percentage", "%", UNIT_INT, &percent, NULL,
724 1, 2, 98, NULL );
725 break;
727 case 2:
728 rb->set_int( "Number of Rows", "", UNIT_INT, &height, NULL,
729 1, 1, MAX_HEIGHT, NULL );
730 break;
732 case 3:
733 rb->set_int( "Number of Columns", "", UNIT_INT, &width, NULL,
734 1, 1, MAX_WIDTH, NULL );
735 break;
737 case 4:
738 playback_control( NULL );
739 break;
741 default:
742 result = MINESWEEPER_QUIT; /* quit program */
743 menu_quit = true;
744 break;
748 return result;
751 /* the big and ugly game function */
752 static enum minesweeper_status minesweeper( void )
754 int i, j;
755 int button;
756 #if defined(HAVE_TOUCHSCREEN) || defined(MINESWP_TOGGLE_PRE)
757 int lastbutton = BUTTON_NONE;
758 #endif
760 /* the cursor coordinates */
761 int x=0, y=0;
764 * Show the menu
766 if( ( i = menu() ) != MINESWEEPER_WIN ) return i;
769 * Init game
771 top = (LCD_HEIGHT-height*TileSize)/2;
772 left = (LCD_WIDTH-width*TileSize)/2;
774 #ifdef HAVE_TOUCHSCREEN
775 mine_raster.tl_x = left;
776 mine_raster.tl_y = top;
777 mine_raster.width = width*TileSize;
778 mine_raster.height = height*TileSize;
779 #endif
781 rb->srand( *rb->current_tick );
782 minesweeper_init();
783 x = 0;
784 y = 0;
787 * Play
789 while( true )
792 /* clear the screen buffer */
793 #ifdef HAVE_LCD_COLOR
794 rb->lcd_set_background( BackgroundColor );
795 #endif
796 rb->lcd_clear_display();
798 /* display the mine field */
799 for( i = 0; i < height; i++ )
801 for( j = 0; j < width; j++ )
803 if( minefield[i][j].known )
805 draw_tile( minefield[i][j].neighbors, j, i );
807 else if(minefield[i][j].flag)
809 draw_tile( Flag, j, i );
811 else
813 draw_tile( Unknown, j, i );
818 /* display the cursor */
819 invert_tile( x, y );
821 /* update the screen */
822 rb->lcd_update();
824 button = rb->button_get(true);
825 #ifdef HAVE_TOUCHSCREEN
826 if(button & BUTTON_TOUCHSCREEN)
828 struct ts_raster_result res;
829 if(touchscreen_map_raster(&mine_raster, rb->button_get_data() >> 16, rb->button_get_data() & 0xffff, &res) == 1)
831 button &= ~BUTTON_TOUCHSCREEN;
832 lastbutton &= ~BUTTON_TOUCHSCREEN;
834 if(button & BUTTON_REPEAT && lastbutton != MINESWP_TOGGLE && lastbutton ^ BUTTON_REPEAT)
835 button = MINESWP_TOGGLE;
836 else if(button == BUTTON_REL && lastbutton ^ BUTTON_REPEAT)
837 button = MINESWP_DISCOVER;
838 else
839 button |= BUTTON_TOUCHSCREEN;
841 x = res.x;
842 y = res.y;
845 #endif
846 switch(button)
848 /* quit minesweeper (you really shouldn't use this button ...) */
849 #ifdef MINESWP_RC_QUIT
850 case MINESWP_RC_QUIT:
851 #endif
852 case MINESWP_QUIT:
853 return MINESWEEPER_QUIT;
855 /* move cursor left */
856 case MINESWP_LEFT:
857 case MINESWP_LEFT|BUTTON_REPEAT:
858 if( --x < 0)
859 x += width;
860 break;
862 /* move cursor right */
863 case MINESWP_RIGHT:
864 case MINESWP_RIGHT|BUTTON_REPEAT:
865 if( ++x >= width )
866 x -= width;
867 break;
869 /* move cursor down */
870 case MINESWP_DOWN:
871 case MINESWP_DOWN|BUTTON_REPEAT:
872 if( ++y >= height )
873 y -= height;
874 break;
876 /* move cursor up */
877 case MINESWP_UP:
878 case MINESWP_UP|BUTTON_REPEAT:
879 if( --y < 0 )
880 y += height;
881 break;
883 /*move cursor though the entire field*/
884 #ifdef MINESWP_SCROLLWHEEL
885 case MINESWP_NEXT:
886 case MINESWP_NEXT|BUTTON_REPEAT:
887 if (x == width -1 ) {
888 if( ++y >= height )
889 y -= height;
891 if( ++x >= width )
892 x -= width;
893 break;
895 case MINESWP_PREV:
896 case MINESWP_PREV|BUTTON_REPEAT:
897 if (x == 0) {
898 if( --y < 0 )
899 y += height;
901 if( --x < 0 )
902 x += width;
903 break;
904 #endif
905 /* discover a tile (and it's neighbors if .neighbors == 0) */
906 case MINESWP_DISCOVER:
907 #ifdef MINESWP_DISCOVER2
908 case MINESWP_DISCOVER2:
909 #endif
910 if( minefield[y][x].flag ) break;
911 /* we put the mines on the first "click" so that you don't
912 * lose on the first "click" */
913 if( tiles_left == width*height && no_mines )
914 minesweeper_putmines(percent,x,y);
916 if( discover( y, x, true ) )
918 minefield[y][x].known = 1;
919 return MINESWEEPER_LOSE;
922 tiles_left = count_tiles_left();
923 if( tiles_left == mine_num )
925 return MINESWEEPER_WIN;
927 break;
929 /* toggle flag under cursor */
930 case MINESWP_TOGGLE:
931 #ifdef MINESWP_TOGGLE_PRE
932 if( lastbutton != MINESWP_TOGGLE_PRE )
933 break;
934 #endif
935 #ifdef MINESWP_TOGGLE2
936 case MINESWP_TOGGLE2:
937 #endif
938 if( !minefield[y][x].known )
939 minefield[y][x].flag = !minefield[y][x].flag;
940 break;
942 /* show how many mines you think you have found and how many
943 * there really are on the game */
944 case MINESWP_INFO:
945 if( no_mines )
946 break;
947 int flags_used = count_flags();
948 if (flags_used == 1) {
949 rb->splashf( HZ*2, "You marked 1 field. There are %d mines.",
950 mine_num );
952 else
954 rb->splashf( HZ*2, "You marked %d fields. There are %d mines.",
955 flags_used, mine_num );
957 break;
959 default:
960 if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
961 return MINESWEEPER_USB;
962 break;
964 #if defined(HAVE_TOUCHSCREEN) || defined(MINESWP_TOGGLE_PRE)
965 if( button != BUTTON_NONE )
966 lastbutton = button;
967 #endif
972 /* plugin entry point */
973 enum plugin_status plugin_start(const void* parameter)
975 bool exit = false;
977 (void)parameter;
978 #if LCD_DEPTH > 1
979 rb->lcd_set_backdrop(NULL);
980 #endif
982 while( !exit )
984 switch( minesweeper() )
986 case MINESWEEPER_WIN:
987 rb->splash( HZ, "You Win!" );
988 rb->lcd_clear_display();
989 mine_show();
990 break;
992 case MINESWEEPER_LOSE:
993 rb->splash( HZ, "You Lose!" );
994 rb->lcd_clear_display();
995 mine_show();
996 break;
998 case MINESWEEPER_USB:
999 return PLUGIN_USB_CONNECTED;
1001 case MINESWEEPER_QUIT:
1002 exit = true;
1003 break;
1005 default:
1006 break;
1010 return PLUGIN_OK;