summaryrefslogtreecommitdiff
path: root/apps/plugins/solitaire.c
diff options
context:
space:
mode:
authorBjörn Stenberg <bjorn@haxx.se>2004-08-17 06:50:14 +0000
committerBjörn Stenberg <bjorn@haxx.se>2004-08-17 06:50:14 +0000
commit619a5ca1d3dfbc86839245b9ce8977002efd187e (patch)
tree546dbcd5d22de4f7b730ba48c8565cbdce204326 /apps/plugins/solitaire.c
parent0ceaa5e365b3f6dc78269ed5c4cd43df5c0144eb (diff)
downloadrockbox-619a5ca1d3dfbc86839245b9ce8977002efd187e.zip
rockbox-619a5ca1d3dfbc86839245b9ce8977002efd187e.tar.gz
rockbox-619a5ca1d3dfbc86839245b9ce8977002efd187e.tar.bz2
rockbox-619a5ca1d3dfbc86839245b9ce8977002efd187e.tar.xz
Minesweeper and Solitaire plugins by Antoine Cellerier
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4997 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/solitaire.c')
-rw-r--r--apps/plugins/solitaire.c867
1 files changed, 867 insertions, 0 deletions
diff --git a/apps/plugins/solitaire.c b/apps/plugins/solitaire.c
new file mode 100644
index 0000000..2833443
--- /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) (a<b?a:b)
+
+#define HELP_CASE( key ) case BUTTON_ ## key: \
+ rb->splash(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; i<MENU_LENGTH; i++){
+ rb->lcd_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;i<COLORS;i++){
+ for(j=0;j<CARDS_PER_COLOR;j++){
+ deck[i*CARDS_PER_COLOR+j].color = i;
+ deck[i*CARDS_PER_COLOR+j].num = j;
+ deck[i*CARDS_PER_COLOR+j].known = 0;
+ deck[i*CARDS_PER_COLOR+j].used = 0;
+ deck[i*CARDS_PER_COLOR+j].next = NOT_A_CARD;
+ }
+ }
+
+ //deal the cards ...
+ //... in the columns
+ for(i=0; i<COL_NUM; i++){
+ c = NOT_A_CARD;
+ for(j=0; j<=i; j++){
+ if(c == NOT_A_CARD){
+ cols[i] = next_random_card(deck);
+ c = cols[i];
+ } else {
+ deck[c].next = next_random_card(deck);
+ c = deck[c].next;
+ }
+ if(j==i) deck[c].known = 1;
+ }
+ }
+
+ //... shuffle what's left of the deck
+ rem = next_random_card(deck);
+ c = rem;
+
+ for(i=1; i<COLORS * CARDS_PER_COLOR - COL_NUM * (COL_NUM + 1)/2; i++){
+ deck[c].next = next_random_card(deck);
+ c = deck[c].next;
+ }
+
+ //we now finished dealing the cards. The game can start ! (at last)
+
+ //init the stack
+ for(i = 0; i<COL_NUM;i++){
+ stacks[i] = NOT_A_CARD;
+ }
+
+ //the cursor starts on upper left card
+ cur_card = cols[0];
+ cur_col = 0;
+
+ //no card is selected
+ sel_card = NOT_A_CARD;
+
+ //init the remainder
+ cur_rem = NOT_A_CARD;
+}
+
+
+//the game
+void solitaire(void){
+
+ int i,j;
+ unsigned char c;
+ int biggest_col_length;
+
+ if(solitaire_menu(MENU_BEFOREGAME) == MENU_QUIT) return;
+ solitaire_init();
+
+ while(true){
+
+ rb->lcd_clear_display();
+
+ //get the biggest column length so that display can be "optimized"
+ biggest_col_length = 0;
+
+ for(i=0;i<COL_NUM;i++){
+ j = 0;
+ c = cols[i];
+ while(c != NOT_A_CARD){
+ j++;
+ c = deck[c].next;
+ }
+ if(j>biggest_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;i<COL_NUM;i++){
+ c = cols[i];
+ j = 0;
+ while(true){
+ if(c==NOT_A_CARD) break;
+ //clear the card's spot
+ rb->lcd_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; i<COLORS; i++){
+ c = stacks[i];
+ if(c!=NOT_A_CARD){
+ while(deck[c].next != NOT_A_CARD){
+ c = deck[c].next;
+ }
+ }
+ if(c != NOT_A_CARD) {
+ rb->lcd_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;i<COL_NUM;i++){
+ if(cols[i]==cur_card) cols[i] = NOT_A_CARD;
+ }
+ //... and in the entire deck
+ //TODO : check if looking in the cols is really needed
+ for(i=0;i<COLORS*CARDS_PER_COLOR;i++){
+ if(deck[i].next==cur_card) deck[i].next = NOT_A_CARD;
+ }
+ //move cur_card on top of the stack
+ stacks[deck[cur_card].color] = cur_card;
+ //assign the card at the bottom of cur_col to cur_card
+ 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;
+ }
+ }
+ //clear the selection indicator
+ sel_card = NOT_A_CARD;
+ }
+ }
+ //the stack is not empty
+ //so we can move any card other than an ace
+ //we thus check that the card we are moving is the next on the stack and that it isn't under any card
+ else if(deck[cur_card].num == deck[c].num + 1 && deck[cur_card].next == NOT_A_CARD){
+ //same as above
+ for(i=0;i<COL_NUM;i++){
+ if(cols[i]==cur_card) cols[i] = NOT_A_CARD;
+ }
+ //re same
+ for(i=0;i<COLORS*CARDS_PER_COLOR;i++){
+ if(deck[i].next==cur_card) deck[i].next = NOT_A_CARD;
+ }
+ //...
+ deck[c].next = cur_card;
+ 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;
+ }
+ }
+ sel_card = NOT_A_CARD;
+ }
+ }
+ break;
+
+ //Move cards arround, Uncover cards, ...
+ case BUTTON_ON:
+ if(sel_card == NOT_A_CARD) {
+ if((cur_card != NOT_A_CARD?deck[cur_card].next == NOT_A_CARD && deck[cur_card].known==0:0)){
+ deck[cur_card].known = 1;
+ } else {
+ sel_card = cur_card;
+ }
+ } else if(sel_card == cur_card) {
+ sel_card = NOT_A_CARD;
+ } else if(cur_card != NOT_A_CARD){
+ if(deck[cur_card].num == deck[sel_card].num + 1 && (deck[cur_card].color + deck[sel_card].color)%2 == 1 ){
+ for(i=0;i<COL_NUM;i++){
+ if(cols[i]==sel_card) cols[i] = NOT_A_CARD;
+ }
+ for(i=0;i<COLORS*CARDS_PER_COLOR;i++){
+ if(deck[i].next==sel_card) deck[i].next = NOT_A_CARD;
+ }
+ deck[cur_card].next = sel_card;
+ sel_card = NOT_A_CARD;
+ }
+ } else if(cur_card == NOT_A_CARD){
+ if(deck[sel_card].num == CARDS_PER_COLOR - 1){
+ for(i=0;i<COL_NUM;i++){
+ if(cols[i]==sel_card) cols[i] = NOT_A_CARD;
+ }
+ for(i=0;i<COLORS*CARDS_PER_COLOR;i++){
+ if(deck[i].next==sel_card) deck[i].next = NOT_A_CARD;
+ }
+ cols[cur_col] = sel_card;
+ sel_card = NOT_A_CARD;
+ }
+ }
+ break;
+
+ //If the card on the top of the remains can be put where
+ //the cursor is, go ahead
+ case BUTTON_PLAY:
+ //check if a card is face up on the remains' stack
+ if(cur_rem != NOT_A_CARD){
+ //if no card is selected, it means the col is empty
+ //thus, only a king can be moved
+ if(cur_card == NOT_A_CARD){
+ //check if selcted card is a king
+ if(deck[cur_rem].num == CARDS_PER_COLOR - 1){
+ //find the previous card on the remains' stack
+ c = rem;
+ //if the current card on the remains' stack
+ //is the first card of the stack, then ...
+ if(c == cur_rem){
+ c = NOT_A_CARD;
+ rem = deck[cur_rem].next;
+ }
+ //else ...
+ else {
+ while(deck[c].next != cur_rem){
+ c = deck[c].next;
+ }
+ deck[c].next = deck[cur_rem].next;
+ }
+ cols[cur_col] = cur_rem;
+ deck[cur_rem].next = NOT_A_CARD;
+ deck[cur_rem].known = 1;
+ cur_rem = c;
+ }
+ } else if(deck[cur_rem].num + 1 == deck[cur_card].num && (deck[cur_rem].color + deck[cur_card].color)%2==1) {
+ c = rem;
+ if(c == cur_rem){
+ c = NOT_A_CARD;
+ rem = deck[cur_rem].next;
+ } else {
+ while(deck[c].next != cur_rem){
+ c = deck[c].next;
+ }
+ deck[c].next = deck[cur_rem].next;
+ }
+ deck[cur_card].next = cur_rem;
+ deck[cur_rem].next = NOT_A_CARD;
+ deck[cur_rem].known = 1;
+ cur_rem = c;
+ }
+ }
+ break;
+
+ //If the card on top of the remains can be put on one
+ //of the stacks, do so
+ case BUTTON_F3:
+ if(cur_rem != NOT_A_CARD){
+ if(deck[cur_rem].num == 0){
+ c = rem;
+ if(c == cur_rem){
+ c = NOT_A_CARD;
+ rem = deck[cur_rem].next;
+ } else {
+ while(deck[c].next != cur_rem){
+ c = deck[c].next;
+ }
+ deck[c].next = deck[cur_rem].next;
+ }
+ deck[cur_rem].next = NOT_A_CARD;
+ deck[cur_rem].known = 1;
+ stacks[deck[cur_rem].color] = cur_rem;
+ cur_rem = c;
+ } else {
+
+ i = stacks[deck[cur_rem].color];
+ if(i==NOT_A_CARD) break;
+ while(deck[i].next != NOT_A_CARD){
+ i = deck[i].next;
+ }
+ if(deck[i].num + 1 == deck[cur_rem].num){
+ c = rem;
+ if(c == cur_rem){
+ c = NOT_A_CARD;
+ rem = deck[cur_rem].next;
+ } else {
+ while(deck[c].next != cur_rem){
+ c = deck[c].next;
+ }
+ deck[c].next = deck[cur_rem].next;
+ }
+ deck[i].next = cur_rem;
+ deck[cur_rem].next = NOT_A_CARD;
+ deck[cur_rem].known = 1;
+ cur_rem = c;
+ }
+ }
+ }
+ break;
+
+ //unselect selected card or ...
+ //draw new cards from the remains of the deck
+ case BUTTON_F2:
+ if(sel_card != NOT_A_CARD){
+ //unselect selected card
+ sel_card = NOT_A_CARD;
+ } else if(rem != NOT_A_CARD) {
+ //draw new cards form the remains of the deck
+ if(cur_rem == NOT_A_CARD){
+ cur_rem = rem;
+ i = CARDS_PER_DRAW - 1;
+ } else {
+ i = CARDS_PER_DRAW;
+ }
+ while(i>0 && 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