Skip to content

Instantly share code, notes, and snippets.

@DeMarko
Created November 7, 2010 04:28
Show Gist options
  • Save DeMarko/665962 to your computer and use it in GitHub Desktop.
Save DeMarko/665962 to your computer and use it in GitHub Desktop.
a blackjack program I wrote in C++, it's kind of awful
/**
* 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