From fd18a4e9d38a478dcefea6842ec9352ee7273c89 Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Sat, 20 May 2006 14:01:00 +0000 Subject: Patch #5384 from Alexander Spyridakis - fixes bug report 4908 git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9968 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/minesweeper.c | 299 +++++++++++++++++++++++++++++++++------------ 1 file changed, 223 insertions(+), 76 deletions(-) diff --git a/apps/plugins/minesweeper.c b/apps/plugins/minesweeper.c index 23d6e07157..663d5c45ec 100644 --- a/apps/plugins/minesweeper.c +++ b/apps/plugins/minesweeper.c @@ -19,12 +19,6 @@ /***************************************************************************** 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" @@ -50,6 +44,8 @@ PLUGIN_HEADER #define MINESWP_DISCOVER BUTTON_ON #define MINESWP_DISCOVER2 BUTTON_F2 #define MINESWP_INFO BUTTON_F3 +#define MINESWP_RIGHT (BUTTON_F1 | BUTTON_RIGHT) +#define MINESWP_LEFT (BUTTON_F1 | BUTTON_LEFT) #elif CONFIG_KEYPAD == ONDIO_PAD #define MINESWP_UP BUTTON_UP @@ -60,6 +56,8 @@ PLUGIN_HEADER #define MINESWP_TOGGLE (BUTTON_MENU | BUTTON_REL) #define MINESWP_DISCOVER (BUTTON_MENU | BUTTON_REPEAT) #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) || \ (CONFIG_KEYPAD == IRIVER_H300_PAD) @@ -67,9 +65,11 @@ PLUGIN_HEADER #define MINESWP_DOWN BUTTON_DOWN #define MINESWP_QUIT BUTTON_OFF #define MINESWP_START BUTTON_SELECT -#define MINESWP_TOGGLE BUTTON_SELECT -#define MINESWP_DISCOVER BUTTON_ON +#define MINESWP_TOGGLE BUTTON_ON +#define MINESWP_DISCOVER BUTTON_SELECT #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) || \ (CONFIG_KEYPAD == IPOD_3G_PAD) @@ -80,6 +80,8 @@ PLUGIN_HEADER #define MINESWP_TOGGLE BUTTON_PLAY #define MINESWP_DISCOVER (BUTTON_SELECT | BUTTON_PLAY) #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) #define MINESWP_UP BUTTON_UP @@ -89,15 +91,19 @@ PLUGIN_HEADER #define MINESWP_TOGGLE BUTTON_PLAY #define MINESWP_DISCOVER BUTTON_SELECT #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) #define MINESWP_UP BUTTON_UP #define MINESWP_DOWN BUTTON_DOWN #define MINESWP_QUIT BUTTON_A #define MINESWP_START BUTTON_SELECT -#define MINESWP_TOGGLE BUTTON_SELECT -#define MINESWP_DISCOVER BUTTON_POWER +#define MINESWP_TOGGLE BUTTON_POWER +#define MINESWP_DISCOVER BUTTON_SELECT #define MINESWP_INFO BUTTON_MENU +#define MINESWP_RIGHT (BUTTON_SELECT | BUTTON_RIGHT) +#define MINESWP_LEFT (BUTTON_SELECT | BUTTON_LEFT) #endif @@ -109,7 +115,7 @@ static struct plugin_api* rb; /* define how numbers are displayed (that way we don't have to */ /* worry about fonts) */ -static unsigned char num[9][8] = { +static unsigned char num[10][8] = { /*reading the sprites: on screen f123 4567 @@ -203,6 +209,16 @@ static unsigned char num[9][8] = { 0x28, /* ..O..O.. */ 0x00, /* ...OO... */ 0x00},/* ........ */ + /* mine */ + {0x00, /* ........ */ + 0x00, /* ........ */ + 0x18, /* ...OO... */ + 0x3c, /* ..OOOO.. */ + 0x3c, /* ..OOOO.. */ + 0x18, /* ...OO... */ + 0x00, /* ........ */ + 0x00},/* ........ */ + }; /* the tile struct @@ -222,49 +238,109 @@ typedef struct tile { int height = LCD_HEIGHT/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]; /* total number of mines on the game */ int mine_num = 0; -/* discovers the tile when player clears one of them */ -/* a chain reaction (of discovery) occurs if tile has no mines */ -/* as neighbors */ -void discover(int, int); -void discover(int x, int y){ +/* percentage of mines on minefield used durring generation */ +int p=16; - if(x<0) return; - if(y<0) return; - if(x>width-1) return; - if(y>height-1) return; - if(minefield[y][x].known) return; +/* number of tiles left on the game */ +int tiles_left; - minefield[y][x].known = 1; - if(minefield[y][x].neighbors == 0){ - discover(x-1,y-1); - discover(x,y-1); - discover(x+1,y-1); - discover(x+1,y); - discover(x+1,y+1); - discover(x,y+1); - discover(x-1,y+1); - discover(x-1,y); +/* Because mines are set after the first move... */ +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; } - return; } +/* 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; + /* Exit if the tile is not empty. (no mines nearby) */ + if(minefield[y][x].neighbors) return; + + push(stack, y, x); + + /* Scan all nearby tiles. If we meet a tile with a number we just unveil + it. If we meet an empty tile, we push the location in stack. For each + location in stack we do the same thing. (scan again all nearby tiles) */ + 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); + } +} -/* init not mine related elements of the mine field */ +/* Reset the whole board for a new game. */ void minesweeper_init(void){ int i,j; - for(i=0;irand()%100

0){ if(j>0) minefield[i][j].neighbors += minefield[i-1][j-1].mine; minefield[i][j].neighbors += minefield[i-1][j].mine; - if(j0) minefield[i][j].neighbors += minefield[i][j-1].mine; - if(j0) minefield[i][j].neighbors += minefield[i+1][j-1].mine; minefield[i][j].neighbors += minefield[i+1][j].mine; - if(j 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 */ int minesweeper(void) { int i,j; int button; int lastbutton = BUTTON_NONE; - + /* the cursor coordinates */ - 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; + int x=0, y=0; /* a usefull string for snprintf */ char str[30]; @@ -351,27 +463,47 @@ int minesweeper(void) #elif CONFIG_KEYPAD == IRIVER_H100_PAD rb->lcd_puts(0,6,"SELECT to start"); #endif - rb->lcd_update(); - button = rb->button_get(true); switch(button){ case MINESWP_DOWN: + case (MINESWP_DOWN | BUTTON_REPEAT): p = (p + 98)%100; + /* Don't let the user play without mines. */ + if(!p) p = 98; break; case MINESWP_UP: + case (MINESWP_UP | BUTTON_REPEAT): p = (p + 2)%100; + /* Don't let the user play without mines. */ + if(!p) p = 2; break; case BUTTON_RIGHT: + case (BUTTON_RIGHT | BUTTON_REPEAT): height = height%(LCD_HEIGHT/8)+1; break; case BUTTON_LEFT: + case (BUTTON_LEFT | BUTTON_REPEAT): width = width%(LCD_WIDTH/8)+1; 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 */ i = 1; @@ -395,6 +527,8 @@ int minesweeper(void) ********************/ minesweeper_init(); + x = c_width(); + y = c_height(); /********************** * play * @@ -406,8 +540,8 @@ int minesweeper(void) rb->lcd_clear_display(); /*display the mine field */ - for(i=0;i 1 rb->lcd_set_foreground(LCD_DARKGRAY); 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); #endif if(minefield[i][j].known){ - if(minefield[i][j].mine){ - rb->lcd_putsxy(j*8+1,i*8+1,"b"); - } else if(minefield[i][j].neighbors){ + 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_mono_bitmap(num[minefield[i][j].neighbors], + j*8,i*8,8,8); rb->lcd_set_drawmode(DRMODE_SOLID); } } else if(minefield[i][j].flag) { @@ -451,29 +584,33 @@ int minesweeper(void) /* quit minesweeper (you really shouldn't use this button ...) */ case MINESWP_QUIT: return MINESWEEPER_QUIT; - + /* move cursor left */ case BUTTON_LEFT: case (BUTTON_LEFT | BUTTON_REPEAT): - x = (x + width - 1)%width; + if(x<=c_width()) x = width + c_width(); + x = x-1; break; /* move cursor right */ case BUTTON_RIGHT: case (BUTTON_RIGHT | BUTTON_REPEAT): - x = (x + 1)%width; + if(x>=width + c_width() - 1) x = c_width() - 1; + x = x+1; break; /* move cursor down */ case MINESWP_DOWN: case (MINESWP_DOWN | BUTTON_REPEAT): - y = (y + 1)%height; + if(y>=height + c_height() - 1) y = c_height() - 1; + y = y+1; break; /* move cursor up */ case MINESWP_UP: case (MINESWP_UP | BUTTON_REPEAT): - y = (y + height - 1)%height; + if(y<=c_height()) y = height + c_height(); + y = y-1; break; /* discover a tile (and it's neighbors if .neighbors == 0) */ @@ -484,14 +621,17 @@ int minesweeper(void) if(minefield[y][x].flag) break; /* we put the mines on the first "click" so that you don't */ /* lose on the first "click" */ - if(tiles_left == width*height) minesweeper_putmines(p,x,y); - discover(x,y); + if(tiles_left == width*height && no_mines) + minesweeper_putmines(p,x,y); + + discover(y, x); + if(minefield[y][x].mine){ return MINESWEEPER_LOSE; } tiles_left = 0; - for(i=0;isplash(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; default: @@ -547,11 +690,15 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter) while(!exit) { switch(minesweeper()){ 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; 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; case MINESWEEPER_USB: -- cgit v1.2.3