/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2004-2006 Antoine Cellerier * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ /***************************************************************************** Mine Sweeper by dionoea *****************************************************************************/ #include "plugin.h" #ifdef HAVE_LCD_BITMAP PLUGIN_HEADER /*what the minesweeper() function can return */ #define MINESWEEPER_USB 3 #define MINESWEEPER_QUIT 2 #define MINESWEEPER_LOSE 1 #define MINESWEEPER_WIN 0 /* variable button definitions */ #if CONFIG_KEYPAD == RECORDER_PAD #define MINESWP_UP BUTTON_UP #define MINESWP_DOWN BUTTON_DOWN #define MINESWP_QUIT BUTTON_OFF #define MINESWP_START BUTTON_ON #define MINESWP_TOGGLE BUTTON_PLAY #define MINESWP_TOGGLE2 BUTTON_F1 #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 #define MINESWP_DOWN BUTTON_DOWN #define MINESWP_QUIT BUTTON_OFF #define MINESWP_START BUTTON_MENU #define MINESWP_TOGGLE_PRE BUTTON_MENU #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) #define MINESWP_UP BUTTON_UP #define MINESWP_DOWN BUTTON_DOWN #define MINESWP_QUIT BUTTON_OFF #define MINESWP_START BUTTON_SELECT #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) #define MINESWP_RC_QUIT BUTTON_RC_STOP #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ (CONFIG_KEYPAD == IPOD_3G_PAD) #define MINESWP_UP BUTTON_SCROLL_BACK #define MINESWP_DOWN BUTTON_SCROLL_FWD #define MINESWP_QUIT BUTTON_MENU #define MINESWP_START BUTTON_SELECT #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 #define MINESWP_DOWN BUTTON_DOWN #define MINESWP_QUIT BUTTON_POWER #define MINESWP_START BUTTON_REC #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_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) #elif (CONFIG_KEYPAD == IRIVER_H10_PAD) #define MINESWP_UP BUTTON_SCROLL_UP #define MINESWP_DOWN BUTTON_SCROLL_DOWN #define MINESWP_QUIT BUTTON_POWER #define MINESWP_START BUTTON_FF #define MINESWP_TOGGLE BUTTON_PLAY #define MINESWP_DISCOVER BUTTON_REW #define MINESWP_INFO (BUTTON_REW | BUTTON_PLAY) #define MINESWP_RIGHT (BUTTON_RIGHT | BUTTON_PLAY) #define MINESWP_LEFT (BUTTON_LEFT | BUTTON_PLAY) #endif /* here is a global api struct pointer. while not strictly necessary, it's nice not to have to pass the api pointer in all function calls in the plugin */ static struct plugin_api* rb; /* define how numbers are displayed (that way we don't have to */ /* worry about fonts) */ static unsigned char num[10][8] = { /*reading the sprites: on screen f123 4567 890a bcde in binary b84f c951 d062 ea73 */ /* 0 */ {0x00, /* ........ */ 0x00, /* ........ */ 0x00, /* ........ */ 0x00, /* ........ */ 0x00, /* ........ */ 0x00, /* ........ */ 0x00, /* ........ */ 0x00},/* ........ */ /* 1 */ {0x00, /* ........ */ 0x00, /* ........ */ 0x00, /* ...OO... */ 0x44, /* ....O... */ 0x7c, /* ....O... */ 0x40, /* ....O... */ 0x00, /* ...OOO.. */ 0x00},/* ........ */ /* 2 */ {0x00, /* ........ */ 0x00, /* ........ */ 0x48, /* ...OO... */ 0x64, /* ..O..O.. */ 0x54, /* ....O... */ 0x48, /* ...O.... */ 0x00, /* ..OOOO.. */ 0x00},/* ........ */ /* 3 */ {0x00, /* ........ */ 0x00, /* ........ */ 0x44, /* ..OOO... */ 0x54, /* .....O.. */ 0x54, /* ...OO... */ 0x28, /* .....O.. */ 0x00, /* ..OOO... */ 0x00},/* ........ */ /* 4 */ {0x00, /* ........ */ 0x00, /* ........ */ 0x1c, /* ..O..... */ 0x10, /* ..O..... */ 0x70, /* ..OOOO.. */ 0x10, /* ....O... */ 0x00, /* ....O... */ 0x00},/* ........ */ /* 5 */ {0x00, /* ........ */ 0x00, /* ........ */ 0x5c, /* ..OOOO.. */ 0x54, /* ..O..... */ 0x54, /* ..OOO... */ 0x24, /* .....O.. */ 0x00, /* ..OOO... */ 0x00},/* ........ */ /* 6 */ {0x00, /* ........ */ 0x00, /* ........ */ 0x38, /* ...OOO.. */ 0x54, /* ..O..... */ 0x54, /* ..OOO... */ 0x24, /* ..O..O.. */ 0x00, /* ...OO... */ 0x00},/* ........ */ /* 7 */ {0x00, /* ........ */ 0x00, /* ........ */ 0x44, /* ..OOOO.. */ 0x24, /* .....O.. */ 0x14, /* ....O... */ 0x0c, /* ...O.... */ 0x00, /* ..O..... */ 0x00},/* ........ */ /* 8 */ {0x00, /* ........ */ 0x00, /* ........ */ 0x28, /* ...OO... */ 0x54, /* ..O..O.. */ 0x54, /* ...OO... */ 0x28, /* ..O..O.. */ 0x00, /* ...OO... */ 0x00},/* ........ */ /* mine */ {0x00, /* ........ */ 0x00, /* ........ */ 0x18, /* ...OO... */ 0x3c, /* ..OOOO.. */ 0x3c, /* ..OOOO.. */ 0x18, /* ...OO... */ 0x00, /* ........ */ 0x00},/* ........ */ }; /* the tile struct if there is a mine, mine is true if tile is known by player, known is true if tile has a flag, flag is true neighbors is the total number of mines arround tile */ typedef struct tile { unsigned char mine : 1; unsigned char known : 1; unsigned char flag : 1; unsigned char neighbors : 4; } tile; /* the height and width of the field */ int height = LCD_HEIGHT/8; int width = LCD_WIDTH/8; /* 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; /* percentage of mines on minefield used during generation */ int p=16; /* number of tiles left on the game */ int tiles_left; /* 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; } } /* 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); } } /* 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_COMPLEMENT); 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_COMPLEMENT); rb->lcd_mono_bitmap(num[minefield[i][j].neighbors], j*8,i*8,8,8); rb->lcd_set_drawmode(DRMODE_SOLID); } } } } rb->lcd_update(); do button = rb->button_get(true); while ((button == BUTTON_NONE) || (button & (BUTTON_REL|BUTTON_REPEAT))); } /* 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; /* a usefull string for snprintf */ char str[30]; /* welcome screen where player can chose mine percentage */ i = 0; while(true){ rb->lcd_clear_display(); rb->lcd_puts(0,0,"Mine Sweeper"); rb->snprintf(str, 20, "%d%% mines", p); rb->lcd_puts(0,2,str); rb->lcd_puts(0,3,"down / up"); rb->snprintf(str, 20, "%d cols x %d rows", width, height); rb->lcd_puts(0,4,str); rb->lcd_puts(0,5,"left x right "); #if CONFIG_KEYPAD == RECORDER_PAD rb->lcd_puts(0,6,"ON to start"); #elif CONFIG_KEYPAD == ONDIO_PAD rb->lcd_puts(0,6,"MODE to start"); #elif (CONFIG_KEYPAD==IRIVER_H100_PAD) || (CONFIG_KEYPAD==IPOD_4G_PAD) rb->lcd_puts(0,6,"SELECT to start"); #elif CONFIG_KEYPAD == IAUDIO_X5_PAD rb->lcd_puts(0,6,"REC 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; break; #ifdef MINESWP_RC_QUIT case MINESWP_RC_QUIT: #endif case MINESWP_QUIT:/* quit program */ return MINESWEEPER_QUIT; default: if (rb->default_event_handler(button) == SYS_USB_CONNECTED) return MINESWEEPER_USB; break; } if(i==1) break; } /******************** * init * ********************/ minesweeper_init(); x = c_width(); y = c_height(); /********************** * play * **********************/ while(true){ /*clear the screen buffer */ rb->lcd_clear_display(); /*display the mine field */ for(i=c_height();i 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].neighbors){ rb->lcd_set_drawmode(DRMODE_COMPLEMENT); 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) { rb->lcd_drawline(j*8+2,i*8+2,j*8+5,i*8+5); rb->lcd_drawline(j*8+2,i*8+5,j*8+5,i*8+2); } else { #if LCD_DEPTH > 1 rb->lcd_set_foreground(LCD_LIGHTGRAY); rb->lcd_fillrect(j*8+1,i*8+1,6,6); rb->lcd_set_foreground(LCD_BLACK); #else rb->lcd_fillrect(j*8+2,i*8+2,4,4); #endif } } } /* display the cursor */ rb->lcd_set_drawmode(DRMODE_COMPLEMENT); rb->lcd_fillrect(x*8,y*8,8,8); rb->lcd_set_drawmode(DRMODE_SOLID); /* update the screen */ rb->lcd_update(); button = rb->button_get(true); switch(button){ /* quit minesweeper (you really shouldn't use this button ...) */ #ifdef MINESWP_RC_QUIT case MINESWP_RC_QUIT: #endif case MINESWP_QUIT: return MINESWEEPER_QUIT; /* move cursor left */ case BUTTON_LEFT: case (BUTTON_LEFT | BUTTON_REPEAT): if(x<=c_width()) x = width + c_width(); x = x-1; break; /* move cursor right */ case BUTTON_RIGHT: case (BUTTON_RIGHT | BUTTON_REPEAT): 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): 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): if(y<=c_height()) y = height + c_height(); y = y-1; break; /* discover a tile (and it's neighbors if .neighbors == 0) */ case MINESWP_DISCOVER: #ifdef MINESWP_DISCOVER2 case MINESWP_DISCOVER2: #endif 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 && no_mines) minesweeper_putmines(p,x,y); discover(y, x); if(minefield[y][x].mine){ return MINESWEEPER_LOSE; } tiles_left = 0; for(i=c_height();isplash(HZ*2, true, "You found %d mines out of %d", tiles_left, mine_num); break; default: if (rb->default_event_handler(button) == SYS_USB_CONNECTED) return MINESWEEPER_USB; break; } if (button != BUTTON_NONE) lastbutton = button; } } /* plugin entry point */ enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { bool exit = false; /* plugin init */ (void)parameter; rb = api; /* end of plugin init */ while(!exit) { switch(minesweeper()){ case MINESWEEPER_WIN: rb->splash(HZ*2, true, "You Win! Press a key"); rb->lcd_clear_display(); mine_show(); break; case MINESWEEPER_LOSE: rb->splash(HZ*2, true, "You Lose! Press a key"); rb->lcd_clear_display(); mine_show(); break; case MINESWEEPER_USB: return PLUGIN_USB_CONNECTED; case MINESWEEPER_QUIT: exit = true; break; default: break; } } return PLUGIN_OK; } #endif