Minesweeper and Solitaire plugins by Antoine Cellerier


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4997 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/plugins/minesweeper.c b/apps/plugins/minesweeper.c
new file mode 100644
index 0000000..b155c3f
--- /dev/null
+++ b/apps/plugins/minesweeper.c
@@ -0,0 +1,436 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   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.
+ *
+ ****************************************************************************/
+
+/*****************************************************************************
+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 "button.h"
+#include "lcd.h"
+
+#ifdef HAVE_LCD_BITMAP
+
+//what the minesweeper() function can return
+#define MINESWEEPER_QUIT 2
+#define MINESWEEPER_LOSE 1
+#define MINESWEEPER_WIN  0
+
+
+/* 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[9][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},//........
+};
+
+/* 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
+//could be variable if malloc worked in the API :)
+const int height = LCD_HEIGHT/8;
+const int width = LCD_WIDTH/8;
+
+//the minefield
+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){
+    
+    if(x<0) return;
+    if(y<0) return;
+    if(x>width-1) return;
+    if(y>height-1) return;
+    if(minefield[y][x].known) return;
+    
+    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);
+    }
+    return;
+}
+
+
+//init not mine related elements of the mine field
+void minesweeper_init(void){
+    int i,j;
+    
+    for(i=0;i<height;i++){
+        for(j=0;j<width;j++){
+            minefield[i][j].known = 0;
+            minefield[i][j].flag = 0;
+        }
+    }
+}
+
+
+//put mines on the mine field
+//there is p% chance that a tile is a mine
+//if the tile has coordinates (x,y), then it can't be a mine
+void minesweeper_putmines(int p, int x, int y){
+    int i,j;
+
+    for(i=0;i<height;i++){
+        for(j=0;j<width;j++){
+            if(rb->rand()%100<p && !(y==i && x==j)){
+                minefield[i][j].mine = 1;
+                mine_num++;
+            } else {
+                minefield[i][j].mine = 0;
+            }
+            minefield[i][j].neighbors = 0;
+        }
+    }
+    
+    //we need to compute the neighbor element for each tile
+    for(i=0;i<height;i++){
+        for(j=0;j<width;j++){
+            if(i>0){
+                if(j>0) minefield[i][j].neighbors += minefield[i-1][j-1].mine;
+                minefield[i][j].neighbors += minefield[i-1][j].mine;
+                if(j<width-1) minefield[i][j].neighbors += minefield[i-1][j+1].mine;
+            }
+            if(j>0) minefield[i][j].neighbors += minefield[i][j-1].mine;
+            if(j<width-1) minefield[i][j].neighbors += minefield[i][j+1].mine;
+            if(i<height-1){
+                if(j>0) minefield[i][j].neighbors += minefield[i+1][j-1].mine;
+                minefield[i][j].neighbors += minefield[i+1][j].mine;
+                if(j<width-1) minefield[i][j].neighbors += minefield[i+1][j+1].mine;
+            }
+        }
+    }
+}
+
+//the big and ugly function that is the game
+int minesweeper(void){
+
+
+    int i,j;
+    
+    //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;
+
+    //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_putsxy(1,1,"Mine Sweeper");
+
+        rb->snprintf(str, 20, "%d%% mines", p);
+        rb->lcd_putsxy(1,19,str);
+        rb->lcd_putsxy(1,28,"down / up");
+        rb->lcd_putsxy(1,44,"ON to start");
+
+        rb->lcd_update();
+
+
+        switch(rb->button_get(true)){
+            case BUTTON_DOWN:
+            case BUTTON_LEFT:
+                p = (p + 98)%100;
+                break;
+            
+            case BUTTON_UP:
+            case BUTTON_RIGHT:
+                p = (p + 2)%100;
+                break;
+
+            case BUTTON_ON://start playing
+                i = 1;
+                break;
+
+            case BUTTON_OFF://quit program
+                return MINESWEEPER_QUIT;
+        }
+        if(i==1) break;
+    }
+    
+
+    /********************
+    *       init        *
+    ********************/
+
+    minesweeper_init();
+
+    /**********************
+    *        play         *
+    **********************/
+
+    while(true){
+    
+        //clear the screen buffer
+        rb->lcd_clear_display();
+    
+        //display the mine field
+        for(i=0;i<height;i++){
+            for(j=0;j<width;j++){
+                rb->lcd_drawrect(j*8,i*8,8,8);
+                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){
+                        rb->lcd_bitmap(num[minefield[i][j].neighbors],j*8,i*8,8,8,false); 
+                    } 
+                } 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 {
+                    rb->lcd_fillrect(j*8+2,i*8+2,4,4);
+                }
+            }
+        }
+
+        //display the cursor
+        rb->lcd_invertrect(x*8,y*8,8,8);
+    
+        //update the screen
+        rb->lcd_update();
+        
+        switch(rb->button_get(true)){
+            //quit minesweeper (you really shouldn't use this button ...)
+            case BUTTON_OFF:
+                return MINESWEEPER_QUIT;
+            
+            //move cursor left
+            case BUTTON_LEFT:
+                x = (x + width - 1)%width;
+                break;
+            
+            //move cursor right
+            case BUTTON_RIGHT:
+                x = (x + 1)%width;
+                break;
+            
+            //move cursor down
+            case BUTTON_DOWN:
+                y = (y + 1)%height;
+                break;
+            
+            //move cursor up
+            case BUTTON_UP:
+                y = (y + height - 1)%height;
+                break;
+
+            //discover a tile (and it's neighbors if .neighbors == 0)
+            case BUTTON_ON:
+            case BUTTON_F2:
+                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(minefield[y][x].mine){
+                    return MINESWEEPER_LOSE;
+                }
+                tiles_left = 0;
+                for(i=0;i<height;i++){
+                    for(j=0;j<width;j++){
+                         if(minefield[i][j].known == 0) tiles_left++;
+                    }
+                }
+                if(tiles_left == mine_num){
+                    return MINESWEEPER_WIN;
+                }
+                break; 
+            
+            //toggle flag under cursor
+            case BUTTON_PLAY:
+            case BUTTON_F1:
+                minefield[y][x].flag = (minefield[y][x].flag + 1)%2;
+                break;
+
+            //show how many mines you think you have found and how many
+            //there really are on the game
+            case BUTTON_F3:
+                tiles_left = 0;
+                for(i=0;i<height;i++){
+                    for(j=0;j<width;j++){
+                         if(minefield[i][j].flag) tiles_left++;
+                    }
+                }
+                rb->splash(HZ*2, true, "You found %d mines out of %d", tiles_left, mine_num);
+                break;
+        }
+    }
+ 
+}
+
+//plugin entry point
+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
+
+    switch(minesweeper()){
+         case MINESWEEPER_WIN:
+             rb->splash(HZ*2, true, "You Win :)");
+             break;
+
+        case MINESWEEPER_LOSE:
+             rb->splash(HZ*2, true, "You Lost :(");
+             break;
+
+        default:
+            break;
+    }
+
+    return PLUGIN_OK;
+}
+
+#endif
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
diff --git a/docs/CREDITS b/docs/CREDITS
index 2237772..0a49e6a 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -90,3 +90,4 @@
 Steve Cundari
 Mat Holton
 Jan Gajdos
+Antoine Cellerier