//Blackjack //final project ECE 476 //Benjamin Hysell, Jeff Hantson, #include <90s8515.h> #include #include #include #include // LCD driver routines #include //Defines #define t1 50 // timeout value for checking button pushes and one minute #define maxkeys 16 // for keypad #define LCDwidth 16 // characters LCD screen //timer 1 constants #define prescale1 1 #define clear_on_match 8 //key pad scan table, key entered and number of keys entered flash unsigned char keytbl[16]={0xd7, 0xee, 0xde, 0xbe, 0xed, 0xdd, 0xbd, 0xeb, 0xdb, 0xbb, 0x7e, 0x7d, 0x7b, 0x77, 0xe7, 0xb7}; unsigned char key, butnum; // states for decting push button -- debouncer enum buttonS {no_push, maybe_push, maybe_no_push, pushed} push_state; enum playing {bank_state, bet_state, user_play_state, user_hit_state, house_play_state, winner_state, game_over} play_state; unsigned char pushed_flag; int digitEntered; // card structure struct Card { char *face; char *suit; }; // define deck struct Deck { struct Card card[52]; int position; }deck1; // card values flash char face[13] = {'A', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K'}; flash char suit[4] = {'H', 'D', 'C', 'S'}; //the subroutines void fillDeck(struct Deck *); // create the deck void shuffle(struct Deck *); // shuffle the deck void deal(struct Deck *); // deal cards to house and user void keypad(void); // read in users bank and bet void start(void); // super state machine for whole program void initialize(void); // all the usual mcu stuff char sum_cards(char, struct Card []); // calculate what each person holds void show_hand(char, struct Card [], char, char, struct Card [], char); // display house/user hand to lcd void user_hit(void); // user's play state...user hits or stays void house_hit(void); // allow the user to see how the deal plays void check_bust(char); // check if house or user busts void check_21(char , char); // check for blackjack //lcd display unsigned char lcd_buffer[17]; // LCD display buffer //random variables needed long bankroll; //users bankroll unsigned long bet_amount; //bet amount unsigned int userCode[5]; //hold users inputed bankroll/bet amount struct Card user_Card[5]; //the users hand struct Card house_Card[5]; //the houses hand unsigned char user_Total; //users total for number of cards unsigned char house_Total; //houses total unsigned char temp_house_Total; //display the house total to start, don't reveal both of houses cards unsigned char user_Hand; //number of cards the user has unsigned char house_Hand; //number of cards house has unsigned long bank_value; //temp value to calc bankroll unsigned long bet_value; //bet amount // timer re-loads unsigned char reload; // timer 0 reload to set 1 mSec unsigned char time1; // task scheduleing timeout counters // keypad flags -- tell the program what state we are in unsigned char bank_flag; // read in users bank unsigned char bet_flag; // read in users bet unsigned char hit_flag; // the user is hitting unsigned char house_hit_flag; // house is hitting unsigned char user_busted_flag; // user busted unsigned char house_busted_flag; // house busted unsigned char user_21_flag; //user hit 21 unsigned char house_21_flag; // house hit 21 unsigned char user_double_down_flag; // user is doubling down //timer 0 overflow ISR controll movement through our program interrupt [TIM0_OVF] void timer0_overflow(void) { //reload to force 1 mSec overflow TCNT0=reload; //Decrement the time if not already zero if (time1>0) --time1; } //********************************************************** //Entry point and task scheduler loop void main(void) { initialize(); //main task scheduler loop -- never exits! while(1) { if (time1==0) { if (bank_flag || bet_flag) // read in bet/users bankroll { keypad(); } else if (hit_flag) // allow the user to hit { user_hit(); } else if (house_hit_flag) // allow the house to play { house_hit(); } else // else enter state machine { start(); } } } } //********************************************************** //keypad -- detect a pusbutton from keypad for users bankroll and bet void keypad(void) { char k; char l; time1 = t1; // reload the timer //get lower nibble DDRA = 0x0f; PORTA = 0xf0; delay_us(5); key = PINA; //get upper nibble DDRA = 0xf0; PORTA = 0x0f; delay_us(5); key = key | PINA; switch(push_state) // debounce keypad { case no_push: if (key != 0xff) { push_state = maybe_push; } break; case maybe_push: if (key != 0xff) { pushed_flag = 1; push_state = pushed; } else { push_state = no_push; } break; case maybe_no_push: if (key != 0xff) { push_state = pushed; } else { push_state = no_push; } break; case pushed: //find matching keycode in keytbl (key != 0xff) && if (pushed_flag == 1) { pushed_flag = 0; //decode button from keypad for (butnum=0; butnum < maxkeys; butnum++) { if (keytbl[butnum]==key) { switch(butnum) { case 10: // case 10 -- user pressed enter if(bank_flag == 1) // calculate how much the user entered { for(k=0; k 21) // print to the screen somene busted { lcd_gotoxy(0,1); lcd_putsf("BUSTED "); user_busted_flag = hit_flag; // figure out who busted by who was playing that minute house_busted_flag = house_hit_flag; // state machine to allow user to read screen and progress with program while (flag) { DDRA = 0x0f; PORTA = 0xf0; delay_us(5); key = PINA; //get upper nibble DDRA = 0xf0; PORTA = 0x0f; delay_us(5); key = key | PINA; switch(push_state) // debounce button pushes { case no_push: if (key != 0xff) // if button 0 or 1 is pushed { push_state = maybe_push; } break; case maybe_push: if (key != 0xff) { pushed_flag = 1; push_state = pushed; } else { push_state = no_push; } break; case maybe_no_push: if (key != 0xff) { push_state = pushed; } else { push_state = no_push; } break; case pushed: if (pushed_flag == 1) { pushed_flag = 0; // hit or stand for (butnum=0; butnum < maxkeys; butnum++) { if (keytbl[butnum]==key) { switch(butnum) { case 10: // once enter is pressed move the game along play_state = winner_state; hit_flag = 0; house_hit_flag = 0; flag = 0; break; } // end switch } // end if } //end for } // end if else { push_state = maybe_no_push; } break; } // end switch statement } // end while } // end if } //********************************************************** //user_hit -- detects if user wants to hit or stand void user_hit(void) { time1 = t1; // reload the timer //get lower nibble DDRA = 0x0f; PORTA = 0xf0; delay_us(5); key = PINA; //get upper nibble DDRA = 0xf0; PORTA = 0x0f; delay_us(5); key = key | PINA; switch(push_state) // debounce button pushes { case no_push: if (key != 0xff) // if button 0 or 1 is pushed { push_state = maybe_push; } break; case maybe_push: if (key != 0xff) { pushed_flag = 1; push_state = pushed; } else { push_state = no_push; } break; case maybe_no_push: if (key != 0xff) { push_state = pushed; } else { push_state = no_push; } break; case pushed: if (pushed_flag == 1) { pushed_flag = 0; // hit or stand for (butnum=0; butnum < maxkeys; butnum++) { if (keytbl[butnum]==key) { switch(butnum) { case 10: // deal an extra card to the user user_Card[user_Hand].suit = deck1.card[deck1.position].suit; user_Card[user_Hand].face = deck1.card[deck1.position].face; user_Hand++; // inc number of cards user has deck1.position++; // inc position in deck user_Total = sum_cards(user_Hand, user_Card); // re-sum users cards show_hand(user_Hand, user_Card, user_Total, house_Hand - 1, house_Card, house_Total); // show the hands check_bust(user_Total); // check for bust if(user_Hand == 5) // only deal 5 cards { hit_flag = 0; if(user_busted_flag) { play_state = winner_state; } else { play_state = house_play_state; } } user_double_down_flag = 0; break; case 11: // user stands hit_flag = 0; play_state = house_play_state; user_double_down_flag = 0; break; case 13: // double down state deal one extra card and move to next state if(user_double_down_flag) { user_double_down_flag = 0; user_Card[user_Hand].suit = deck1.card[deck1.position].suit; user_Card[user_Hand].face = deck1.card[deck1.position].face; user_Hand++; deck1.position++; user_Total = sum_cards(user_Hand, user_Card); show_hand(user_Hand, user_Card, user_Total, house_Hand, house_Card, house_Total); bet_amount = bet_amount + bet_amount; // double bet amount play_state = house_play_state; check_bust(user_Total); hit_flag = 0; } break; } // end switch } // end if } //end for } // end if else { push_state = maybe_no_push; } break; } // end switch statement } // end user hit //********************************************************** //house_hit -- allow the house to play till he busts or wins void house_hit(void) { time1 = t1; // reload the timer DDRA = 0x0f; PORTA = 0xf0; delay_us(5); key = PINA; //get upper nibble DDRA = 0xf0; PORTA = 0x0f; delay_us(5); key = key | PINA; switch(push_state) // debounce button pushes { case no_push: if (key != 0xff) // if button 0 or 1 is pushed { push_state = maybe_push; } break; case maybe_push: if (key != 0xff) { pushed_flag = 1; push_state = pushed; } else { push_state = no_push; } break; case maybe_no_push: if (key != 0xff) { push_state = pushed; } else { push_state = no_push; } break; case pushed: if (pushed_flag == 1) { pushed_flag = 0; // hit or stand for (butnum=0; butnum < maxkeys; butnum++) { if (keytbl[butnum]==key) { switch(butnum) { case 10: // allow the user to see the cards the dealer gets, progress the program if(house_Total < 17) { house_Card[house_Hand].suit = deck1.card[deck1.position].suit; house_Card[house_Hand].face = deck1.card[deck1.position].face; house_Hand++; deck1.position++; house_Total = sum_cards(house_Hand, house_Card); show_hand(house_Hand, house_Card, house_Total, user_Hand, user_Card, user_Total); check_bust(house_Total); if(house_Hand == 5) { house_hit_flag = 0; play_state = winner_state; } } else { house_hit_flag = 0; play_state = winner_state; } break; } // end switch } // end if } //end for } // end if else { push_state = maybe_no_push; } break; } // end switch statement } // end user hit //********************************************************** //check_21 -- check if user or deal has 21 set the proper flags if that is the case void check_21(char user_Total, char house_Total) { if(user_Total == 21) { user_21_flag = 1; } else if(house_Total == 21) { house_21_flag = 1; } } //********************************************************** //Start -- start the program void start(void) { int flag; time1 = t1; flag = 0; switch(play_state) { case bank_state: // user enters bankroll lcd_gotoxy(0,0); lcd_putsf("Bank=$"); bank_flag = 1; break; case bet_state: // user bets lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("Bank=$"); sprintf(lcd_buffer,"%-i", bankroll); lcd_puts(lcd_buffer); lcd_gotoxy(0,1); lcd_putsf("Bet =$"); bet_flag = 1; break; case user_play_state: // user hits lcd_clear(); if(deck1.position > 42) // reshuffle the deck if less than 10 cards { lcd_gotoxy(0,0); // goto position to print digit lcd_putsf("Shuffling"); // print it out // shuffle deck shuffle(&deck1); } lcd_clear(); deal(&deck1); // deal using current deck user_Total = sum_cards(user_Hand, user_Card); // sum house and users cards house_Total = sum_cards(house_Hand - 1, house_Card); show_hand(user_Hand, user_Card, user_Total, house_Hand - 1, house_Card, house_Total); // show eachs hands, not showing the houses down card temp_house_Total = sum_cards(house_Hand, house_Card); check_21(user_Total, temp_house_Total); // check for 21 by user or house if((user_21_flag == 1) || (house_21_flag == 1)) // if either has 21 end current hand { play_state = winner_state; } else // else allow the user to hit and double down { play_state = user_hit_state; user_double_down_flag = 1; } break; case user_hit_state: // let the user hit hit_flag = 1; break; case house_play_state: // show the houses cards and allow the user to see the rest of the cards the house gets house_Total = sum_cards(house_Hand, house_Card); show_hand(house_Hand, house_Card, house_Total, user_Hand, user_Card, user_Total); house_hit_flag = 1; play_state = winner_state; break; case game_over: // once the user has broken the bank or fallen too far into debt end the program lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("Game Over"); // print it out lcd_gotoxy(0,1); lcd_putsf("Press Reset"); // print it out break; case winner_state: // calculate winner and looser and adjust users bank lcd_gotoxy(0,0); if (user_21_flag) { bankroll = bankroll + 1.5*bet_amount; lcd_gotoxy(0,1); lcd_putsf("Blackjack "); } else if (house_21_flag) { bankroll = bankroll - bet_amount; show_hand(user_Hand, user_Card, user_Total, house_Hand, house_Card, temp_house_Total); lcd_gotoxy(0,0); lcd_putsf("Blackjack "); } else if (house_busted_flag) { bankroll = bankroll + bet_amount; lcd_putsf("YOU WIN!!! "); } else if (user_busted_flag) { bankroll = bankroll - bet_amount; lcd_putsf("YOU LOSE!!! "); } else if (user_Total > house_Total) { bankroll = bankroll + bet_amount; lcd_putsf("YOU WIN!!! "); } else if (house_Total > user_Total) { bankroll = bankroll - bet_amount; lcd_putsf("YOU LOSE!!! "); } else // no one won...push { bankroll = bankroll; lcd_gotoxy(10,1); lcd_putsf("Push"); } flag = 1; while (flag) // wait for user to see results before resetting the system { DDRA = 0x0f; PORTA = 0xf0; delay_us(5); key = PINA; //get upper nibble DDRA = 0xf0; PORTA = 0x0f; delay_us(5); key = key | PINA; switch(push_state) // debounce button pushes { case no_push: if (key != 0xff) // if button 0 or 1 is pushed { push_state = maybe_push; } break; case maybe_push: if (key != 0xff) { pushed_flag = 1; push_state = pushed; } else { push_state = no_push; } break; case maybe_no_push: if (key != 0xff) { push_state = pushed; } else { push_state = no_push; } break; case pushed: if (pushed_flag == 1) { pushed_flag = 0; // hit or stand for (butnum=0; butnum < maxkeys; butnum++) { if (keytbl[butnum]==key) { switch(butnum) { case 10: // see if the user won too much or lost it all or reset the system if ((bankroll > 25000) || (bankroll < -25000)) { play_state = game_over; } else { play_state = bet_state; } house_busted_flag = 0; // reset the system for the next hand user_busted_flag = 0; user_Total = 0; house_Total = 0; user_Hand = 0; house_Hand = 0; flag = 0; house_21_flag = 0; user_21_flag = 0; bet_amount = 0; user_double_down_flag = 0; break; } // end switch } // end if } //end for } // end if else { push_state = maybe_no_push; } break; } // end switch statement } // end while break; } }// end start //********************************************************** //sum_cards -- calculate point values for hand passed in char sum_cards(char Number, struct Card Hand[]) { int i; char Total = 0; char flag_ace = 0; // flag if we have an ace, make ace 1 or 11 depending on hand for(i=0; i < Number; i++) // calculate value of hand { switch(Hand[i]->face) { case 'A': flag_ace++; break; case '2': Total = Total + 2; break; case '3': Total = Total + 3; break; case '4': Total = Total + 4; break; case '5': Total = Total + 5; break; case '6': Total = Total + 6; break; case '7': Total = Total + 7; break; case '8': Total = Total + 8; break; case '9': Total = Total + 9; break; default: Total = Total + 10; break; } } // set ace to be one or eleven depending on sum of cards in hand if(flag_ace) { for(i = 0; i < flag_ace; i++) { if(Total < 11) { Total = Total + 11; } else { Total++; } } } return Total; } //********************************************************** //show_hand -- display cards to screen void show_hand(char Number1, struct Card Card1[], char Total1, char Number2, struct Card Card2[], char Total2) { int i; int j; lcd_clear(); lcd_gotoxy(0,0); // goto position to print digit for(i=0; i < Number1; i++) //show faces and suits { sprintf(lcd_buffer,"%-c", Card1[i]->face); lcd_puts(lcd_buffer); sprintf(lcd_buffer,"%-c", Card1[i]->suit); lcd_puts(lcd_buffer); } lcd_gotoxy(14,0); sprintf(lcd_buffer,"%-i", Total1); // print out total lcd_puts(lcd_buffer); lcd_gotoxy(0,1); for(j=0; j < Number2; j++) // do the same for the second hand { sprintf(lcd_buffer,"%-c", Card2[j]->face); lcd_puts(lcd_buffer); sprintf(lcd_buffer,"%-c", Card2[j]->suit); lcd_puts(lcd_buffer); } lcd_gotoxy(14,1); sprintf(lcd_buffer,"%-i", Total2); lcd_puts(lcd_buffer); } //********************************************************** //deal -- the inital deal of cards to user and house void deal(struct Deck *wDeck) { user_Card[user_Hand].suit = wDeck->card[wDeck->position].suit; user_Card[user_Hand].face = wDeck->card[wDeck->position].face; wDeck->position++; user_Hand++; house_Card[house_Hand].suit = wDeck->card[wDeck->position].suit; house_Card[house_Hand].face = wDeck->card[wDeck->position].face; wDeck->position++; house_Hand++; user_Card[user_Hand].suit = wDeck->card[wDeck->position].suit; user_Card[user_Hand].face = wDeck->card[wDeck->position].face; wDeck->position++; user_Hand++; house_Card[house_Hand].suit = wDeck->card[wDeck->position].suit; house_Card[house_Hand].face = wDeck->card[wDeck->position].face; wDeck->position++; house_Hand++; } //********************************************************** //fillDeck -- create the deck of cards void fillDeck(struct Deck *wDeck) { int i = 0; for (i; i < 52; i++) { wDeck->card[i].face = face[i % 13]; wDeck->card[i].suit = suit[i / 13]; }// end for i } // end fillDeck //********************************************************** //shuffle -- shuffle the deck void shuffle(struct Deck *wDeck) { int i = 0; int j; struct Card temp; srand(TCNT1); for (i; i <= 51; i++) { // random value j = rand() % 52; //swap cards temp.suit = wDeck->card[i].suit; temp.face = wDeck->card[i].face; wDeck->card[i].suit = wDeck->card[j].suit; wDeck->card[i].face = wDeck->card[j].face; wDeck->card[j].suit = temp.suit; wDeck->card[j].face = temp.face; } // end for i wDeck->position = 0; // reset position varible } // end shuffle //********************************************************** //Set it all up void initialize(void) { //set up the ports DDRD=0x00; DDRC=0xff; //set up timer 0 //62.5 x (64x.25) microSec = 1.0 mSec, so prescale 64, and count 62 times. reload=256-125; //value for 1 Msec TCNT0=reload; //preload timer 1 so that is interrupts after 1 mSec. TCCR0=3; //prescalar to 64 TIMSK=2; //turn on timer 0 overflow ISR //init the task timers time1=t1; //set up timer 1 TCCR1B = 1; //disable timer 1 until song starts TCNT1 = 0; //and zero the timer TCCR1B = prescale1; lcd_init(LCDwidth); //initialize the display lcd_clear(); //clear the display digitEntered = 0; bankroll = 0; bank_value = 0; //crank up the ISRs set up lcd on port B #asm sei .equ __lcd_port=0x15 #endasm // create the deck play_state = bank_state; fillDeck(&deck1); user_busted_flag = 0; house_busted_flag = 0; shuffle(&deck1); }