1
0
Fork 0
forked from len0rd/rockbox

Patch #5384 from Alexander Spyridakis - fixes bug report 4908

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9968 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Dave Chapman 2006-05-20 14:01:00 +00:00
parent 217f54bc69
commit fd18a4e9d3

View file

@ -19,12 +19,6 @@
/***************************************************************************** /*****************************************************************************
Mine Sweeper by dionoea Mine Sweeper by dionoea
use arrow keys to move cursor
use ON or F2 to clear a tile
use PLAY or F1 to put a flag on a tile
use F3 to see how many mines are left (supposing all your flags are correct)
*****************************************************************************/ *****************************************************************************/
#include "plugin.h" #include "plugin.h"
@ -50,6 +44,8 @@ PLUGIN_HEADER
#define MINESWP_DISCOVER BUTTON_ON #define MINESWP_DISCOVER BUTTON_ON
#define MINESWP_DISCOVER2 BUTTON_F2 #define MINESWP_DISCOVER2 BUTTON_F2
#define MINESWP_INFO BUTTON_F3 #define MINESWP_INFO BUTTON_F3
#define MINESWP_RIGHT (BUTTON_F1 | BUTTON_RIGHT)
#define MINESWP_LEFT (BUTTON_F1 | BUTTON_LEFT)
#elif CONFIG_KEYPAD == ONDIO_PAD #elif CONFIG_KEYPAD == ONDIO_PAD
#define MINESWP_UP BUTTON_UP #define MINESWP_UP BUTTON_UP
@ -60,6 +56,8 @@ PLUGIN_HEADER
#define MINESWP_TOGGLE (BUTTON_MENU | BUTTON_REL) #define MINESWP_TOGGLE (BUTTON_MENU | BUTTON_REL)
#define MINESWP_DISCOVER (BUTTON_MENU | BUTTON_REPEAT) #define MINESWP_DISCOVER (BUTTON_MENU | BUTTON_REPEAT)
#define MINESWP_INFO (BUTTON_MENU | BUTTON_OFF) #define MINESWP_INFO (BUTTON_MENU | BUTTON_OFF)
#define MINESWP_RIGHT (BUTTON_MENU | BUTTON_RIGHT)
#define MINESWP_LEFT (BUTTON_MENU | BUTTON_LEFT)
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
(CONFIG_KEYPAD == IRIVER_H300_PAD) (CONFIG_KEYPAD == IRIVER_H300_PAD)
@ -67,9 +65,11 @@ PLUGIN_HEADER
#define MINESWP_DOWN BUTTON_DOWN #define MINESWP_DOWN BUTTON_DOWN
#define MINESWP_QUIT BUTTON_OFF #define MINESWP_QUIT BUTTON_OFF
#define MINESWP_START BUTTON_SELECT #define MINESWP_START BUTTON_SELECT
#define MINESWP_TOGGLE BUTTON_SELECT #define MINESWP_TOGGLE BUTTON_ON
#define MINESWP_DISCOVER BUTTON_ON #define MINESWP_DISCOVER BUTTON_SELECT
#define MINESWP_INFO BUTTON_MODE #define MINESWP_INFO BUTTON_MODE
#define MINESWP_RIGHT (BUTTON_ON | BUTTON_RIGHT)
#define MINESWP_LEFT (BUTTON_ON | BUTTON_LEFT)
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) (CONFIG_KEYPAD == IPOD_3G_PAD)
@ -80,6 +80,8 @@ PLUGIN_HEADER
#define MINESWP_TOGGLE BUTTON_PLAY #define MINESWP_TOGGLE BUTTON_PLAY
#define MINESWP_DISCOVER (BUTTON_SELECT | BUTTON_PLAY) #define MINESWP_DISCOVER (BUTTON_SELECT | BUTTON_PLAY)
#define MINESWP_INFO (BUTTON_SELECT | BUTTON_MENU) #define MINESWP_INFO (BUTTON_SELECT | BUTTON_MENU)
#define MINESWP_RIGHT (BUTTON_SELECT | BUTTON_RIGHT)
#define MINESWP_LEFT (BUTTON_SELECT | BUTTON_LEFT)
#elif (CONFIG_KEYPAD == IAUDIO_X5_PAD) #elif (CONFIG_KEYPAD == IAUDIO_X5_PAD)
#define MINESWP_UP BUTTON_UP #define MINESWP_UP BUTTON_UP
@ -89,15 +91,19 @@ PLUGIN_HEADER
#define MINESWP_TOGGLE BUTTON_PLAY #define MINESWP_TOGGLE BUTTON_PLAY
#define MINESWP_DISCOVER BUTTON_SELECT #define MINESWP_DISCOVER BUTTON_SELECT
#define MINESWP_INFO (BUTTON_REC | BUTTON_PLAY) #define MINESWP_INFO (BUTTON_REC | BUTTON_PLAY)
#define MINESWP_RIGHT (BUTTON_PLAY | BUTTON_RIGHT)
#define MINESWP_LEFT (BUTTON_PLAY | BUTTON_LEFT)
#elif (CONFIG_KEYPAD == GIGABEAT_PAD) #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
#define MINESWP_UP BUTTON_UP #define MINESWP_UP BUTTON_UP
#define MINESWP_DOWN BUTTON_DOWN #define MINESWP_DOWN BUTTON_DOWN
#define MINESWP_QUIT BUTTON_A #define MINESWP_QUIT BUTTON_A
#define MINESWP_START BUTTON_SELECT #define MINESWP_START BUTTON_SELECT
#define MINESWP_TOGGLE BUTTON_SELECT #define MINESWP_TOGGLE BUTTON_POWER
#define MINESWP_DISCOVER BUTTON_POWER #define MINESWP_DISCOVER BUTTON_SELECT
#define MINESWP_INFO BUTTON_MENU #define MINESWP_INFO BUTTON_MENU
#define MINESWP_RIGHT (BUTTON_SELECT | BUTTON_RIGHT)
#define MINESWP_LEFT (BUTTON_SELECT | BUTTON_LEFT)
#endif #endif
@ -109,7 +115,7 @@ static struct plugin_api* rb;
/* define how numbers are displayed (that way we don't have to */ /* define how numbers are displayed (that way we don't have to */
/* worry about fonts) */ /* worry about fonts) */
static unsigned char num[9][8] = { static unsigned char num[10][8] = {
/*reading the sprites: /*reading the sprites:
on screen f123 on screen f123
4567 4567
@ -203,6 +209,16 @@ static unsigned char num[9][8] = {
0x28, /* ..O..O.. */ 0x28, /* ..O..O.. */
0x00, /* ...OO... */ 0x00, /* ...OO... */
0x00},/* ........ */ 0x00},/* ........ */
/* mine */
{0x00, /* ........ */
0x00, /* ........ */
0x18, /* ...OO... */
0x3c, /* ..OOOO.. */
0x3c, /* ..OOOO.. */
0x18, /* ...OO... */
0x00, /* ........ */
0x00},/* ........ */
}; };
/* the tile struct /* the tile struct
@ -222,49 +238,109 @@ typedef struct tile {
int height = LCD_HEIGHT/8; int height = LCD_HEIGHT/8;
int width = LCD_WIDTH/8; int width = LCD_WIDTH/8;
/* the minefield */ /* The Minefield. Caution it is defined as Y, X! Not the opposite. */
tile minefield[LCD_HEIGHT/8][LCD_WIDTH/8]; tile minefield[LCD_HEIGHT/8][LCD_WIDTH/8];
/* total number of mines on the game */ /* total number of mines on the game */
int mine_num = 0; int mine_num = 0;
/* discovers the tile when player clears one of them */ /* percentage of mines on minefield used durring generation */
/* a chain reaction (of discovery) occurs if tile has no mines */ int p=16;
/* as neighbors */
void discover(int, int);
void discover(int x, int y){
if(x<0) return; /* number of tiles left on the game */
if(y<0) return; int tiles_left;
if(x>width-1) return;
if(y>height-1) return; /* Because mines are set after the first move... */
if(minefield[y][x].known) return; bool no_mines = true;
/* We need a stack (created on discover()) for the cascade algorithm. */
int stack_pos = 0;
/* Functions to center the board on screen. */
int c_height(void){
return LCD_HEIGHT/16 - height/2;
}
int c_width(void){
return LCD_WIDTH/16 - width/2;
}
void push (int *stack, int y, int x){
if(stack_pos <= height*width){
stack_pos++;
stack[stack_pos] = y;
stack_pos++;
stack[stack_pos] = x;
}
}
/* Unveil tiles and push them to stack if they are empty. */
void unveil(int *stack, int y, int x){
if(x < c_width() || y < c_height() || x > c_width() + width-1
|| y > c_height() + height-1 || minefield[y][x].known
|| minefield[y][x].mine || minefield[y][x].flag) return;
if(minefield[y][x].neighbors == 0){
minefield[y][x].known = 1;
push(stack, y, x);
} else
minefield[y][x].known = 1;
}
void discover(int y, int x){
int stack[height*width];
/* Selected tile. */
if(x < c_width() || y < c_height() || x > c_width() + width-1
|| y > c_height() + height-1 || minefield[y][x].known
|| minefield[y][x].mine || minefield[y][x].flag) return;
minefield[y][x].known = 1; minefield[y][x].known = 1;
if(minefield[y][x].neighbors == 0){ /* Exit if the tile is not empty. (no mines nearby) */
discover(x-1,y-1); if(minefield[y][x].neighbors) return;
discover(x,y-1);
discover(x+1,y-1); push(stack, y, x);
discover(x+1,y);
discover(x+1,y+1); /* Scan all nearby tiles. If we meet a tile with a number we just unveil
discover(x,y+1); it. If we meet an empty tile, we push the location in stack. For each
discover(x-1,y+1); location in stack we do the same thing. (scan again all nearby tiles) */
discover(x-1,y); while(stack_pos){
/* Retrieve x, y from stack. */
x = stack[stack_pos];
y = stack[stack_pos-1];
/* Pop. */
if(stack_pos > 0) stack_pos -= 2;
else rb->splash(HZ,true,"ERROR");
unveil(stack, y-1, x-1);
unveil(stack, y-1, x);
unveil(stack, y-1, x+1);
unveil(stack, y, x+1);
unveil(stack, y+1, x+1);
unveil(stack, y+1, x);
unveil(stack, y+1, x-1);
unveil(stack, y, x-1);
} }
return;
} }
/* Reset the whole board for a new game. */
/* init not mine related elements of the mine field */
void minesweeper_init(void){ void minesweeper_init(void){
int i,j; int i,j;
for(i=0;i<height;i++){ for(i=0;i<LCD_HEIGHT/8;i++){
for(j=0;j<width;j++){ for(j=0;j<LCD_WIDTH/8;j++){
minefield[i][j].known = 0; minefield[i][j].known = 0;
minefield[i][j].flag = 0; minefield[i][j].flag = 0;
minefield[i][j].mine = 0;
minefield[i][j].neighbors = 0;
} }
} }
no_mines = true;
tiles_left = width*height;
} }
@ -275,8 +351,8 @@ void minesweeper_putmines(int p, int x, int y){
int i,j; int i,j;
mine_num = 0; mine_num = 0;
for(i=0;i<height;i++){ for(i=c_height();i<c_height() + height;i++){
for(j=0;j<width;j++){ for(j=c_width();j<c_width() + width;j++){
if(rb->rand()%100<p && !(y==i && x==j)){ if(rb->rand()%100<p && !(y==i && x==j)){
minefield[i][j].mine = 1; minefield[i][j].mine = 1;
mine_num++; mine_num++;
@ -288,30 +364,72 @@ void minesweeper_putmines(int p, int x, int y){
} }
/* we need to compute the neighbor element for each tile */ /* we need to compute the neighbor element for each tile */
for(i=0;i<height;i++){ for(i=c_height();i<c_height() + height;i++){
for(j=0;j<width;j++){ for(j=c_width();j<c_width() + width;j++){
if(i>0){ if(i>0){
if(j>0) if(j>0)
minefield[i][j].neighbors += minefield[i-1][j-1].mine; minefield[i][j].neighbors += minefield[i-1][j-1].mine;
minefield[i][j].neighbors += minefield[i-1][j].mine; minefield[i][j].neighbors += minefield[i-1][j].mine;
if(j<width-1) if(j<c_width() + width-1)
minefield[i][j].neighbors += minefield[i-1][j+1].mine; minefield[i][j].neighbors += minefield[i-1][j+1].mine;
} }
if(j>0) if(j>0)
minefield[i][j].neighbors += minefield[i][j-1].mine; minefield[i][j].neighbors += minefield[i][j-1].mine;
if(j<width-1) if(j<c_width() + width-1)
minefield[i][j].neighbors += minefield[i][j+1].mine; minefield[i][j].neighbors += minefield[i][j+1].mine;
if(i<height-1){ if(i<c_height() + height-1){
if(j>0) if(j>0)
minefield[i][j].neighbors += minefield[i+1][j-1].mine; minefield[i][j].neighbors += minefield[i+1][j-1].mine;
minefield[i][j].neighbors += minefield[i+1][j].mine; minefield[i][j].neighbors += minefield[i+1][j].mine;
if(j<width-1) if(j<c_width() + width-1)
minefield[i][j].neighbors += minefield[i+1][j+1].mine; minefield[i][j].neighbors += minefield[i+1][j+1].mine;
} }
} }
} }
no_mines = false;
/* In case the user is lucky and there are no mines positioned. */
if(!mine_num && height*width != 1) minesweeper_putmines(p, x, y);
} }
/* A function that will uncover all the board, when the user wins or loses.
can easily be expanded, (just a call assigned to a button) as a solver. */
void mine_show(void){
int i, j, button;
for(i=c_height();i<c_height() + height;i++){
for(j=c_width();j<c_width() + width;j++){
#if LCD_DEPTH > 1
rb->lcd_set_foreground(LCD_DARKGRAY);
rb->lcd_drawrect(j*8,i*8,8,8);
rb->lcd_set_foreground(LCD_BLACK);
#else
rb->lcd_drawrect(j*8,i*8,8,8);
#endif
if(!minefield[i][j].known){
if(minefield[i][j].mine){
rb->lcd_set_drawmode(DRMODE_FG);
rb->lcd_mono_bitmap(num[9], j*8,i*8,8,8);
rb->lcd_set_drawmode(DRMODE_SOLID);
} else if(minefield[i][j].neighbors){
rb->lcd_set_drawmode(DRMODE_FG);
rb->lcd_mono_bitmap(num[minefield[i][j].neighbors],
j*8,i*8,8,8);
rb->lcd_set_drawmode(DRMODE_SOLID);
}
}
}
}
rb->lcd_update();
bool k = true;
while(k){
button = rb->button_get_w_tmo(HZ/10);
if(button != BUTTON_NONE && !(button & BUTTON_REL)) k = false;
}
}
/* the big and ugly function that is the game */ /* the big and ugly function that is the game */
int minesweeper(void) int minesweeper(void)
{ {
@ -322,12 +440,6 @@ int minesweeper(void)
/* the cursor coordinates */ /* the cursor coordinates */
int x=0, y=0; int x=0, y=0;
/* number of tiles left on the game */
int tiles_left=width*height;
/* percentage of mines on minefield used durring generation */
int p=16;
/* a usefull string for snprintf */ /* a usefull string for snprintf */
char str[30]; char str[30];
@ -351,28 +463,48 @@ int minesweeper(void)
#elif CONFIG_KEYPAD == IRIVER_H100_PAD #elif CONFIG_KEYPAD == IRIVER_H100_PAD
rb->lcd_puts(0,6,"SELECT to start"); rb->lcd_puts(0,6,"SELECT to start");
#endif #endif
rb->lcd_update(); rb->lcd_update();
button = rb->button_get(true); button = rb->button_get(true);
switch(button){ switch(button){
case MINESWP_DOWN: case MINESWP_DOWN:
case (MINESWP_DOWN | BUTTON_REPEAT):
p = (p + 98)%100; p = (p + 98)%100;
/* Don't let the user play without mines. */
if(!p) p = 98;
break; break;
case MINESWP_UP: case MINESWP_UP:
case (MINESWP_UP | BUTTON_REPEAT):
p = (p + 2)%100; p = (p + 2)%100;
/* Don't let the user play without mines. */
if(!p) p = 2;
break; break;
case BUTTON_RIGHT: case BUTTON_RIGHT:
case (BUTTON_RIGHT | BUTTON_REPEAT):
height = height%(LCD_HEIGHT/8)+1; height = height%(LCD_HEIGHT/8)+1;
break; break;
case BUTTON_LEFT: case BUTTON_LEFT:
case (BUTTON_LEFT | BUTTON_REPEAT):
width = width%(LCD_WIDTH/8)+1; width = width%(LCD_WIDTH/8)+1;
break; break;
case MINESWP_RIGHT:
case (MINESWP_RIGHT | BUTTON_REPEAT):
height--;
if(height < 1) height = LCD_HEIGHT/8;
if(height > LCD_HEIGHT) height = 1;
break;
case MINESWP_LEFT:
case (MINESWP_LEFT | BUTTON_REPEAT):
width--;
if(width < 1) width = LCD_WIDTH/8;
if(width > LCD_WIDTH) width = 1;
break;
case MINESWP_START:/* start playing */ case MINESWP_START:/* start playing */
i = 1; i = 1;
break; break;
@ -395,6 +527,8 @@ int minesweeper(void)
********************/ ********************/
minesweeper_init(); minesweeper_init();
x = c_width();
y = c_height();
/********************** /**********************
* play * * play *
@ -406,8 +540,8 @@ int minesweeper(void)
rb->lcd_clear_display(); rb->lcd_clear_display();
/*display the mine field */ /*display the mine field */
for(i=0;i<height;i++){ for(i=c_height();i<c_height() + height;i++){
for(j=0;j<width;j++){ for(j=c_width();j<c_width() + width;j++){
#if LCD_DEPTH > 1 #if LCD_DEPTH > 1
rb->lcd_set_foreground(LCD_DARKGRAY); rb->lcd_set_foreground(LCD_DARKGRAY);
rb->lcd_drawrect(j*8,i*8,8,8); rb->lcd_drawrect(j*8,i*8,8,8);
@ -416,11 +550,10 @@ int minesweeper(void)
rb->lcd_drawrect(j*8,i*8,8,8); rb->lcd_drawrect(j*8,i*8,8,8);
#endif #endif
if(minefield[i][j].known){ if(minefield[i][j].known){
if(minefield[i][j].mine){ if(minefield[i][j].neighbors){
rb->lcd_putsxy(j*8+1,i*8+1,"b");
} else if(minefield[i][j].neighbors){
rb->lcd_set_drawmode(DRMODE_FG); rb->lcd_set_drawmode(DRMODE_FG);
rb->lcd_mono_bitmap(num[minefield[i][j].neighbors],j*8,i*8,8,8); rb->lcd_mono_bitmap(num[minefield[i][j].neighbors],
j*8,i*8,8,8);
rb->lcd_set_drawmode(DRMODE_SOLID); rb->lcd_set_drawmode(DRMODE_SOLID);
} }
} else if(minefield[i][j].flag) { } else if(minefield[i][j].flag) {
@ -455,25 +588,29 @@ int minesweeper(void)
/* move cursor left */ /* move cursor left */
case BUTTON_LEFT: case BUTTON_LEFT:
case (BUTTON_LEFT | BUTTON_REPEAT): case (BUTTON_LEFT | BUTTON_REPEAT):
x = (x + width - 1)%width; if(x<=c_width()) x = width + c_width();
x = x-1;
break; break;
/* move cursor right */ /* move cursor right */
case BUTTON_RIGHT: case BUTTON_RIGHT:
case (BUTTON_RIGHT | BUTTON_REPEAT): case (BUTTON_RIGHT | BUTTON_REPEAT):
x = (x + 1)%width; if(x>=width + c_width() - 1) x = c_width() - 1;
x = x+1;
break; break;
/* move cursor down */ /* move cursor down */
case MINESWP_DOWN: case MINESWP_DOWN:
case (MINESWP_DOWN | BUTTON_REPEAT): case (MINESWP_DOWN | BUTTON_REPEAT):
y = (y + 1)%height; if(y>=height + c_height() - 1) y = c_height() - 1;
y = y+1;
break; break;
/* move cursor up */ /* move cursor up */
case MINESWP_UP: case MINESWP_UP:
case (MINESWP_UP | BUTTON_REPEAT): case (MINESWP_UP | BUTTON_REPEAT):
y = (y + height - 1)%height; if(y<=c_height()) y = height + c_height();
y = y-1;
break; break;
/* discover a tile (and it's neighbors if .neighbors == 0) */ /* discover a tile (and it's neighbors if .neighbors == 0) */
@ -484,14 +621,17 @@ int minesweeper(void)
if(minefield[y][x].flag) break; if(minefield[y][x].flag) break;
/* we put the mines on the first "click" so that you don't */ /* we put the mines on the first "click" so that you don't */
/* lose on the first "click" */ /* lose on the first "click" */
if(tiles_left == width*height) minesweeper_putmines(p,x,y); if(tiles_left == width*height && no_mines)
discover(x,y); minesweeper_putmines(p,x,y);
discover(y, x);
if(minefield[y][x].mine){ if(minefield[y][x].mine){
return MINESWEEPER_LOSE; return MINESWEEPER_LOSE;
} }
tiles_left = 0; tiles_left = 0;
for(i=0;i<height;i++){ for(i=c_height();i<c_height() + height;i++){
for(j=0;j<width;j++){ for(j=c_width();j<c_width() + width;j++){
if(minefield[i][j].known == 0) tiles_left++; if(minefield[i][j].known == 0) tiles_left++;
} }
} }
@ -515,13 +655,16 @@ int minesweeper(void)
/* show how many mines you think you have found and how many */ /* show how many mines you think you have found and how many */
/* there really are on the game */ /* there really are on the game */
case MINESWP_INFO: case MINESWP_INFO:
if(no_mines) break;
tiles_left = 0; tiles_left = 0;
for(i=0;i<height;i++){ for(i=c_height();i<c_height() + height;i++){
for(j=0;j<width;j++){ for(j=c_width();j<c_width() + width;j++){
if(minefield[i][j].flag) tiles_left++; if(minefield[i][j].flag && !minefield[i][j].known)
tiles_left++;
} }
} }
rb->splash(HZ*2, true, "You found %d mines out of %d", tiles_left, mine_num); rb->splash(HZ*2, true, "You found %d mines out of %d",
tiles_left, mine_num);
break; break;
default: default:
@ -547,11 +690,15 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
while(!exit) { while(!exit) {
switch(minesweeper()){ switch(minesweeper()){
case MINESWEEPER_WIN: case MINESWEEPER_WIN:
rb->splash(HZ*2, true, "You Win :)"); rb->splash(HZ*2, true, "You Won! Press a key");
rb->lcd_clear_display();
mine_show();
break; break;
case MINESWEEPER_LOSE: case MINESWEEPER_LOSE:
rb->splash(HZ*2, true, "You Lose :("); rb->splash(HZ*2, true, "You Lost! Press a key");
rb->lcd_clear_display();
mine_show();
break; break;
case MINESWEEPER_USB: case MINESWEEPER_USB: