rockbox/apps/plugins/blackjack.c

1598 lines
57 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id: $
*
* Copyright (C) 2006 Tom Ross
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "plugin.h"
#include "card_deck.h"
#include "card_back.h"
PLUGIN_HEADER
/* save files */
#define SCORE_FILE PLUGIN_GAMES_DIR "/blackjack.score"
#define SAVE_FILE PLUGIN_GAMES_DIR "/blackjack.save"
#define NUM_SCORES LCD_HEIGHT/8-2
/* final game return status */
#define BJ_END 3
#define BJ_USB 2
#define BJ_QUIT 1
#define BJ_LOSE 0
#if CONFIG_KEYPAD == RECORDER_PAD
#define BJACK_START BUTTON_ON
#define BJACK_QUIT BUTTON_OFF
#define BJACK_MAX (BUTTON_ON|BUTTON_UP)
#define BJACK_MIN (BUTTON_ON|BUTTON_DOWN)
#define BJACK_HIT BUTTON_F1
#define BJACK_STAY BUTTON_F2
#define BJACK_DOUBLEDOWN BUTTON_F3
#define BJACK_SCORES BUTTON_RIGHT
#define BJACK_RESUME BUTTON_PLAY
#define BJACK_UP BUTTON_UP
#define BJACK_DOWN BUTTON_DOWN
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
#elif CONFIG_KEYPAD == ONDIO_PAD
#define BJACK_START BUTTON_MENU
#define BJACK_QUIT BUTTON_OFF
#define BJACK_MAX (BUTTON_MENU|BUTTON_UP)
#define BJACK_MIN (BUTTON_MENU|BUTTON_DOWN)
#define BJACK_HIT BUTTON_LEFT
#define BJACK_STAY BUTTON_RIGHT
#define BJACK_DOUBLEDOWN BUTTON_UP
#define BJACK_SCORES BUTTON_UP
#define BJACK_RESUME BUTTON_DOWN
#define BJACK_UP BUTTON_UP
#define BJACK_DOWN BUTTON_DOWN
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
#elif CONFIG_KEYPAD == IRIVER_H10_PAD
#define BJACK_START BUTTON_PLAY
#define BJACK_QUIT BUTTON_POWER
#define BJACK_MAX (BUTTON_PLAY|BUTTON_SCROLL_UP)
#define BJACK_MIN (BUTTON_PLAY|BUTTON_SCROLL_DOWN)
#define BJACK_HIT BUTTON_PLAY
#define BJACK_STAY BUTTON_FF
#define BJACK_DOUBLEDOWN BUTTON_REW
#define BJACK_SCORES BUTTON_LEFT
#define BJACK_RESUME BUTTON_RIGHT
#define BJACK_UP BUTTON_SCROLL_UP
#define BJACK_DOWN BUTTON_SCROLL_DOWN
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
(CONFIG_KEYPAD == IRIVER_H300_PAD)
#define BJACK_START BUTTON_ON
#define BJACK_QUIT BUTTON_OFF
#define BJACK_MAX (BUTTON_ON|BUTTON_UP)
#define BJACK_MIN (BUTTON_ON|BUTTON_DOWN)
#define BJACK_HIT BUTTON_ON
#define BJACK_STAY BUTTON_REC
#define BJACK_DOUBLEDOWN BUTTON_SELECT
#define BJACK_SCORES BUTTON_SELECT
#define BJACK_RESUME BUTTON_MODE
#define BJACK_UP BUTTON_UP
#define BJACK_DOWN BUTTON_DOWN
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
#define BJACK_START BUTTON_SELECT
#define BJACK_QUIT BUTTON_MENU
#define BJACK_MAX (BUTTON_SELECT|BUTTON_SCROLL_FWD)
#define BJACK_MIN (BUTTON_SELECT|BUTTON_SCROLL_BACK)
#define BJACK_HIT BUTTON_SELECT
#define BJACK_STAY BUTTON_RIGHT
#define BJACK_DOUBLEDOWN BUTTON_LEFT
#define BJACK_SCORES BUTTON_RIGHT
#define BJACK_RESUME BUTTON_PLAY
#define BJACK_UP BUTTON_SCROLL_FWD
#define BJACK_DOWN BUTTON_SCROLL_BACK
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
#define BJACK_START BUTTON_PLAY
#define BJACK_QUIT BUTTON_POWER
#define BJACK_MAX (BUTTON_PLAY|BUTTON_UP)
#define BJACK_MIN (BUTTON_PLAY|BUTTON_DOWN)
#define BJACK_HIT BUTTON_SELECT
#define BJACK_STAY BUTTON_REC
#define BJACK_DOUBLEDOWN BUTTON_PLAY
#define BJACK_SCORES BUTTON_RIGHT
#define BJACK_RESUME BUTTON_DOWN
#define BJACK_UP BUTTON_UP
#define BJACK_DOWN BUTTON_DOWN
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
#elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
#define BJACK_START BUTTON_MODE
#define BJACK_QUIT BUTTON_PLAY
#define BJACK_MAX (BUTTON_EQ|BUTTON_UP)
#define BJACK_MIN (BUTTON_EQ|BUTTON_DOWN)
#define BJACK_HIT BUTTON_EQ
#define BJACK_STAY BUTTON_MODE
#define BJACK_DOUBLEDOWN BUTTON_SELECT
#define BJACK_SCORES BUTTON_SELECT
#define BJACK_RESUME (BUTTON_EQ|BUTTON_MODE)
#define BJACK_UP BUTTON_UP
#define BJACK_DOWN BUTTON_DOWN
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
#elif CONFIG_KEYPAD == GIGABEAT_PAD
#define BJACK_START BUTTON_A
#define BJACK_QUIT BUTTON_POWER
#define BJACK_MAX BUTTON_VOL_UP
#define BJACK_MIN BUTTON_VOL_DOWN
#define BJACK_HIT BUTTON_VOL_UP
#define BJACK_STAY BUTTON_VOL_DOWN
#define BJACK_DOUBLEDOWN BUTTON_SELECT
#define BJACK_SCORES BUTTON_RIGHT
#define BJACK_RESUME BUTTON_MENU
#define BJACK_UP BUTTON_UP
#define BJACK_DOWN BUTTON_DOWN
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
#elif CONFIG_KEYPAD == SANSA_E200_PAD
#define BJACK_START BUTTON_SELECT
#define BJACK_QUIT BUTTON_POWER
#define BJACK_MAX (BUTTON_REC|BUTTON_UP)
#define BJACK_MIN (BUTTON_REC|BUTTON_DOWN)
#define BJACK_HIT BUTTON_SELECT
#define BJACK_STAY BUTTON_RIGHT
#define BJACK_DOUBLEDOWN BUTTON_LEFT
#define BJACK_SCORES BUTTON_UP
#define BJACK_RESUME BUTTON_REC
#define BJACK_UP BUTTON_SCROLL_FWD
#define BJACK_DOWN BUTTON_SCROLL_BACK
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
#elif CONFIG_KEYPAD == SANSA_C200_PAD
#define BJACK_START BUTTON_SELECT
#define BJACK_QUIT BUTTON_POWER
#define BJACK_MAX BUTTON_VOL_UP
#define BJACK_MIN BUTTON_VOL_DOWN
#define BJACK_HIT BUTTON_SELECT
#define BJACK_STAY BUTTON_RIGHT
#define BJACK_DOUBLEDOWN BUTTON_LEFT
#define BJACK_SCORES BUTTON_REC
#define BJACK_RESUME BUTTON_DOWN
#define BJACK_UP BUTTON_UP
#define BJACK_DOWN BUTTON_DOWN
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
#elif CONFIG_KEYPAD == ELIO_TPJ1022_PAD
#define BJACK_START BUTTON_MAIN
#define BJACK_QUIT BUTTON_POWER
#define BJACK_MAX (BUTTON_REC|BUTTON_UP)
#define BJACK_MIN (BUTTON_REC|BUTTON_DOWN)
#define BJACK_HIT BUTTON_MAIN
#define BJACK_STAY BUTTON_MENU
#define BJACK_DOUBLEDOWN BUTTON_DOWN
#define BJACK_SCORES BUTTON_UP
#define BJACK_RESUME BUTTON_FF
#define BJACK_UP BUTTON_UP
#define BJACK_DOWN BUTTON_DOWN
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
#define BJACK_START BUTTON_PLAY
#define BJACK_QUIT BUTTON_BACK
#define BJACK_MAX BUTTON_VOL_UP
#define BJACK_MIN BUTTON_VOL_DOWN
#define BJACK_HIT BUTTON_VOL_UP
#define BJACK_STAY BUTTON_VOL_DOWN
#define BJACK_DOUBLEDOWN BUTTON_SELECT
#define BJACK_SCORES BUTTON_RIGHT
#define BJACK_RESUME BUTTON_MENU
#define BJACK_UP BUTTON_UP
#define BJACK_DOWN BUTTON_DOWN
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
#elif CONFIG_KEYPAD == MROBE100_PAD
#define BJACK_START BUTTON_SELECT
#define BJACK_QUIT BUTTON_POWER
#define BJACK_MAX BUTTON_MENU
#define BJACK_MIN BUTTON_DISPLAY
#define BJACK_HIT BUTTON_MENU
#define BJACK_STAY BUTTON_DISPLAY
#define BJACK_DOUBLEDOWN BUTTON_DOWN
#define BJACK_SCORES BUTTON_RIGHT
#define BJACK_RESUME BUTTON_PLAY
#define BJACK_UP BUTTON_UP
#define BJACK_DOWN BUTTON_DOWN
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
#define BJACK_START BUTTON_RC_PLAY
#define BJACK_QUIT BUTTON_RC_REC
#define BJACK_MAX (BUTTON_RC_PLAY|BUTTON_RC_VOL_UP)
#define BJACK_MIN (BUTTON_RC_PLAY|BUTTON_RC_VOL_DOWN)
#define BJACK_HIT BUTTON_RC_PLAY
#define BJACK_STAY BUTTON_RC_FF
#define BJACK_DOUBLEDOWN BUTTON_RC_REW
#define BJACK_SCORES BUTTON_RC_MENU
#define BJACK_RESUME BUTTON_RC_MODE
#define BJACK_UP BUTTON_RC_VOL_UP
#define BJACK_DOWN BUTTON_RC_VOL_DOWN
#define BJACK_RIGHT BUTTON_RC_FF
#define BJACK_LEFT BUTTON_RC_REW
#elif CONFIG_KEYPAD == COWOND2_PAD
#define BJACK_QUIT BUTTON_POWER
#define BJACK_DOUBLEDOWN BUTTON_MINUS
#define BJACK_SCORES BUTTON_MENU
#else
#error No keymap defined!
#endif
#ifdef HAVE_TOUCHSCREEN
#ifndef BJACK_START
#define BJACK_START BUTTON_CENTER
#endif
#ifndef BJACK_HIT
#define BJACK_HIT BUTTON_CENTER
#endif
#ifndef BJACK_MAX
#define BJACK_MAX BUTTON_TOPRIGHT
#endif
#ifndef BJACK_MIN
#define BJACK_MIN BUTTON_TOPLEFT
#endif
#ifndef BJACK_RESUME
#define BJACK_RESUME BUTTON_BOTTOMRIGHT
#endif
#ifndef BJACK_STAY
#define BJACK_STAY BUTTON_BOTTOMLEFT
#endif
#ifndef BJACK_UP
#define BJACK_UP BUTTON_TOPMIDDLE
#endif
#ifndef BJACK_DOWN
#define BJACK_DOWN BUTTON_BOTTOMMIDDLE
#endif
#ifndef BJACK_RIGHT
#define BJACK_RIGHT BUTTON_MIDRIGHT
#endif
#ifndef BJACK_LEFT
#define BJACK_LEFT BUTTON_MIDLEFT
#endif
#endif
#ifdef HAVE_LCD_COLOR
#define BG_COLOR LCD_RGBPACK(0,157,0)
#define FG_COLOR LCD_WHITE
#elif LCD_DEPTH > 1
#define BG_COLOR LCD_WHITE
#define FG_COLOR LCD_BLACK
#endif
#define CARD_WIDTH BMPWIDTH_card_back
#define CARD_HEIGHT BMPHEIGHT_card_back
/* This is the max amount of cards onscreen before condensing */
#define MAX_CARDS LCD_WIDTH/(CARD_WIDTH+4)
extern const fb_data card_deck[];
extern const fb_data card_back[];
#define NEXT_CARD bj->player_cards[done][bj->num_player_cards[done]]
/* global rockbox api */
static const struct plugin_api* rb;
MEM_FUNCTION_WRAPPERS(rb);
/* dealer and player card positions */
unsigned int dealer_x, dealer_y, player_x, player_y;
typedef struct card {
unsigned int value; /* Card's value in Blackjack */
unsigned int num; /* Value on card face 0-12 (0=Ace, 1=2, 11=Q) */
unsigned int suit; /* 0:Spades, 1:Hearts, 2: Clubs; 3: Diamonds */
bool is_soft_ace;
} card;
typedef struct game_context {
struct card player_cards[2][22]; /* 22 Cards means the deal was all aces */
struct card dealer_cards[22]; /* That is the worst-case scenario */
unsigned int player_total;
unsigned int dealer_total;
signed int player_money;
unsigned int num_player_cards[2];
unsigned int num_dealer_cards;
unsigned int current_bet;
unsigned int split_status; /* 0 = split hasn't been asked, *
* 1 = split did not occur *
* 2 = split occurred *
* 3 = split occurred and 1st hand done */
bool is_blackjack;
bool end_hand;
bool asked_insurance;
signed short highscores[NUM_SCORES];
bool resume;
bool dirty;
} game_context;
/*****************************************************************************
* blackjack_init() initializes blackjack data structures.
******************************************************************************/
static void blackjack_init(struct game_context* bj) {
/* seed the rand generator */
rb->srand(*rb->current_tick);
/* reset card positions */
dealer_x = 4;
dealer_y = LCD_HEIGHT/4 - CARD_HEIGHT/2;
player_x = 4;
player_y = LCD_HEIGHT - LCD_HEIGHT/4 - CARD_HEIGHT/2;
/* check for resumed game */
if(bj->resume) return;
/* reset scoring */
bj->player_total = 0;
bj->dealer_total = 0;
bj->num_player_cards[0] = 2;
bj->num_player_cards[1] = 0;
bj->num_dealer_cards = 2;
bj->end_hand = false;
bj->split_status = 0;
bj->is_blackjack = false;
bj->asked_insurance = false;
}
/*****************************************************************************
* blackjack_drawtable() draws the table and some text.
******************************************************************************/
static void blackjack_drawtable(struct game_context* bj) {
unsigned int w, h, y_loc;
char str[10];
#if LCD_HEIGHT <= 64
rb->lcd_getstringsize("Bet", &w, &h);
rb->lcd_putsxy(LCD_WIDTH - w, 2*h + 1, "Bet");
rb->snprintf(str, 9, "$%d", bj->current_bet);
rb->lcd_getstringsize(str, &w, &h);
rb->lcd_putsxy(LCD_WIDTH - w, 3*h + 1, str);
y_loc = LCD_HEIGHT/2;
#else
rb->lcd_getstringsize("Bet", &w, &h);
rb->lcd_putsxy(LCD_WIDTH - w, 5*h / 2, "Bet");
rb->snprintf(str, 9, "$%d", bj->current_bet);
rb->lcd_getstringsize(str, &w, &h);
rb->lcd_putsxy(LCD_WIDTH - w, 7*h / 2, str);
rb->lcd_hline(0, LCD_WIDTH, LCD_HEIGHT/2);
y_loc = LCD_HEIGHT/2 + h;
#endif
rb->lcd_putsxy(0,0, "Dealer");
rb->lcd_getstringsize("Player", &w, &h);
rb->lcd_putsxy(0, y_loc, "Player");
rb->lcd_getstringsize("Total", &w, &h);
rb->lcd_putsxy(LCD_WIDTH - w, y_loc, "Total");
rb->lcd_getstringsize("Money", &w, &h);
rb->lcd_putsxy(LCD_WIDTH - w, 0, "Money");
rb->snprintf(str, 9, "$%d", bj->player_money - bj->current_bet);
rb->lcd_getstringsize(str, &w, &h);
rb->lcd_putsxy(LCD_WIDTH - w, h + 1, str);
rb->snprintf(str, 3, "%d", bj->player_total);
rb->lcd_getstringsize(str, &w, &h);
rb->lcd_putsxy(LCD_WIDTH - w, y_loc + h, str);
}
/*****************************************************************************
* find_value() is passed a card and returns its blackjack value.
******************************************************************************/
static unsigned int find_value(unsigned int number) {
unsigned int thisValue;
if (number == 0)
thisValue = 11; /* Aces get a value of 11 at first */
else if (number < 10)
thisValue = number + 1;
else
thisValue = 10; /* Anything 10 or higher gets a value of 10 */
return thisValue;
}
/*****************************************************************************
* draw_card() draws a card to the screen.
******************************************************************************/
static void draw_card(struct card temp_card, bool shown, unsigned int x,
unsigned int y) {
if(shown)
rb->lcd_bitmap_part(card_deck, CARD_WIDTH*temp_card.num,
CARD_HEIGHT*temp_card.suit, BMPWIDTH_card_deck,
x+1, y+1, CARD_WIDTH, CARD_HEIGHT);
else
rb->lcd_bitmap(card_back, x+1, y+1,CARD_WIDTH, CARD_HEIGHT);
#if LCD_DEPTH > 1
rb->lcd_set_foreground(LCD_BLACK);
#endif
/* Print outlines */
#if CARD_WIDTH >= 26
rb->lcd_hline(x+2, x+CARD_WIDTH-1, y);
rb->lcd_hline(x+2, x+CARD_WIDTH-1, y+CARD_HEIGHT+1);
rb->lcd_vline(x, y+2, y+CARD_HEIGHT-3);
rb->lcd_vline(x+CARD_WIDTH+1, y+2, y+CARD_HEIGHT-1);
rb->lcd_drawpixel(x+1, y+1);
rb->lcd_drawpixel(x+1, y+CARD_HEIGHT);
rb->lcd_drawpixel(x+CARD_WIDTH, y+1);
rb->lcd_drawpixel(x+CARD_WIDTH, y+CARD_HEIGHT);
#else
rb->lcd_hline(x+1, x+CARD_WIDTH, y);
rb->lcd_hline(x+1, x+CARD_WIDTH, y+CARD_HEIGHT+1);
rb->lcd_vline(x, y+1, y+CARD_HEIGHT);
rb->lcd_vline(x+CARD_WIDTH+1, y+1, y+CARD_HEIGHT);
#endif
#if LCD_DEPTH > 1
rb->lcd_set_foreground(FG_COLOR);
#endif
}
/*****************************************************************************
* new_card() initializes a new card and gives it values.
******************************************************************************/
static struct card new_card(void) {
struct card new_card;
new_card.suit = rb->rand()%4; /* Random number 0-3 */
new_card.num = rb->rand()%13; /* Random number 0-12 */
new_card.value = find_value(new_card.num);
new_card.is_soft_ace = new_card.num == 0 ? true : false;
return new_card;
}
/*****************************************************************************
* deal_init_card() deals and draws to the screen the player's and dealer's
* initial cards.
******************************************************************************/
static void deal_init_cards(struct game_context* bj) {
bj->dealer_cards[0] = new_card();
bj->dealer_total += bj->dealer_cards[0].value;
draw_card(bj->dealer_cards[0], false, dealer_x, dealer_y);
bj->dealer_cards[1] = new_card();
bj->dealer_total += bj->dealer_cards[1].value;
draw_card(bj->dealer_cards[1], true, dealer_x + CARD_WIDTH + 4, dealer_y);
bj->player_cards[0][0] = new_card();
bj->player_total += bj->player_cards[0][0].value;
draw_card(bj->player_cards[0][0], true, player_x, player_y);
player_x += CARD_WIDTH + 4;
bj->player_cards[0][1] = new_card();
bj->player_total += bj->player_cards[0][1].value;
draw_card(bj->player_cards[0][1], true, player_x, player_y);
player_x += CARD_WIDTH + 4;
}
/*****************************************************************************
* redraw_board() redraws all the cards and the board
******************************************************************************/
static void redraw_board(struct game_context* bj) {
unsigned int i, n, upper_bound;
rb->lcd_clear_display();
blackjack_drawtable(bj);
player_x = 4;
dealer_x = 4;
upper_bound = bj->split_status > 1 ? 2 : 1;
for (i = 0; i < bj->num_dealer_cards; i++) {
if (!bj->end_hand) {
draw_card(bj->dealer_cards[0], false, dealer_x, dealer_y);
/* increment i so the dealer's first card isn't displayed */
i++;
dealer_x += CARD_WIDTH + 4;
}
draw_card(bj->dealer_cards[i], true, dealer_x, dealer_y);
if (bj->num_dealer_cards > MAX_CARDS-1)
dealer_x += 10;
else
dealer_x += CARD_WIDTH + 4;
}
for (n = 0; n < upper_bound; n++) {
for (i = 0; i < bj->num_player_cards[n]; i++) {
draw_card(bj->player_cards[n][i], true, player_x, player_y);
if (bj->split_status>1 || bj->num_player_cards[n]>MAX_CARDS)
player_x += 10;
else
player_x += CARD_WIDTH + 4;
}
if (bj->split_status > 1)
player_x = LCD_WIDTH/2 + 4;
}
}
/*****************************************************************************
* update_total updates the player's total
******************************************************************************/
static void update_total(struct game_context* bj) {
char total[3];
unsigned int w, h;
rb->snprintf(total, 3, "%d", bj->player_total);
rb->lcd_getstringsize(total, &w, &h);
#if LCD_HEIGHT > 64
h *= 2;
#endif
rb->lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 + h, total);
rb->lcd_update_rect(LCD_WIDTH - w, LCD_HEIGHT/2 + h, w, h);
}
/*****************************************************************************
* check_for_aces() is passed an array of cards and returns where an ace is
* located. Otherwise, returns -1.
******************************************************************************/
static signed int check_for_aces(struct card temp_cards[],
unsigned int size) {
unsigned int i;
for(i = 0; i < size; i++) {
if (temp_cards[i].is_soft_ace == true)
return i;
}
return -1;
}
/*****************************************************************************
* check_totals() compares player and dealer totals.
* 0: bust 1: loss, 2: push, 3: win, 4: blackjack, 5: something's not right...
******************************************************************************/
static unsigned int check_totals(struct game_context* bj)
{
unsigned int temp;
if (bj->player_total > 21)
temp = 0;
else if (bj->player_total == 21 && bj->is_blackjack)
if (bj->dealer_total == 21 && bj->num_dealer_cards == 2)
temp = 2;
else
temp = 4;
else if (bj->player_total == bj->dealer_total)
temp = 2;
else if (bj->dealer_total > 21 && bj->player_total < 22)
temp = 3;
else if (bj->dealer_total > bj->player_total)
temp = 1;
else if (bj->player_total > bj->dealer_total)
temp = 3;
else
temp = 5;
return temp;
}
/*****************************************************************************
* finish_dealer() draws cards for the dealer until he has 17 or more.
******************************************************************************/
static void finish_dealer(struct game_context* bj) {
signed int temp = 0;
if (bj->dealer_total > 16 && bj->dealer_total < 22)
return;
while (bj->dealer_total < 17) {
bj->dealer_cards[bj->num_dealer_cards] = new_card();
bj->dealer_total += bj->dealer_cards[bj->num_dealer_cards].value;
bj->num_dealer_cards++;
}
while (bj->dealer_total > 21) {
temp = check_for_aces(bj->dealer_cards, bj->num_dealer_cards);
if(temp != -1) {
bj->dealer_cards[temp].is_soft_ace = false;
bj->dealer_total -= 10;
}
else
return;
}
}
/*****************************************************************************
* finish_game() completes the game once player's turn is over.
******************************************************************************/
static void finish_game(struct game_context* bj) {
unsigned int rValue, w, h;
char str[19];
do {
finish_dealer(bj);
} while (bj->dealer_total < 17);
redraw_board(bj);
rValue = check_totals(bj);
if (rValue == 0) {
rb->snprintf(str, sizeof(str), " Bust! ");
bj->player_money -= bj->current_bet;
}
else if (rValue == 1) {
rb->snprintf(str, sizeof(str), " Sorry, you lost. ");
bj->player_money -= bj->current_bet;
}
else if (rValue == 2) {
rb->snprintf(str, sizeof(str), " Push ");
}
else if (rValue == 3) {
rb->snprintf(str, sizeof(str), " You won! ");
bj->player_money+= bj->current_bet;
}
else {
rb->snprintf(str, sizeof(str), " Blackjack! ");
bj->player_money += bj->current_bet * 3 / 2;
}
rb->lcd_getstringsize(str, &w, &h);
#if LCD_HEIGHT <= 64
rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
rb->lcd_fillrect(0, LCD_HEIGHT/2, LCD_WIDTH, LCD_HEIGHT/2);
rb->lcd_set_drawmode(DRMODE_SOLID);
rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + h, str);
rb->snprintf(str, 12, "You have %d", bj->player_total);
rb->lcd_getstringsize(str, &w, &h);
rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2, str);
#else
rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, str);
#endif
rb->lcd_update();
}
/*****************************************************************************
* blackjack_recordscore() inserts a high score into the high scores list and
* returns the high score position.
******************************************************************************/
static unsigned int blackjack_recordscore(struct game_context* bj) {
unsigned int i;
unsigned int position = 0;
signed short current, temp;
/* calculate total score */
current = bj->player_money;
if(current <= 10) return 0;
/* insert the current score into the high scores */
for(i=0; i<NUM_SCORES; i++) {
if(current >= bj->highscores[i]) {
if(!position) {
position = i+1;
bj->dirty = true;
}
temp = bj->highscores[i];
bj->highscores[i] = current;
current = temp;
}
}
return position;
}
/*****************************************************************************
* blackjack_loadscores() loads the high scores saved file.
******************************************************************************/
static void blackjack_loadscores(struct game_context* bj) {
signed int fd;
bj->dirty = false;
/* clear high scores */
rb->memset(bj->highscores, 0, sizeof(bj->highscores));
/* open scores file */
fd = rb->open(SCORE_FILE, O_RDONLY);
if(fd < 0) return;
/* read in high scores */
if(rb->read(fd, bj->highscores, sizeof(bj->highscores)) <= 0) {
/* scores are bad, reset */
rb->memset(bj->highscores, 0, sizeof(bj->highscores));
}
rb->close(fd);
}
/*****************************************************************************
* blackjack_savescores() saves the high scores saved file.
******************************************************************************/
static void blackjack_savescores(struct game_context* bj) {
unsigned int fd;
/* write out the high scores to the save file */
fd = rb->open(SCORE_FILE, O_WRONLY|O_CREAT);
rb->write(fd, bj->highscores, sizeof(bj->highscores));
rb->close(fd);
bj->dirty = false;
}
/*****************************************************************************
* blackjack_loadgame() loads the saved game and returns load success.
******************************************************************************/
static bool blackjack_loadgame(struct game_context* bj) {
signed int fd;
bool loaded = false;
/* open game file */
fd = rb->open(SAVE_FILE, O_RDONLY);
if(fd < 0) return loaded;
/* read in saved game */
while(true) {
if(rb->read(fd, &bj->player_money, sizeof(bj->player_money)) <= 0) break;
if(rb->read(fd, &bj->player_total, sizeof(bj->player_total)) <= 0) break;
if(rb->read(fd, &bj->dealer_total, sizeof(bj->dealer_total)) <= 0) break;
if(rb->read(fd, &bj->num_player_cards, sizeof(bj->num_player_cards))<=0)
break;
if(rb->read(fd, &bj->num_dealer_cards, sizeof(bj->num_dealer_cards))<=0)
break;
if(rb->read(fd, &bj->current_bet, sizeof(bj->current_bet)) <= 0) break;
if(rb->read(fd, &bj->is_blackjack, sizeof(bj->is_blackjack)) <= 0) break;
if(rb->read(fd, &bj->split_status, sizeof(bj->split_status)) <= 0) break;
if(rb->read(fd, &bj->asked_insurance, sizeof(bj->asked_insurance)) <= 0)
break;
if(rb->read(fd, &bj->end_hand, sizeof(bj->end_hand)) <= 0) break;
if(rb->read(fd, &bj->player_cards, sizeof(bj->player_cards)) <= 0) break;
if(rb->read(fd, &bj->dealer_cards, sizeof(bj->dealer_cards)) <= 0) break;
bj->resume = true;
loaded = true;
break;
}
rb->close(fd);
/* delete saved file */
rb->remove(SAVE_FILE);
return loaded;
}
/*****************************************************************************
* blackjack_savegame() saves the current game state.
******************************************************************************/
static void blackjack_savegame(struct game_context* bj) {
unsigned int fd;
/* write out the game state to the save file */
fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
rb->write(fd, &bj->player_money, sizeof(bj->player_money));
rb->write(fd, &bj->player_total, sizeof(bj->player_total));
rb->write(fd, &bj->dealer_total, sizeof(bj->dealer_total));
rb->write(fd, &bj->num_player_cards, sizeof(bj->num_player_cards));
rb->write(fd, &bj->num_dealer_cards, sizeof(bj->num_dealer_cards));
rb->write(fd, &bj->current_bet, sizeof(bj->current_bet));
rb->write(fd, &bj->is_blackjack, sizeof(bj->is_blackjack));
rb->write(fd, &bj->split_status, sizeof(bj->split_status));
rb->write(fd, &bj->asked_insurance, sizeof(bj->asked_insurance));
rb->write(fd, &bj->end_hand, sizeof(bj->end_hand));
rb->write(fd, &bj->player_cards, sizeof(bj->player_cards));
rb->write(fd, &bj->dealer_cards, sizeof(bj->dealer_cards));
rb->close(fd);
bj->resume = true;
}
/*****************************************************************************
* blackjack_callback() is the default event handler callback which is called
* on usb connect and shutdown.
******************************************************************************/
static void blackjack_callback(void* param) {
struct game_context* bj = (struct game_context*) param;
if(bj->dirty) {
rb->splash(HZ, "Saving high scores...");
blackjack_savescores(bj);
}
}
/*****************************************************************************
* blackjack_get_yes_no() gets a yes/no answer from the user
******************************************************************************/
static unsigned int blackjack_get_yes_no(char message[20]) {
int button;
unsigned int w, h, b, choice = 0;
bool breakout = false;
char message_yes[24], message_no[24];
rb->strcpy(message_yes, message);
rb->strcpy(message_no, message);
rb->strcat(message_yes, " Yes");
rb->strcat(message_no, " No");
rb->lcd_getstringsize(message_yes, &w, &h);
const char *stg[] = {message_yes, message_no};
#if LCD_HEIGHT <= 64
b = 2*h+1;
#else
b = h-1;
#endif
#ifdef HAVE_LCD_COLOR
rb->lcd_fillrect(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b, w+1, h+3);
rb->lcd_set_foreground(LCD_BLACK);
rb->lcd_set_background(LCD_WHITE);
#else
rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
rb->lcd_fillrect(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b, w+1, h+3);
rb->lcd_set_drawmode(DRMODE_SOLID);
#endif
rb->lcd_drawrect(LCD_WIDTH/2 - w/2 - 1, LCD_HEIGHT/2 + b - 1, w+3, h+4);
while(!breakout) {
rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b +1, stg[choice]);
rb->lcd_update_rect(LCD_WIDTH/2 - w/2 - 1, LCD_HEIGHT/2 + b -1,
w+3, h+4);
button = rb->button_get(true);
switch(button) {
case BJACK_LEFT:
case (BJACK_LEFT|BUTTON_REPEAT):
case BJACK_RIGHT:
case (BJACK_RIGHT|BUTTON_REPEAT):
choice ^= 1;
break;
case BJACK_START: breakout = true;
break;
case BJACK_QUIT: breakout = true;
choice = BJ_QUIT;
break;
}
}
#if LCD_DEPTH > 1
rb->lcd_set_foreground(FG_COLOR);
rb->lcd_set_background(BG_COLOR);
#endif
return choice;
}
/*****************************************************************************
* blackjack_get_amount() gets an amount from the player to be used
******************************************************************************/
static signed int blackjack_get_amount(char message[20], signed int lower_limit,
signed int upper_limit,
signed int start) {
int button;
char str[6];
bool changed = false;
unsigned int w, h;
signed int amount;
rb->lcd_getstringsize("A", &w, &h); /* find the size of one character */
if (start > upper_limit)
amount = upper_limit;
else if (start < lower_limit)
amount = lower_limit;
else
amount = start;
#if LCD_DEPTH > 1
rb->lcd_set_background(LCD_WHITE);
rb->lcd_set_foreground(LCD_BLACK);
#endif
#if LCD_HEIGHT <= 64
rb->lcd_clear_display();
rb->lcd_puts(0, 1, message);
rb->snprintf(str, 9, "$%d", amount);
rb->lcd_puts(0, 2, str);
rb->lcd_puts(0, 3, "RIGHT: +1");
rb->lcd_puts(0, 4, "LEFT: -1");
rb->lcd_puts(0, 5, "UP: +10");
rb->lcd_puts(0, 6, "DOWN: -10");
rb->lcd_update();
#else
rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
rb->lcd_fillrect(LCD_WIDTH/2 - 9*w - 1, LCD_HEIGHT/2 - 4*h - 3, 37*w / 2,
8*h -3);
rb->lcd_set_drawmode(DRMODE_SOLID);
rb->lcd_drawrect(LCD_WIDTH/2 - 9*w - 1, LCD_HEIGHT/2 - 4*h - 3, 37*w / 2,
8*h -3);
rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 4*h - 1, message);
rb->snprintf(str, 9, "$%d", amount);
rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, str);
#if (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, " >>|: +1");
rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, " |<<: -1");
rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "SCROLL+: +10");
rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "SCROLL-: -10");
#elif CONFIG_KEYPAD == IRIVER_H10_PAD
rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, "RIGHT: +1");
rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, "LEFT: -1");
rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "SCROLL+: +10");
rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "SCROLL-: -10");
#else
rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, "RIGHT: +1");
rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, "LEFT: -1");
rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "UP: +10");
rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "DOWN: -10");
#endif
rb->lcd_update_rect(LCD_WIDTH/2 - 9*w - 2, LCD_HEIGHT/2 - 9*h/2, 37*w/2 + 1,
8*h-2);
#endif
while(true) {
button = rb->button_get(true);
switch(button) {
case BJACK_UP:
case (BJACK_UP|BUTTON_REPEAT):
if (amount + 10 < upper_limit + 1) {
amount += 10;
changed = true;
}
break;
case BJACK_DOWN:
case (BJACK_DOWN|BUTTON_REPEAT):
if (amount - 10 > lower_limit - 1) {
amount -= 10;
changed = true;
}
break;
case BJACK_RIGHT:
case (BJACK_RIGHT|BUTTON_REPEAT):
if (amount + 1 < upper_limit + 1) {
amount++;
changed = true;
}
break;
case BJACK_LEFT:
case (BJACK_LEFT|BUTTON_REPEAT):
if (amount - 1 > lower_limit - 1) {
amount--;
changed = true;
}
break;
case BJACK_MAX :
amount = upper_limit;
changed = true;
break;
case BJACK_MIN :
amount = lower_limit;
changed = true;
break;
case BJACK_QUIT:
return 0;
case BJACK_START:
#if LCD_DEPTH > 1
rb->lcd_set_foreground(FG_COLOR);
rb->lcd_set_background(BG_COLOR);
#endif
rb->lcd_clear_display();
return amount;
}
if(changed) {
rb->snprintf(str, 9, "$%d", amount);
#if LCD_HEIGHT <= 64
rb->lcd_puts(0, 2, str);
rb->lcd_update();
#else
rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
rb->lcd_fillrect(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, 5*w, h);
rb->lcd_set_drawmode(DRMODE_SOLID);
rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, str);
rb->lcd_update_rect(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, 5*w, h);
#endif
changed = false;
}
}
}
/*****************************************************************************
* blackjack_get_bet() gets the player's bet.
******************************************************************************/
static void blackjack_get_bet(struct game_context* bj) {
bj->current_bet = blackjack_get_amount("Please enter a bet", 10,
bj->player_money, bj->current_bet);
}
/*****************************************************************************
* double_down() returns one final card then finishes the game
******************************************************************************/
static void double_down(struct game_context* bj) {
bj->current_bet *= 2;
bj->player_cards[0][bj->num_player_cards[0]] = new_card();
bj->player_total += bj->player_cards[0][bj->num_player_cards[0]].value;
bj->num_player_cards[0]++;
}
/*****************************************************************************
* split() checks if the player wants to split and acts accordingly.
* When bj->split_status is 1, no split occurred. 2 means the player split and 3
* means a split has already occurred and the first hand is done.
******************************************************************************/
static void split(struct game_context* bj) {
if (blackjack_get_yes_no("Split?") == 1)
bj->split_status = 1;
else {
bj->split_status = 2;
bj->current_bet *= 2;
bj->num_player_cards[0] = 1;
bj->num_player_cards[1] = 1;
bj->player_cards[1][0] = bj->player_cards[0][1];
bj->player_total = bj->player_cards[0][0].value;
}
}
/*****************************************************************************
* insurance() see if the player wants to buy insurance and how much.
******************************************************************************/
static unsigned int insurance(struct game_context* bj) {
unsigned int insurance, max_amount;
insurance = blackjack_get_yes_no("Buy Insurance?");
bj->asked_insurance = true;
max_amount = bj->current_bet < (unsigned int)bj->player_money ?
bj->current_bet/2 : (unsigned int)bj->player_money;
if (insurance == 1) return 0;
insurance = blackjack_get_amount("How much?", 0, max_amount, 0);
redraw_board(bj);
return insurance;
}
/*****************************************************************************
* play_again() checks to see if the player wants to keep playing.
******************************************************************************/
static unsigned int play_again(void) {
return blackjack_get_yes_no("Play Again?");
}
/*****************************************************************************
* blackjack_menu() is the initial menu at the start of the game.
******************************************************************************/
static unsigned int blackjack_menu(struct game_context* bj) {
int button;
char *title = "Blackjack";
char str[18];
unsigned int i, w, h;
bool breakout = false;
bool showscores = false;
while(true){
#if LCD_DEPTH > 1
rb->lcd_set_background(BG_COLOR);
rb->lcd_set_foreground(FG_COLOR);
#endif
rb->lcd_clear_display();
if(!showscores) {
/* welcome screen to display key bindings */
rb->lcd_getstringsize(title, &w, &h);
rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, title);
#if CONFIG_KEYPAD == RECORDER_PAD
rb->lcd_puts(0, 1, "ON: start");
rb->lcd_puts(0, 2, "OFF: exit");
rb->lcd_puts(0, 3, "F1: hit");
rb->lcd_puts(0, 4, "F2: stay");
rb->lcd_puts(0, 5, "F3: double down");
rb->lcd_puts(0, 6, "PLAY: save/resume");
rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
rb->lcd_puts(0, 7, str);
#elif CONFIG_KEYPAD == ONDIO_PAD
rb->lcd_puts(0, 1, "MENU: start");
rb->lcd_puts(0, 2, "OFF: exit");
rb->lcd_puts(0, 3, "LEFT: hit");
rb->lcd_puts(0, 4, "RIGHT: stay");
rb->lcd_puts(0, 5, "UP: double down");
rb->lcd_puts(0, 6, "DOWN: save/resume");
rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
rb->lcd_puts(0, 7, str);
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
rb->lcd_puts(0, 2, "PLAY to start & to hit");
rb->lcd_puts(0, 3, "STOP to exit");
rb->lcd_puts(0, 4, "REC to stay");
rb->lcd_puts(0, 5, "NAVI to double down ");
rb->lcd_puts(0, 6, " & to view highscores");
rb->lcd_puts(0, 7, "AB to save/resume");
rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
rb->lcd_puts(0, 8, str);
#elif CONFIG_KEYPAD == IRIVER_H10_PAD
rb->lcd_puts(0, 2, "PLAY to start & hit");
rb->lcd_puts(0, 3, "POWER to exit");
rb->lcd_puts(0, 4, ">>| to stay");
rb->lcd_puts(0, 5, "|<< to double down");
rb->lcd_puts(0, 6, "LEFT to view scores");
rb->lcd_puts(0, 7, "RIGHT to save/resume");
rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
rb->lcd_puts(0, 8, str);
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
#if LCD_WIDTH >=176
rb->lcd_puts(0, 2, "SELECT to start & to hit");
rb->lcd_puts(0, 3, "MENU to exit");
rb->lcd_puts(0, 4, ">>| to stay & to view highscores");
rb->lcd_puts(0, 5, "|<< to double down");
rb->lcd_puts(0, 6, "PLAY to save/resume");
rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
rb->lcd_puts(0, 7, str);
#else
rb->lcd_puts(0, 2, "SELECT to start & to ");
rb->lcd_puts(0, 3, " hit");
rb->lcd_puts(0, 4, "MENU to exit");
rb->lcd_puts(0, 5, ">>| to stay & to view ");
rb->lcd_puts(0, 6, " highscores");
rb->lcd_puts(0, 7, "|<< to double down");
rb->lcd_puts(0, 8, "PLAY to save/resume");
rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
rb->lcd_puts(0, 9, str);
#endif
#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
rb->lcd_puts(0, 2, "PLAY to start to hit");
rb->lcd_puts(0, 3, "POWER to exit");
rb->lcd_puts(0, 4, "SELECT to hit");
rb->lcd_puts(0, 5, "REC to stay");
rb->lcd_puts(0, 6, "PLAY to double down");
rb->lcd_puts(0, 7, "RIGHT to view highscores ");
rb->lcd_puts(0, 8, "DOWN to save/resume");
rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
rb->lcd_puts(0, 9, str);
#elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
rb->lcd_puts(0, 2, "AB to start & to");
rb->lcd_puts(0, 3, " stay");
rb->lcd_puts(0, 4, "EQ to hit");
rb->lcd_puts(0, 5, "PLAY to exit");
rb->lcd_puts(0, 6, "CLICK to double down");
rb->lcd_puts(0, 7, "& to view highscores");
rb->lcd_puts(0, 8, "AB+EQ to save/resume");
rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
rb->lcd_puts(0, 9, str);
#elif CONFIG_KEYPAD == GIGABEAT_PAD
rb->lcd_puts(0, 2, "A to start");
rb->lcd_puts(0, 3, "POWER to exit");
rb->lcd_puts(0, 4, "VOL+ to hit");
rb->lcd_puts(0, 5, "VOL- to stay");
rb->lcd_puts(0, 6, "CENTER to double down");
rb->lcd_puts(0, 6, "RIGHT to view highscores ");
rb->lcd_puts(0, 8, "MENU to save/resume");
rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
rb->lcd_puts(0, 9, str);
#elif CONFIG_KEYPAD == MROBE100_PAD
rb->lcd_puts(0, 2, "CENTER to start");
rb->lcd_puts(0, 3, "POWER to exit");
rb->lcd_puts(0, 4, "MENU to hit");
rb->lcd_puts(0, 5, "DISPLAY to stay");
rb->lcd_puts(0, 6, "DOWN to double down");
rb->lcd_puts(0, 6, "RIGHT to view highscores ");
rb->lcd_puts(0, 8, "PLAY to save/resume");
rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
rb->lcd_puts(0, 9, str);
#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
rb->lcd_puts(0, 2, "SELECT to start & to hit");
rb->lcd_puts(0, 3, "POWER to exit");
rb->lcd_puts(0, 4, "RIGHT to stay");
rb->lcd_puts(0, 5, "LEFT to double down");
rb->lcd_puts(0, 6, "REC to save/resume");
rb->lcd_puts(0, 7, "UP to view scores");
rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
rb->lcd_puts(0, 8, str);
#elif (CONFIG_KEYPAD == SANSA_C200_PAD)
rb->lcd_puts(0, 2, "SELECT to start & to hit");
rb->lcd_puts(0, 3, "POWER to exit");
rb->lcd_puts(0, 4, "RIGHT to stay");
rb->lcd_puts(0, 5, "LEFT to double down");
rb->lcd_puts(0, 6, "DOWN to save/resume");
rb->lcd_puts(0, 7, "REC to view scores");
rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
rb->lcd_puts(0, 9, str);
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
rb->lcd_puts(0, 2, "PLAY to start & to");
rb->lcd_puts(0, 3, " hit");
rb->lcd_puts(0, 4, "REC to exit");
rb->lcd_puts(0, 5, "FF to stay");
rb->lcd_puts(0, 6, "REW to double down");
rb->lcd_puts(0, 7, "MODE to save/resume");
rb->lcd_puts(0, 8, "MENU to view scores");
rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
rb->lcd_puts(0, 10, str);
#elif CONFIG_KEYPAD == COWOND2_PAD
rb->lcd_puts(0, 6, "POWER to exit");
rb->lcd_puts(0, 7, "MINUS to double down");
rb->lcd_puts(0, 8, "MENU to view scores");
rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
rb->lcd_puts(0, 10, str);
#endif
#ifdef HAVE_TOUCHSCREEN
rb->lcd_puts(0, 2, "LCD CENTRE to start & to hit");
rb->lcd_puts(0, 3, "LCD BOTTOMLEFT to stay");
rb->lcd_puts(0, 4, "LCD BOTTOMRIGHT to save/resume");
#endif
} else {
rb->snprintf(str, 12, "%s", "High Scores");
rb->lcd_getstringsize(str, &w, &h);
rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str);
/* print high scores */
for(i=0; i<NUM_SCORES; i++) {
rb->snprintf(str, 14, "#%02d: $%d", i+1, bj->highscores[i]);
rb->lcd_puts(0, i+1, str);
}
}
rb->lcd_update();
/* handle menu button presses */
button = rb->button_get(true);
switch(button) {
case BJACK_START: /* start playing */
breakout = true;
break;
case BJACK_QUIT: /* quit program */
if(showscores) {
showscores = 0;
break;
}
return BJ_QUIT;
case BJACK_RESUME:/* resume game */
if(!blackjack_loadgame(bj)) {
rb->splash(HZ*2, "Nothing to resume");
} else {
rb->splash(HZ*2, "Loading...");
breakout = true;
}
break;
case BJACK_SCORES:/* toggle high scores */
showscores = !showscores;
break;
default:
if(rb->default_event_handler_ex(button, blackjack_callback,
(void*) bj) == SYS_USB_CONNECTED)
return BJ_USB;
break;
}
if(breakout) break;
}
return(0);
}
/*****************************************************************************
* blackjack() is the main game subroutine, it returns the final game status.
******************************************************************************/
static int blackjack(struct game_context* bj) {
int button;
unsigned int w, h, temp_var, done = 0, todo = 1;
signed int temp;
bool breakout = false;
bool dbl_down = false;
/* don't resume by default */
bj->resume = false;
/********************
* menu *
********************/
temp_var = blackjack_menu(bj);
if (temp_var == BJ_QUIT || temp_var == BJ_USB)
return temp_var;
/********************
* init *
********************/
blackjack_init(bj);
bj->current_bet=10;
/********************
* play *
********************/
/* check for resumed game */
if(bj->resume) {
bj->resume = false;
redraw_board(bj);
if (bj->split_status == 2) {
todo=2;
player_x = bj->num_player_cards[0] * 10 + 4;
}
else if (bj->split_status == 3) {
player_x = bj->num_player_cards[1] * 10 + LCD_WIDTH/2 + 4;
todo=2;
done=1;
}
}
else {
bj->player_money = 1000;
blackjack_get_bet(bj);
if (bj->current_bet == 0)
return BJ_QUIT;
rb->lcd_clear_display();
deal_init_cards(bj);
blackjack_drawtable(bj);
}
rb->lcd_update();
breakout = false;
while(true){
if(bj->player_total == 21 && bj->num_player_cards[0] == 2) {
bj->is_blackjack = true;
bj->end_hand = true;
finish_game(bj);
}
else if(bj->dealer_cards[1].is_soft_ace && !breakout &&
!bj->asked_insurance) {
temp_var = insurance(bj);
if (bj->dealer_total == 21) {
rb->splash(HZ, "Dealer has blackjack");
bj->player_money += temp_var;
bj->end_hand = true;
breakout = true;
redraw_board(bj);
finish_game(bj);
}
else {
rb->splash(HZ, "Dealer does not have blackjack");
bj->player_money -= temp_var;
breakout = true;
redraw_board(bj);
rb->lcd_update();
}
}
if(bj->split_status == 0 &&
bj->player_cards[0][0].num == bj->player_cards[0][1].num) {
split(bj);
redraw_board(bj);
rb->lcd_update_rect(0, LCD_HEIGHT/2, LCD_WIDTH, LCD_HEIGHT/2);
if (bj->split_status == 2) {
todo++;
player_x = bj->num_player_cards[0] * 10 + 4;
}
}
while(done < todo) {
button = rb->button_get(true);
switch(button) {
case BJACK_HIT:
NEXT_CARD = new_card();
bj->player_total += NEXT_CARD.value;
draw_card(NEXT_CARD, true, player_x, player_y);
bj->num_player_cards[done]++;
if (bj->num_player_cards[done] == MAX_CARDS + 1) {
redraw_board(bj);
rb->lcd_update_rect(0, LCD_HEIGHT/2, LCD_WIDTH,
LCD_HEIGHT/2);
}
else if (bj->num_player_cards[done]>MAX_CARDS || todo > 1) {
rb->lcd_update_rect(player_x, player_y, CARD_WIDTH+2,
CARD_HEIGHT+2);
player_x += 10;
}
else {
rb->lcd_update_rect(player_x, player_y, CARD_WIDTH+2,
CARD_HEIGHT+2);
player_x += CARD_WIDTH + 4;
}
update_total(bj);
break;
case BJACK_STAY:
bj->end_hand = true;
break;
case BJACK_DOUBLEDOWN:
if ((signed int)bj->current_bet * 2 < bj->player_money + 1 &&
bj->num_player_cards[0]==2 && todo==1) {
double_down(bj);
dbl_down = true;
if (bj->player_total < 22) {
bj->end_hand = true;
finish_game(bj);
}
}
else if((signed int)bj->current_bet * 2 > bj->player_money) {
rb->splash(HZ, "Not enough money to double down.");
redraw_board(bj);
rb->lcd_update();
}
break;
case BJACK_RESUME: /* save and end game */
rb->splash(HZ, "Saving game...");
blackjack_savegame(bj);
/* fall through to BJACK_QUIT */
case BJACK_QUIT:
return BJ_END;
}
while (bj->player_total > 21 && !bj->end_hand) {
temp = check_for_aces(bj->player_cards[done],
bj->num_player_cards[done]);
if(temp != -1) {
bj->player_cards[done][temp].is_soft_ace = false;
bj->player_total -= 10;
update_total(bj);
if (dbl_down) {
bj->end_hand = true;
finish_game(bj);
}
}
else
bj->end_hand = true;
}
if (bj->end_hand) {
done++;
if(todo > 1) {
if (done == 2) {
temp = bj->player_total;
bj->player_total = temp_var;
temp_var = temp;
finish_game(bj);
rb->lcd_getstringsize(" Split 1 ", &w, &h);
rb->lcd_putsxy(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
" Split 1 ");
rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
w,h);
bj->current_bet /= 2;
rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
w,h);
rb->sleep(HZ*2);
bj->player_total = temp_var;
finish_game(bj);
rb->lcd_getstringsize(" Split 2 ", &w, &h);
rb->lcd_putsxy(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
" Split 2 ");
rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
w,h);
rb->sleep(HZ*2);
}
else {
bj->end_hand = false;
bj->split_status = 3;
temp_var = bj->player_total;
bj->player_total = bj->player_cards[1][0].value;
update_total(bj);
redraw_board(bj);
player_x += 10;
rb->lcd_update();
}
}
else
finish_game(bj);
}
}
if (bj->player_money < 10) {
rb->sleep(HZ);
return BJ_LOSE;
}
if (bj->end_hand) { /* If hand is over */
if (play_again() != 0) /* User wants to quit */
return BJ_END;
else { /* User keeps playing */
breakout = false;
redraw_board(bj);
if(dbl_down) {
bj->current_bet /= 2;
dbl_down = false;
}
done = 0;
todo = 1;
blackjack_init(bj);
blackjack_get_bet(bj);
if (bj->current_bet == 0)
return BJ_END;
deal_init_cards(bj);
blackjack_drawtable(bj);
rb->lcd_update();
}
}
}
/* Never reached */
return PLUGIN_OK;
}
/*****************************************************************************
* plugin entry point.
******************************************************************************/
enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
{
struct game_context bj;
bool exit = false;
unsigned int position;
char str[19];
(void)parameter;
rb = api;
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
/* load high scores */
blackjack_loadscores(&bj);
rb->lcd_setfont(FONT_SYSFIXED);
while(!exit) {
switch(blackjack(&bj)){
case BJ_LOSE:
rb->splash(HZ, "Not enough money to continue");
/* fall through to BJ_END */
case BJ_END:
if(!bj.resume) {
if((position = blackjack_recordscore(&bj))) {
rb->snprintf(str, 19, "New high score #%d!", position);
rb->splash(HZ*2, str);
}
}
break;
case BJ_USB:
rb->lcd_setfont(FONT_UI);
return PLUGIN_USB_CONNECTED;
case BJ_QUIT:
if(bj.dirty) {
rb->splash(HZ, "Saving high scores...");
blackjack_savescores(&bj);
}
exit = true;
break;
default:
break;
}
}
rb->lcd_setfont(FONT_UI);
return PLUGIN_OK;
}