[comp.sources.games] v06i044: bj1 - blackjack game

games@tekred.CNA.TEK.COM (04/06/89)

Submitted-by: srt@aerospace.aero.org
Posting-number: Volume 6, Issue 44
Archive-name: bj1

	[Once again, two implementations arrive within days of each other.
	 They both have their advantages, so I'm posting both of them.  This
	 one has a cute curses interface that draws little cards and prompts
	 you with the proper move when it thinks you've made a bad decision.
	 The second version has configuration files for the rules specific to
	 several Las Vegas casinos and also has a makefile for Unix and MS-DOS. -br]

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 1)."
# Contents:  README Makefile blackjack.c
# Wrapped by billr@saab on Wed Apr  5 17:07:10 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(606 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XMuch as I hate to write a Blackjack program, there don't seem to be any
Xreally good ones available.  Hence this program.
X
XThis version allows:
X
X(1) Hit, Stand, Double Down, Splitting
X(2) Advice (Basic Strategy)
X
XAs distributed, this program plays a 2 deck game using Las Vegas 
Xdowntown rules (hit soft 17).  These parameters can be easily 
Xmodified.
X
XYou can hit (h), stand, (space), double down (d), or split (s).
X
XThere is currently no differential betting or count.
X
XThe program will beep and advise you of incorrect plays according
Xto the basic blackjack strategy.
X
XScott R. Turner (srt@cs.ucla.edu).
END_OF_FILE
if test 606 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(130 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# Makefile for Blackjack
X
Xbj: blackjack.c
X	cc -O -o bj blackjack.c -lcurses -ltermcap
X
Xinstall: bj
X	strip bj; mv bj /usr/games/bj
END_OF_FILE
if test 130 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'blackjack.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'blackjack.c'\"
else
echo shar: Extracting \"'blackjack.c'\" \(14933 characters\)
sed "s/^X//" >'blackjack.c' <<'END_OF_FILE'
X/*
X *  Blackjack
X *  Copyright (C) 1988 Scott R. Turner (srt@cs.ucla.edu).
X *
X * ----------------------------------------------------------------------
X *                 LICENSE AGREEMENT
X *
X * You may copy and distribute copies or portions of this program
X * as you receive it, in any medium, provided that you conspicuously and
X * appropriately publish on each copy a valid copyright notice "Copyright
X * (C) 1988 Scott R. Turner" (or with whatever year is appropriate) and keep 
X * intact the notices on all files that refer to this License Agreement.
X * You may not charge a fee for distribution or copying of this
X * program or any program which contains this program in whole or part.
X * ----------------------------------------------------------------------
X *  
X *  Author's Notes
X *  
X *  Much as I hate to write a Blackjack program, there don't seem to be any
X *  really good ones available.  Hence this program.
X *
X *  This version allows:
X *
X *  (1) Hit, Stand, Double Down, Splitting
X *  (2) Advice (Basic Strategy)
X *
X *  As distributed, this program plays a 2 deck game using Las Vegas 
X *  downtown rules (hit soft 17).  These parameters can be easily 
X *  modified.
X *
X *  You can hit (h), stand, (space), double down (d), or split (s).
X *
X *  There is currently no differential betting or count.
X *
X *  The program will beep and advise you of incorrect plays according
X *  to the basic blackjack strategy.
X *
X *  Program Notes
X *
X *  To change the dealer rules, see the play_dealer routine.
X *
X *  To change the # of decks (to a maximum of 4), change the value
X *  of numcards in main to the appropriate number.  To play more than
X *  4 decks you'll also have to change some declarations.
X *
X *  Cards are represented as integers from 1-52.  N mod 13 gives the
X *  rank of the card.  N mod 4 gives the suit.
X *
X *  Cards are shuffled by exchange.  The number of exchanges is set 
X *  in NUMEXCHANGE.  Currently 2000.
X *
X *  Note various timing "delay"s in the code; these can be modified to 
X *  suit your own machine.  LONGSLEEP and SHORTSLEEP for Unix-y.
X * 
X *  Correct_play encapsulates the "basic strategy" and determines whether
X *  the player's action was correct.
X */
X#include <ctype.h>
X#include <stdio.h>
X#include <curses.h>
X
X#define NUMEXCHANGE 2000 
X#define MESSAGES 7
X#define STATUS 8
X#define DEALER 3
X#define DEALERLINE 2
X#define PLINE1 10
X#define PLINE2 18
X#define LONGSLEEP 20000
X#define SHORTSLEEP 5000
X
Xint cards[209];  /* Maximum # of cards in 4 decks.  */
Xint numcards;	 /* Number of cards (decks * 52)    */
Xint top;  	 /* Top card in the deck.           */
Xint player[2][21];	 /* 21 is the maximum # of cards in a hand. */
Xint dealer[21];
Xint pnum[2],
X    dnum;	 /* Number actually in the hand. */
Xint ptot[2], dtot;	 /* Totals (?) */
Xint bet[2];
Xint money;
Xint ddown[2];
Xint stand[2];
Xint split;	 /* Split flag. */
Xint quit;
Xchar suitc[]={0,'C','D','H','S'};	/*  Suits */
X
X/* Session Statistics */
X
Xint numhands, numbust, numbj, nummp;
X
X/*
X *  init_hand initializes all the global variables
X *  pertinent to the beginning of a new hand.
X *
X */
X 
Xvoid init_hand()
X{
X	pnum[0] = pnum[1] = dnum = 0;
X	ptot[0] = ptot[1] = dtot = 0;
X	bet[0] = bet[1] = 2;
X	ddown[0] = ddown[1] = 0;
X	stand[0] = stand[1] = 0;
X	split = 0;
X};
X
X/*
X *  Shuffle initializes the deck and shuffles it up.
X *
X */
X
Xvoid shuffle()
X{
X    int i,j,tmp;
X    
X    for(i=1;i<=numcards;i++)
X        cards[i] = i;
X    top = 1;
X	
X    /*
X     *  Exchange.
X     *
X     */
X    for(i=0;i<=NUMEXCHANGE;i++) {
X#ifdef TURBOC      
X        j = random(numcards)+1;
X#else
X	j = (random()%numcards)+1;
X#endif	
X	tmp = cards[j];
X	cards[j] = cards[1];
X	cards[1] = tmp;
X    };
X    
X};
X			
X/*
X *  Rank returns the rank of a card.
X *
X */
X 
Xint rank(i)
X    int i;
X{	
X    return((i % 13) + 1);
X};
X
X/*
X *  value returns the value of a hand closest
X *  to 21.
X *
X */
X 
Xint value(h,s)
X    int *h,s;
X{
X    int tot, aces, i, r;
X    
X    tot = aces = 0;
X    
X    for(i=0;i<s;i++) {
X        r = rank(h[i]);
X	if (r == 1) aces++;
X	if (r > 10) r = 10;
X	tot += r;
X    };
X    
X    while (tot < 12 && aces) {
X        aces--;
X	tot += 10;
X    };
X    
X    return(tot);
X};
X
X/*
X *  Bust is similar to value, except it returns 0/1 for bust.
X *
X */
X 
Xint bust(h,s)
X    int *h,s;
X{
X    int tot, i, r;
X    
X    tot = 0;
X    
X    for(i=0;i<s;i++) {
X        r = rank(h[i]);
X	if (r > 10) r = 10;
X	tot += r;
X    };
X
X    if (tot > 21) return(1);
X    else return(0);
X    
X};
X
X/*
X *  My front end to getc.
X *
X */
X 
Xchar mgetc(allowed)
X    char *allowed;
X{
X    char c;
X
X    do {
X      c = getch();
X      if (islower(c)) c = toupper(c);
X      if (c == 'Q') {
X         clear();
X         move(10,0);
X	 printw("Session Statistics");
X	 move(12,0);
X	 printw("   Hands played: %d.",numhands-1);
X	 move(13,0);
X	 printw("   Hands misplayed: %d.", nummp);
X	 move(14,0);
X	 printw("   Hands busted: %d.",numbust);
X	 move(15,0);
X	 printw("   Blackjacks: %d.",numbj);
X	 move(16,0);
X	 printw("   Winnings: $%d.",money);
X	 move(20,0);
X	 refresh();
X         nocrmode();
X	 /**
X	 quit = 1;
X	 break;
X	 **/
X      	 exit(1);
X      	};
X    } while (!strchr(allowed,c));
X    return(c);
X    
X};
X
X/*
X *  determine_results figures out what happened with 
X *  the bets.
X *
X */
X 
Xvoid determine_results()
X{
X    int i;
X    
X    for(i=0;i<=split;i++) {
X	move(MESSAGES,0);
X	clrtoeol();
X        /*  First, if you bust, you lose.  */
X        if (bust(player[i],pnum[i])) {
X	    printw("Bust!  You lose $%d.",bet[i]);
X	    money -= bet[i];
X	} else if (bust(dealer,dnum)) {
X	    printw("Dealer busts.  You win $%d.",bet[i]);
X	    money += bet[i];
X	} else if (value(dealer,dnum) == value(player[i],pnum[i])) {
X            printw("Push.");	
X	} else if (value(dealer,dnum) > value(player[i],pnum[i])) {
X	    printw("Dealer has %d.  You lose $%d.",value(dealer,dnum),bet[i]);
X	    money -= bet[i];
X	} else {
X	    printw("Dealer had %d.  You win $%d.",value(dealer,dnum),bet[i]);
X	    money += bet[i];
X	};
X	move(STATUS,0);
X	clrtoeol();
X	printw("Stake $%d",money);
X	refresh();
X#ifdef TURBOC	
X	delay(1500);
X#else
X	usleep(LONGSLEEP);
X#endif
X    };
X};
X
X/*
X *  display_card prints the card on the screen.
X *
X */
X 
Xvoid display_card(hand,num,line)
X    int *hand, num, line;
X{
X    int r,suit,start,tot;
X    
X    r = (hand[num-1] % 13) + 1;
X    if (r<1 || r>13) {
X    	move(MESSAGES,0);
X	printw("Weird value: %d.\n",r);
X	refresh();
X	mgetc(" ");
X    };
X    suit = (hand[num-1] - 1 / 13)%4;
X    start = 5 * (num - 1);
X    move(line, start);
X    printw("+----+");
X    move(line + 1,start);
X    printw("|    |");
X    move(line + 2,start);
X    printw("|    |");
X    move(line + 3,start);
X    printw("+----+");
X    
X    if (line == DEALERLINE && num == 1) {
X      /* Special - hide this card. */
X      move(line+1,start+1);
X      printw("####");
X      move(line+2,start+1);
X      printw("####");
X      return;
X    };
X
X    move(line + 1,start+1);
X    switch(r) {
X        case 1: printw("A"); break;
X	case 10: printw("10"); break;
X        case 11: printw("J"); break;
X	case 12: printw("Q"); break;
X	case 13: printw("K"); break;
X	default: printw("%d",r); break;
X    };
X    
X    printw("%c",suitc[suit+1]);
X};
X
X/*
X *  Deal deals a card to the indicated hand or player, 
X *  also displaying it on the screen at the appropriate
X *  spot.
X */
X 
Xvoid deal(who)
X    int who;
X{
X    if (who == DEALER) {
X    	dealer[dnum++] = cards[top++];
X	dtot = value(dealer,dnum);
X	display_card(dealer,dnum,DEALERLINE);
X    } else {
X        player[who][pnum[who]++] = cards[top++];
X	ptot[who] = value(player[who],pnum[who]);
X	if (!who) {
X	    display_card(player[who],pnum[who],PLINE1);
X	    move(PLINE1+2,(5*pnum[who]+2));
X	    printw("(%d)",ptot[who]);
X        } else {
X	    display_card(player[who],pnum[who],PLINE2);
X	    move(PLINE2+2,(5*pnum[who]+2));
X	    printw("(%d)",ptot[who]);
X	};
X    };
X    refresh();
X#ifdef TURBOC    
X    delay(100);
X#else
X    usleep(SHORTSLEEP);
X#endif
X};
X
X/* 
X *  play_dealer plays the dealers hand according to the
X *  standard rules.
X *
X *  Downtown (hit soft 17) rules.
X */
X 
Xvoid play_dealer()
X{   
X    while (dtot < 17 || (dtot == 17 && soft(dealer,dnum))) {
X        deal(DEALER);
X    };
X};
X
X/*
X *  Reveal the dealer's card.
X *
X */
X 
Xvoid reveal()
X{
X    int r,suit,start,line;
X    
X    r = (dealer[0] % 13) + 1;
X    suit = (dealer[0] - 1 / 13)%4;
X    start = 0;
X    line = DEALERLINE;
X    /* Special - unhide this card. */
X    move(line+1,start+1);
X    printw("    ");
X    move(line+2,start+1);
X    printw("    ");
X    move(line + 1,start+1);
X    switch(r) {
X        case 1: printw("A"); break;
X	case 10: printw("10"); break;
X        case 11: printw("J"); break;
X	case 12: printw("Q"); break;
X	case 13: printw("K"); break;
X	default: printw("%d",r); break;
X    };
X    
X    printw("%c",suitc[suit+1]);
X    refresh();
X};
X
X/*
X *  True if a hand is "soft".
X *
X */
X 
Xint soft(h,s)
X    int *h,s;
X{
X    int tot, aces, i, r;
X    
X    tot = aces = 0;
X    
X    for(i=0;i<s;i++) {
X        r = rank(h[i]);
X	if (r == 1) aces++;
X	if (r > 10) r = 10;
X	tot += r;
X    };
X    
X    if (aces && tot < 12) return(1);
X    return(0);
X};
X
Xchar correct_play(h,z)
X    int *h,z;
X{
X    int s,t,u,r;
X    
X    s = soft(h,z);
X    t = value(h,z);
X    u = rank(dealer[1]);
X    if (u > 10) u = 10;
X    
X    if (t < 9) return('H');
X    
X    /*
X     *  Splitting.
X     *
X     */
X    
X    if (rank(h[0]) == rank(h[1]) && z == 2) {
X        r = rank(h[0]);
X        if (r == 1 || r == 8 || (r == 9 && u != 7 && u != 10 && u != 1) ||
X            (r == 7 && u < 8) || (r == 6 && u < 7 && u > 2) ||
X            (r == 2 && u > 3 && u < 8) ||
X            (r == 3 && u > 3 && u < 8)) {
X                return('S');
X	};
X    };
X    
X    /*
X     *  Doubling
X     *
X     */
X
X    /*  Soft  */    
X    if (s && z == 2 &&
X    	(((t == 18 || t == 17) && u > 2 && u < 7) ||
X         ((t == 16 || t == 15) && u > 3 && u < 7) ||
X         ((t == 14 || t == 13) && u > 4 && u < 7))) {
X            	return('D');
X    }
X    /*  Hard */
X    if (!s && z == 2 &&
X        (t == 11 && u != 1) ||
X        (t == 10 && u != 1 && u != 10) ||
X        (t == 9 && u > 2 && u < 7)) {
X                return('D');
X    }
X    
X    /*
X     * Hit or Stand.
X     *
X     */
X    
X    if (!s) {
X        /*  Hard hand. */
X	if (t > 16) {
X	    return(' ');
X	} else if (t == 12 && (u == 2 || u == 3)) {
X	    return('H');
X	} else if (u < 7 && u != 1) {
X	    return(' ');
X	} return('H');
X    } else {
X        /*  Soft hand. */
X        if (t < 18) {
X            return('H');
X	} else if (t == 18 && (u > 8 || u == 1)) {
X	    return('H');
X	} else return(' ');
X    };
X};
X
X    
X
Xmain()
X{
X    /*
X     *  Logic
X     *
X     *  (1) Shuffle if necessary.
X     *  (2) Initialize players.
X     *  (3) Deal two cards to each player.
X     *      (a) blackjack check.
X     *  (4) Enter play loop.
X     *      For each hand:
X     *      (a) ask player what to do.
X     *      (b) do it, if legal.
X     *      (c) check for bust.
X     *  (5) Play dealer's hand.
X     *  (6) Determine results.
X     *  (7) Repeat
X     *
X     */
X     
X    int i;
X    char c,correct;
X
X#ifdef TURBOC    
X    randomize();
X#else
X    srandom((int) time(0));
X#endif
X    quit = 0;
X    numhands = 0;
X    numbust = 0;
X    numbj = 0;
X    nummp = 0;
X
X    /*
X     *  Curses initialization.
X     *
X     */
X    initscr();
X    crmode();
X    noecho();
X    move(0,20);
X    printw("*B*L*A*C*K*J*A*C*K*");
X    top = 300;
X    numcards = 104;  /* 2 deck game */
X
X    while(!quit) {
X        
X       /* Shuffle check. */
X       if (top >= (numcards * .60)) {
X            move(MESSAGES,0);
X	    clrtoeol();
X	    printw("Shuffling...          ");
X	    refresh();
X#ifdef TURBOC	    
X	    delay(250);
X#else
X	    usleep(SHORTSLEEP);
X#endif
X	    shuffle();
X	};
X
X        /* Initialize. */
X	
X        init_hand();
X	
X	/* Clear appropriate parts of the screen. */
X	for(i=0;i<=4;i++) {
X	    move(DEALERLINE+i,0);
X	    clrtoeol();
X	    move(PLINE1+i,0);
X	    clrtoeol();
X	    move(PLINE2+i,0);
X	    clrtoeol();
X	    move(STATUS,0);
X	    clrtoeol();
X	    move(STATUS+1,0);
X	    clrtoeol();
X	};
X	
X	/* Deal two cards to each. */
X
X	deal(DEALER);	
X	deal(DEALER);
X	deal(0);
X	deal(0);
X	numhands++;
X	
X	/*  Blackjacks? */
X	
X	if (dtot == 21 && ptot[0] == 21) {
X	    reveal();
X	    move(MESSAGES,0);
X	    clrtoeol();
X	    printw("Double Blackjack!  Push.");
X	    refresh();
X#ifdef TURBOC	    
X	    delay(1000);
X#else
X	    usleep(LONGSLEEP);
X#endif
X	    continue;
X	} else if (dtot == 21) {
X	    reveal();
X	    move(MESSAGES,0);
X	    clrtoeol();
X	    printw("Dealer Blackjack!  You lose $%d.",bet[0]);
X	    money -= bet[0];
X	    move(STATUS,0);
X	    clrtoeol();
X	    printw("Stake $%d",money);
X	    refresh();
X#ifdef TURBOC	    
X	    delay(1000);
X#else
X	    usleep(LONGSLEEP);
X#endif
X	    continue;
X	} else if (ptot[0] == 21) {
X	    reveal();
X	    move(MESSAGES,0);
X	    clrtoeol();
X	    printw("Blackjack!  You win $%d.",(bet[0]*1.5));
X	    money += bet[0]*1.5;
X	    move(STATUS,0);
X	    clrtoeol();
X	    printw("Stake $%d",money);
X	    refresh();
X#ifdef TURBOC	    
X	    delay(1000);
X#else
X	    usleep(LONGSLEEP);
X#endif
X	    numbj++;
X	    continue;
X	};
X
X        /*  Play loop. */
X	
X	while (!stand[0] || (split && !stand[1])) {
X	    
X	   again:
X	    
X	    for(i=0;i<=split;i++)
X	        if (!stand[i]) {
X		    
X		    /* Go to the proper spot. */
X		    
X		    if (i == 0) move(PLINE1+2,(5*pnum[0]+6));
X		    else move(PLINE2+2,(5*pnum[1]+6));
X		    refresh();
X	            
X	            /* Ask him what to do. */
X		    c = mgetc(" HSD");
X		    correct = correct_play(player[i],pnum[i]);
X		    
X		    /* Beep to annoy him. */
X		    if (c != correct) {
X#ifdef TURBOC
X		       sound(75);
X		       delay(100);
X		       sound(35);
X		       delay(50);
X		       nosound();
X#else
X#endif
X		       move(STATUS+1,0);
X		       printw("Correct play was '%c'.",correct);
X		       refresh();
X		       nummp++;
X		    };
X		    
X	            /* Do it. */
X		    switch(c) {
X			case 'Q':
X			/* quit */
X			break;
X
X		        case ' ':
X		        /*  Stand. */
X			stand[i] = 1;
X			break;
X			
X			case 'H':
X			/*  Hit. */
X			deal(i);
X			break;
X			
X			case 'S':
X			/*  Split.  */
X			if (!split && pnum[0] == 2 &&
X			    rank(player[0][0]) == rank(player[0][1])) {
X			    split = 1;
X			    player[1][0] = player[0][1];
X			    pnum[0] = 1;
X			    display_card(player[0],1,PLINE1);
X			    deal(0);
X			    display_card(player[1],1,PLINE2);
X			    pnum[1] = 1;
X			    deal(1);
X			    /*  Only one card after splitting aces. */
X			    if (rank(player[0][0]) == 1) {
X			    	stand[0] = 1;
X				stand[1] = 1;
X			    };
X			    goto again;
X			};
X			break;
X			
X			case 'D':
X			/*  Double Down.  */
X			if (pnum[i] == 2) {
X			    bet[i] *= 2;
X			    deal(i);
X			    stand[i] = 1;
X			};
X			break;
X			
X		};
X		
X		/*  Bust check.  */
X		
X		if (bust(player[i],pnum[i])) {
X		    move(MESSAGES,0);
X		    clrtoeol();
X		    printw("Bust!");
X		    stand[i] = 1;
X		    refresh();
X		    numbust++;
X		};
X	    };
X	};
X	
X	/*  Play dealers hand. */
X
X        reveal();
X	play_dealer();
X
X
X        /*  Determine Results. */
X	
X	determine_results();
X
X    };
X    
X};
END_OF_FILE
if test 14933 -ne `wc -c <'blackjack.c'`; then
    echo shar: \"'blackjack.c'\" unpacked with wrong size!
fi
# end of 'blackjack.c'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have the archive.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0