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