summaryrefslogtreecommitdiff
path: root/apps/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/minesweeper.c436
-rw-r--r--apps/plugins/solitaire.c867
2 files changed, 1303 insertions, 0 deletions
diff --git a/apps/plugins/minesweeper.c b/apps/plugins/minesweeper.c
new file mode 100644
index 0000000000..b155c3fbad
--- /dev/null
+++ b/apps/plugins/minesweeper.c
@@ -0,0 +1,436 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2004 dionoea (Antoine Cellerier)
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20/*****************************************************************************
21Mine Sweeper by dionoea
22
23use arrow keys to move cursor
24use ON or F2 to clear a tile
25use PLAY or F1 to put a flag on a tile
26use F3 to see how many mines are left (supposing all your flags are correct)
27
28*****************************************************************************/
29
30#include "plugin.h"
31#include "button.h"
32#include "lcd.h"
33
34#ifdef HAVE_LCD_BITMAP
35
36//what the minesweeper() function can return
37#define MINESWEEPER_QUIT 2
38#define MINESWEEPER_LOSE 1
39#define MINESWEEPER_WIN 0
40
41
42/* here is a global api struct pointer. while not strictly necessary,
43 it's nice not to have to pass the api pointer in all function calls
44 in the plugin */
45static struct plugin_api* rb;
46
47
48//define how numbers are displayed (that way we don't have to
49//worry about fonts)
50static unsigned char num[9][8] = {
51 /*reading the sprites:
52 on screen f123
53 4567
54 890a
55 bcde
56
57 in binary b84f
58 c951
59 d062
60 ea73
61 */
62
63 //0
64 {0x00, //........
65 0x00, //........
66 0x00, //........
67 0x00, //........
68 0x00, //........
69 0x00, //........
70 0x00, //........
71 0x00},//........
72 //1
73 {0x00, //........
74 0x00, //........
75 0x00, //...OO...
76 0x44, //....O...
77 0x7c, //....O...
78 0x40, //....O...
79 0x00, //...OOO..
80 0x00},//........
81 //2
82 {0x00, //........
83 0x00, //........
84 0x48, //...OO...
85 0x64, //..O..O..
86 0x54, //....O...
87 0x48, //...O....
88 0x00, //..OOOO..
89 0x00},//........
90 //3
91 {0x00, //........
92 0x00, //........
93 0x44, //..OOO...
94 0x54, //.....O..
95 0x54, //...OO...
96 0x28, //.....O..
97 0x00, //..OOO...
98 0x00},//........
99 //4
100 {0x00, //........
101 0x00, //........
102 0x1c, //..O.....
103 0x10, //..O.....
104 0x70, //..OOOO..
105 0x10, //....O...
106 0x00, //....O...
107 0x00},//........
108 //5
109 {0x00, //........
110 0x00, //........
111 0x5c, //..OOOO..
112 0x54, //..O.....
113 0x54, //..OOO...
114 0x24, //.....O..
115 0x00, //..OOO...
116 0x00},//........
117 //6
118 {0x00, //........
119 0x00, //........
120 0x38, //...OOO..
121 0x54, //..O.....
122 0x54, //..OOO...
123 0x24, //..O..O..
124 0x00, //...OO...
125 0x00},//........
126 //7
127 {0x00, //........
128 0x00, //........
129 0x44, //..OOOO..
130 0x24, //.....O..
131 0x14, //....O...
132 0x0c, //...O....
133 0x00, //..O.....
134 0x00},//........
135 //8
136 {0x00, //........
137 0x00, //........
138 0x28, //...OO...
139 0x54, //..O..O..
140 0x54, //...OO...
141 0x28, //..O..O..
142 0x00, //...OO...
143 0x00},//........
144};
145
146/* the tile struct
147if there is a mine, mine is true
148if tile is known by player, known is true
149if tile has a flag, flag is true
150neighbors is the total number of mines arround tile
151*/
152typedef struct tile {
153 unsigned char mine : 1;
154 unsigned char known : 1;
155 unsigned char flag : 1;
156 unsigned char neighbors : 4;
157} tile;
158
159//the height and width of the field
160//could be variable if malloc worked in the API :)
161const int height = LCD_HEIGHT/8;
162const int width = LCD_WIDTH/8;
163
164//the minefield
165tile minefield[LCD_HEIGHT/8][LCD_WIDTH/8];
166
167//total number of mines on the game
168int mine_num = 0;
169
170//discovers the tile when player clears one of them
171//a chain reaction (of discovery) occurs if tile has no mines
172//as neighbors
173void discover(int, int);
174void discover(int x, int y){
175
176 if(x<0) return;
177 if(y<0) return;
178 if(x>width-1) return;
179 if(y>height-1) return;
180 if(minefield[y][x].known) return;
181
182 minefield[y][x].known = 1;
183 if(minefield[y][x].neighbors == 0){
184 discover(x-1,y-1);
185 discover(x,y-1);
186 discover(x+1,y-1);
187 discover(x+1,y);
188 discover(x+1,y+1);
189 discover(x,y+1);
190 discover(x-1,y+1);
191 discover(x-1,y);
192 }
193 return;
194}
195
196
197//init not mine related elements of the mine field
198void minesweeper_init(void){
199 int i,j;
200
201 for(i=0;i<height;i++){
202 for(j=0;j<width;j++){
203 minefield[i][j].known = 0;
204 minefield[i][j].flag = 0;
205 }
206 }
207}
208
209
210//put mines on the mine field
211//there is p% chance that a tile is a mine
212//if the tile has coordinates (x,y), then it can't be a mine
213void minesweeper_putmines(int p, int x, int y){
214 int i,j;
215
216 for(i=0;i<height;i++){
217 for(j=0;j<width;j++){
218 if(rb->rand()%100<p && !(y==i && x==j)){
219 minefield[i][j].mine = 1;
220 mine_num++;
221 } else {
222 minefield[i][j].mine = 0;
223 }
224 minefield[i][j].neighbors = 0;
225 }
226 }
227
228 //we need to compute the neighbor element for each tile
229 for(i=0;i<height;i++){
230 for(j=0;j<width;j++){
231 if(i>0){
232 if(j>0) minefield[i][j].neighbors += minefield[i-1][j-1].mine;
233 minefield[i][j].neighbors += minefield[i-1][j].mine;
234 if(j<width-1) minefield[i][j].neighbors += minefield[i-1][j+1].mine;
235 }
236 if(j>0) minefield[i][j].neighbors += minefield[i][j-1].mine;
237 if(j<width-1) minefield[i][j].neighbors += minefield[i][j+1].mine;
238 if(i<height-1){
239 if(j>0) minefield[i][j].neighbors += minefield[i+1][j-1].mine;
240 minefield[i][j].neighbors += minefield[i+1][j].mine;
241 if(j<width-1) minefield[i][j].neighbors += minefield[i+1][j+1].mine;
242 }
243 }
244 }
245}
246
247//the big and ugly function that is the game
248int minesweeper(void){
249
250
251 int i,j;
252
253 //the cursor coordinates
254 int x=0,y=0;
255
256 //number of tiles left on the game
257 int tiles_left=width*height;
258
259 //percentage of mines on minefield used durring generation
260 int p=16;
261
262 //a usefull string for snprintf
263 char str[30];
264
265 //welcome screen where player can chose mine percentage
266 i = 0;
267 while(true){
268 rb->lcd_clear_display();
269
270 rb->lcd_putsxy(1,1,"Mine Sweeper");
271
272 rb->snprintf(str, 20, "%d%% mines", p);
273 rb->lcd_putsxy(1,19,str);
274 rb->lcd_putsxy(1,28,"down / up");
275 rb->lcd_putsxy(1,44,"ON to start");
276
277 rb->lcd_update();
278
279
280 switch(rb->button_get(true)){
281 case BUTTON_DOWN:
282 case BUTTON_LEFT:
283 p = (p + 98)%100;
284 break;
285
286 case BUTTON_UP:
287 case BUTTON_RIGHT:
288 p = (p + 2)%100;
289 break;
290
291 case BUTTON_ON://start playing
292 i = 1;
293 break;
294
295 case BUTTON_OFF://quit program
296 return MINESWEEPER_QUIT;
297 }
298 if(i==1) break;
299 }
300
301
302 /********************
303 * init *
304 ********************/
305
306 minesweeper_init();
307
308 /**********************
309 * play *
310 **********************/
311
312 while(true){
313
314 //clear the screen buffer
315 rb->lcd_clear_display();
316
317 //display the mine field
318 for(i=0;i<height;i++){
319 for(j=0;j<width;j++){
320 rb->lcd_drawrect(j*8,i*8,8,8);
321 if(minefield[i][j].known){
322 if(minefield[i][j].mine){
323 rb->lcd_putsxy(j*8+1,i*8+1,"b");
324 } else if(minefield[i][j].neighbors){
325 rb->lcd_bitmap(num[minefield[i][j].neighbors],j*8,i*8,8,8,false);
326 }
327 } else if(minefield[i][j].flag) {
328 rb->lcd_drawline(j*8+2,i*8+2,j*8+5,i*8+5);
329 rb->lcd_drawline(j*8+2,i*8+5,j*8+5,i*8+2);
330 } else {
331 rb->lcd_fillrect(j*8+2,i*8+2,4,4);
332 }
333 }
334 }
335
336 //display the cursor
337 rb->lcd_invertrect(x*8,y*8,8,8);
338
339 //update the screen
340 rb->lcd_update();
341
342 switch(rb->button_get(true)){
343 //quit minesweeper (you really shouldn't use this button ...)
344 case BUTTON_OFF:
345 return MINESWEEPER_QUIT;
346
347 //move cursor left
348 case BUTTON_LEFT:
349 x = (x + width - 1)%width;
350 break;
351
352 //move cursor right
353 case BUTTON_RIGHT:
354 x = (x + 1)%width;
355 break;
356
357 //move cursor down
358 case BUTTON_DOWN:
359 y = (y + 1)%height;
360 break;
361
362 //move cursor up
363 case BUTTON_UP:
364 y = (y + height - 1)%height;
365 break;
366
367 //discover a tile (and it's neighbors if .neighbors == 0)
368 case BUTTON_ON:
369 case BUTTON_F2:
370 if(minefield[y][x].flag) break;
371 //we put the mines on the first "click" so that you don't
372 //lose on the first "click"
373 if(tiles_left == width*height) minesweeper_putmines(p,x,y);
374 discover(x,y);
375 if(minefield[y][x].mine){
376 return MINESWEEPER_LOSE;
377 }
378 tiles_left = 0;
379 for(i=0;i<height;i++){
380 for(j=0;j<width;j++){
381 if(minefield[i][j].known == 0) tiles_left++;
382 }
383 }
384 if(tiles_left == mine_num){
385 return MINESWEEPER_WIN;
386 }
387 break;
388
389 //toggle flag under cursor
390 case BUTTON_PLAY:
391 case BUTTON_F1:
392 minefield[y][x].flag = (minefield[y][x].flag + 1)%2;
393 break;
394
395 //show how many mines you think you have found and how many
396 //there really are on the game
397 case BUTTON_F3:
398 tiles_left = 0;
399 for(i=0;i<height;i++){
400 for(j=0;j<width;j++){
401 if(minefield[i][j].flag) tiles_left++;
402 }
403 }
404 rb->splash(HZ*2, true, "You found %d mines out of %d", tiles_left, mine_num);
405 break;
406 }
407 }
408
409}
410
411//plugin entry point
412enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
413{
414 //plugin init
415 TEST_PLUGIN_API(api);
416 (void)parameter;
417 rb = api;
418 //end of plugin init
419
420 switch(minesweeper()){
421 case MINESWEEPER_WIN:
422 rb->splash(HZ*2, true, "You Win :)");
423 break;
424
425 case MINESWEEPER_LOSE:
426 rb->splash(HZ*2, true, "You Lost :(");
427 break;
428
429 default:
430 break;
431 }
432
433 return PLUGIN_OK;
434}
435
436#endif
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2004 dionoea (Antoine Cellerier)
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20/*****************************************************************************
21Solitaire by dionoea
22
23use arrows to move the cursor
24use ON to select cards in the columns, move cards inside the columns,
25 reveal hidden cards, ...
26use PLAY to move a card from the remains' stack to the top of the cursor
27use F1 to put card under cursor on one of the 4 final color stacks
28use F2 to un-select card if a card was selected, else draw 3 new cards
29 out of the remains' stack
30use F3 to put card on top of the remains' stack on one of the 4 final color
31 stacks
32
33*****************************************************************************/
34
35#include "plugin.h"
36#include "button.h"
37#include "lcd.h"
38
39#ifdef HAVE_LCD_BITMAP
40
41/* here is a global api struct pointer. while not strictly necessary,
42 it's nice not to have to pass the api pointer in all function calls
43 in the plugin */
44static struct plugin_api* rb;
45
46#define min(a,b) (a<b?a:b)
47
48#define HELP_CASE( key ) case BUTTON_ ## key: \
49 rb->splash(HZ*1, true, # key " : " HELP_BUTTON_ ## key); \
50 break;
51
52#define HELP_BUTTON_UP "Move the cursor up in the column."
53#define HELP_BUTTON_DOWN "Move the cursor down in the column."
54#define HELP_BUTTON_LEFT "Move the cursor to the previous column."
55#define HELP_BUTTON_RIGHT "Move the cursor to the next column."
56#define HELP_BUTTON_F1 "Put the card under the cursor on one of the 4 final color stacks."
57#define HELP_BUTTON_F2 "Un-select a card if it was selected. Else, draw 3 new cards out of the remains' stack."
58#define HELP_BUTTON_F3 "Put the card on top of the remains' stack on one of the 4 final color stacks."
59#define HELP_BUTTON_PLAY "Put the card on top of the remains' stack on top of the cursor."
60#define HELP_BUTTON_ON "Select cards in the columns, Move cards inside the columns, reveal hidden cards ..."
61
62static unsigned char colors[4][8] = {
63//Spades
64{0x00, //........
65 0x18, //...O....
66 0x1c, //..OOO...
67 0x3e, //.OOOOO..
68 0x1c, //.OOOOO..
69 0x18, //...O....
70 0x00, //........
71 0x00},//........
72//Hearts
73{0x00, //........
74 0x0c, //..O.O...
75 0x1e, //.OOOOO..
76 0x3c, //.OOOOO..
77 0x1e, //..OOO...
78 0x0c, //...O....
79 0x00, //........
80 0x00},//........
81//Clubs
82{0x00, //........
83 0x18, //..OOO...
84 0x0a, //...O....
85 0x3e, //.OOOOO..
86 0x0a, //.O.O.O..
87 0x18, //...O....
88 0x00, //........
89 0x00},//........
90//Diamonds
91{0x00, //........
92 0x08, //...O....
93 0x1c, //..OOO...
94 0x3e, //.OOOOO..
95 0x1c, //..OOO...
96 0x08, //...O....
97 0x00, //........
98 0x00} //........
99};
100
101static unsigned char numbers[13][8] = {
102//Ace
103{0x00, //........
104 0x38, //...O....
105 0x14, //..O.O...
106 0x12, //.O...O..
107 0x14, //.OOOOO..
108 0x38, //.O...O..
109 0x00, //........
110 0x00},//........
111//2
112{0x00, //........
113 0x24, //..OOO...
114 0x32, //.O...O..
115 0x32, //....O...
116 0x2a, //..OO....
117 0x24, //.OOOOO..
118 0x00, //........
119 0x00},//........
120//3
121{0x00, //........
122 0x22, //.OOOO...
123 0x2a, //.....O..
124 0x2a, //..OOO...
125 0x2a, //.....O..
126 0x14, //.OOOO...
127 0x00, //........
128 0x00},//........
129//4
130{0x00, //........
131 0x10, //....O...
132 0x18, //...O....
133 0x34, //..O.....
134 0x12, //.OOOOO..
135 0x10, //...O....
136 0x00, //........
137 0x00},//........
138//5
139{0x00, //........
140 0x2e, //.OOOOO..
141 0x2a, //.O......
142 0x2a, //.OOOO...
143 0x2a, //.....O..
144 0x12, //.OOOO...
145 0x00, //........
146 0x00},//........
147//6
148{0x00, //........
149 0x1c, //..OOO...
150 0x2a, //.O......
151 0x2a, //.OOOO...
152 0x2a, //.O...O..
153 0x10, //..OOO...
154 0x00, //........
155 0x00},//........
156//7
157{0x00, //........
158 0x22, //.OOOOO..
159 0x12, //....O...
160 0x0a, //...O....
161 0x06, //..O.....
162 0x02, //.O......
163 0x00, //........
164 0x00},//........
165//8
166{0x00, //........
167 0x14, //..OOO...
168 0x2a, //.O...O..
169 0x2a, //..OOO...
170 0x2a, //.O...O..
171 0x14, //..OOO...
172 0x00, //........
173 0x00},//........
174//9
175{0x00, //........
176 0x04, //..OOO...
177 0x2a, //.O...O..
178 0x2a, //..OOOO..
179 0x2a, //.....O..
180 0x1c, //..OOO...
181 0x00, //........
182 0x00},//........
183//10
184{0x00, //........
185 0x3e, //.O..O...
186 0x00, //.O.O.O..
187 0x1c, //.O.O.O..
188 0x22, //.O.O.O..
189 0x1c, //.O..O...
190 0x00, //........
191 0x00},//........
192//Jack
193{0x00, //........
194 0x12, //.OOOOO..
195 0x22, //...O....
196 0x1e, //...O....
197 0x02, //.O.O....
198 0x02, //..O.....
199 0x00, //........
200 0x00},//........
201//Queen
202{0x00, //........
203 0x1c, //..OOO...
204 0x22, //.O...O..
205 0x32, //.O...O..
206 0x22, //.O.O.O..
207 0x1c, //..OOO...
208 0x00, //........
209 0x00},//........
210//King
211{0x00, //........
212 0x3e, //.O...O..
213 0x08, //.O..O...
214 0x08, //.OOO....
215 0x14, //.O..O...
216 0x22, //.O...O..
217 0x00, //........
218 0x00} //........
219};
220
221#define NOT_A_CARD 255
222
223//number of cards per color
224#define CARDS_PER_COLOR 13
225
226//number of colors
227#define COLORS 4
228
229//number of columns
230#define COL_NUM 7
231
232//number of cards that are drawn on the remains' stack (by pressing F2)
233#define CARDS_PER_DRAW 3
234
235//size of a card on the screen
236#define CARD_WIDTH 14
237#define CARD_HEIGHT 10
238
239typedef struct card {
240 unsigned char color : 2;
241 unsigned char num : 4;
242 unsigned char known : 1;
243 unsigned char used : 1;//this is what is used when dealing cards
244 unsigned char next;
245} card;
246
247unsigned char next_random_card(card *deck){
248 unsigned char i,r;
249
250 r = rb->rand()%(COLORS * CARDS_PER_COLOR)+1;
251 i = 0;
252
253 while(r>0){
254 i = (i + 1)%(COLORS * CARDS_PER_COLOR);
255 if(!deck[i].used) r--;
256 }
257
258 deck[i].used = 1;
259
260 return i;
261}
262
263//help for the not so intuitive interface
264void solitaire_help(void){
265
266 rb->lcd_clear_display();
267
268 rb->lcd_putsxy(0, 0, "Press a key to see");
269 rb->lcd_putsxy(0, 7, "it's role.");
270 rb->lcd_putsxy(0, 21, "Press OFF to");
271 rb->lcd_putsxy(0, 28, "return to menu");
272
273 rb->lcd_update();
274
275 while(1){
276
277 switch(rb->button_get(true)){
278 HELP_CASE( UP );
279 HELP_CASE( DOWN );
280 HELP_CASE( LEFT );
281 HELP_CASE( RIGHT );
282 HELP_CASE( F1 );
283 HELP_CASE( F2 );
284 HELP_CASE( F3 );
285 HELP_CASE( PLAY );
286 HELP_CASE( ON );
287
288 case BUTTON_OFF:
289 return;
290 }
291 }
292}
293
294//menu return codes
295#define MENU_RESUME 0
296#define MENU_RESTART 1
297#define MENU_HELP 2
298#define MENU_QUIT 3
299
300//menu item number
301#define MENU_LENGTH 4
302
303//different menu behaviors
304#define MENU_BEFOREGAME 0
305#define MENU_DURINGGAME 1
306
307//the menu
308//text displayed changes depending on the 'when' parameter
309int solitaire_menu(unsigned char when){
310
311 static char menu[2][MENU_LENGTH][13] =
312 { { "Start Game",
313 "",
314 "Help",
315 "Quit" },
316 { "Resume Game",
317 "Restart Game",
318 "Help",
319 "Quit"} };
320
321 int i;
322 int cursor=0;
323
324 if(when!=MENU_BEFOREGAME && when!=MENU_DURINGGAME) when = MENU_DURINGGAME;
325
326 while(1){
327
328 rb->lcd_clear_display();
329
330 rb->lcd_putsxy(20, 1, "Solitaire");
331
332 for(i = 0; i<MENU_LENGTH; i++){
333 rb->lcd_putsxy(1, 17+9*i, menu[when][i]);
334 if(cursor == i)
335 rb->lcd_invertrect(0,17-1+9*i, LCD_WIDTH, 9);
336 }
337
338 rb->lcd_update();
339
340 switch(rb->button_get(true)){
341 case BUTTON_UP:
342 cursor = (cursor + MENU_LENGTH - 1)%MENU_LENGTH;
343 break;
344
345 case BUTTON_DOWN:
346 cursor = (cursor + 1)%MENU_LENGTH;
347 break;
348
349 case BUTTON_LEFT:
350 return MENU_RESUME;
351
352 case BUTTON_PLAY:
353 case BUTTON_RIGHT:
354 switch(cursor){
355 case MENU_RESUME:
356 case MENU_RESTART:
357 case MENU_QUIT:
358 return cursor;
359
360 case MENU_HELP:
361 solitaire_help();
362 break;
363 }
364
365 case BUTTON_F1:
366 case BUTTON_F2:
367 case BUTTON_F3:
368 rb->splash(HZ, true, "Solitaire for Rockbox by dionoea");
369 break;
370
371 case BUTTON_OFF:
372 return MENU_QUIT;
373
374 default:
375 break;
376 }
377 }
378}
379
380//player's cursor
381unsigned char cur_card;
382//player's cursor column num
383unsigned char cur_col;
384
385//selected card
386unsigned char sel_card;
387
388//the deck
389card deck[COLORS * CARDS_PER_COLOR];
390
391//the remaining cards
392unsigned char rem;
393unsigned char cur_rem;
394
395//the 7 game columns
396unsigned char cols[COL_NUM];
397
398//the 4 final color stacks
399unsigned char stacks[COLORS];
400
401//initialize the game
402void solitaire_init(void){
403 unsigned char c;
404 int i,j;
405
406 //init deck
407 for(i=0;i<COLORS;i++){
408 for(j=0;j<CARDS_PER_COLOR;j++){
409 deck[i*CARDS_PER_COLOR+j].color = i;
410 deck[i*CARDS_PER_COLOR+j].num = j;
411 deck[i*CARDS_PER_COLOR+j].known = 0;
412 deck[i*CARDS_PER_COLOR+j].used = 0;
413 deck[i*CARDS_PER_COLOR+j].next = NOT_A_CARD;
414 }
415 }
416
417 //deal the cards ...
418 //... in the columns
419 for(i=0; i<COL_NUM; i++){
420 c = NOT_A_CARD;
421 for(j=0; j<=i; j++){
422 if(c == NOT_A_CARD){
423 cols[i] = next_random_card(deck);
424 c = cols[i];
425 } else {
426 deck[c].next = next_random_card(deck);
427 c = deck[c].next;
428 }
429 if(j==i) deck[c].known = 1;
430 }
431 }
432
433 //... shuffle what's left of the deck
434 rem = next_random_card(deck);
435 c = rem;
436
437 for(i=1; i<COLORS * CARDS_PER_COLOR - COL_NUM * (COL_NUM + 1)/2; i++){
438 deck[c].next = next_random_card(deck);
439 c = deck[c].next;
440 }
441
442 //we now finished dealing the cards. The game can start ! (at last)
443
444 //init the stack
445 for(i = 0; i<COL_NUM;i++){
446 stacks[i] = NOT_A_CARD;
447 }
448
449 //the cursor starts on upper left card
450 cur_card = cols[0];
451 cur_col = 0;
452
453 //no card is selected
454 sel_card = NOT_A_CARD;
455
456 //init the remainder
457 cur_rem = NOT_A_CARD;
458}
459
460
461//the game
462void solitaire(void){
463
464 int i,j;
465 unsigned char c;
466 int biggest_col_length;
467
468 if(solitaire_menu(MENU_BEFOREGAME) == MENU_QUIT) return;
469 solitaire_init();
470
471 while(true){
472
473 rb->lcd_clear_display();
474
475 //get the biggest column length so that display can be "optimized"
476 biggest_col_length = 0;
477
478 for(i=0;i<COL_NUM;i++){
479 j = 0;
480 c = cols[i];
481 while(c != NOT_A_CARD){
482 j++;
483 c = deck[c].next;
484 }
485 if(j>biggest_col_length) biggest_col_length = j;
486 }
487
488 //check if there are cards remaining in the game.
489 //if there aren't any, that means you won :)
490 if(biggest_col_length == 0 && rem == NOT_A_CARD){
491 rb->splash(HZ*2, true, "You Won :)");
492 return;
493 }
494
495 //draw the columns
496 for(i=0;i<COL_NUM;i++){
497 c = cols[i];
498 j = 0;
499 while(true){
500 if(c==NOT_A_CARD) break;
501 //clear the card's spot
502 rb->lcd_clearrect(i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM, j+1, CARD_WIDTH, CARD_HEIGHT-1);
503 //known card
504 if(deck[c].known){
505 rb->lcd_bitmap(numbers[deck[c].num], i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+1, j, 8, 8, true);
506 rb->lcd_bitmap(colors[deck[c].color], i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+7, j, 8, 8, true);
507 }
508 //draw top line of the card
509 rb->lcd_drawline(i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+1,j,i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+CARD_WIDTH-1,j);
510 //selected card
511 if(c == sel_card && sel_card != NOT_A_CARD){
512 rb->lcd_drawrect(i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+1, j+1, CARD_WIDTH-1, CARD_HEIGHT-1);
513 }
514 //cursor (or not)
515 if(c == cur_card){
516 rb->lcd_invertrect(i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM+1, j+1, CARD_WIDTH-1, CARD_HEIGHT-1);
517 //go to the next card
518 c = deck[c].next;
519 if(c == NOT_A_CARD) break;
520 j += CARD_HEIGHT - 2;
521 } else {
522 //go to the next card
523 c = deck[c].next;
524 if(c == NOT_A_CARD) break;
525 j += min(CARD_HEIGHT - 2, (LCD_HEIGHT - CARD_HEIGHT)/biggest_col_length);
526 }
527 }
528 //draw line to the left of the column
529 rb->lcd_drawline(i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM,1,i*(LCD_WIDTH - CARD_WIDTH)/COL_NUM,j+CARD_HEIGHT-1);
530 //draw line to the right of the column
531 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);
532 //draw bottom of the last card
533 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);
534 }
535
536 //draw the stacks
537 for(i=0; i<COLORS; i++){
538 c = stacks[i];
539 if(c!=NOT_A_CARD){
540 while(deck[c].next != NOT_A_CARD){
541 c = deck[c].next;
542 }
543 }
544 if(c != NOT_A_CARD) {
545 rb->lcd_bitmap(numbers[deck[c].num], LCD_WIDTH - CARD_WIDTH+1, i*CARD_HEIGHT, 8, 8, true);
546 }
547 rb->lcd_bitmap(colors[i], LCD_WIDTH - CARD_WIDTH+7, i*CARD_HEIGHT, 8, 8, true);
548 rb->lcd_drawline(LCD_WIDTH - CARD_WIDTH+1,i*CARD_HEIGHT,LCD_WIDTH - 1,i*CARD_HEIGHT);
549 rb->lcd_drawline(LCD_WIDTH - CARD_WIDTH+1,(i+1)*CARD_HEIGHT,LCD_WIDTH - 1,(i+1)*CARD_HEIGHT);
550 }
551
552 //draw the remains
553 rb->lcd_drawline(LCD_WIDTH - CARD_WIDTH+1,LCD_HEIGHT-CARD_HEIGHT-1,LCD_WIDTH - 1,LCD_HEIGHT-CARD_HEIGHT-1);
554 rb->lcd_drawline(LCD_WIDTH - CARD_WIDTH+1,LCD_HEIGHT-1,LCD_WIDTH - 1,LCD_HEIGHT-1);
555 if(cur_rem != NOT_A_CARD){
556 rb->lcd_bitmap(numbers[deck[cur_rem].num], LCD_WIDTH - CARD_WIDTH+1, LCD_HEIGHT-CARD_HEIGHT, 8, 8, true);
557 rb->lcd_bitmap(colors[deck[cur_rem].color], LCD_WIDTH - CARD_WIDTH+7, LCD_HEIGHT-CARD_HEIGHT, 8, 8, true);
558 }
559
560 rb->lcd_update();
561
562 //what to do when a key is pressed ...
563 switch(rb->button_get(true)){
564
565 //move cursor to the last card of the previous column
566 case BUTTON_RIGHT:
567 cur_col = (cur_col+1)%COL_NUM;
568 cur_card = cols[cur_col];
569 if(cur_card != NOT_A_CARD){
570 while(deck[cur_card].next != NOT_A_CARD){
571 cur_card = deck[cur_card].next;
572 }
573 }
574 break;
575
576 //move cursor to the last card of the next column
577 case BUTTON_LEFT:
578 cur_col = (cur_col + COL_NUM - 1)%COL_NUM;
579 cur_card = cols[cur_col];
580 if(cur_card != NOT_A_CARD){
581 while(deck[cur_card].next != NOT_A_CARD){
582 cur_card = deck[cur_card].next;
583 }
584 }
585 break;
586
587 //move cursor to card that's bellow
588 case BUTTON_DOWN:
589 if(cur_card == NOT_A_CARD) break;
590 if(deck[cur_card].next != NOT_A_CARD){
591 cur_card = deck[cur_card].next;
592 } else {
593 cur_card = cols[cur_col];
594 }
595 break;
596
597 //move cursor to card that's above
598 case BUTTON_UP:
599 if(cur_card == NOT_A_CARD) break;
600 if(cols[cur_col] == cur_card){
601 while(deck[cur_card].next != NOT_A_CARD){
602 cur_card = deck[cur_card].next;
603 }
604 } else {
605 c = cols[cur_col];
606 while(deck[c].next != cur_card){
607 c = deck[c].next;
608 }
609 cur_card = c;
610 }
611 break;
612
613 //Try to put card under cursor on one of the stacks
614 case BUTTON_F1:
615 //check if a card is selected
616 //else there would be nothing to move on the stacks !
617 if(cur_card != NOT_A_CARD){
618 //find the last card in the color's stack and put it's number in 'c'.
619 c = stacks[deck[cur_card].color];
620 if(c!=NOT_A_CARD){
621 while(deck[c].next!=NOT_A_CARD){
622 c = deck[c].next;
623 }
624 }
625 //if 'c' isn't a card, that means that the stack is empty
626 //which implies that only an ace can be moved
627 if(c == NOT_A_CARD){
628 //check if the selected card is an ace
629 //we don't have to check if any card is in the *.next
630 //position since the ace is the last possible card
631 if(deck[cur_card].num == 0){
632 //remove 'cur_card' from any *.next postition ...
633 //... by looking in the columns
634 for(i=0;i<COL_NUM;i++){
635 if(cols[i]==cur_card) cols[i] = NOT_A_CARD;
636 }
637 //... and in the entire deck
638 //TODO : check if looking in the cols is really needed
639 for(i=0;i<COLORS*CARDS_PER_COLOR;i++){
640 if(deck[i].next==cur_card) deck[i].next = NOT_A_CARD;
641 }
642 //move cur_card on top of the stack
643 stacks[deck[cur_card].color] = cur_card;
644 //assign the card at the bottom of cur_col to cur_card
645 cur_card = cols[cur_col];
646 if(cur_card != NOT_A_CARD){
647 while(deck[cur_card].next != NOT_A_CARD){
648 cur_card = deck[cur_card].next;
649 }
650 }
651 //clear the selection indicator
652 sel_card = NOT_A_CARD;
653 }
654 }
655 //the stack is not empty
656 //so we can move any card other than an ace
657 //we thus check that the card we are moving is the next on the stack and that it isn't under any card
658 else if(deck[cur_card].num == deck[c].num + 1 && deck[cur_card].next == NOT_A_CARD){
659 //same as above
660 for(i=0;i<COL_NUM;i++){
661 if(cols[i]==cur_card) cols[i] = NOT_A_CARD;
662 }
663 //re same
664 for(i=0;i<COLORS*CARDS_PER_COLOR;i++){
665 if(deck[i].next==cur_card) deck[i].next = NOT_A_CARD;
666 }
667 //...
668 deck[c].next = cur_card;
669 cur_card = cols[cur_col];
670 if(cur_card != NOT_A_CARD){
671 while(deck[cur_card].next != NOT_A_CARD){
672 cur_card = deck[cur_card].next;
673 }
674 }
675 sel_card = NOT_A_CARD;
676 }
677 }
678 break;
679
680 //Move cards arround, Uncover cards, ...
681 case BUTTON_ON:
682 if(sel_card == NOT_A_CARD) {
683 if((cur_card != NOT_A_CARD?deck[cur_card].next == NOT_A_CARD && deck[cur_card].known==0:0)){
684 deck[cur_card].known = 1;
685 } else {
686 sel_card = cur_card;
687 }
688 } else if(sel_card == cur_card) {
689 sel_card = NOT_A_CARD;
690 } else if(cur_card != NOT_A_CARD){
691 if(deck[cur_card].num == deck[sel_card].num + 1 && (deck[cur_card].color + deck[sel_card].color)%2 == 1 ){
692 for(i=0;i<COL_NUM;i++){
693 if(cols[i]==sel_card) cols[i] = NOT_A_CARD;
694 }
695 for(i=0;i<COLORS*CARDS_PER_COLOR;i++){
696 if(deck[i].next==sel_card) deck[i].next = NOT_A_CARD;
697 }
698 deck[cur_card].next = sel_card;
699 sel_card = NOT_A_CARD;
700 }
701 } else if(cur_card == NOT_A_CARD){
702 if(deck[sel_card].num == CARDS_PER_COLOR - 1){
703 for(i=0;i<COL_NUM;i++){
704 if(cols[i]==sel_card) cols[i] = NOT_A_CARD;
705 }
706 for(i=0;i<COLORS*CARDS_PER_COLOR;i++){
707 if(deck[i].next==sel_card) deck[i].next = NOT_A_CARD;
708 }
709 cols[cur_col] = sel_card;
710 sel_card = NOT_A_CARD;
711 }
712 }
713 break;
714
715 //If the card on the top of the remains can be put where
716 //the cursor is, go ahead
717 case BUTTON_PLAY:
718 //check if a card is face up on the remains' stack
719 if(cur_rem != NOT_A_CARD){
720 //if no card is selected, it means the col is empty
721 //thus, only a king can be moved
722 if(cur_card == NOT_A_CARD){
723 //check if selcted card is a king
724 if(deck[cur_rem].num == CARDS_PER_COLOR - 1){
725 //find the previous card on the remains' stack
726 c = rem;
727 //if the current card on the remains' stack
728 //is the first card of the stack, then ...
729 if(c == cur_rem){
730 c = NOT_A_CARD;
731 rem = deck[cur_rem].next;
732 }
733 //else ...
734 else {
735 while(deck[c].next != cur_rem){
736 c = deck[c].next;
737 }
738 deck[c].next = deck[cur_rem].next;
739 }
740 cols[cur_col] = cur_rem;
741 deck[cur_rem].next = NOT_A_CARD;
742 deck[cur_rem].known = 1;
743 cur_rem = c;
744 }
745 } else if(deck[cur_rem].num + 1 == deck[cur_card].num && (deck[cur_rem].color + deck[cur_card].color)%2==1) {
746 c = rem;
747 if(c == cur_rem){
748 c = NOT_A_CARD;
749 rem = deck[cur_rem].next;
750 } else {
751 while(deck[c].next != cur_rem){
752 c = deck[c].next;
753 }
754 deck[c].next = deck[cur_rem].next;
755 }
756 deck[cur_card].next = cur_rem;
757 deck[cur_rem].next = NOT_A_CARD;
758 deck[cur_rem].known = 1;
759 cur_rem = c;
760 }
761 }
762 break;
763
764 //If the card on top of the remains can be put on one
765 //of the stacks, do so
766 case BUTTON_F3:
767 if(cur_rem != NOT_A_CARD){
768 if(deck[cur_rem].num == 0){
769 c = rem;
770 if(c == cur_rem){
771 c = NOT_A_CARD;
772 rem = deck[cur_rem].next;
773 } else {
774 while(deck[c].next != cur_rem){
775 c = deck[c].next;
776 }
777 deck[c].next = deck[cur_rem].next;
778 }
779 deck[cur_rem].next = NOT_A_CARD;
780 deck[cur_rem].known = 1;
781 stacks[deck[cur_rem].color] = cur_rem;
782 cur_rem = c;
783 } else {
784
785 i = stacks[deck[cur_rem].color];
786 if(i==NOT_A_CARD) break;
787 while(deck[i].next != NOT_A_CARD){
788 i = deck[i].next;
789 }
790 if(deck[i].num + 1 == deck[cur_rem].num){
791 c = rem;
792 if(c == cur_rem){
793 c = NOT_A_CARD;
794 rem = deck[cur_rem].next;
795 } else {
796 while(deck[c].next != cur_rem){
797 c = deck[c].next;
798 }
799 deck[c].next = deck[cur_rem].next;
800 }
801 deck[i].next = cur_rem;
802 deck[cur_rem].next = NOT_A_CARD;
803 deck[cur_rem].known = 1;
804 cur_rem = c;
805 }
806 }
807 }
808 break;
809
810 //unselect selected card or ...
811 //draw new cards from the remains of the deck
812 case BUTTON_F2:
813 if(sel_card != NOT_A_CARD){
814 //unselect selected card
815 sel_card = NOT_A_CARD;
816 } else if(rem != NOT_A_CARD) {
817 //draw new cards form the remains of the deck
818 if(cur_rem == NOT_A_CARD){
819 cur_rem = rem;
820 i = CARDS_PER_DRAW - 1;
821 } else {
822 i = CARDS_PER_DRAW;
823 }
824 while(i>0 && deck[cur_rem].next != NOT_A_CARD){
825 cur_rem = deck[cur_rem].next;
826 i--;
827 }
828 //test if any cards are really left on the remains' stack
829 if(i == CARDS_PER_DRAW){
830 cur_rem = NOT_A_CARD;
831 }
832 }
833 break;
834
835 //Show the menu
836 case BUTTON_OFF:
837 switch(solitaire_menu(MENU_DURINGGAME)){
838 case MENU_QUIT:
839 return;
840
841 case MENU_RESTART:
842 solitaire_init();
843 break;
844 }
845 }
846 }
847}
848
849enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
850{
851 //plugin init
852 TEST_PLUGIN_API(api);
853 (void)parameter;
854 rb = api;
855 //end of plugin init
856
857 //Welcome to Solitaire !
858 rb->splash(HZ*2, true, "Welcome to Solitaire !");
859
860 //play the game :)
861 solitaire();
862
863 //Exit the plugin
864 return PLUGIN_OK;
865}
866
867#endif