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_LEFT BUTTON_LEFT
37 # define MINESWP_RIGHT BUTTON_RIGHT
38 # define MINESWP_UP BUTTON_UP
39 # define MINESWP_DOWN BUTTON_DOWN
40 # define MINESWP_QUIT BUTTON_OFF
41 # define MINESWP_TOGGLE BUTTON_ON
42 # define MINESWP_TOGGLE2 BUTTON_F1
43 # define MINESWP_DISCOVER BUTTON_PLAY
44 # define MINESWP_DISCOVER2 BUTTON_F2
45 # define MINESWP_INFO BUTTON_F3
47 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
48 # define MINESWP_LEFT BUTTON_LEFT
49 # define MINESWP_RIGHT BUTTON_RIGHT
50 # define MINESWP_UP BUTTON_UP
51 # define MINESWP_DOWN BUTTON_DOWN
52 # define MINESWP_QUIT BUTTON_OFF
53 # define MINESWP_TOGGLE BUTTON_ON
54 # define MINESWP_TOGGLE2 BUTTON_F1
55 # define MINESWP_DISCOVER BUTTON_SELECT
56 # define MINESWP_DISCOVER2 BUTTON_F2
57 # define MINESWP_INFO BUTTON_F3
59 #elif CONFIG_KEYPAD == ONDIO_PAD
60 # define MINESWP_LEFT BUTTON_LEFT
61 # define MINESWP_RIGHT BUTTON_RIGHT
62 # define MINESWP_UP BUTTON_UP
63 # define MINESWP_DOWN BUTTON_DOWN
64 # define MINESWP_QUIT BUTTON_OFF
65 # define MINESWP_TOGGLE_PRE BUTTON_MENU
66 # define MINESWP_TOGGLE (BUTTON_MENU | BUTTON_REL)
67 # define MINESWP_DISCOVER (BUTTON_MENU | BUTTON_REPEAT)
68 # define MINESWP_INFO (BUTTON_MENU | BUTTON_OFF)
70 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
71 (CONFIG_KEYPAD == IRIVER_H300_PAD)
72 # define MINESWP_LEFT BUTTON_LEFT
73 # define MINESWP_RIGHT BUTTON_RIGHT
74 # define MINESWP_UP BUTTON_UP
75 # define MINESWP_DOWN BUTTON_DOWN
76 # define MINESWP_QUIT BUTTON_OFF
77 # define MINESWP_TOGGLE BUTTON_ON
78 # define MINESWP_TOGGLE2 BUTTON_REC
79 # define MINESWP_DISCOVER BUTTON_SELECT
80 # define MINESWP_INFO BUTTON_MODE
82 # define MINESWP_RC_QUIT BUTTON_RC_STOP
84 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
85 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
86 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
87 # define MINESWP_SCROLLWHEEL
88 # define MINESWP_LEFT BUTTON_LEFT
89 # define MINESWP_RIGHT BUTTON_RIGHT
90 # define MINESWP_UP BUTTON_MENU
91 # define MINESWP_DOWN BUTTON_PLAY
92 # define MINESWP_NEXT BUTTON_SCROLL_FWD
93 # define MINESWP_PREV BUTTON_SCROLL_BACK
94 # define MINESWP_QUIT (BUTTON_SELECT | BUTTON_MENU)
95 # define MINESWP_TOGGLE_PRE BUTTON_SELECT
96 # define MINESWP_TOGGLE (BUTTON_SELECT | BUTTON_REL)
97 # define MINESWP_DISCOVER (BUTTON_SELECT | BUTTON_REPEAT)
98 # define MINESWP_INFO (BUTTON_SELECT | BUTTON_PLAY)
100 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
101 # define MINESWP_LEFT BUTTON_LEFT
102 # define MINESWP_RIGHT BUTTON_RIGHT
103 # define MINESWP_UP BUTTON_UP
104 # define MINESWP_DOWN BUTTON_DOWN
105 # define MINESWP_QUIT BUTTON_POWER
106 # define MINESWP_TOGGLE BUTTON_PLAY
107 # define MINESWP_DISCOVER BUTTON_SELECT
108 # define MINESWP_INFO BUTTON_REC
110 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
111 # define MINESWP_LEFT BUTTON_LEFT
112 # define MINESWP_RIGHT BUTTON_RIGHT
113 # define MINESWP_UP BUTTON_UP
114 # define MINESWP_DOWN BUTTON_DOWN
115 # define MINESWP_QUIT BUTTON_POWER
116 # define MINESWP_TOGGLE BUTTON_A
117 # define MINESWP_DISCOVER BUTTON_SELECT
118 # define MINESWP_INFO BUTTON_MENU
120 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
121 # define MINESWP_SCROLLWHEEL
122 # define MINESWP_LEFT BUTTON_LEFT
123 # define MINESWP_RIGHT BUTTON_RIGHT
124 # define MINESWP_UP BUTTON_UP
125 # define MINESWP_DOWN BUTTON_DOWN
126 # define MINESWP_QUIT BUTTON_POWER
127 # define MINESWP_NEXT BUTTON_SCROLL_FWD
128 # define MINESWP_PREV BUTTON_SCROLL_BACK
129 # define MINESWP_TOGGLE BUTTON_REC
130 # define MINESWP_DISCOVER BUTTON_SELECT
131 # define MINESWP_INFO (BUTTON_REC|BUTTON_REPEAT)
133 #elif (CONFIG_KEYPAD == SANSA_C200_PAD)
134 # define MINESWP_LEFT BUTTON_LEFT
135 # define MINESWP_RIGHT BUTTON_RIGHT
136 # define MINESWP_UP BUTTON_UP
137 # define MINESWP_DOWN BUTTON_DOWN
138 # define MINESWP_QUIT BUTTON_POWER
139 # define MINESWP_TOGGLE_PRE BUTTON_SELECT
140 # define MINESWP_TOGGLE (BUTTON_SELECT | BUTTON_REL)
141 # define MINESWP_TOGGLE2 BUTTON_VOL_DOWN
142 # define MINESWP_DISCOVER (BUTTON_SELECT | BUTTON_REPEAT)
143 # define MINESWP_DISCOVER2 BUTTON_VOL_UP
144 # define MINESWP_INFO BUTTON_REC
146 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
147 # define MINESWP_LEFT BUTTON_LEFT
148 # define MINESWP_RIGHT BUTTON_RIGHT
149 # define MINESWP_UP BUTTON_SCROLL_UP
150 # define MINESWP_DOWN BUTTON_SCROLL_DOWN
151 # define MINESWP_QUIT BUTTON_POWER
152 # define MINESWP_TOGGLE BUTTON_PLAY
153 # define MINESWP_DISCOVER BUTTON_REW
154 # define MINESWP_INFO (BUTTON_REW | BUTTON_PLAY)
156 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
157 # define MINESWP_LEFT BUTTON_LEFT
158 # define MINESWP_RIGHT BUTTON_RIGHT
159 # define MINESWP_UP BUTTON_UP
160 # define MINESWP_DOWN BUTTON_DOWN
161 # define MINESWP_QUIT BUTTON_BACK
162 # define MINESWP_TOGGLE BUTTON_PLAY
163 # define MINESWP_DISCOVER BUTTON_SELECT
164 # define MINESWP_INFO BUTTON_MENU
166 #elif (CONFIG_KEYPAD == MROBE100_PAD)
167 # define MINESWP_LEFT BUTTON_LEFT
168 # define MINESWP_RIGHT BUTTON_RIGHT
169 # define MINESWP_UP BUTTON_UP
170 # define MINESWP_DOWN BUTTON_DOWN
171 # define MINESWP_QUIT BUTTON_POWER
172 # define MINESWP_TOGGLE BUTTON_DISPLAY
173 # define MINESWP_DISCOVER BUTTON_SELECT
174 # define MINESWP_INFO BUTTON_MENU
176 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
177 # define MINESWP_LEFT BUTTON_RC_REW
178 # define MINESWP_RIGHT BUTTON_RC_FF
179 # define MINESWP_UP BUTTON_RC_VOL_UP
180 # define MINESWP_DOWN BUTTON_RC_VOL_DOWN
181 # define MINESWP_QUIT BUTTON_RC_REC
182 # define MINESWP_TOGGLE BUTTON_RC_MODE
183 # define MINESWP_DISCOVER BUTTON_RC_PLAY
184 # define MINESWP_INFO BUTTON_RC_MENU
186 #elif (CONFIG_KEYPAD == COWOND2_PAD)
187 # define MINESWP_LEFT BUTTON_LEFT
188 # define MINESWP_RIGHT BUTTON_RIGHT
189 # define MINESWP_UP BUTTON_UP
190 # define MINESWP_DOWN BUTTON_DOWN
191 # define MINESWP_QUIT BUTTON_POWER
192 # define MINESWP_TOGGLE BUTTON_SELECT
193 # define MINESWP_DISCOVER BUTTON_MENU
194 # define MINESWP_INFO (BUTTON_MENU | BUTTON_SELECT)
197 #error No keymap defined!
200 /* here is a global api struct pointer. while not strictly necessary,
201 * it's nice not to have to pass the api pointer in all function calls
204 static struct plugin_api
*rb
;
206 extern const fb_data minesweeper_tiles
[];
208 #ifdef HAVE_LCD_COLOR
209 # if ( LCD_HEIGHT * LCD_WIDTH ) / ( 16 * 16 ) >= 130
210 /* We want to have at least 130 tiles on the screen */
212 # elif ( LCD_HEIGHT * LCD_WIDTH ) / ( 12 * 12 ) >= 130
217 # define BackgroundColor LCD_RGBPACK( 128, 128, 128 )
227 #define ExplodedMine 12
229 #define draw_tile( num, x, y ) \
230 rb->lcd_bitmap_part( minesweeper_tiles, 0, num * TileSize, \
231 TileSize, left+x*TileSize, top+y*TileSize, \
234 #define invert_tile( x, y ) \
235 rb->lcd_set_drawmode(DRMODE_COMPLEMENT); \
236 rb->lcd_fillrect( left+x*TileSize, top+y*TileSize, TileSize, TileSize ); \
237 rb->lcd_set_drawmode(DRMODE_SOLID);
241 * if there is a mine, mine is true
242 * if tile is known by player, known is true
243 * if tile has a flag, flag is true
244 * neighbors is the total number of mines arround tile
248 unsigned char mine
: 1;
249 unsigned char known
: 1;
250 unsigned char flag
: 1;
251 unsigned char neighbors
: 4;
254 /* the height and width of the field */
255 #define MAX_HEIGHT (LCD_HEIGHT/TileSize)
256 #define MAX_WIDTH (LCD_WIDTH/TileSize)
257 int height
= MAX_HEIGHT
;
258 int width
= MAX_WIDTH
;
262 /* The Minefield. Caution it is defined as Y, X! Not the opposite. */
263 tile minefield
[MAX_HEIGHT
][MAX_WIDTH
];
265 /* total number of mines on the game */
268 /* percentage of mines on minefield used during generation */
271 /* number of tiles left on the game */
274 /* number of used flags on the game */
277 /* Because mines are set after the first move... */
278 bool no_mines
= true;
280 /* We need a stack (created on discover()) for the cascade algorithm. */
283 /* a usefull string for snprintf */
287 void push( int *stack
, int y
, int x
)
289 if( stack_pos
<= height
*width
)
291 stack
[++stack_pos
] = y
;
292 stack
[++stack_pos
] = x
;
296 /* Unveil tiles and push them to stack if they are empty. */
297 void unveil( int *stack
, int y
, int x
)
299 if( x
< 0 || y
< 0 || x
> width
- 1 || y
> height
- 1
300 || minefield
[y
][x
].known
301 || minefield
[y
][x
].mine
|| minefield
[y
][x
].flag
) return;
303 minefield
[y
][x
].known
= 1;
305 if( minefield
[y
][x
].neighbors
== 0 )
309 void discover( int y
, int x
)
311 int stack
[height
*width
];
314 if( x
< 0 || y
< 0 || x
> width
- 1 || y
> height
- 1
315 || minefield
[y
][x
].known
316 || minefield
[y
][x
].mine
|| minefield
[y
][x
].flag
) return;
318 minefield
[y
][x
].known
= 1;
319 /* Exit if the tile is not empty. (no mines nearby) */
320 if( minefield
[y
][x
].neighbors
) return;
324 /* Scan all nearby tiles. If we meet a tile with a number we just unveil
325 * it. If we meet an empty tile, we push the location in stack. For each
326 * location in stack we do the same thing. (scan again all nearby tiles)
330 /* Pop x, y from stack. */
331 x
= stack
[stack_pos
--];
332 y
= stack
[stack_pos
--];
334 unveil( stack
, y
-1, x
-1 );
335 unveil( stack
, y
-1, x
);
336 unveil( stack
, y
-1, x
+1 );
337 unveil( stack
, y
, x
+1 );
338 unveil( stack
, y
+1, x
+1 );
339 unveil( stack
, y
+1, x
);
340 unveil( stack
, y
+1, x
-1 );
341 unveil( stack
, y
, x
-1 );
345 /* Reset the whole board for a new game. */
346 void minesweeper_init( void )
350 for( i
= 0; i
< MAX_HEIGHT
; i
++ )
352 for( j
= 0; j
< MAX_WIDTH
; j
++ )
354 minefield
[i
][j
].known
= 0;
355 minefield
[i
][j
].flag
= 0;
356 minefield
[i
][j
].mine
= 0;
357 minefield
[i
][j
].neighbors
= 0;
361 tiles_left
= width
*height
;
365 /* put mines on the mine field */
366 /* there is p% chance that a tile is a mine */
367 /* if the tile has coordinates (x,y), then it can't be a mine */
368 void minesweeper_putmines( int p
, int x
, int y
)
373 for( i
= 0; i
< height
; i
++ )
375 for( j
= 0; j
< width
; j
++ )
377 if( rb
->rand()%100 < p
&& !( y
==i
&& x
==j
) )
379 minefield
[i
][j
].mine
= 1;
384 minefield
[i
][j
].mine
= 0;
386 minefield
[i
][j
].neighbors
= 0;
390 /* we need to compute the neighbor element for each tile */
391 for( i
= 0; i
< height
; i
++ )
393 for( j
= 0; j
< width
; j
++ )
398 minefield
[i
][j
].neighbors
+= minefield
[i
-1][j
-1].mine
;
399 minefield
[i
][j
].neighbors
+= minefield
[i
-1][j
].mine
;
401 minefield
[i
][j
].neighbors
+= minefield
[i
-1][j
+1].mine
;
404 minefield
[i
][j
].neighbors
+= minefield
[i
][j
-1].mine
;
406 minefield
[i
][j
].neighbors
+= minefield
[i
][j
+1].mine
;
410 minefield
[i
][j
].neighbors
+= minefield
[i
+1][j
-1].mine
;
411 minefield
[i
][j
].neighbors
+= minefield
[i
+1][j
].mine
;
413 minefield
[i
][j
].neighbors
+= minefield
[i
+1][j
+1].mine
;
420 /* In case the user is lucky and there are no mines positioned. */
421 if( !mine_num
&& height
*width
!= 1 )
423 minesweeper_putmines(p
, x
, y
);
427 /* A function that will uncover all the board, when the user wins or loses.
428 can easily be expanded, (just a call assigned to a button) as a solver. */
429 void mine_show( void )
433 for( i
= 0; i
< height
; i
++ )
435 for( j
= 0; j
< width
; j
++ )
437 if( minefield
[i
][j
].mine
)
439 if( minefield
[i
][j
].known
)
441 draw_tile( ExplodedMine
, j
, i
);
445 draw_tile( Mine
, j
, i
);
450 draw_tile( minefield
[i
][j
].neighbors
, j
, i
);
457 button
= rb
->button_get(true);
458 while( ( button
== BUTTON_NONE
)
459 || ( button
& (BUTTON_REL
|BUTTON_REPEAT
) ) );
462 int count_tiles_left( void )
466 for( i
= 0; i
< height
; i
++ )
467 for( j
= 0; j
< width
; j
++ )
468 if( minefield
[i
][j
].known
== 0 )
473 int count_flags( void )
477 for( i
= 0; i
< height
; i
++ )
478 for( j
= 0; j
< width
; j
++ )
479 if( minefield
[i
][j
].flag
== 1 )
484 /* welcome screen where player can chose mine percentage */
485 enum minesweeper_status
menu( void )
487 int selection
, result
= MINESWEEPER_QUIT
;
488 bool menu_quit
= false;
490 MENUITEM_STRINGLIST( menu
, "Minesweeper Menu", NULL
, "Play Minesweeper",
491 "Mine Percentage", "Number of Rows",
492 "Number of Columns", "Quit" );
494 #ifdef HAVE_LCD_COLOR
495 rb
->lcd_set_foreground( rb
->global_settings
->fg_color
);
496 rb
->lcd_set_background( rb
->global_settings
->bg_color
);
501 switch( rb
->do_menu( &menu
, &selection
, NULL
, false ) )
504 result
= MINESWEEPER_WIN
; /* start playing */
509 rb
->set_int( "Mine Percentage", "%", UNIT_INT
, &p
, NULL
,
514 rb
->set_int( "Number of Rows", "", UNIT_INT
, &height
, NULL
,
515 1, 1, MAX_HEIGHT
, NULL
);
519 rb
->set_int( "Number of Columns", "", UNIT_INT
, &width
, NULL
,
520 1, 1, MAX_WIDTH
, NULL
);
524 result
= MINESWEEPER_QUIT
; /* quit program */
533 /* the big and ugly game function */
534 enum minesweeper_status
minesweeper( void )
538 int lastbutton
= BUTTON_NONE
;
540 /* the cursor coordinates */
546 if( ( i
= menu() ) != MINESWEEPER_WIN
) return i
;
551 top
= (LCD_HEIGHT
-height
*TileSize
)/2;
552 left
= (LCD_WIDTH
-width
*TileSize
)/2;
554 rb
->srand( *rb
->current_tick
);
565 /* clear the screen buffer */
566 #ifdef HAVE_LCD_COLOR
567 rb
->lcd_set_background( BackgroundColor
);
569 rb
->lcd_clear_display();
571 /* display the mine field */
572 for( i
= 0; i
< height
; i
++ )
574 for( j
= 0; j
< width
; j
++ )
576 if( minefield
[i
][j
].known
)
578 draw_tile( minefield
[i
][j
].neighbors
, j
, i
);
580 else if(minefield
[i
][j
].flag
)
582 draw_tile( Flag
, j
, i
);
586 draw_tile( Unknown
, j
, i
);
591 /* display the cursor */
594 /* update the screen */
597 switch( button
= rb
->button_get( true ) )
599 /* quit minesweeper (you really shouldn't use this button ...) */
600 #ifdef MINESWP_RC_QUIT
601 case MINESWP_RC_QUIT
:
604 return MINESWEEPER_QUIT
;
606 /* move cursor left */
608 case MINESWP_LEFT
|BUTTON_REPEAT
:
609 x
= ( x
+ width
- 1 )%width
;
612 /* move cursor right */
614 case MINESWP_RIGHT
|BUTTON_REPEAT
:
618 /* move cursor down */
620 case MINESWP_DOWN
|BUTTON_REPEAT
:
621 y
= ( y
+ 1 )%height
;
626 case MINESWP_UP
|BUTTON_REPEAT
:
627 y
= ( y
+ height
- 1 )%height
;
630 /*move cursor though the entire field*/
631 #ifdef MINESWP_SCROLLWHEEL
633 case MINESWP_NEXT
|BUTTON_REPEAT
:
634 if (x
== width
-1 ) {
635 y
= ( y
+ 1 )%height
;
641 case MINESWP_PREV
|BUTTON_REPEAT
:
643 y
= ( y
+ height
- 1 )%height
;
645 x
= ( x
+ width
- 1 )%width
;
648 /* discover a tile (and it's neighbors if .neighbors == 0) */
649 case MINESWP_DISCOVER
:
650 #ifdef MINESWP_DISCOVER2
651 case MINESWP_DISCOVER2
:
653 if( minefield
[y
][x
].flag
) break;
654 /* we put the mines on the first "click" so that you don't
655 * lose on the first "click" */
656 if( tiles_left
== width
*height
&& no_mines
)
657 minesweeper_putmines(p
,x
,y
);
661 if( minefield
[y
][x
].mine
)
663 minefield
[y
][x
].known
= 1;
664 return MINESWEEPER_LOSE
;
666 tiles_left
= count_tiles_left();
667 if( tiles_left
== mine_num
)
669 return MINESWEEPER_WIN
;
673 /* toggle flag under cursor */
675 #ifdef MINESWP_TOGGLE_PRE
676 if( lastbutton
!= MINESWP_TOGGLE_PRE
)
679 #ifdef MINESWP_TOGGLE2
680 case MINESWP_TOGGLE2
:
682 minefield
[y
][x
].flag
= ( minefield
[y
][x
].flag
+ 1 )%2;
685 /* show how many mines you think you have found and how many
686 * there really are on the game */
690 flags_used
= count_flags();
691 if (flags_used
== 1) {
692 rb
->splash( HZ
*2, "You marked 1 field. There are %d mines.",
697 rb
->splash( HZ
*2, "You marked %d fields. There are %d mines.",
698 flags_used
, mine_num
);
703 if( rb
->default_event_handler( button
) == SYS_USB_CONNECTED
)
704 return MINESWEEPER_USB
;
707 if( button
!= BUTTON_NONE
)
713 /* plugin entry point */
714 enum plugin_status
plugin_start(struct plugin_api
* api
, void* parameter
)
721 rb
->lcd_set_backdrop(NULL
);
726 switch( minesweeper() )
728 case MINESWEEPER_WIN
:
729 rb
->splash( HZ
, "You Win!" );
730 rb
->lcd_clear_display();
734 case MINESWEEPER_LOSE
:
735 rb
->splash( HZ
, "You Lose!" );
736 rb
->lcd_clear_display();
740 case MINESWEEPER_USB
:
741 return PLUGIN_USB_CONNECTED
;
743 case MINESWEEPER_QUIT
: