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