1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2004-2006 Antoine Cellerier <dionoea -at- videolan -dot- org>
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 ****************************************************************************/
22 #ifdef HAVE_LCD_BITMAP
26 /* what the minesweeper() function can return */
27 enum minesweeper_status
{
34 /* variable button definitions */
35 #if CONFIG_KEYPAD == RECORDER_PAD
36 # define MINESWP_UP BUTTON_UP
37 # define MINESWP_DOWN BUTTON_DOWN
38 # define MINESWP_QUIT BUTTON_OFF
39 # define MINESWP_TOGGLE BUTTON_ON
40 # define MINESWP_TOGGLE2 BUTTON_F1
41 # define MINESWP_DISCOVER BUTTON_PLAY
42 # define MINESWP_DISCOVER2 BUTTON_F2
43 # define MINESWP_INFO BUTTON_F3
45 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
46 # define MINESWP_UP BUTTON_UP
47 # define MINESWP_DOWN BUTTON_DOWN
48 # define MINESWP_QUIT BUTTON_OFF
49 # define MINESWP_TOGGLE BUTTON_ON
50 # define MINESWP_TOGGLE2 BUTTON_F1
51 # define MINESWP_DISCOVER BUTTON_SELECT
52 # define MINESWP_DISCOVER2 BUTTON_F2
53 # define MINESWP_INFO BUTTON_F3
55 #elif CONFIG_KEYPAD == ONDIO_PAD
56 # define MINESWP_UP BUTTON_UP
57 # define MINESWP_DOWN BUTTON_DOWN
58 # define MINESWP_QUIT BUTTON_OFF
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_TOGGLE BUTTON_ON
70 # define MINESWP_TOGGLE2 BUTTON_REC
71 # define MINESWP_DISCOVER BUTTON_SELECT
72 # define MINESWP_INFO BUTTON_MODE
74 # define MINESWP_RC_QUIT BUTTON_RC_STOP
76 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
77 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
78 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
79 # define MINESWP_SCROLLWHEEL
80 # define MINESWP_UP BUTTON_MENU
81 # define MINESWP_DOWN BUTTON_PLAY
82 # define MINESWP_NEXT BUTTON_SCROLL_FWD
83 # define MINESWP_PREV BUTTON_SCROLL_BACK
84 # define MINESWP_QUIT (BUTTON_SELECT | BUTTON_MENU)
85 # define MINESWP_TOGGLE_PRE BUTTON_SELECT
86 # define MINESWP_TOGGLE (BUTTON_SELECT | BUTTON_REL)
87 # define MINESWP_DISCOVER (BUTTON_SELECT | BUTTON_REPEAT)
88 # define MINESWP_INFO (BUTTON_SELECT | BUTTON_PLAY)
90 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
91 # define MINESWP_UP BUTTON_UP
92 # define MINESWP_DOWN BUTTON_DOWN
93 # define MINESWP_QUIT BUTTON_POWER
94 # define MINESWP_TOGGLE BUTTON_PLAY
95 # define MINESWP_DISCOVER BUTTON_SELECT
96 # define MINESWP_INFO BUTTON_REC
98 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
99 # define MINESWP_UP BUTTON_UP
100 # define MINESWP_DOWN BUTTON_DOWN
101 # define MINESWP_QUIT BUTTON_POWER
102 # define MINESWP_TOGGLE BUTTON_A
103 # define MINESWP_DISCOVER BUTTON_SELECT
104 # define MINESWP_INFO BUTTON_MENU
106 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
107 # define MINESWP_SCROLLWHEEL
108 # define MINESWP_UP BUTTON_UP
109 # define MINESWP_DOWN BUTTON_DOWN
110 # define MINESWP_QUIT BUTTON_POWER
111 # define MINESWP_NEXT BUTTON_SCROLL_DOWN
112 # define MINESWP_PREV BUTTON_SCROLL_UP
113 # define MINESWP_TOGGLE BUTTON_REC
114 # define MINESWP_DISCOVER BUTTON_SELECT
115 # define MINESWP_INFO (BUTTON_REC|BUTTON_REPEAT)
117 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
118 # define MINESWP_UP BUTTON_SCROLL_UP
119 # define MINESWP_DOWN BUTTON_SCROLL_DOWN
120 # define MINESWP_QUIT BUTTON_POWER
121 # define MINESWP_TOGGLE BUTTON_PLAY
122 # define MINESWP_DISCOVER BUTTON_REW
123 # define MINESWP_INFO (BUTTON_REW | BUTTON_PLAY)
126 # warning Missing key definitions for this keypad
129 /* here is a global api struct pointer. while not strictly necessary,
130 * it's nice not to have to pass the api pointer in all function calls
133 static struct plugin_api
*rb
;
135 extern const fb_data minesweeper_tiles
[];
137 #ifdef HAVE_LCD_COLOR
138 # if ( LCD_HEIGHT * LCD_WIDTH ) / ( 16 * 16 ) >= 130
139 /* We want to have at least 130 tiles on the screen */
144 # define BackgroundColor LCD_RGBPACK( 128, 128, 128 )
154 #define ExplodedMine 12
156 #define draw_tile( num, x, y ) \
157 rb->lcd_bitmap_part( minesweeper_tiles, 0, num * TileSize, \
158 TileSize, left+x*TileSize, top+y*TileSize, \
161 #define invert_tile( x, y ) \
162 rb->lcd_set_drawmode(DRMODE_COMPLEMENT); \
163 rb->lcd_fillrect( left+x*TileSize, top+y*TileSize, TileSize, TileSize ); \
164 rb->lcd_set_drawmode(DRMODE_SOLID);
168 * if there is a mine, mine is true
169 * if tile is known by player, known is true
170 * if tile has a flag, flag is true
171 * neighbors is the total number of mines arround tile
175 unsigned char mine
: 1;
176 unsigned char known
: 1;
177 unsigned char flag
: 1;
178 unsigned char neighbors
: 4;
181 /* the height and width of the field */
182 #define MAX_HEIGHT (LCD_HEIGHT/TileSize)
183 #define MAX_WIDTH (LCD_WIDTH/TileSize)
184 int height
= MAX_HEIGHT
;
185 int width
= MAX_WIDTH
;
189 /* The Minefield. Caution it is defined as Y, X! Not the opposite. */
190 tile minefield
[MAX_HEIGHT
][MAX_WIDTH
];
192 /* total number of mines on the game */
195 /* percentage of mines on minefield used during generation */
198 /* number of tiles left on the game */
201 /* number of used flags on the game */
204 /* Because mines are set after the first move... */
205 bool no_mines
= true;
207 /* We need a stack (created on discover()) for the cascade algorithm. */
210 /* a usefull string for snprintf */
214 void push( int *stack
, int y
, int x
)
216 if( stack_pos
<= height
*width
)
218 stack
[++stack_pos
] = y
;
219 stack
[++stack_pos
] = x
;
223 /* Unveil tiles and push them to stack if they are empty. */
224 void unveil( int *stack
, int y
, int x
)
226 if( x
< 0 || y
< 0 || x
> width
- 1 || y
> height
- 1
227 || minefield
[y
][x
].known
228 || minefield
[y
][x
].mine
|| minefield
[y
][x
].flag
) return;
230 minefield
[y
][x
].known
= 1;
232 if( minefield
[y
][x
].neighbors
== 0 )
236 void discover( int y
, int x
)
238 int stack
[height
*width
];
241 if( x
< 0 || y
< 0 || x
> width
- 1 || y
> height
- 1
242 || minefield
[y
][x
].known
243 || minefield
[y
][x
].mine
|| minefield
[y
][x
].flag
) return;
245 minefield
[y
][x
].known
= 1;
246 /* Exit if the tile is not empty. (no mines nearby) */
247 if( minefield
[y
][x
].neighbors
) return;
251 /* Scan all nearby tiles. If we meet a tile with a number we just unveil
252 * it. If we meet an empty tile, we push the location in stack. For each
253 * location in stack we do the same thing. (scan again all nearby tiles)
257 /* Pop x, y from stack. */
258 x
= stack
[stack_pos
--];
259 y
= stack
[stack_pos
--];
261 unveil( stack
, y
-1, x
-1 );
262 unveil( stack
, y
-1, x
);
263 unveil( stack
, y
-1, x
+1 );
264 unveil( stack
, y
, x
+1 );
265 unveil( stack
, y
+1, x
+1 );
266 unveil( stack
, y
+1, x
);
267 unveil( stack
, y
+1, x
-1 );
268 unveil( stack
, y
, x
-1 );
272 /* Reset the whole board for a new game. */
273 void minesweeper_init( void )
277 for( i
= 0; i
< MAX_HEIGHT
; i
++ )
279 for( j
= 0; j
< MAX_WIDTH
; j
++ )
281 minefield
[i
][j
].known
= 0;
282 minefield
[i
][j
].flag
= 0;
283 minefield
[i
][j
].mine
= 0;
284 minefield
[i
][j
].neighbors
= 0;
288 tiles_left
= width
*height
;
292 /* put mines on the mine field */
293 /* there is p% chance that a tile is a mine */
294 /* if the tile has coordinates (x,y), then it can't be a mine */
295 void minesweeper_putmines( int p
, int x
, int y
)
300 for( i
= 0; i
< height
; i
++ )
302 for( j
= 0; j
< width
; j
++ )
304 if( rb
->rand()%100 < p
&& !( y
==i
&& x
==j
) )
306 minefield
[i
][j
].mine
= 1;
311 minefield
[i
][j
].mine
= 0;
313 minefield
[i
][j
].neighbors
= 0;
317 /* we need to compute the neighbor element for each tile */
318 for( i
= 0; i
< height
; i
++ )
320 for( j
= 0; j
< width
; j
++ )
325 minefield
[i
][j
].neighbors
+= minefield
[i
-1][j
-1].mine
;
326 minefield
[i
][j
].neighbors
+= minefield
[i
-1][j
].mine
;
328 minefield
[i
][j
].neighbors
+= minefield
[i
-1][j
+1].mine
;
331 minefield
[i
][j
].neighbors
+= minefield
[i
][j
-1].mine
;
333 minefield
[i
][j
].neighbors
+= minefield
[i
][j
+1].mine
;
337 minefield
[i
][j
].neighbors
+= minefield
[i
+1][j
-1].mine
;
338 minefield
[i
][j
].neighbors
+= minefield
[i
+1][j
].mine
;
340 minefield
[i
][j
].neighbors
+= minefield
[i
+1][j
+1].mine
;
347 /* In case the user is lucky and there are no mines positioned. */
348 if( !mine_num
&& height
*width
!= 1 )
350 minesweeper_putmines(p
, x
, y
);
354 /* A function that will uncover all the board, when the user wins or loses.
355 can easily be expanded, (just a call assigned to a button) as a solver. */
356 void mine_show( void )
360 for( i
= 0; i
< height
; i
++ )
362 for( j
= 0; j
< width
; j
++ )
364 if( minefield
[i
][j
].mine
)
366 if( minefield
[i
][j
].known
)
368 draw_tile( ExplodedMine
, j
, i
);
372 draw_tile( Mine
, j
, i
);
377 draw_tile( minefield
[i
][j
].neighbors
, j
, i
);
384 button
= rb
->button_get(true);
385 while( ( button
== BUTTON_NONE
)
386 || ( button
& (BUTTON_REL
|BUTTON_REPEAT
) ) );
389 int count_tiles_left( void )
393 for( i
= 0; i
< height
; i
++ )
394 for( j
= 0; j
< width
; j
++ )
395 if( minefield
[i
][j
].known
== 0 )
400 int count_flags( void )
404 for( i
= 0; i
< height
; i
++ )
405 for( j
= 0; j
< width
; j
++ )
406 if( minefield
[i
][j
].flag
== 1 )
411 /* welcome screen where player can chose mine percentage */
412 enum minesweeper_status
menu( void )
414 int selection
, result
= MINESWEEPER_QUIT
;
415 bool menu_quit
= false;
417 MENUITEM_STRINGLIST( menu
, "Minesweeper Menu", NULL
, "Play Minesweeper",
418 "Mine Percentage", "Number of Rows",
419 "Number of Columns", "Quit" );
421 #ifdef HAVE_LCD_COLOR
422 rb
->lcd_set_foreground( rb
->global_settings
->fg_color
);
423 rb
->lcd_set_background( rb
->global_settings
->bg_color
);
428 switch( rb
->do_menu( &menu
, &selection
) )
431 result
= MINESWEEPER_WIN
; /* start playing */
436 rb
->set_int( "Mine Percentage", "%", UNIT_INT
, &p
, NULL
,
441 rb
->set_int( "Number of Rows", "", UNIT_INT
, &height
, NULL
,
442 1, 1, MAX_HEIGHT
, NULL
);
446 rb
->set_int( "Number of Columns", "", UNIT_INT
, &width
, NULL
,
447 1, 1, MAX_WIDTH
, NULL
);
451 result
= MINESWEEPER_QUIT
; /* quit program */
460 /* the big and ugly game function */
461 enum minesweeper_status
minesweeper( void )
465 int lastbutton
= BUTTON_NONE
;
467 /* the cursor coordinates */
473 if( ( i
= menu() ) != MINESWEEPER_WIN
) return i
;
478 top
= (LCD_HEIGHT
-height
*TileSize
)/2;
479 left
= (LCD_WIDTH
-width
*TileSize
)/2;
481 rb
->srand( *rb
->current_tick
);
492 /* clear the screen buffer */
493 #ifdef HAVE_LCD_COLOR
494 rb
->lcd_set_background( BackgroundColor
);
496 rb
->lcd_clear_display();
498 /* display the mine field */
499 for( i
= 0; i
< height
; i
++ )
501 for( j
= 0; j
< width
; j
++ )
503 if( minefield
[i
][j
].known
)
505 draw_tile( minefield
[i
][j
].neighbors
, j
, i
);
507 else if(minefield
[i
][j
].flag
)
509 draw_tile( Flag
, j
, i
);
513 draw_tile( Unknown
, j
, i
);
518 /* display the cursor */
521 /* update the screen */
524 switch( button
= rb
->button_get( true ) )
526 /* quit minesweeper (you really shouldn't use this button ...) */
527 #ifdef MINESWP_RC_QUIT
528 case MINESWP_RC_QUIT
:
531 return MINESWEEPER_QUIT
;
533 /* move cursor left */
535 case BUTTON_LEFT
|BUTTON_REPEAT
:
536 x
= ( x
+ width
- 1 )%width
;
539 /* move cursor right */
541 case BUTTON_RIGHT
|BUTTON_REPEAT
:
545 /* move cursor down */
547 case MINESWP_DOWN
|BUTTON_REPEAT
:
548 y
= ( y
+ 1 )%height
;
553 case MINESWP_UP
|BUTTON_REPEAT
:
554 y
= ( y
+ height
- 1 )%height
;
557 /*move cursor though the entire field*/
558 #ifdef MINESWP_SCROLLWHEEL
560 case MINESWP_NEXT
|BUTTON_REPEAT
:
561 if (x
== width
-1 ) {
562 y
= ( y
+ 1 )%height
;
568 case MINESWP_PREV
|BUTTON_REPEAT
:
570 y
= ( y
+ height
- 1 )%height
;
572 x
= ( x
+ width
- 1 )%width
;
575 /* discover a tile (and it's neighbors if .neighbors == 0) */
576 case MINESWP_DISCOVER
:
577 #ifdef MINESWP_DISCOVER2
578 case MINESWP_DISCOVER2
:
580 if( minefield
[y
][x
].flag
) break;
581 /* we put the mines on the first "click" so that you don't
582 * lose on the first "click" */
583 if( tiles_left
== width
*height
&& no_mines
)
584 minesweeper_putmines(p
,x
,y
);
588 if( minefield
[y
][x
].mine
)
590 minefield
[y
][x
].known
= 1;
591 return MINESWEEPER_LOSE
;
593 tiles_left
= count_tiles_left();
594 if( tiles_left
== mine_num
)
596 return MINESWEEPER_WIN
;
600 /* toggle flag under cursor */
602 #ifdef MINESWP_TOGGLE_PRE
603 if( lastbutton
!= MINESWP_TOGGLE_PRE
)
606 #ifdef MINESWP_TOGGLE2
607 case MINESWP_TOGGLE2
:
609 minefield
[y
][x
].flag
= ( minefield
[y
][x
].flag
+ 1 )%2;
612 /* show how many mines you think you have found and how many
613 * there really are on the game */
617 flags_used
= count_flags();
618 if (flags_used
== 1) {
619 rb
->splash( HZ
*2, "You marked 1 field. There are %d mines.",
624 rb
->splash( HZ
*2, "You marked %d fields. There are %d mines.",
625 flags_used
, mine_num
);
630 if( rb
->default_event_handler( button
) == SYS_USB_CONNECTED
)
631 return MINESWEEPER_USB
;
634 if( button
!= BUTTON_NONE
)
640 /* plugin entry point */
641 enum plugin_status
plugin_start(struct plugin_api
* api
, void* parameter
)
648 rb
->lcd_set_backdrop(NULL
);
653 switch( minesweeper() )
655 case MINESWEEPER_WIN
:
656 rb
->splash( HZ
, "You Win!" );
657 rb
->lcd_clear_display();
661 case MINESWEEPER_LOSE
:
662 rb
->splash( HZ
, "You Lose!" );
663 rb
->lcd_clear_display();
667 case MINESWEEPER_USB
:
668 return PLUGIN_USB_CONNECTED
;
670 case MINESWEEPER_QUIT
: