From 619a5ca1d3dfbc86839245b9ce8977002efd187e Mon Sep 17 00:00:00 2001 From: Björn Stenberg Date: Tue, 17 Aug 2004 06:50:14 +0000 Subject: Minesweeper and Solitaire plugins by Antoine Cellerier git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4997 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/solitaire.c | 867 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 867 insertions(+) create mode 100644 apps/plugins/solitaire.c (limited to 'apps/plugins/solitaire.c') diff --git a/apps/plugins/solitaire.c b/apps/plugins/solitaire.c new file mode 100644 index 0000000000..28334437c7 --- /dev/null +++ b/apps/plugins/solitaire.c @@ -0,0 +1,867 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2004 dionoea (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. + * + ****************************************************************************/ + +/***************************************************************************** +Solitaire by dionoea + +use arrows to move the cursor +use ON to select cards in the columns, move cards inside the columns, + reveal hidden cards, ... +use PLAY to move a card from the remains' stack to the top of the cursor +use F1 to put card under cursor on one of the 4 final color stacks +use F2 to un-select card if a card was selected, else draw 3 new cards + out of the remains' stack +use F3 to put card on top of the remains' stack on one of the 4 final color + stacks + +*****************************************************************************/ + +#include "plugin.h" +#include "button.h" +#include "lcd.h" + +#ifdef HAVE_LCD_BITMAP + +/* 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 min(a,b) (asplash(HZ*1, true, # key " : " HELP_BUTTON_ ## key); \ + break; + +#define HELP_BUTTON_UP "Move the cursor up in the column." +#define HELP_BUTTON_DOWN "Move the cursor down in the column." +#define HELP_BUTTON_LEFT "Move the cursor to the previous column." +#define HELP_BUTTON_RIGHT "Move the cursor to the next column." +#define HELP_BUTTON_F1 "Put the card under the cursor on one of the 4 final color stacks." +#define HELP_BUTTON_F2 "Un-select a card if it was selected. Else, draw 3 new cards out of the remains' stack." +#define HELP_BUTTON_F3 "Put the card on top of the remains' stack on one of the 4 final color stacks." +#define HELP_BUTTON_PLAY "Put the card on top of the remains' stack on top of the cursor." +#define HELP_BUTTON_ON "Select cards in the columns, Move cards inside the columns, reveal hidden cards ..." + +static unsigned char colors[4][8] = { +//Spades +{0x00, //........ + 0x18, //...O.... + 0x1c, //..OOO... + 0x3e, //.OOOOO.. + 0x1c, //.OOOOO.. + 0x18, //...O.... + 0x00, //........ + 0x00},//........ +//Hearts +{0x00, //........ + 0x0c, //..O.O... + 0x1e, //.OOOOO.. + 0x3c, //.OOOOO.. + 0x1e, //..OOO... + 0x0c, //...O.... + 0x00, //........ + 0x00},//........ +//Clubs +{0x00, //........ + 0x18, //..OOO... + 0x0a, //...O.... + 0x3e, //.OOOOO.. + 0x0a, //.O.O.O.. + 0x18, //...O.... + 0x00, //........ + 0x00},//........ +//Diamonds +{0x00, //........ + 0x08, //...O.... + 0x1c, //..OOO... + 0x3e, //.OOOOO.. + 0x1c, //..OOO... + 0x08, //...O.... + 0x00, //........ + 0x00} //........ +}; + +static unsigned char numbers[13][8] = { +//Ace +{0x00, //........ + 0x38, //...O.... + 0x14, //..O.O... + 0x12, //.O...O.. + 0x14, //.OOOOO.. + 0x38, //.O...O.. + 0x00, //........ + 0x00},//........ +//2 +{0x00, //........ + 0x24, //..OOO... + 0x32, //.O...O.. + 0x32, //....O... + 0x2a, //..OO.... + 0x24, //.OOOOO.. + 0x00, //........ + 0x00},//........ +//3 +{0x00, //........ + 0x22, //.OOOO... + 0x2a, //.....O.. + 0x2a, //..OOO... + 0x2a, //.....O.. + 0x14, //.OOOO... + 0x00, //........ + 0x00},//........ +//4 +{0x00, //........ + 0x10, //....O... + 0x18, //...O.... + 0x34, //..O..... + 0x12, //.OOOOO.. + 0x10, //...O.... + 0x00, //........ + 0x00},//........ +//5 +{0x00, //........ + 0x2e, //.OOOOO.. + 0x2a, //.O...... + 0x2a, //.OOOO... + 0x2a, //.....O.. + 0x12, //.OOOO... + 0x00, //........ + 0x00},//........ +//6 +{0x00, //........ + 0x1c, //..OOO... + 0x2a, //.O...... + 0x2a, //.OOOO... + 0x2a, //.O...O.. + 0x10, //..OOO... + 0x00, //........ + 0x00},//........ +//7 +{0x00, //........ + 0x22, //.OOOOO.. + 0x12, //....O... + 0x0a, //...O.... + 0x06, //..O..... + 0x02, //.O...... + 0x00, //........ + 0x00},//........ +//8 +{0x00, //........ + 0x14, //..OOO... + 0x2a, //.O...O.. + 0x2a, //..OOO... + 0x2a, //.O...O.. + 0x14, //..OOO... + 0x00, //........ + 0x00},//........ +//9 +{0x00, //........ + 0x04, //..OOO... + 0x2a, //.O...O.. + 0x2a, //..OOOO.. + 0x2a, //.....O.. + 0x1c, //..OOO... + 0x00, //........ + 0x00},//........ +//10 +{0x00, //........ + 0x3e, //.O..O... + 0x00, //.O.O.O.. + 0x1c, //.O.O.O.. + 0x22, //.O.O.O.. + 0x1c, //.O..O... + 0x00, //........ + 0x00},//........ +//Jack +{0x00, //........ + 0x12, //.OOOOO.. + 0x22, //...O.... + 0x1e, //...O.... + 0x02, //.O.O.... + 0x02, //..O..... + 0x00, //........ + 0x00},//........ +//Queen +{0x00, //........ + 0x1c, //..OOO... + 0x22, //.O...O.. + 0x32, //.O...O.. + 0x22, //.O.O.O.. + 0x1c, //..OOO... + 0x00, //........ + 0x00},//........ +//King +{0x00, //........ + 0x3e, //.O...O.. + 0x08, //.O..O... + 0x08, //.OOO.... + 0x14, //.O..O... + 0x22, //.O...O.. + 0x00, //........ + 0x00} //........ +}; + +#define NOT_A_CARD 255 + +//number of cards per color +#define CARDS_PER_COLOR 13 + +//number of colors +#define COLORS 4 + +//number of columns +#define COL_NUM 7 + +//number of cards that are drawn on the remains' stack (by pressing F2) +#define CARDS_PER_DRAW 3 + +//size of a card on the screen +#define CARD_WIDTH 14 +#define CARD_HEIGHT 10 + +typedef struct card { + unsigned char color : 2; + unsigned char num : 4; + unsigned char known : 1; + unsigned char used : 1;//this is what is used when dealing cards + unsigned char next; +} card; + +unsigned char next_random_card(card *deck){ + unsigned char i,r; + + r = rb->rand()%(COLORS * CARDS_PER_COLOR)+1; + i = 0; + + while(r>0){ + i = (i + 1)%(COLORS * CARDS_PER_COLOR); + if(!deck[i].used) r--; + } + + deck[i].used = 1; + + return i; +} + +//help for the not so intuitive interface +void solitaire_help(void){ + + rb->lcd_clear_display(); + + rb->lcd_putsxy(0, 0, "Press a key to see"); + rb->lcd_putsxy(0, 7, "it's role."); + rb->lcd_putsxy(0, 21, "Press OFF to"); + rb->lcd_putsxy(0, 28, "return to menu"); + + rb->lcd_update(); + + while(1){ + + switch(rb->button_get(true)){ + HELP_CASE( UP ); + HELP_CASE( DOWN ); + HELP_CASE( LEFT ); + HELP_CASE( RIGHT ); + HELP_CASE( F1 ); + HELP_CASE( F2 ); + HELP_CASE( F3 ); + HELP_CASE( PLAY ); + HELP_CASE( ON ); + + case BUTTON_OFF: + return; + } + } +} + +//menu return codes +#define MENU_RESUME 0 +#define MENU_RESTART 1 +#define MENU_HELP 2 +#define MENU_QUIT 3 + +//menu item number +#define MENU_LENGTH 4 + +//different menu behaviors +#define MENU_BEFOREGAME 0 +#define MENU_DURINGGAME 1 + +//the menu +//text displayed changes depending on the 'when' parameter +int solitaire_menu(unsigned char when){ + + static char menu[2][MENU_LENGTH][13] = + { { "Start Game", + "", + "Help", + "Quit" }, + { "Resume Game", + "Restart Game", + "Help", + "Quit"} }; + + int i; + int cursor=0; + + if(when!=MENU_BEFOREGAME && when!=MENU_DURINGGAME) when = MENU_DURINGGAME; + + while(1){ + + rb->lcd_clear_display(); + + rb->lcd_putsxy(20, 1, "Solitaire"); + + for(i = 0; ilcd_putsxy(1, 17+9*i, menu[when][i]); + if(cursor == i) + rb->lcd_invertrect(0,17-1+9*i, LCD_WIDTH, 9); + } + + rb->lcd_update(); + + switch(rb->button_get(true)){ + case BUTTON_UP: + cursor = (cursor + MENU_LENGTH - 1)%MENU_LENGTH; + break; + + case BUTTON_DOWN: + cursor = (cursor + 1)%MENU_LENGTH; + break; + + case BUTTON_LEFT: + return MENU_RESUME; + + case BUTTON_PLAY: + case BUTTON_RIGHT: + switch(cursor){ + case MENU_RESUME: + case MENU_RESTART: + case MENU_QUIT: + return cursor; + + case MENU_HELP: + solitaire_help(); + break; + } + + case BUTTON_F1: + case BUTTON_F2: + case BUTTON_F3: + rb->splash(HZ, true, "Solitaire for Rockbox by dionoea"); + break; + + case BUTTON_OFF: + return MENU_QUIT; + + default: + break; + } + } +} + +//player's cursor +unsigned char cur_card; +//player's cursor column num +unsigned char cur_col; + +//selected card +unsigned char sel_card; + +//the deck +card deck[COLORS * CARDS_PER_COLOR]; + +//the remaining cards +unsigned char rem; +unsigned char cur_rem; + +//the 7 game columns +unsigned char cols[COL_NUM]; + +//the 4 final color stacks +unsigned char stacks[COLORS]; + +//initialize the game +void solitaire_init(void){ + unsigned char c; + int i,j; + + //init deck + for(i=0;ilcd_clear_display(); + + //get the biggest column length so that display can be "optimized" + biggest_col_length = 0; + + for(i=0;ibiggest_col_length) biggest_col_length = j; + } + + //check if there are cards remaining in the game. + //if there aren't any, that means you won :) + if(biggest_col_length == 0 && rem == NOT_A_CARD){ + rb->splash(HZ*2, true, "You Won :)"); + return; + } + + //draw the columns + for(i=0;ilcd_clearrect(i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM, j+1, CARD_WIDTH, CARD_HEIGHT-1); + //known card + if(deck[c].known){ + rb->lcd_bitmap(numbers[deck[c].num], i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+1, j, 8, 8, true); + rb->lcd_bitmap(colors[deck[c].color], i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+7, j, 8, 8, true); + } + //draw top line of the card + rb->lcd_drawline(i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+1,j,i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+CARD_WIDTH-1,j); + //selected card + if(c == sel_card && sel_card != NOT_A_CARD){ + rb->lcd_drawrect(i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+1, j+1, CARD_WIDTH-1, CARD_HEIGHT-1); + } + //cursor (or not) + if(c == cur_card){ + rb->lcd_invertrect(i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+1, j+1, CARD_WIDTH-1, CARD_HEIGHT-1); + //go to the next card + c = deck[c].next; + if(c == NOT_A_CARD) break; + j += CARD_HEIGHT - 2; + } else { + //go to the next card + c = deck[c].next; + if(c == NOT_A_CARD) break; + j += min(CARD_HEIGHT - 2, (LCD_HEIGHT - CARD_HEIGHT)/biggest_col_length); + } + } + //draw line to the left of the column + rb->lcd_drawline(i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM,1,i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM,j+CARD_HEIGHT-1); + //draw line to the right of the column + rb->lcd_drawline(i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+CARD_WIDTH,1,i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+CARD_WIDTH,j+CARD_HEIGHT-1); + //draw bottom of the last card + rb->lcd_drawline(i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+1,j+CARD_HEIGHT,i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+CARD_WIDTH-1,j+CARD_HEIGHT); + } + + //draw the stacks + for(i=0; ilcd_bitmap(numbers[deck[c].num], LCD_WIDTH - CARD_WIDTH+1, i*CARD_HEIGHT, 8, 8, true); + } + rb->lcd_bitmap(colors[i], LCD_WIDTH - CARD_WIDTH+7, i*CARD_HEIGHT, 8, 8, true); + rb->lcd_drawline(LCD_WIDTH - CARD_WIDTH+1,i*CARD_HEIGHT,LCD_WIDTH - 1,i*CARD_HEIGHT); + rb->lcd_drawline(LCD_WIDTH - CARD_WIDTH+1,(i+1)*CARD_HEIGHT,LCD_WIDTH - 1,(i+1)*CARD_HEIGHT); + } + + //draw the remains + rb->lcd_drawline(LCD_WIDTH - CARD_WIDTH+1,LCD_HEIGHT-CARD_HEIGHT-1,LCD_WIDTH - 1,LCD_HEIGHT-CARD_HEIGHT-1); + rb->lcd_drawline(LCD_WIDTH - CARD_WIDTH+1,LCD_HEIGHT-1,LCD_WIDTH - 1,LCD_HEIGHT-1); + if(cur_rem != NOT_A_CARD){ + rb->lcd_bitmap(numbers[deck[cur_rem].num], LCD_WIDTH - CARD_WIDTH+1, LCD_HEIGHT-CARD_HEIGHT, 8, 8, true); + rb->lcd_bitmap(colors[deck[cur_rem].color], LCD_WIDTH - CARD_WIDTH+7, LCD_HEIGHT-CARD_HEIGHT, 8, 8, true); + } + + rb->lcd_update(); + + //what to do when a key is pressed ... + switch(rb->button_get(true)){ + + //move cursor to the last card of the previous column + case BUTTON_RIGHT: + cur_col = (cur_col+1)%COL_NUM; + cur_card = cols[cur_col]; + if(cur_card != NOT_A_CARD){ + while(deck[cur_card].next != NOT_A_CARD){ + cur_card = deck[cur_card].next; + } + } + break; + + //move cursor to the last card of the next column + case BUTTON_LEFT: + cur_col = (cur_col + COL_NUM - 1)%COL_NUM; + cur_card = cols[cur_col]; + if(cur_card != NOT_A_CARD){ + while(deck[cur_card].next != NOT_A_CARD){ + cur_card = deck[cur_card].next; + } + } + break; + + //move cursor to card that's bellow + case BUTTON_DOWN: + if(cur_card == NOT_A_CARD) break; + if(deck[cur_card].next != NOT_A_CARD){ + cur_card = deck[cur_card].next; + } else { + cur_card = cols[cur_col]; + } + break; + + //move cursor to card that's above + case BUTTON_UP: + if(cur_card == NOT_A_CARD) break; + if(cols[cur_col] == cur_card){ + while(deck[cur_card].next != NOT_A_CARD){ + cur_card = deck[cur_card].next; + } + } else { + c = cols[cur_col]; + while(deck[c].next != cur_card){ + c = deck[c].next; + } + cur_card = c; + } + break; + + //Try to put card under cursor on one of the stacks + case BUTTON_F1: + //check if a card is selected + //else there would be nothing to move on the stacks ! + if(cur_card != NOT_A_CARD){ + //find the last card in the color's stack and put it's number in 'c'. + c = stacks[deck[cur_card].color]; + if(c!=NOT_A_CARD){ + while(deck[c].next!=NOT_A_CARD){ + c = deck[c].next; + } + } + //if 'c' isn't a card, that means that the stack is empty + //which implies that only an ace can be moved + if(c == NOT_A_CARD){ + //check if the selected card is an ace + //we don't have to check if any card is in the *.next + //position since the ace is the last possible card + if(deck[cur_card].num == 0){ + //remove 'cur_card' from any *.next postition ... + //... by looking in the columns + for(i=0;i0 && deck[cur_rem].next != NOT_A_CARD){ + cur_rem = deck[cur_rem].next; + i--; + } + //test if any cards are really left on the remains' stack + if(i == CARDS_PER_DRAW){ + cur_rem = NOT_A_CARD; + } + } + break; + + //Show the menu + case BUTTON_OFF: + switch(solitaire_menu(MENU_DURINGGAME)){ + case MENU_QUIT: + return; + + case MENU_RESTART: + solitaire_init(); + break; + } + } + } +} + +enum plugin_status plugin_start(struct plugin_api* api, void* parameter) +{ + //plugin init + TEST_PLUGIN_API(api); + (void)parameter; + rb = api; + //end of plugin init + + //Welcome to Solitaire ! + rb->splash(HZ*2, true, "Welcome to Solitaire !"); + + //play the game :) + solitaire(); + + //Exit the plugin + return PLUGIN_OK; +} + +#endif -- cgit v1.2.3