A bit of adapting.
[Rockbox.git] / apps / plugins / blackjack.c
blob421326e9c012dfd3ab73b7a23fdae702202cf285
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: $
10 * Copyright (C) 2006 Tom Ross
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include "plugin.h"
21 #include "card_deck.h"
22 #include "card_back.h"
24 PLUGIN_HEADER
26 /* save files */
27 #define SCORE_FILE PLUGIN_GAMES_DIR "/blackjack.score"
28 #define SAVE_FILE PLUGIN_GAMES_DIR "/blackjack.save"
30 #define NUM_SCORES LCD_HEIGHT/8-2
32 /* final game return status */
33 #define BJ_END 3
34 #define BJ_USB 2
35 #define BJ_QUIT 1
36 #define BJ_LOSE 0
38 #if CONFIG_KEYPAD == RECORDER_PAD
39 #define BJACK_START BUTTON_ON
40 #define BJACK_QUIT BUTTON_OFF
41 #define BJACK_MAX (BUTTON_ON|BUTTON_UP)
42 #define BJACK_MIN (BUTTON_ON|BUTTON_DOWN)
43 #define BJACK_HIT BUTTON_F1
44 #define BJACK_STAY BUTTON_F2
45 #define BJACK_DOUBLEDOWN BUTTON_F3
46 #define BJACK_SCORES BUTTON_RIGHT
47 #define BJACK_RESUME BUTTON_PLAY
48 #define BJACK_UP BUTTON_UP
49 #define BJACK_DOWN BUTTON_DOWN
50 #define BJACK_RIGHT BUTTON_RIGHT
51 #define BJACK_LEFT BUTTON_LEFT
53 #elif CONFIG_KEYPAD == ONDIO_PAD
54 #define BJACK_START BUTTON_MENU
55 #define BJACK_QUIT BUTTON_OFF
56 #define BJACK_MAX (BUTTON_MENU|BUTTON_UP)
57 #define BJACK_MIN (BUTTON_MENU|BUTTON_DOWN)
58 #define BJACK_HIT BUTTON_LEFT
59 #define BJACK_STAY BUTTON_RIGHT
60 #define BJACK_DOUBLEDOWN BUTTON_UP
61 #define BJACK_SCORES BUTTON_UP
62 #define BJACK_RESUME BUTTON_DOWN
63 #define BJACK_UP BUTTON_UP
64 #define BJACK_DOWN BUTTON_DOWN
65 #define BJACK_RIGHT BUTTON_RIGHT
66 #define BJACK_LEFT BUTTON_LEFT
68 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
69 #define BJACK_START BUTTON_PLAY
70 #define BJACK_QUIT BUTTON_POWER
71 #define BJACK_MAX (BUTTON_PLAY|BUTTON_SCROLL_UP)
72 #define BJACK_MIN (BUTTON_PLAY|BUTTON_SCROLL_DOWN)
73 #define BJACK_HIT BUTTON_PLAY
74 #define BJACK_STAY BUTTON_FF
75 #define BJACK_DOUBLEDOWN BUTTON_REW
76 #define BJACK_SCORES BUTTON_LEFT
77 #define BJACK_RESUME BUTTON_RIGHT
78 #define BJACK_UP BUTTON_SCROLL_UP
79 #define BJACK_DOWN BUTTON_SCROLL_DOWN
80 #define BJACK_RIGHT BUTTON_RIGHT
81 #define BJACK_LEFT BUTTON_LEFT
83 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
84 (CONFIG_KEYPAD == IRIVER_H300_PAD)
85 #define BJACK_START BUTTON_ON
86 #define BJACK_QUIT BUTTON_OFF
87 #define BJACK_MAX (BUTTON_ON|BUTTON_UP)
88 #define BJACK_MIN (BUTTON_ON|BUTTON_DOWN)
89 #define BJACK_HIT BUTTON_ON
90 #define BJACK_STAY BUTTON_REC
91 #define BJACK_DOUBLEDOWN BUTTON_SELECT
92 #define BJACK_SCORES BUTTON_SELECT
93 #define BJACK_RESUME BUTTON_MODE
94 #define BJACK_UP BUTTON_UP
95 #define BJACK_DOWN BUTTON_DOWN
96 #define BJACK_RIGHT BUTTON_RIGHT
97 #define BJACK_LEFT BUTTON_LEFT
99 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
100 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
101 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
102 #define BJACK_START BUTTON_SELECT
103 #define BJACK_QUIT BUTTON_MENU
104 #define BJACK_MAX (BUTTON_SELECT|BUTTON_SCROLL_FWD)
105 #define BJACK_MIN (BUTTON_SELECT|BUTTON_SCROLL_BACK)
106 #define BJACK_HIT BUTTON_SELECT
107 #define BJACK_STAY BUTTON_RIGHT
108 #define BJACK_DOUBLEDOWN BUTTON_LEFT
109 #define BJACK_SCORES BUTTON_RIGHT
110 #define BJACK_RESUME BUTTON_PLAY
111 #define BJACK_UP BUTTON_SCROLL_FWD
112 #define BJACK_DOWN BUTTON_SCROLL_BACK
113 #define BJACK_RIGHT BUTTON_RIGHT
114 #define BJACK_LEFT BUTTON_LEFT
116 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
117 #define BJACK_START BUTTON_PLAY
118 #define BJACK_QUIT BUTTON_POWER
119 #define BJACK_MAX (BUTTON_PLAY|BUTTON_UP)
120 #define BJACK_MIN (BUTTON_PLAY|BUTTON_DOWN)
121 #define BJACK_HIT BUTTON_SELECT
122 #define BJACK_STAY BUTTON_REC
123 #define BJACK_DOUBLEDOWN BUTTON_PLAY
124 #define BJACK_SCORES BUTTON_RIGHT
125 #define BJACK_RESUME BUTTON_DOWN
126 #define BJACK_UP BUTTON_UP
127 #define BJACK_DOWN BUTTON_DOWN
128 #define BJACK_RIGHT BUTTON_RIGHT
129 #define BJACK_LEFT BUTTON_LEFT
131 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
132 #define BJACK_START BUTTON_MODE
133 #define BJACK_QUIT BUTTON_PLAY
134 #define BJACK_MAX (BUTTON_EQ|BUTTON_UP)
135 #define BJACK_MIN (BUTTON_EQ|BUTTON_DOWN)
136 #define BJACK_HIT BUTTON_EQ
137 #define BJACK_STAY BUTTON_MODE
138 #define BJACK_DOUBLEDOWN BUTTON_SELECT
139 #define BJACK_SCORES BUTTON_SELECT
140 #define BJACK_RESUME (BUTTON_EQ|BUTTON_MODE)
141 #define BJACK_UP BUTTON_UP
142 #define BJACK_DOWN BUTTON_DOWN
143 #define BJACK_RIGHT BUTTON_RIGHT
144 #define BJACK_LEFT BUTTON_LEFT
146 #elif CONFIG_KEYPAD == GIGABEAT_PAD
147 #define BJACK_START BUTTON_POWER
148 #define BJACK_QUIT BUTTON_A
149 #define BJACK_MAX BUTTON_VOL_UP
150 #define BJACK_MIN BUTTON_VOL_DOWN
151 #define BJACK_HIT BUTTON_VOL_UP
152 #define BJACK_STAY BUTTON_VOL_DOWN
153 #define BJACK_DOUBLEDOWN BUTTON_SELECT
154 #define BJACK_SCORES BUTTON_RIGHT
155 #define BJACK_RESUME BUTTON_MENU
156 #define BJACK_UP BUTTON_UP
157 #define BJACK_DOWN BUTTON_DOWN
158 #define BJACK_RIGHT BUTTON_RIGHT
159 #define BJACK_LEFT BUTTON_LEFT
161 #elif CONFIG_KEYPAD == SANSA_E200_PAD
162 #define BJACK_START BUTTON_SELECT
163 #define BJACK_QUIT BUTTON_POWER
164 #define BJACK_MAX (BUTTON_REC|BUTTON_UP)
165 #define BJACK_MIN (BUTTON_REC|BUTTON_DOWN)
166 #define BJACK_HIT BUTTON_SELECT
167 #define BJACK_STAY BUTTON_RIGHT
168 #define BJACK_DOUBLEDOWN BUTTON_LEFT
169 #define BJACK_SCORES BUTTON_UP
170 #define BJACK_RESUME BUTTON_REC
171 #define BJACK_UP BUTTON_SCROLL_UP
172 #define BJACK_DOWN BUTTON_SCROLL_DOWN
173 #define BJACK_RIGHT BUTTON_RIGHT
174 #define BJACK_LEFT BUTTON_LEFT
176 #elif CONFIG_KEYPAD == SANSA_C200_PAD
177 #define BJACK_START BUTTON_SELECT
178 #define BJACK_QUIT BUTTON_POWER
179 #define BJACK_MAX BUTTON_VOL_UP
180 #define BJACK_MIN BUTTON_VOL_DOWN
181 #define BJACK_HIT BUTTON_SELECT
182 #define BJACK_STAY BUTTON_RIGHT
183 #define BJACK_DOUBLEDOWN BUTTON_LEFT
184 #define BJACK_SCORES BUTTON_REC
185 #define BJACK_RESUME BUTTON_DOWN
186 #define BJACK_UP BUTTON_UP
187 #define BJACK_DOWN BUTTON_DOWN
188 #define BJACK_RIGHT BUTTON_RIGHT
189 #define BJACK_LEFT BUTTON_LEFT
191 #elif CONFIG_KEYPAD == ELIO_TPJ1022_PAD
192 #define BJACK_START BUTTON_MAIN
193 #define BJACK_QUIT BUTTON_POWER
194 #define BJACK_MAX (BUTTON_REC|BUTTON_UP)
195 #define BJACK_MIN (BUTTON_REC|BUTTON_DOWN)
196 #define BJACK_HIT BUTTON_MAIN
197 #define BJACK_STAY BUTTON_MENU
198 #define BJACK_DOUBLEDOWN BUTTON_DOWN
199 #define BJACK_SCORES BUTTON_UP
200 #define BJACK_RESUME BUTTON_FF
201 #define BJACK_UP BUTTON_UP
202 #define BJACK_DOWN BUTTON_DOWN
203 #define BJACK_RIGHT BUTTON_RIGHT
204 #define BJACK_LEFT BUTTON_LEFT
206 #else
207 #error BLACKJACK: Unsupported keypad
208 #endif
210 #ifdef HAVE_LCD_COLOR
211 #define BG_COLOR LCD_RGBPACK(0,157,0)
212 #define FG_COLOR LCD_WHITE
213 #elif LCD_DEPTH > 1
214 #define BG_COLOR LCD_WHITE
215 #define FG_COLOR LCD_BLACK
216 #endif
218 #define CARD_WIDTH BMPWIDTH_card_back
219 #define CARD_HEIGHT BMPHEIGHT_card_back
221 /* This is the max amount of cards onscreen before condensing */
222 #define MAX_CARDS LCD_WIDTH/(CARD_WIDTH+4)
224 extern const fb_data card_deck[];
225 extern const fb_data card_back[];
227 #define NEXT_CARD bj->player_cards[done][bj->num_player_cards[done]]
229 /* global rockbox api */
230 static struct plugin_api* rb;
232 MEM_FUNCTION_WRAPPERS(rb);
234 /* dealer and player card positions */
235 unsigned int dealer_x, dealer_y, player_x, player_y;
237 typedef struct card {
238 unsigned int value; /* Card's value in Blackjack */
239 unsigned int num; /* Value on card face 0-12 (0=Ace, 1=2, 11=Q) */
240 unsigned int suit; /* 0:Spades, 1:Hearts, 2: Clubs; 3: Diamonds */
241 bool is_soft_ace;
242 } card;
244 typedef struct game_context {
245 struct card player_cards[2][22]; /* 22 Cards means the deal was all aces */
246 struct card dealer_cards[22]; /* That is the worst-case scenario */
247 unsigned int player_total;
248 unsigned int dealer_total;
249 signed int player_money;
250 unsigned int num_player_cards[2];
251 unsigned int num_dealer_cards;
252 unsigned int current_bet;
253 unsigned int split_status; /* 0 = split hasn't been asked, *
254 * 1 = split did not occur *
255 * 2 = split occurred *
256 * 3 = split occurred and 1st hand done */
257 bool is_blackjack;
258 bool end_hand;
259 bool asked_insurance;
260 signed short highscores[NUM_SCORES];
261 bool resume;
262 bool dirty;
263 } game_context;
265 /*****************************************************************************
266 * blackjack_init() initializes blackjack data structures.
267 ******************************************************************************/
268 static void blackjack_init(struct game_context* bj) {
269 /* seed the rand generator */
270 rb->srand(*rb->current_tick);
272 /* reset card positions */
273 dealer_x = 4;
274 dealer_y = LCD_HEIGHT/4 - CARD_HEIGHT/2;
275 player_x = 4;
276 player_y = LCD_HEIGHT - LCD_HEIGHT/4 - CARD_HEIGHT/2;
278 /* check for resumed game */
279 if(bj->resume) return;
281 /* reset scoring */
282 bj->player_total = 0;
283 bj->dealer_total = 0;
284 bj->num_player_cards[0] = 2;
285 bj->num_player_cards[1] = 0;
286 bj->num_dealer_cards = 2;
287 bj->end_hand = false;
288 bj->split_status = 0;
289 bj->is_blackjack = false;
290 bj->asked_insurance = false;
293 /*****************************************************************************
294 * blackjack_drawtable() draws the table and some text.
295 ******************************************************************************/
296 static void blackjack_drawtable(struct game_context* bj) {
297 unsigned int w, h, y_loc;
298 char str[10];
300 #if LCD_HEIGHT <= 64
301 rb->lcd_getstringsize("Bet", &w, &h);
302 rb->lcd_putsxy(LCD_WIDTH - w, 2*h + 1, "Bet");
303 rb->snprintf(str, 9, "$%d", bj->current_bet);
304 rb->lcd_getstringsize(str, &w, &h);
305 rb->lcd_putsxy(LCD_WIDTH - w, 3*h + 1, str);
306 y_loc = LCD_HEIGHT/2;
307 #else
308 rb->lcd_getstringsize("Bet", &w, &h);
309 rb->lcd_putsxy(LCD_WIDTH - w, 5*h / 2, "Bet");
310 rb->snprintf(str, 9, "$%d", bj->current_bet);
311 rb->lcd_getstringsize(str, &w, &h);
312 rb->lcd_putsxy(LCD_WIDTH - w, 7*h / 2, str);
313 rb->lcd_hline(0, LCD_WIDTH, LCD_HEIGHT/2);
314 y_loc = LCD_HEIGHT/2 + h;
315 #endif
317 rb->lcd_putsxy(0,0, "Dealer");
318 rb->lcd_getstringsize("Player", &w, &h);
319 rb->lcd_putsxy(0, y_loc, "Player");
320 rb->lcd_getstringsize("Total", &w, &h);
321 rb->lcd_putsxy(LCD_WIDTH - w, y_loc, "Total");
322 rb->lcd_getstringsize("Money", &w, &h);
323 rb->lcd_putsxy(LCD_WIDTH - w, 0, "Money");
324 rb->snprintf(str, 9, "$%d", bj->player_money - bj->current_bet);
325 rb->lcd_getstringsize(str, &w, &h);
326 rb->lcd_putsxy(LCD_WIDTH - w, h + 1, str);
327 rb->snprintf(str, 3, "%d", bj->player_total);
328 rb->lcd_getstringsize(str, &w, &h);
329 rb->lcd_putsxy(LCD_WIDTH - w, y_loc + h, str);
332 /*****************************************************************************
333 * find_value() is passed a card and returns its blackjack value.
334 ******************************************************************************/
335 static unsigned int find_value(unsigned int number) {
336 unsigned int thisValue;
337 if (number == 0)
338 thisValue = 11; /* Aces get a value of 11 at first */
339 else if (number < 10)
340 thisValue = number + 1;
341 else
342 thisValue = 10; /* Anything 10 or higher gets a value of 10 */
344 return thisValue;
347 /*****************************************************************************
348 * draw_card() draws a card to the screen.
349 ******************************************************************************/
350 static void draw_card(struct card temp_card, bool shown, unsigned int x,
351 unsigned int y) {
352 if(shown)
353 rb->lcd_bitmap_part(card_deck, CARD_WIDTH*temp_card.num,
354 CARD_HEIGHT*temp_card.suit, BMPWIDTH_card_deck,
355 x+1, y+1, CARD_WIDTH, CARD_HEIGHT);
356 else
357 rb->lcd_bitmap(card_back, x+1, y+1,CARD_WIDTH, CARD_HEIGHT);
358 #if LCD_DEPTH > 1
359 rb->lcd_set_foreground(LCD_BLACK);
360 #endif
362 /* Print outlines */
363 #if CARD_WIDTH >= 26
364 rb->lcd_hline(x+2, x+CARD_WIDTH-1, y);
365 rb->lcd_hline(x+2, x+CARD_WIDTH-1, y+CARD_HEIGHT+1);
366 rb->lcd_vline(x, y+2, y+CARD_HEIGHT-3);
367 rb->lcd_vline(x+CARD_WIDTH+1, y+2, y+CARD_HEIGHT-1);
368 rb->lcd_drawpixel(x+1, y+1);
369 rb->lcd_drawpixel(x+1, y+CARD_HEIGHT);
370 rb->lcd_drawpixel(x+CARD_WIDTH, y+1);
371 rb->lcd_drawpixel(x+CARD_WIDTH, y+CARD_HEIGHT);
372 #else
373 rb->lcd_hline(x+1, x+CARD_WIDTH, y);
374 rb->lcd_hline(x+1, x+CARD_WIDTH, y+CARD_HEIGHT+1);
375 rb->lcd_vline(x, y+1, y+CARD_HEIGHT);
376 rb->lcd_vline(x+CARD_WIDTH+1, y+1, y+CARD_HEIGHT);
377 #endif
379 #if LCD_DEPTH > 1
380 rb->lcd_set_foreground(FG_COLOR);
381 #endif
384 /*****************************************************************************
385 * new_card() initializes a new card and gives it values.
386 ******************************************************************************/
387 static struct card new_card(void) {
388 struct card new_card;
389 new_card.suit = rb->rand()%4; /* Random number 0-3 */
390 new_card.num = rb->rand()%13; /* Random number 0-12 */
391 new_card.value = find_value(new_card.num);
392 new_card.is_soft_ace = new_card.num == 0 ? true : false;
393 return new_card;
396 /*****************************************************************************
397 * deal_init_card() deals and draws to the screen the player's and dealer's
398 * initial cards.
399 ******************************************************************************/
400 static void deal_init_cards(struct game_context* bj) {
401 bj->dealer_cards[0] = new_card();
402 bj->dealer_total += bj->dealer_cards[0].value;
404 draw_card(bj->dealer_cards[0], false, dealer_x, dealer_y);
406 bj->dealer_cards[1] = new_card();
407 bj->dealer_total += bj->dealer_cards[1].value;
408 draw_card(bj->dealer_cards[1], true, dealer_x + CARD_WIDTH + 4, dealer_y);
410 bj->player_cards[0][0] = new_card();
411 bj->player_total += bj->player_cards[0][0].value;
412 draw_card(bj->player_cards[0][0], true, player_x, player_y);
413 player_x += CARD_WIDTH + 4;
415 bj->player_cards[0][1] = new_card();
416 bj->player_total += bj->player_cards[0][1].value;
417 draw_card(bj->player_cards[0][1], true, player_x, player_y);
418 player_x += CARD_WIDTH + 4;
421 /*****************************************************************************
422 * redraw_board() redraws all the cards and the board
423 ******************************************************************************/
424 static void redraw_board(struct game_context* bj) {
425 unsigned int i, n, upper_bound;
426 rb->lcd_clear_display();
428 blackjack_drawtable(bj);
429 player_x = 4;
430 dealer_x = 4;
431 upper_bound = bj->split_status > 1 ? 2 : 1;
433 for (i = 0; i < bj->num_dealer_cards; i++) {
434 if (!bj->end_hand) {
435 draw_card(bj->dealer_cards[0], false, dealer_x, dealer_y);
437 /* increment i so the dealer's first card isn't displayed */
438 i++;
439 dealer_x += CARD_WIDTH + 4;
441 draw_card(bj->dealer_cards[i], true, dealer_x, dealer_y);
443 if (bj->num_dealer_cards > MAX_CARDS-1)
444 dealer_x += 10;
445 else
446 dealer_x += CARD_WIDTH + 4;
449 for (n = 0; n < upper_bound; n++) {
450 for (i = 0; i < bj->num_player_cards[n]; i++) {
451 draw_card(bj->player_cards[n][i], true, player_x, player_y);
452 if (bj->split_status>1 || bj->num_player_cards[n]>MAX_CARDS)
453 player_x += 10;
454 else
455 player_x += CARD_WIDTH + 4;
457 if (bj->split_status > 1)
458 player_x = LCD_WIDTH/2 + 4;
462 /*****************************************************************************
463 * update_total updates the player's total
464 ******************************************************************************/
465 static void update_total(struct game_context* bj) {
466 char total[3];
467 unsigned int w, h;
468 rb->snprintf(total, 3, "%d", bj->player_total);
469 rb->lcd_getstringsize(total, &w, &h);
470 #if LCD_HEIGHT > 64
471 h *= 2;
472 #endif
473 rb->lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 + h, total);
474 rb->lcd_update_rect(LCD_WIDTH - w, LCD_HEIGHT/2 + h, w, h);
478 /*****************************************************************************
479 * check_for_aces() is passed an array of cards and returns where an ace is
480 * located. Otherwise, returns -1.
481 ******************************************************************************/
482 static signed int check_for_aces(struct card temp_cards[],
483 unsigned int size) {
484 unsigned int i;
485 for(i = 0; i < size; i++) {
486 if (temp_cards[i].is_soft_ace == true)
487 return i;
489 return -1;
492 /*****************************************************************************
493 * check_totals() compares player and dealer totals.
494 * 0: bust 1: loss, 2: push, 3: win, 4: blackjack, 5: something's not right...
495 ******************************************************************************/
496 static unsigned int check_totals(struct game_context* bj)
498 unsigned int temp;
499 if (bj->player_total > 21)
500 temp = 0;
501 else if (bj->player_total == 21 && bj->is_blackjack)
502 if (bj->dealer_total == 21 && bj->num_dealer_cards == 2)
503 temp = 2;
504 else
505 temp = 4;
506 else if (bj->player_total == bj->dealer_total)
507 temp = 2;
508 else if (bj->dealer_total > 21 && bj->player_total < 22)
509 temp = 3;
510 else if (bj->dealer_total > bj->player_total)
511 temp = 1;
512 else if (bj->player_total > bj->dealer_total)
513 temp = 3;
514 else
515 temp = 5;
517 return temp;
520 /*****************************************************************************
521 * finish_dealer() draws cards for the dealer until he has 17 or more.
522 ******************************************************************************/
523 static void finish_dealer(struct game_context* bj) {
524 signed int temp = 0;
526 if (bj->dealer_total > 16 && bj->dealer_total < 22)
527 return;
529 while (bj->dealer_total < 17) {
530 bj->dealer_cards[bj->num_dealer_cards] = new_card();
531 bj->dealer_total += bj->dealer_cards[bj->num_dealer_cards].value;
532 bj->num_dealer_cards++;
535 while (bj->dealer_total > 21) {
536 temp = check_for_aces(bj->dealer_cards, bj->num_dealer_cards);
537 if(temp != -1) {
538 bj->dealer_cards[temp].is_soft_ace = false;
539 bj->dealer_total -= 10;
541 else
542 return;
546 /*****************************************************************************
547 * finish_game() completes the game once player's turn is over.
548 ******************************************************************************/
549 static void finish_game(struct game_context* bj) {
550 unsigned int rValue, w, h;
551 char str[19];
553 do {
554 finish_dealer(bj);
555 } while (bj->dealer_total < 17);
557 redraw_board(bj);
558 rValue = check_totals(bj);
560 if (rValue == 0) {
561 rb->snprintf(str, sizeof(str), " Bust! ");
562 bj->player_money -= bj->current_bet;
564 else if (rValue == 1) {
565 rb->snprintf(str, sizeof(str), " Sorry, you lost. ");
566 bj->player_money -= bj->current_bet;
568 else if (rValue == 2) {
569 rb->snprintf(str, sizeof(str), " Push ");
571 else if (rValue == 3) {
572 rb->snprintf(str, sizeof(str), " You won! ");
573 bj->player_money+= bj->current_bet;
575 else {
576 rb->snprintf(str, sizeof(str), " Blackjack! ");
577 bj->player_money += bj->current_bet * 3 / 2;
579 rb->lcd_getstringsize(str, &w, &h);
581 #if LCD_HEIGHT <= 64
582 rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
583 rb->lcd_fillrect(0, LCD_HEIGHT/2, LCD_WIDTH, LCD_HEIGHT/2);
584 rb->lcd_set_drawmode(DRMODE_SOLID);
585 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + h, str);
586 rb->snprintf(str, 12, "You have %d", bj->player_total);
587 rb->lcd_getstringsize(str, &w, &h);
588 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2, str);
589 #else
590 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, str);
591 #endif
592 rb->lcd_update();
595 /*****************************************************************************
596 * blackjack_recordscore() inserts a high score into the high scores list and
597 * returns the high score position.
598 ******************************************************************************/
599 static unsigned int blackjack_recordscore(struct game_context* bj) {
600 unsigned int i;
601 unsigned int position = 0;
602 signed short current, temp;
604 /* calculate total score */
605 current = bj->player_money;
606 if(current <= 10) return 0;
608 /* insert the current score into the high scores */
609 for(i=0; i<NUM_SCORES; i++) {
610 if(current >= bj->highscores[i]) {
611 if(!position) {
612 position = i+1;
613 bj->dirty = true;
615 temp = bj->highscores[i];
616 bj->highscores[i] = current;
617 current = temp;
621 return position;
624 /*****************************************************************************
625 * blackjack_loadscores() loads the high scores saved file.
626 ******************************************************************************/
627 static void blackjack_loadscores(struct game_context* bj) {
628 signed int fd;
630 bj->dirty = false;
632 /* clear high scores */
633 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
635 /* open scores file */
636 fd = rb->open(SCORE_FILE, O_RDONLY);
637 if(fd < 0) return;
639 /* read in high scores */
640 if(rb->read(fd, bj->highscores, sizeof(bj->highscores)) <= 0) {
641 /* scores are bad, reset */
642 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
645 rb->close(fd);
648 /*****************************************************************************
649 * blackjack_savescores() saves the high scores saved file.
650 ******************************************************************************/
651 static void blackjack_savescores(struct game_context* bj) {
652 unsigned int fd;
654 /* write out the high scores to the save file */
655 fd = rb->open(SCORE_FILE, O_WRONLY|O_CREAT);
656 rb->write(fd, bj->highscores, sizeof(bj->highscores));
657 rb->close(fd);
658 bj->dirty = false;
661 /*****************************************************************************
662 * blackjack_loadgame() loads the saved game and returns load success.
663 ******************************************************************************/
664 static bool blackjack_loadgame(struct game_context* bj) {
665 signed int fd;
666 bool loaded = false;
668 /* open game file */
669 fd = rb->open(SAVE_FILE, O_RDONLY);
670 if(fd < 0) return loaded;
672 /* read in saved game */
673 while(true) {
674 if(rb->read(fd, &bj->player_money, sizeof(bj->player_money)) <= 0) break;
675 if(rb->read(fd, &bj->player_total, sizeof(bj->player_total)) <= 0) break;
676 if(rb->read(fd, &bj->dealer_total, sizeof(bj->dealer_total)) <= 0) break;
677 if(rb->read(fd, &bj->num_player_cards, sizeof(bj->num_player_cards))<=0)
678 break;
679 if(rb->read(fd, &bj->num_dealer_cards, sizeof(bj->num_dealer_cards))<=0)
680 break;
681 if(rb->read(fd, &bj->current_bet, sizeof(bj->current_bet)) <= 0) break;
682 if(rb->read(fd, &bj->is_blackjack, sizeof(bj->is_blackjack)) <= 0) break;
683 if(rb->read(fd, &bj->split_status, sizeof(bj->split_status)) <= 0) break;
684 if(rb->read(fd, &bj->asked_insurance, sizeof(bj->asked_insurance)) <= 0)
685 break;
686 if(rb->read(fd, &bj->end_hand, sizeof(bj->end_hand)) <= 0) break;
687 if(rb->read(fd, &bj->player_cards, sizeof(bj->player_cards)) <= 0) break;
688 if(rb->read(fd, &bj->dealer_cards, sizeof(bj->dealer_cards)) <= 0) break;
689 bj->resume = true;
690 loaded = true;
691 break;
694 rb->close(fd);
696 /* delete saved file */
697 rb->remove(SAVE_FILE);
698 return loaded;
701 /*****************************************************************************
702 * blackjack_savegame() saves the current game state.
703 ******************************************************************************/
704 static void blackjack_savegame(struct game_context* bj) {
705 unsigned int fd;
707 /* write out the game state to the save file */
708 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
709 rb->write(fd, &bj->player_money, sizeof(bj->player_money));
710 rb->write(fd, &bj->player_total, sizeof(bj->player_total));
711 rb->write(fd, &bj->dealer_total, sizeof(bj->dealer_total));
712 rb->write(fd, &bj->num_player_cards, sizeof(bj->num_player_cards));
713 rb->write(fd, &bj->num_dealer_cards, sizeof(bj->num_dealer_cards));
714 rb->write(fd, &bj->current_bet, sizeof(bj->current_bet));
715 rb->write(fd, &bj->is_blackjack, sizeof(bj->is_blackjack));
716 rb->write(fd, &bj->split_status, sizeof(bj->split_status));
717 rb->write(fd, &bj->asked_insurance, sizeof(bj->asked_insurance));
718 rb->write(fd, &bj->end_hand, sizeof(bj->end_hand));
719 rb->write(fd, &bj->player_cards, sizeof(bj->player_cards));
720 rb->write(fd, &bj->dealer_cards, sizeof(bj->dealer_cards));
721 rb->close(fd);
723 bj->resume = true;
726 /*****************************************************************************
727 * blackjack_callback() is the default event handler callback which is called
728 * on usb connect and shutdown.
729 ******************************************************************************/
730 static void blackjack_callback(void* param) {
731 struct game_context* bj = (struct game_context*) param;
732 if(bj->dirty) {
733 rb->splash(HZ, "Saving high scores...");
734 blackjack_savescores(bj);
738 /*****************************************************************************
739 * blackjack_get_yes_no() gets a yes/no answer from the user
740 ******************************************************************************/
741 static unsigned int blackjack_get_yes_no(char message[20]) {
742 int button;
743 unsigned int w, h, b, choice = 0;
744 bool breakout = false;
745 char message_yes[24], message_no[24];
747 rb->strcpy(message_yes, message);
748 rb->strcpy(message_no, message);
749 rb->strcat(message_yes, " Yes");
750 rb->strcat(message_no, " No");
751 rb->lcd_getstringsize(message_yes, &w, &h);
752 const char *stg[] = {message_yes, message_no};
754 #if LCD_HEIGHT <= 64
755 b = 2*h+1;
756 #else
757 b = h-1;
758 #endif
760 #ifdef HAVE_LCD_COLOR
761 rb->lcd_fillrect(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b, w+1, h+3);
762 rb->lcd_set_foreground(LCD_BLACK);
763 rb->lcd_set_background(LCD_WHITE);
764 #else
765 rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
766 rb->lcd_fillrect(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b, w+1, h+3);
767 rb->lcd_set_drawmode(DRMODE_SOLID);
768 #endif
769 rb->lcd_drawrect(LCD_WIDTH/2 - w/2 - 1, LCD_HEIGHT/2 + b - 1, w+3, h+4);
771 while(!breakout) {
772 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b +1, stg[choice]);
773 rb->lcd_update_rect(LCD_WIDTH/2 - w/2 - 1, LCD_HEIGHT/2 + b -1,
774 w+3, h+4);
775 button = rb->button_get(true);
777 switch(button) {
778 case BJACK_LEFT:
779 case (BJACK_LEFT|BUTTON_REPEAT):
780 case BJACK_RIGHT:
781 case (BJACK_RIGHT|BUTTON_REPEAT):
782 choice ^= 1;
783 break;
784 case BJACK_START: breakout = true;
785 break;
786 case BJACK_QUIT: breakout = true;
787 choice = BJ_QUIT;
788 break;
792 #if LCD_DEPTH > 1
793 rb->lcd_set_foreground(FG_COLOR);
794 rb->lcd_set_background(BG_COLOR);
795 #endif
796 return choice;
799 /*****************************************************************************
800 * blackjack_get_amount() gets an amount from the player to be used
801 ******************************************************************************/
802 static signed int blackjack_get_amount(char message[20], signed int lower_limit,
803 signed int upper_limit,
804 signed int start) {
805 int button;
806 char str[6];
807 bool changed = false;
808 unsigned int w, h;
809 signed int amount;
811 rb->lcd_getstringsize("A", &w, &h); /* find the size of one character */
813 if (start > upper_limit)
814 amount = upper_limit;
815 else if (start < lower_limit)
816 amount = lower_limit;
817 else
818 amount = start;
820 #if LCD_DEPTH > 1
821 rb->lcd_set_background(LCD_WHITE);
822 rb->lcd_set_foreground(LCD_BLACK);
823 #endif
825 #if LCD_HEIGHT <= 64
826 rb->lcd_clear_display();
827 rb->lcd_puts(0, 1, message);
828 rb->snprintf(str, 9, "$%d", amount);
829 rb->lcd_puts(0, 2, str);
830 rb->lcd_puts(0, 3, "RIGHT: +1");
831 rb->lcd_puts(0, 4, "LEFT: -1");
832 rb->lcd_puts(0, 5, "UP: +10");
833 rb->lcd_puts(0, 6, "DOWN: -10");
834 rb->lcd_update();
835 #else
836 rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
837 rb->lcd_fillrect(LCD_WIDTH/2 - 9*w - 1, LCD_HEIGHT/2 - 4*h - 3, 37*w / 2,
838 8*h -3);
839 rb->lcd_set_drawmode(DRMODE_SOLID);
840 rb->lcd_drawrect(LCD_WIDTH/2 - 9*w - 1, LCD_HEIGHT/2 - 4*h - 3, 37*w / 2,
841 8*h -3);
842 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 4*h - 1, message);
843 rb->snprintf(str, 9, "$%d", amount);
844 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, str);
845 #if (CONFIG_KEYPAD == IPOD_4G_PAD) || \
846 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
847 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
848 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, " >>|: +1");
849 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, " |<<: -1");
850 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "SCROLL+: +10");
851 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "SCROLL-: -10");
852 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
853 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, "RIGHT: +1");
854 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, "LEFT: -1");
855 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "SCROLL+: +10");
856 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "SCROLL-: -10");
857 #else
858 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, "RIGHT: +1");
859 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, "LEFT: -1");
860 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "UP: +10");
861 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "DOWN: -10");
862 #endif
863 rb->lcd_update_rect(LCD_WIDTH/2 - 9*w - 2, LCD_HEIGHT/2 - 9*h/2, 37*w/2 + 1,
864 8*h-2);
865 #endif
867 while(true) {
868 button = rb->button_get(true);
870 switch(button) {
871 case BJACK_UP:
872 case (BJACK_UP|BUTTON_REPEAT):
873 if (amount + 10 < upper_limit + 1) {
874 amount += 10;
875 changed = true;
877 break;
878 case BJACK_DOWN:
879 case (BJACK_DOWN|BUTTON_REPEAT):
880 if (amount - 10 > lower_limit - 1) {
881 amount -= 10;
882 changed = true;
884 break;
885 case BJACK_RIGHT:
886 case (BJACK_RIGHT|BUTTON_REPEAT):
887 if (amount + 1 < upper_limit + 1) {
888 amount++;
889 changed = true;
891 break;
892 case BJACK_LEFT:
893 case (BJACK_LEFT|BUTTON_REPEAT):
894 if (amount - 1 > lower_limit - 1) {
895 amount--;
896 changed = true;
898 break;
899 case BJACK_MAX :
900 amount = upper_limit;
901 changed = true;
902 break;
903 case BJACK_MIN :
904 amount = lower_limit;
905 changed = true;
906 break;
907 case BJACK_QUIT:
908 return 0;
909 case BJACK_START:
910 #if LCD_DEPTH > 1
911 rb->lcd_set_foreground(FG_COLOR);
912 rb->lcd_set_background(BG_COLOR);
913 #endif
914 rb->lcd_clear_display();
915 return amount;
918 if(changed) {
919 rb->snprintf(str, 9, "$%d", amount);
920 #if LCD_HEIGHT <= 64
921 rb->lcd_puts(0, 2, str);
922 rb->lcd_update();
923 #else
924 rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
925 rb->lcd_fillrect(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, 5*w, h);
926 rb->lcd_set_drawmode(DRMODE_SOLID);
927 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, str);
928 rb->lcd_update_rect(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, 5*w, h);
929 #endif
930 changed = false;
935 /*****************************************************************************
936 * blackjack_get_bet() gets the player's bet.
937 ******************************************************************************/
938 static void blackjack_get_bet(struct game_context* bj) {
939 bj->current_bet = blackjack_get_amount("Please enter a bet", 10,
940 bj->player_money, bj->current_bet);
943 /*****************************************************************************
944 * double_down() returns one final card then finishes the game
945 ******************************************************************************/
946 static void double_down(struct game_context* bj) {
947 bj->current_bet *= 2;
948 bj->player_cards[0][bj->num_player_cards[0]] = new_card();
949 bj->player_total += bj->player_cards[0][bj->num_player_cards[0]].value;
950 bj->num_player_cards[0]++;
953 /*****************************************************************************
954 * split() checks if the player wants to split and acts accordingly.
955 * When bj->split_status is 1, no split occurred. 2 means the player split and 3
956 * means a split has already occurred and the first hand is done.
957 ******************************************************************************/
958 static void split(struct game_context* bj) {
959 if (blackjack_get_yes_no("Split?") == 1)
960 bj->split_status = 1;
961 else {
962 bj->split_status = 2;
963 bj->current_bet *= 2;
964 bj->num_player_cards[0] = 1;
965 bj->num_player_cards[1] = 1;
966 bj->player_cards[1][0] = bj->player_cards[0][1];
967 bj->player_total = bj->player_cards[0][0].value;
971 /*****************************************************************************
972 * insurance() see if the player wants to buy insurance and how much.
973 ******************************************************************************/
974 static unsigned int insurance(struct game_context* bj) {
975 unsigned int insurance, max_amount;
977 insurance = blackjack_get_yes_no("Buy Insurance?");
978 bj->asked_insurance = true;
979 max_amount = bj->current_bet < (unsigned int)bj->player_money ?
980 bj->current_bet/2 : (unsigned int)bj->player_money;
981 if (insurance == 1) return 0;
983 insurance = blackjack_get_amount("How much?", 0, max_amount, 0);
984 redraw_board(bj);
985 return insurance;
988 /*****************************************************************************
989 * play_again() checks to see if the player wants to keep playing.
990 ******************************************************************************/
991 static unsigned int play_again(void) {
992 return blackjack_get_yes_no("Play Again?");
995 /*****************************************************************************
996 * blackjack_menu() is the initial menu at the start of the game.
997 ******************************************************************************/
998 static unsigned int blackjack_menu(struct game_context* bj) {
999 int button;
1000 char *title = "Blackjack";
1001 char str[18];
1002 unsigned int i, w, h;
1003 bool breakout = false;
1004 bool showscores = false;
1006 while(true){
1007 #if LCD_DEPTH > 1
1008 rb->lcd_set_background(BG_COLOR);
1009 rb->lcd_set_foreground(FG_COLOR);
1010 #endif
1011 rb->lcd_clear_display();
1013 if(!showscores) {
1014 /* welcome screen to display key bindings */
1015 rb->lcd_getstringsize(title, &w, &h);
1016 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, title);
1018 #if CONFIG_KEYPAD == RECORDER_PAD
1019 rb->lcd_puts(0, 1, "ON: start");
1020 rb->lcd_puts(0, 2, "OFF: exit");
1021 rb->lcd_puts(0, 3, "F1: hit");
1022 rb->lcd_puts(0, 4, "F2: stay");
1023 rb->lcd_puts(0, 5, "F3: double down");
1024 rb->lcd_puts(0, 6, "PLAY: save/resume");
1025 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1026 rb->lcd_puts(0, 7, str);
1027 #elif CONFIG_KEYPAD == ONDIO_PAD
1028 rb->lcd_puts(0, 1, "MENU: start");
1029 rb->lcd_puts(0, 2, "OFF: exit");
1030 rb->lcd_puts(0, 3, "LEFT: hit");
1031 rb->lcd_puts(0, 4, "RIGHT: stay");
1032 rb->lcd_puts(0, 5, "UP: double down");
1033 rb->lcd_puts(0, 6, "DOWN: save/resume");
1034 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1035 rb->lcd_puts(0, 7, str);
1036 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
1037 rb->lcd_puts(0, 2, "PLAY to start & to hit");
1038 rb->lcd_puts(0, 3, "STOP to exit");
1039 rb->lcd_puts(0, 4, "REC to stay");
1040 rb->lcd_puts(0, 5, "NAVI to double down ");
1041 rb->lcd_puts(0, 6, " & to view highscores");
1042 rb->lcd_puts(0, 7, "AB to save/resume");
1043 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1044 rb->lcd_puts(0, 8, str);
1045 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
1046 rb->lcd_puts(0, 2, "PLAY to start & hit");
1047 rb->lcd_puts(0, 3, "POWER to exit");
1048 rb->lcd_puts(0, 4, ">>| to stay");
1049 rb->lcd_puts(0, 5, "|<< to double down");
1050 rb->lcd_puts(0, 6, "LEFT to view scores");
1051 rb->lcd_puts(0, 7, "RIGHT to save/resume");
1052 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1053 rb->lcd_puts(0, 8, str);
1055 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
1056 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
1057 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
1058 #if LCD_WIDTH >=176
1059 rb->lcd_puts(0, 2, "SELECT to start & to hit");
1060 rb->lcd_puts(0, 3, "MENU to exit");
1061 rb->lcd_puts(0, 4, ">>| to stay & to view highscores");
1062 rb->lcd_puts(0, 5, "|<< to double down");
1063 rb->lcd_puts(0, 6, "PLAY to save/resume");
1064 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1065 rb->lcd_puts(0, 7, str);
1066 #else
1067 rb->lcd_puts(0, 2, "SELECT to start & to ");
1068 rb->lcd_puts(0, 3, " hit");
1069 rb->lcd_puts(0, 4, "MENU to exit");
1070 rb->lcd_puts(0, 5, ">>| to stay & to view ");
1071 rb->lcd_puts(0, 6, " highscores");
1072 rb->lcd_puts(0, 7, "|<< to double down");
1073 rb->lcd_puts(0, 8, "PLAY to save/resume");
1074 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1075 rb->lcd_puts(0, 9, str);
1076 #endif
1077 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1078 rb->lcd_puts(0, 2, "PLAY to start to hit");
1079 rb->lcd_puts(0, 3, "POWER to exit");
1080 rb->lcd_puts(0, 4, "SELECT to hit");
1081 rb->lcd_puts(0, 5, "REC to stay");
1082 rb->lcd_puts(0, 6, "PLAY to double down");
1083 rb->lcd_puts(0, 7, "RIGHT to view highscores ");
1084 rb->lcd_puts(0, 8, "DOWN to save/resume");
1085 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1086 rb->lcd_puts(0, 9, str);
1087 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
1088 rb->lcd_puts(0, 2, "AB to start & to");
1089 rb->lcd_puts(0, 3, " stay");
1090 rb->lcd_puts(0, 4, "EQ to hit");
1091 rb->lcd_puts(0, 5, "PLAY to exit");
1092 rb->lcd_puts(0, 6, "CLICK to double down");
1093 rb->lcd_puts(0, 7, "& to view highscores");
1094 rb->lcd_puts(0, 8, "AB+EQ to save/resume");
1095 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1096 rb->lcd_puts(0, 9, str);
1097 #elif CONFIG_KEYPAD == GIGABEAT_PAD
1098 rb->lcd_puts(0, 2, "POWER to start");
1099 rb->lcd_puts(0, 3, "A to exit");
1100 rb->lcd_puts(0, 4, "VOL+ to hit");
1101 rb->lcd_puts(0, 5, "VOL- to stay");
1102 rb->lcd_puts(0, 6, "CENTER to double down");
1103 rb->lcd_puts(0, 6, "RIGHT to view highscores ");
1104 rb->lcd_puts(0, 8, "MENU to save/resume");
1105 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1106 rb->lcd_puts(0, 9, str);
1107 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
1108 rb->lcd_puts(0, 2, "SELECT to start & to hit");
1109 rb->lcd_puts(0, 3, "POWER to exit");
1110 rb->lcd_puts(0, 4, "RIGHT to stay");
1111 rb->lcd_puts(0, 5, "LEFT to double down");
1112 rb->lcd_puts(0, 6, "REC to save/resume");
1113 rb->lcd_puts(0, 7, "UP to view scores");
1114 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1115 rb->lcd_puts(0, 8, str);
1116 #elif (CONFIG_KEYPAD == SANSA_C200_PAD)
1117 rb->lcd_puts(0, 2, "SELECT to start & to hit");
1118 rb->lcd_puts(0, 3, "POWER to exit");
1119 rb->lcd_puts(0, 4, "RIGHT to stay");
1120 rb->lcd_puts(0, 5, "LEFT to double down");
1121 rb->lcd_puts(0, 6, "DOWN to save/resume");
1122 rb->lcd_puts(0, 7, "REC to view scores");
1123 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1124 rb->lcd_puts(0, 9, str);
1125 #endif
1126 } else {
1127 rb->snprintf(str, 12, "%s", "High Scores");
1128 rb->lcd_getstringsize(str, &w, &h);
1129 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str);
1131 /* print high scores */
1132 for(i=0; i<NUM_SCORES; i++) {
1133 rb->snprintf(str, 14, "#%02d: $%d", i+1, bj->highscores[i]);
1134 rb->lcd_puts(0, i+1, str);
1138 rb->lcd_update();
1140 /* handle menu button presses */
1141 button = rb->button_get(true);
1143 switch(button) {
1144 case BJACK_START: /* start playing */
1145 breakout = true;
1146 break;
1148 case BJACK_QUIT: /* quit program */
1149 if(showscores) {
1150 showscores = 0;
1151 break;
1153 return BJ_QUIT;
1155 case BJACK_RESUME:/* resume game */
1156 if(!blackjack_loadgame(bj)) {
1157 rb->splash(HZ*2, "Nothing to resume");
1158 } else {
1159 rb->splash(HZ*2, "Loading...");
1160 breakout = true;
1162 break;
1164 case BJACK_SCORES:/* toggle high scores */
1165 showscores = !showscores;
1166 break;
1168 default:
1169 if(rb->default_event_handler_ex(button, blackjack_callback,
1170 (void*) bj) == SYS_USB_CONNECTED)
1171 return BJ_USB;
1172 break;
1175 if(breakout) break;
1178 return(0);
1181 /*****************************************************************************
1182 * blackjack() is the main game subroutine, it returns the final game status.
1183 ******************************************************************************/
1184 static int blackjack(struct game_context* bj) {
1185 int button;
1186 unsigned int w, h, temp_var, done = 0, todo = 1;
1187 signed int temp;
1188 bool breakout = false;
1189 bool dbl_down = false;
1191 /* don't resume by default */
1192 bj->resume = false;
1194 /********************
1195 * menu *
1196 ********************/
1197 temp_var = blackjack_menu(bj);
1198 if (temp_var == BJ_QUIT || temp_var == BJ_USB)
1199 return temp_var;
1202 /********************
1203 * init *
1204 ********************/
1205 blackjack_init(bj);
1206 bj->current_bet=10;
1208 /********************
1209 * play *
1210 ********************/
1212 /* check for resumed game */
1213 if(bj->resume) {
1214 bj->resume = false;
1215 redraw_board(bj);
1216 if (bj->split_status == 2) {
1217 todo=2;
1218 player_x = bj->num_player_cards[0] * 10 + 4;
1220 else if (bj->split_status == 3) {
1221 player_x = bj->num_player_cards[1] * 10 + LCD_WIDTH/2 + 4;
1222 todo=2;
1223 done=1;
1227 else {
1228 bj->player_money = 1000;
1229 blackjack_get_bet(bj);
1230 if (bj->current_bet == 0)
1231 return BJ_QUIT;
1232 rb->lcd_clear_display();
1233 deal_init_cards(bj);
1234 blackjack_drawtable(bj);
1237 rb->lcd_update();
1239 breakout = false;
1241 while(true){
1242 if(bj->player_total == 21 && bj->num_player_cards[0] == 2) {
1243 bj->is_blackjack = true;
1244 bj->end_hand = true;
1245 finish_game(bj);
1247 else if(bj->dealer_cards[1].is_soft_ace && !breakout &&
1248 !bj->asked_insurance) {
1249 temp_var = insurance(bj);
1250 if (bj->dealer_total == 21) {
1251 rb->splash(HZ, "Dealer has blackjack");
1252 bj->player_money += temp_var;
1253 bj->end_hand = true;
1254 breakout = true;
1255 redraw_board(bj);
1256 finish_game(bj);
1258 else {
1259 rb->splash(HZ, "Dealer does not have blackjack");
1260 bj->player_money -= temp_var;
1261 breakout = true;
1262 redraw_board(bj);
1263 rb->lcd_update();
1266 if(bj->split_status == 0 &&
1267 bj->player_cards[0][0].num == bj->player_cards[0][1].num) {
1268 split(bj);
1269 redraw_board(bj);
1270 rb->lcd_update_rect(0, LCD_HEIGHT/2, LCD_WIDTH, LCD_HEIGHT/2);
1271 if (bj->split_status == 2) {
1272 todo++;
1273 player_x = bj->num_player_cards[0] * 10 + 4;
1277 while(done < todo) {
1278 button = rb->button_get(true);
1280 switch(button) {
1281 case BJACK_HIT:
1282 NEXT_CARD = new_card();
1283 bj->player_total += NEXT_CARD.value;
1284 draw_card(NEXT_CARD, true, player_x, player_y);
1285 bj->num_player_cards[done]++;
1286 if (bj->num_player_cards[done] == MAX_CARDS + 1) {
1287 redraw_board(bj);
1288 rb->lcd_update_rect(0, LCD_HEIGHT/2, LCD_WIDTH,
1289 LCD_HEIGHT/2);
1291 else if (bj->num_player_cards[done]>MAX_CARDS || todo > 1) {
1292 rb->lcd_update_rect(player_x, player_y, CARD_WIDTH+2,
1293 CARD_HEIGHT+2);
1294 player_x += 10;
1296 else {
1297 rb->lcd_update_rect(player_x, player_y, CARD_WIDTH+2,
1298 CARD_HEIGHT+2);
1299 player_x += CARD_WIDTH + 4;
1301 update_total(bj);
1303 break;
1304 case BJACK_STAY:
1305 bj->end_hand = true;
1306 break;
1307 case BJACK_DOUBLEDOWN:
1308 if ((signed int)bj->current_bet * 2 < bj->player_money + 1 &&
1309 bj->num_player_cards[0]==2 && todo==1) {
1310 double_down(bj);
1311 dbl_down = true;
1312 if (bj->player_total < 22) {
1313 bj->end_hand = true;
1314 finish_game(bj);
1317 else if((signed int)bj->current_bet * 2 > bj->player_money) {
1318 rb->splash(HZ, "Not enough money to double down.");
1319 redraw_board(bj);
1320 rb->lcd_update();
1322 break;
1323 case BJACK_RESUME: /* save and end game */
1324 rb->splash(HZ, "Saving game...");
1325 blackjack_savegame(bj);
1326 /* fall through to BJACK_QUIT */
1328 case BJACK_QUIT:
1329 return BJ_END;
1332 while (bj->player_total > 21 && !bj->end_hand) {
1333 temp = check_for_aces(bj->player_cards[done],
1334 bj->num_player_cards[done]);
1335 if(temp != -1) {
1336 bj->player_cards[done][temp].is_soft_ace = false;
1337 bj->player_total -= 10;
1338 update_total(bj);
1339 if (dbl_down) {
1340 bj->end_hand = true;
1341 finish_game(bj);
1344 else
1345 bj->end_hand = true;
1348 if (bj->end_hand) {
1349 done++;
1350 if(todo > 1) {
1351 if (done == 2) {
1352 temp = bj->player_total;
1353 bj->player_total = temp_var;
1354 temp_var = temp;
1355 finish_game(bj);
1356 rb->lcd_getstringsize(" Split 1 ", &w, &h);
1357 rb->lcd_putsxy(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
1358 " Split 1 ");
1359 rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
1360 w,h);
1361 bj->current_bet /= 2;
1362 rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
1363 w,h);
1364 rb->sleep(HZ*2);
1365 bj->player_total = temp_var;
1366 finish_game(bj);
1367 rb->lcd_getstringsize(" Split 2 ", &w, &h);
1368 rb->lcd_putsxy(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
1369 " Split 2 ");
1370 rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
1371 w,h);
1372 rb->sleep(HZ*2);
1374 else {
1375 bj->end_hand = false;
1376 bj->split_status = 3;
1377 temp_var = bj->player_total;
1378 bj->player_total = bj->player_cards[1][0].value;
1379 update_total(bj);
1380 redraw_board(bj);
1381 player_x += 10;
1382 rb->lcd_update();
1385 else
1386 finish_game(bj);
1390 if (bj->player_money < 10) {
1391 rb->sleep(HZ);
1392 return BJ_LOSE;
1395 if (bj->end_hand) { /* If hand is over */
1396 if (play_again() != 0) /* User wants to quit */
1397 return BJ_END;
1398 else { /* User keeps playing */
1399 breakout = false;
1400 redraw_board(bj);
1401 if(dbl_down) {
1402 bj->current_bet /= 2;
1403 dbl_down = false;
1405 done = 0;
1406 todo = 1;
1407 blackjack_init(bj);
1408 blackjack_get_bet(bj);
1409 if (bj->current_bet == 0)
1410 return BJ_END;
1411 deal_init_cards(bj);
1412 blackjack_drawtable(bj);
1413 rb->lcd_update();
1417 /* Never reached */
1418 return PLUGIN_OK;
1421 /*****************************************************************************
1422 * plugin entry point.
1423 ******************************************************************************/
1424 enum plugin_status plugin_start(struct plugin_api* api, void* parameter) {
1425 struct game_context bj;
1426 bool exit = false;
1427 unsigned int position;
1428 char str[19];
1430 (void)parameter;
1431 rb = api;
1433 #if LCD_DEPTH > 1
1434 rb->lcd_set_backdrop(NULL);
1435 #endif
1437 /* load high scores */
1438 blackjack_loadscores(&bj);
1440 rb->lcd_setfont(FONT_SYSFIXED);
1442 while(!exit) {
1443 switch(blackjack(&bj)){
1444 case BJ_LOSE:
1445 rb->splash(HZ, "Not enough money to continue");
1446 /* fall through to BJ_END */
1448 case BJ_END:
1449 if(!bj.resume) {
1450 if((position = blackjack_recordscore(&bj))) {
1451 rb->snprintf(str, 19, "New high score #%d!", position);
1452 rb->splash(HZ*2, str);
1455 break;
1457 case BJ_USB:
1458 rb->lcd_setfont(FONT_UI);
1459 return PLUGIN_USB_CONNECTED;
1461 case BJ_QUIT:
1462 if(bj.dirty) {
1463 rb->splash(HZ, "Saving high scores...");
1464 blackjack_savescores(&bj);
1466 exit = true;
1467 break;
1469 default:
1470 break;
1474 rb->lcd_setfont(FONT_UI);
1475 return PLUGIN_OK;