Created
November 7, 2010 04:28
-
-
Save DeMarko/665962 to your computer and use it in GitHub Desktop.
a blackjack program I wrote in C++, it's kind of awful
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* written by Dannel Jurado | |
* version: 03052009 | |
* | |
* changelog 03052009: Welcome screen behavior, instead of just welcome screen | |
* changelog 03032009: submitted version | |
*/ | |
#include <iostream> | |
#include <iomanip> | |
#include <string> | |
#include <vector> | |
#include <algorithm> | |
#include <ctime> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/select.h> | |
#include <termios.h> | |
using namespace std; | |
#define DECKSIZE 52 | |
#define MAXHANDSIZE 5 | |
#define RED 31 | |
#define GREEN 32 | |
#define CYAN 36 | |
string suits[] = {"Spades", "Hearts", "Clubs", "Diamonds"}; | |
void reset_terminal_mode(); | |
void set_conio_terminal_mode(); | |
int kbhit(); | |
int getch(); | |
/* BEGIN DECK UTILITIES */ | |
void deck_shuffle(vector <int> &deck) | |
{ | |
srand (time(NULL)); // seed the random number with the system clock | |
random_shuffle (deck.begin(), deck.end()); | |
} | |
void regenShuffledDeck (vector <int> &deck) | |
{ | |
if(!deck.empty()) | |
deck.clear(); | |
for (int i = 0; i < DECKSIZE; i++) | |
deck.push_back (i); | |
deck_shuffle(deck); | |
} | |
/* END DECK UTILITIES */ | |
/* BEGIN CARD UTILITIES */ | |
/** | |
* return value of blackjack card, face cards are 10, aces are returned as 1 | |
* (an ace's value changes the overall value of the hand in the total function) | |
*/ | |
int blackjackValueOf (int card) | |
{ | |
int val = card/4 + 1; | |
switch (val){ | |
case 1: //ACE (deal with it while summing totals) | |
return 1; | |
case 11: //FACE | |
case 12: | |
case 13: | |
return 10; | |
default: | |
return val; | |
} | |
} | |
/** | |
* transforms the values 0 - 51 into a human readable card | |
*/ | |
void printCard (int card) | |
{ | |
int val = card/4 + 1; | |
switch(val){ | |
case 1: | |
printf("Ace"); | |
break; | |
case 11: | |
printf("Jack"); | |
break; | |
case 12: | |
printf("Queen"); | |
break; | |
case 13: | |
printf("King"); | |
break; | |
default: | |
printf("%d", val); | |
} | |
string suit = suits[card%4]; | |
printf(" of %s\n", suit.c_str()); | |
} | |
/* END CARD UTILITIES */ | |
/* BEGIN HAND HELPER FUNCTIONS */ | |
/** | |
* blank out hand | |
*/ | |
void emptyHand(int hand[]) | |
{ | |
for(int i = 0; i < MAXHANDSIZE; i++) | |
hand[i] = -1; | |
} | |
/** | |
* sum up values of cards in hand, count aces high or low depending on value | |
* | |
* return correct total of blackjack hand | |
*/ | |
int total(int hand[]) | |
{ | |
int total = 0, i; | |
bool aceseen = false; | |
for(i = 0; (i < MAXHANDSIZE) && (hand[i] != -1); i++) | |
total += blackjackValueOf(hand[i]); | |
for(i = 0; i < MAXHANDSIZE; i++) { | |
if(hand[i] == 0 || hand[i] == 1 || hand[i] == 2 || hand[i] == 3) // values 0 - 3 are ACES | |
aceseen = true; | |
} | |
if((total < 12) && aceseen) | |
total += 10; //ACE IS HIGH | |
return total; | |
} | |
/** | |
* print cards in hand | |
*/ | |
void printHand(int hand[], string who) | |
{ | |
printf("\n%s's Hand: %d\n", who.c_str(), total(hand)); | |
for(int i = 0; (i < MAXHANDSIZE) && (hand[i] != -1); i++) | |
printCard(hand[i]); | |
} | |
/** | |
* determine whether hand (whose value must be 17) is a soft 17 | |
* | |
* return true if it is, false if not | |
*/ | |
bool soft17(int hand[]) | |
{ | |
for(int i = 0; i < MAXHANDSIZE && (hand[i] != -1); i++) { | |
if(blackjackValueOf(hand[i]) == 1) | |
return true; | |
} | |
return false; | |
} | |
/* END HAND HELPER FUNCTIONS */ | |
/** | |
* Waits on non-buffered keyboard input, validates against two possibile values | |
* returns whichever was entered, ignores invalid input | |
* | |
* IMPORTANT: | |
* This is done as a way of "cleaning up" the interface | |
* meaning, I thought it was messy that the player had to press ENTER after | |
* every command. It involves less keystrokes and less hand movements so I | |
* think it's a win. | |
*/ | |
char validateInput(string request, char val1, char val2) | |
{ | |
char inp; | |
do { //INPUT VALIDATION LOOP | |
printf(request.c_str()); | |
fflush(stdout); | |
set_conio_terminal_mode(); | |
while(!kbhit()){ | |
//wait for keyboardhit | |
} | |
inp = getch(); //gets around annoying cin bug | |
reset_terminal_mode(); | |
inp = tolower(inp); | |
} while((inp != val1) && (inp != val2)); | |
printf("\n"); | |
return inp; | |
} | |
void printBanner() | |
{ | |
printf("######################################\n"); | |
printf("######################################\n"); | |
printf("## Welcome to \x1b[1mThe Riviera\x1b[0m ##\n"); | |
printf("## ##\n"); | |
printf("## The only casino for the ##\n"); | |
printf("## ambitious UNIX/C hacker! ##\n"); | |
printf("## ##\n"); | |
printf("## \x1b[1;31m \x1b[0m _ \x1b[1;31m _ _ \x1b[0m __ ##\n"); | |
printf("## \x1b[1;31m /\\ \x1b[0m _/ \\_ \x1b[1;31m / \\/ \\\x1b[0m / \\ ##\n"); | |
printf("## \x1b[1;31m/ \\\x1b[0m / \\_/ \\ \x1b[1;31m \\ /\x1b[0m | | ##\n"); | |
printf("## \x1b[1;31m\\ /\x1b[0m \\_/^\\_/ \x1b[1;31m \\ / \x1b[0m \\_/\\_/ ##\n"); | |
printf("## \x1b[1;31m \\/ \x1b[0m /_\\ \x1b[1;31m \\/ \x1b[0m /__\\ ##\n"); | |
printf("## ##\n"); | |
printf("## BLACKJACK! ##\n"); | |
printf("## ##\n"); | |
printf("## House Rules: ##\n"); | |
printf("## - Dealer hits on soft 17 ##\n"); | |
printf("## - 1 deck ##\n"); | |
printf("## - deck shuffled every 6 rounds ##\n"); | |
printf("## ##\n"); | |
printf("######################################\n"); | |
printf("######################################\n"); | |
int play = validateInput("\nShall we play a game? (press ENTER to play, 'q' to quit): ", '\r', 'q'); | |
if(play == 'q') | |
exit(EXIT_SUCCESS); | |
} | |
void printSummary(double ratio, int playerwins, int dealerwins, int numrounds) | |
{ | |
printf("\n######################################\n"); | |
printf("# PLAY SUMMARY: #\n"); | |
printf("# #\n"); | |
printf("# Player Win Ratio: \x1b[1;%dm%.2f\x1b[0m #\n", (playerwins > dealerwins) ? GREEN : RED, ratio); | |
printf("# # of Player Wins: %4d #\n", playerwins); | |
printf("# # of Dealer Wins: %4d #\n", dealerwins); | |
printf("# Total # of Rounds: %4d #\n", numrounds); | |
printf("# #\n"); | |
printf("######################################\n"); | |
} | |
int main () | |
{ | |
int numrounds = 0; | |
int playerwins = 0, dealerwins = 0; | |
int playertotal = 0, dealertotal = 0; | |
int n; | |
char quit = '\n', hs; | |
int playerhand[MAXHANDSIZE]; | |
int dealerhand[MAXHANDSIZE]; | |
printBanner(); | |
// create the deck | |
vector <int> deck; | |
do { | |
if((numrounds % 6) == 0) { // gone through 6 rounds | |
regenShuffledDeck(deck); | |
printf("\n\x1b[%dmooo SHUFFLING DECK ooo\x1b[0m\n", CYAN); | |
} | |
//BEGIN ROUND | |
emptyHand(playerhand); emptyHand(dealerhand); | |
numrounds++; | |
printf("\nRound #%d\n", numrounds); | |
//deal initial hands (two cards each, only reveal one of the dealer's cards) | |
n = 0; | |
playerhand[n] = deck.back(); deck.pop_back(); | |
dealerhand[n] = deck.back(); deck.pop_back(); | |
n++; | |
playerhand[n] = deck.back(); deck.pop_back(); | |
dealerhand[n] = deck.back(); deck.pop_back(); | |
n++; | |
// the Dealer's hand is always partially hidden at the beginning of the | |
// round. | |
printf("\nDealer's Hand:\nHIDDEN\n"); | |
printCard(dealerhand[1]); | |
printHand(playerhand, "Player"); | |
//instant win conditions | |
if(total(playerhand) == 21){ | |
if(total(dealerhand) == 21) | |
printf("\n\x1b[%dm=== PUSH! ===\x1b[0m\n", CYAN); | |
else { | |
playerwins++; | |
printf("\n\x1b[%dm=== BLACKJACK ===\x1b[0m\n", GREEN); | |
} | |
goto win; | |
} | |
// player decides whether he wants to (h)it or (s)tand (may happen up to 5 times) | |
for( ; n < MAXHANDSIZE; n++) { | |
int cur; | |
if((cur = total(playerhand)) == 21) { | |
playerwins++; | |
printf("\n\x1b[%dm=== PLAYER WINS ===\x1b[0m\n", GREEN); | |
goto win; | |
} else if(cur > 21) { | |
dealerwins++; | |
printf("\n\x1b[%dm=== PLAYER BUST ===\n=== DEALER WINS ===\x1b[0m\n", RED); | |
goto win; | |
} | |
if((hs = validateInput("\n(h)it or (s)tand? ", 'h', 's')) == 's') | |
break; | |
printf("\n"); | |
printf("\nplayer hits\n"); | |
playerhand[n] = deck.back(); deck.pop_back(); | |
printHand(playerhand, "Player"); | |
} | |
printf("\nplayer stand\n"); | |
if(total(playerhand) > 21) { | |
dealerwins++; | |
printf("\n\x1b[%dm=== PLAYER BUST ===\n=== DEALER WINS ===\x1b[0m\n", RED); | |
goto win; | |
} | |
//after this point, player total cannot change | |
playertotal = total(playerhand); | |
// when player is done, dealer goes, hits on soft 17 | |
// goes until loses, wins or reaches 5 cards | |
// dealer's hand is revealed | |
dealertotal = total(dealerhand); | |
printHand(dealerhand, "Dealer"); | |
if(dealertotal == 21) { | |
if(playertotal == dealertotal) { | |
printf("\n\x1b[%dm=== PUSH! ===\x1b[0m\n", CYAN); | |
} else { | |
dealerwins++; | |
printf("\n\x1b[%dm=== DEALER WINS ===\x1b[0m\n", RED); | |
} | |
goto win; | |
} else if(dealertotal >= 17) { | |
if((dealertotal > 17) || (dealertotal == 17 && !soft17(dealerhand))) | |
printf("\ndealer stands\n"); | |
goto finaleval; | |
} else { | |
for(n = 2; n < MAXHANDSIZE && dealertotal < 18; n++) { | |
dealerhand[n] = deck.back(); deck.pop_back(); //hit | |
printf("\ndealer hits\n"); | |
dealertotal = total(dealerhand); | |
printHand(dealerhand, "Dealer"); | |
if(dealertotal == 21) { | |
if(playertotal == total(dealerhand)) { | |
printf("\n\x1b[%dm=== PUSH! ===\x1b[0m\n", CYAN); | |
} else { | |
dealerwins++; | |
printf("\n\x1b[%dm=== DEALER WINS ===\x1b[0m\n", RED); | |
} | |
goto win; | |
} else if(dealertotal > 21) { | |
playerwins++; | |
printf("\n\x1b[%dm=== DEALER BUST ===\n=== PLAYER WINS ===\x1b[0m\n", GREEN); | |
goto win; | |
} else if(dealertotal == 17) { | |
if(!soft17(dealerhand)) { | |
printf("\ndealer stands\n"); | |
break; //DEALER STANDS ON HARD 17 | |
} | |
} | |
} | |
} | |
// both player and dealer have gone, check to see who wins | |
finaleval: | |
dealertotal = total(dealerhand); | |
if((playertotal > dealertotal) && (playertotal <= 21)) { | |
playerwins++; | |
printf("\n\x1b[%dm=== PLAYER WINS ===\x1b[0m\n", GREEN); | |
} else if(dealertotal == playertotal) { | |
printf("\n\x1b[%dm=== PUSH! ===\x1b[0m\n", CYAN); | |
}else if(dealertotal > playertotal && dealertotal <= 21){ | |
dealerwins++; | |
printf("\n\x1b[%dm=== DEALER WINS ===\x1b[0m\n", RED); | |
} | |
win: //SOMEONE WON | |
printHand(dealerhand, "Dealer"); | |
printHand(playerhand, "Player"); | |
double ratio = (double)playerwins / numrounds; | |
printSummary(ratio, playerwins, dealerwins, numrounds); | |
quit = validateInput("\nPress ENTER to play again, 'q' to quit. ", '\r', 'q'); | |
} while(quit != 'q'); | |
printf("\n"); | |
return EXIT_SUCCESS; | |
} | |
/*########################################################################## | |
I DID NOT WRITE THE FOLLOWING CODE | |
It was written by user Alnitak on the Stack Overflow forums/Q&A | |
(http://stackoverflow.com/questions/448944/c-non-blocking-keyboard-input) | |
It essentially emulates the kbhit() and getch() functions from the DOS | |
<conio.h> library. The other solutions would have involved using the one | |
of the curses libraries. | |
##########################################################################*/ | |
struct termios orig_termios; | |
void reset_terminal_mode() | |
{ | |
tcsetattr(0, TCSANOW, &orig_termios); | |
} | |
void set_conio_terminal_mode() | |
{ | |
struct termios new_termios; | |
/* take two copies - one for now, one for later */ | |
tcgetattr(0, &orig_termios); | |
memcpy(&new_termios, &orig_termios, sizeof(new_termios)); | |
/* register cleanup handler, and set the new terminal mode */ | |
atexit(reset_terminal_mode); | |
cfmakeraw(&new_termios); | |
tcsetattr(0, TCSANOW, &new_termios); | |
} | |
int kbhit() | |
{ | |
struct timeval tv = { 0L, 0L }; | |
fd_set fds; | |
FD_SET(0, &fds); | |
return select(1, &fds, NULL, NULL, &tv); | |
} | |
int getch() | |
{ | |
int r; | |
unsigned char c; | |
if ((r = read(0, &c, sizeof(c))) < 0) { | |
return r; | |
} else { | |
return c; | |
} | |
} | |
/*######################################################################### | |
END OF Altinak code | |
##########################################################################*/ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment