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_START BUTTON_ON
40 # define MINESWP_TOGGLE BUTTON_PLAY
41 # define MINESWP_TOGGLE2 BUTTON_F1
42 # define MINESWP_DISCOVER BUTTON_ON
43 # define MINESWP_DISCOVER2 BUTTON_F2
44 # define MINESWP_INFO BUTTON_F3
45 # define MINESWP_RIGHT (BUTTON_F1 | BUTTON_RIGHT)
46 # define MINESWP_LEFT (BUTTON_F1 | BUTTON_LEFT)
48 #elif CONFIG_KEYPAD == ONDIO_PAD
49 # define MINESWP_UP BUTTON_UP
50 # define MINESWP_DOWN BUTTON_DOWN
51 # define MINESWP_QUIT BUTTON_OFF
52 # define MINESWP_START BUTTON_MENU
53 # define MINESWP_TOGGLE_PRE BUTTON_MENU
54 # define MINESWP_TOGGLE (BUTTON_MENU | BUTTON_REL)
55 # define MINESWP_DISCOVER (BUTTON_MENU | BUTTON_REPEAT)
56 # define MINESWP_INFO (BUTTON_MENU | BUTTON_OFF)
57 # define MINESWP_RIGHT (BUTTON_MENU | BUTTON_RIGHT)
58 # define MINESWP_LEFT (BUTTON_MENU | BUTTON_LEFT)
60 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
61 (CONFIG_KEYPAD == IRIVER_H300_PAD)
62 # define MINESWP_UP BUTTON_UP
63 # define MINESWP_DOWN BUTTON_DOWN
64 # define MINESWP_QUIT BUTTON_OFF
65 # define MINESWP_START BUTTON_SELECT
66 # define MINESWP_TOGGLE BUTTON_ON
67 # define MINESWP_DISCOVER BUTTON_SELECT
68 # define MINESWP_INFO BUTTON_MODE
69 # define MINESWP_RIGHT (BUTTON_ON | BUTTON_RIGHT)
70 # define MINESWP_LEFT (BUTTON_ON | BUTTON_LEFT)
72 # define MINESWP_RC_QUIT BUTTON_RC_STOP
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)
83 # define MINESWP_RIGHT (BUTTON_SELECT | BUTTON_RIGHT)
84 # define MINESWP_LEFT (BUTTON_SELECT | BUTTON_LEFT)
86 #elif (CONFIG_KEYPAD == IAUDIO_X5_PAD)
87 # define MINESWP_UP BUTTON_UP
88 # define MINESWP_DOWN BUTTON_DOWN
89 # define MINESWP_QUIT BUTTON_POWER
90 # define MINESWP_START BUTTON_REC
91 # define MINESWP_TOGGLE BUTTON_PLAY
92 # define MINESWP_DISCOVER BUTTON_SELECT
93 # define MINESWP_INFO (BUTTON_REC | BUTTON_PLAY)
94 # define MINESWP_RIGHT (BUTTON_PLAY | BUTTON_RIGHT)
95 # define MINESWP_LEFT (BUTTON_PLAY | BUTTON_LEFT)
97 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
98 # define MINESWP_UP BUTTON_UP
99 # define MINESWP_DOWN BUTTON_DOWN
100 # define MINESWP_QUIT BUTTON_A
101 # define MINESWP_START BUTTON_SELECT
102 # define MINESWP_TOGGLE BUTTON_POWER
103 # define MINESWP_DISCOVER BUTTON_SELECT
104 # define MINESWP_INFO BUTTON_MENU
105 # define MINESWP_RIGHT (BUTTON_SELECT | BUTTON_RIGHT)
106 # define MINESWP_LEFT (BUTTON_SELECT | BUTTON_LEFT)
108 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
109 # define MINESWP_UP BUTTON_SCROLL_UP
110 # define MINESWP_DOWN BUTTON_SCROLL_DOWN
111 # define MINESWP_QUIT BUTTON_POWER
112 # define MINESWP_START BUTTON_FF
113 # define MINESWP_TOGGLE BUTTON_PLAY
114 # define MINESWP_DISCOVER BUTTON_REW
115 # define MINESWP_INFO (BUTTON_REW | BUTTON_PLAY)
116 # define MINESWP_RIGHT (BUTTON_RIGHT | BUTTON_PLAY)
117 # define MINESWP_LEFT (BUTTON_LEFT | BUTTON_PLAY)
120 # warning Missing key definitions for this keypad
123 /* here is a global api struct pointer. while not strictly necessary,
124 * it's nice not to have to pass the api pointer in all function calls
127 static struct plugin_api
*rb
;
129 extern const fb_data minesweeper_tiles
[];
131 #ifdef HAVE_LCD_COLOR
132 # if ( LCD_HEIGHT * LCD_WIDTH ) / ( 16 * 16 ) >= 130
133 /* We want to have at least 130 tiles on the screen */
138 # define BackgroundColor LCD_RGBPACK( 128, 128, 128 )
148 #define ExplodedMine 12
150 #define draw_tile( num, x, y ) \
151 rb->lcd_bitmap_part( minesweeper_tiles, 0, num * TileSize, \
152 TileSize, left+x*TileSize, top+y*TileSize, \
155 #define invert_tile( x, y ) \
156 rb->lcd_set_drawmode(DRMODE_COMPLEMENT); \
157 rb->lcd_fillrect( left+x*TileSize, top+y*TileSize, TileSize, TileSize ); \
158 rb->lcd_set_drawmode(DRMODE_SOLID);
162 * if there is a mine, mine is true
163 * if tile is known by player, known is true
164 * if tile has a flag, flag is true
165 * neighbors is the total number of mines arround tile
169 unsigned char mine
: 1;
170 unsigned char known
: 1;
171 unsigned char flag
: 1;
172 unsigned char neighbors
: 4;
175 /* the height and width of the field */
176 #define MAX_HEIGHT (LCD_HEIGHT/TileSize)
177 #define MAX_WIDTH (LCD_WIDTH/TileSize)
178 int height
= MAX_HEIGHT
;
179 int width
= MAX_WIDTH
;
183 /* The Minefield. Caution it is defined as Y, X! Not the opposite. */
184 tile minefield
[MAX_HEIGHT
][MAX_WIDTH
];
186 /* total number of mines on the game */
189 /* percentage of mines on minefield used during generation */
192 /* number of tiles left on the game */
195 /* Because mines are set after the first move... */
196 bool no_mines
= true;
198 /* We need a stack (created on discover()) for the cascade algorithm. */
201 /* a usefull string for snprintf */
205 void push( int *stack
, int y
, int x
)
207 if( stack_pos
<= height
*width
)
209 stack
[++stack_pos
] = y
;
210 stack
[++stack_pos
] = x
;
214 /* Unveil tiles and push them to stack if they are empty. */
215 void unveil( int *stack
, int y
, int x
)
217 if( x
< 0 || y
< 0 || x
> width
- 1 || y
> height
- 1
218 || minefield
[y
][x
].known
219 || minefield
[y
][x
].mine
|| minefield
[y
][x
].flag
) return;
221 minefield
[y
][x
].known
= 1;
223 if( minefield
[y
][x
].neighbors
== 0 )
227 void discover( int y
, int x
)
229 int stack
[height
*width
];
232 if( x
< 0 || y
< 0 || x
> width
- 1 || y
> height
- 1
233 || minefield
[y
][x
].known
234 || minefield
[y
][x
].mine
|| minefield
[y
][x
].flag
) return;
236 minefield
[y
][x
].known
= 1;
237 /* Exit if the tile is not empty. (no mines nearby) */
238 if( minefield
[y
][x
].neighbors
) return;
242 /* Scan all nearby tiles. If we meet a tile with a number we just unveil
243 * it. If we meet an empty tile, we push the location in stack. For each
244 * location in stack we do the same thing. (scan again all nearby tiles)
248 /* Pop x, y from stack. */
249 x
= stack
[stack_pos
--];
250 y
= stack
[stack_pos
--];
252 unveil( stack
, y
-1, x
-1 );
253 unveil( stack
, y
-1, x
);
254 unveil( stack
, y
-1, x
+1 );
255 unveil( stack
, y
, x
+1 );
256 unveil( stack
, y
+1, x
+1 );
257 unveil( stack
, y
+1, x
);
258 unveil( stack
, y
+1, x
-1 );
259 unveil( stack
, y
, x
-1 );
263 /* Reset the whole board for a new game. */
264 void minesweeper_init( void )
268 for( i
= 0; i
< MAX_HEIGHT
; i
++ )
270 for( j
= 0; j
< MAX_WIDTH
; j
++ )
272 minefield
[i
][j
].known
= 0;
273 minefield
[i
][j
].flag
= 0;
274 minefield
[i
][j
].mine
= 0;
275 minefield
[i
][j
].neighbors
= 0;
279 tiles_left
= width
*height
;
283 /* put mines on the mine field */
284 /* there is p% chance that a tile is a mine */
285 /* if the tile has coordinates (x,y), then it can't be a mine */
286 void minesweeper_putmines( int p
, int x
, int y
)
291 for( i
= 0; i
< height
; i
++ )
293 for( j
= 0; j
< width
; j
++ )
295 if( rb
->rand()%100 < p
&& !( y
==i
&& x
==j
) )
297 minefield
[i
][j
].mine
= 1;
302 minefield
[i
][j
].mine
= 0;
304 minefield
[i
][j
].neighbors
= 0;
308 /* we need to compute the neighbor element for each tile */
309 for( i
= 0; i
< height
; i
++ )
311 for( j
= 0; j
< width
; j
++ )
316 minefield
[i
][j
].neighbors
+= minefield
[i
-1][j
-1].mine
;
317 minefield
[i
][j
].neighbors
+= minefield
[i
-1][j
].mine
;
319 minefield
[i
][j
].neighbors
+= minefield
[i
-1][j
+1].mine
;
322 minefield
[i
][j
].neighbors
+= minefield
[i
][j
-1].mine
;
324 minefield
[i
][j
].neighbors
+= minefield
[i
][j
+1].mine
;
328 minefield
[i
][j
].neighbors
+= minefield
[i
+1][j
-1].mine
;
329 minefield
[i
][j
].neighbors
+= minefield
[i
+1][j
].mine
;
331 minefield
[i
][j
].neighbors
+= minefield
[i
+1][j
+1].mine
;
338 /* In case the user is lucky and there are no mines positioned. */
339 if( !mine_num
&& height
*width
!= 1 )
341 minesweeper_putmines(p
, x
, y
);
345 /* A function that will uncover all the board, when the user wins or loses.
346 can easily be expanded, (just a call assigned to a button) as a solver. */
347 void mine_show( void )
351 for( i
= 0; i
< height
; i
++ )
353 for( j
= 0; j
< width
; j
++ )
355 if( minefield
[i
][j
].mine
)
357 if( minefield
[i
][j
].known
)
359 draw_tile( ExplodedMine
, j
, i
);
363 draw_tile( Mine
, j
, i
);
368 draw_tile( minefield
[i
][j
].neighbors
, j
, i
);
375 button
= rb
->button_get(true);
376 while( ( button
== BUTTON_NONE
)
377 || ( button
& (BUTTON_REL
|BUTTON_REPEAT
) ) );
380 int count_tiles_left( void )
384 for( i
= 0; i
< height
; i
++ )
385 for( j
= 0; j
< width
; j
++ )
386 if( minefield
[i
][j
].known
== 0 )
391 /* welcome screen where player can chose mine percentage */
392 enum minesweeper_status
menu( void )
398 #ifdef HAVE_LCD_COLOR
399 rb
->lcd_set_background( LCD_WHITE
);
400 rb
->lcd_set_foreground( LCD_BLACK
);
402 rb
->lcd_clear_display();
404 rb
->lcd_puts( 0, 0, "Mine Sweeper" );
406 rb
->snprintf( str
, 20, "%d%% mines", p
);
407 rb
->lcd_puts( 0, 2, str
);
408 rb
->lcd_puts( 0, 3, "down / up" );
409 rb
->snprintf( str
, 20, "%d cols x %d rows", width
, height
);
410 rb
->lcd_puts( 0, 4, str
);
411 rb
->lcd_puts( 0, 5, "left x right" );
413 #if CONFIG_KEYPAD == RECORDER_PAD
415 #elif CONFIG_KEYPAD == ONDIO_PAD
417 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) \
418 || (CONFIG_KEYPAD == IRIVER_H300_PAD ) \
419 || (CONFIG_KEYPAD == IPOD_4G_PAD)
421 #elif CONFIG_KEYPAD == IAUDIO_X5_PAD
425 # warning Please define help string for this keypad.
430 switch( button
= rb
->button_get( true ) )
433 case MINESWP_DOWN
|BUTTON_REPEAT
:
438 case MINESWP_UP
|BUTTON_REPEAT
:
443 case BUTTON_RIGHT
|BUTTON_REPEAT
:
444 height
= height
%MAX_HEIGHT
+ 1;
448 case BUTTON_LEFT
|BUTTON_REPEAT
:
449 width
= width
%MAX_WIDTH
+ 1;
453 case MINESWP_RIGHT
|BUTTON_REPEAT
:
455 if( height
< 1 ) height
= MAX_HEIGHT
;
459 case MINESWP_LEFT
|BUTTON_REPEAT
:
461 if( width
< 1 ) width
= MAX_WIDTH
;
464 case MINESWP_START
:/* start playing */
465 return MINESWEEPER_WIN
;
467 #ifdef MINESWP_RC_QUIT
468 case MINESWP_RC_QUIT
:
470 case MINESWP_QUIT
:/* quit program */
471 return MINESWEEPER_QUIT
;
474 if( rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
475 return MINESWEEPER_USB
;
481 /* the big and ugly game function */
482 enum minesweeper_status
minesweeper( void )
486 int lastbutton
= BUTTON_NONE
;
488 /* the cursor coordinates */
494 if( ( i
= menu() ) != MINESWEEPER_WIN
) return i
;
499 top
= (LCD_HEIGHT
-height
*TileSize
)/2;
500 left
= (LCD_WIDTH
-width
*TileSize
)/2;
502 rb
->srand( *rb
->current_tick
);
513 /* clear the screen buffer */
514 #ifdef HAVE_LCD_COLOR
515 rb
->lcd_set_background( BackgroundColor
);
517 rb
->lcd_clear_display();
519 /* display the mine field */
520 for( i
= 0; i
< height
; i
++ )
522 for( j
= 0; j
< width
; j
++ )
524 if( minefield
[i
][j
].known
)
526 draw_tile( minefield
[i
][j
].neighbors
, j
, i
);
528 else if(minefield
[i
][j
].flag
)
530 draw_tile( Flag
, j
, i
);
534 draw_tile( Unknown
, j
, i
);
539 /* display the cursor */
542 /* update the screen */
545 switch( button
= rb
->button_get( true ) )
547 /* quit minesweeper (you really shouldn't use this button ...) */
548 #ifdef MINESWP_RC_QUIT
549 case MINESWP_RC_QUIT
:
552 return MINESWEEPER_QUIT
;
554 /* move cursor left */
556 case BUTTON_LEFT
|BUTTON_REPEAT
:
557 x
= ( x
+ width
- 1 )%width
;
560 /* move cursor right */
562 case BUTTON_RIGHT
|BUTTON_REPEAT
:
566 /* move cursor down */
568 case MINESWP_DOWN
|BUTTON_REPEAT
:
569 y
= ( y
+ 1 )%height
;
574 case MINESWP_UP
|BUTTON_REPEAT
:
575 y
= ( y
+ height
- 1 )%height
;
578 /* discover a tile (and it's neighbors if .neighbors == 0) */
579 case MINESWP_DISCOVER
:
580 #ifdef MINESWP_DISCOVER2
581 case MINESWP_DISCOVER2
:
583 if( minefield
[y
][x
].flag
) break;
584 /* we put the mines on the first "click" so that you don't
585 * lose on the first "click" */
586 if( tiles_left
== width
*height
&& no_mines
)
587 minesweeper_putmines(p
,x
,y
);
591 if( minefield
[y
][x
].mine
)
593 minefield
[y
][x
].known
= 1;
594 return MINESWEEPER_LOSE
;
596 tiles_left
= count_tiles_left();
597 if( tiles_left
== mine_num
)
599 return MINESWEEPER_WIN
;
603 /* toggle flag under cursor */
605 #ifdef MINESWP_TOGGLE_PRE
606 if( lastbutton
!= MINESWP_TOGGLE_PRE
)
609 #ifdef MINESWP_TOGGLE2
610 case MINESWP_TOGGLE2
:
612 minefield
[y
][x
].flag
= ( minefield
[y
][x
].flag
+ 1 )%2;
615 /* show how many mines you think you have found and how many
616 * there really are on the game */
620 tiles_left
= count_tiles_left();
621 rb
->splash( HZ
*2, true, "You found %d mines out of %d",
622 tiles_left
, mine_num
);
626 if( rb
->default_event_handler( button
) == SYS_USB_CONNECTED
)
627 return MINESWEEPER_USB
;
630 if( button
!= BUTTON_NONE
)
636 /* plugin entry point */
637 enum plugin_status
plugin_start(struct plugin_api
* api
, void* parameter
)
646 switch( minesweeper() )
648 case MINESWEEPER_WIN
:
649 rb
->splash( HZ
, true, "You Win!" );
650 rb
->lcd_clear_display();
654 case MINESWEEPER_LOSE
:
655 rb
->splash( HZ
, true, "You Lose!" );
656 rb
->lcd_clear_display();
660 case MINESWEEPER_USB
:
661 return PLUGIN_USB_CONNECTED
;
663 case MINESWEEPER_QUIT
: