[alt.sources] Atlantic City blackjack v1.0

cs00chs@unccvax.uncc.edu (charles spell) (10/12/90)

Here is an Atlantic city blackjack game I hacked together one weekend before
going to Atlantic City. To compile (System V only):

     cc bj.c -o bj -lcurses

A small contribution will be appreciated...I will send out fixes/enhancements
if there is enough monetary interest...

---cut here---cut here---cut here---cut here---cut here---cut here---cut here-
#include <curses.h>
#include <ctype.h>
#include <signal.h>
/*****************************************************************************
* You may freely distribute this file as long as the contents of this file
* have not been altered in any way or form. If you decide that you would like
* to re-distribute a change, please send to me for testing first - this keeps
* one current tested version out there...
*
* If you enjoy or find the program (in part or whole) useful,
* please send a contribution to:
*   John J. Ribera, Jr.
*   9505-J University Terrace Dr.
*   Charlotte, NC 28262
*   Voice: (704) 549-5571
* Please send suggestions, comments, complaints, ideas, etc... to the author
* at the address above or e-mail : uunet!mcnc!unccvax!cs00chs
*/

/*****************************************************************************
* NAME                                                                  Oct 90
*   bj - a blackjack program
*
* SYNOPSIS
*   bj
*
* DESCRIPTION
*  This is an eight-deck Atlantic City blackjack game for system V. Splits and
*  double downs are supported. No insurance. Use the Spacebar to Hit and CR to
*  stand if desired. A redeal will occur when half of the all decks are used.
*  bj uses terminfo GRAPHICS capablities - iff they are activated.
*
* NOTE
*  The program looks much nicer if you have the smacs=, rmacs= and acsc=
*  capabilities in your terminfo database...but it will still work
*  without them...
*
*  How to add graphics capabilities for vt100, vt101 and vt102 emulators:
*  Type the following commands (re-start here if anything goes wrong):
*  csh> setenv TERM vt100      # vt100 is an example-use TERM name youre that
*  csh> mkdir ~/term           #   emulates a vt10[012] (in terminfo database)
*  csh> infocmp > ~/term/info  # this will create a terminfo source file.
*  csh> setenv TERMINFO ~/term # Tell curses to use the database in ~/term
*  csh> tic ~/term/info        # Ignore warnings (if any)...
*  Edit the ~/term/info file and add the following line to the proper entry:
*    -go to the entry that emulates vt100 (eg. vt100,vt101, vt102)
*    -add the following line to it...
*    smacs=\E(0\E)0, rmacs=\E(B\E)B, acsc=jjkkllmmnnqqssttuuvvww++\,\,,
*    -save it and type the following command:
*  csh> tic ~/term/info
*
*  For non vt100 emulators you must read your terminal reference manual and
*  terminfo(5) to set it up properly. Some old terminals do not have a text
*  graphics mode - i.e. you cannot see the pretty graphics.
*
* BUGS
*  There are a few bugs. These will be fixed on the next version iff there is
*  enough mmonetary interest. The dealer continues to deal to himself even
*  after the outcome of a score is certain. No insurance. After the first
*  shuffle, the WIN-LOSE is not displayed properly.
*
* AUTHOR
*
*   (E-mail: uunet!mcnc!unccvax!cs00chs)
*   John J. Ribera, Jr.
*   9505-J University Terrace Dr.
*   Charlotte, NC 28262
*   Voice: (704) 549-5571
*
*   If you find this program or any part of it useful please send a
*   contribution to the above address. This will allow you to receive
*   the most recent fixes and versions. Make checks payable to
*   John J. Ribera, Jr.
*/

/* flags for get_str() and get_chr()... */
#define CLR_FLD     0x0001
#define INIT_ONKEY  0x0010
#define MAP_UPPER   0x0002
#define MAP_LOWER   0x0004
#define AUTO_RET    0x0008
#define NOPAD       0x0020
#define NOECHO      0x0040
#define BEEP        0x0080

/* keystroke defines for readablity.... */
#define ESC         0x1b
#define CR          '\r'
#define NL          '\n'
#define BS          '\b'

#define MAX_HANDS   3
#define DECKS       8
#define DEALER      0
#define DOWN        0
#define UP          1
#define WIN         0x0001
#define LOSE        0x0002
#define PUSH        0x0004
#define BJ          0x0008
#define GETCNT(i)   (short) abs((int) (getcnt(i)))
typedef struct
    {
    short   amt;
    short   bet;
    short   cnt;
    short   cards[13];
    char    name[9+1];
    WINDOW  *win;
    } HAND;

HAND    Hand[MAX_HANDS];
short   Hands = 2;
short   *Cards;
short   Cur_card;
main()
{
    short   i, n, cnt, pcnt, dcnt;
    short   decks, cmp, bet;
    short   *cards, *shuffle();
    short   getcnt(), bj(), getbet();
    char    *cardtype();
    char    input[3], *ptr;
    void    done();
    void    inithand(), disphand();

signal(SIGINT, done);
initscr();
noecho();
crmode();

for (n = 0; n < MAX_HANDS; n++)
    inithand(n);

srand(time((long *) 0));
while (TRUE)
    {
    clearok(stdscr, TRUE);
    refresh();
    for (i = 0; i < Hands; i++)
        disphand(i);
    wstandout(Hand[DEALER].win);
    mvwprintw(Hand[DEALER].win, 0, 1, " B L A C K J A C K ");
    wstandend(Hand[DEALER].win);
    wprintw(Hand[DEALER].win, " by clt2!jjr... (press DEL to quit)");
    mvwprintw(Hand[DEALER].win, 5, 1, "Shuffle...");
    wrefresh(Hand[DEALER].win);
    sleep(2);
    Cards = shuffle((decks = DECKS));
    Cur_card = 0;
    while (Cur_card < decks * 52 / 2)
        {
        for (i = 1;Hands > 2; Hands--, i++)
            werase(Hand[i+1].win), wrefresh(Hand[i+1].win);
        Hands = 2;
        for (n = 1; n < Hands; n++)
            {
            if (!getbet(n))
                done();
            Hand[n].cnt = 0;
            disphand(n);
            }
        Hand[DEALER].cnt = 0;
        disphand(DEALER);
        getcard(1); getcard(DEALER);
        getcard(1); getcard(DEALER);
        getcard(1);
        if (Hands > 2)
            {
            getcard(2);
            getcard(2);
            }
        getcard(DEALER);
        for (dcnt = GETCNT(DEALER), i = 1;  i < Hands; i++)
            {
            if ((pcnt = GETCNT(i)) > 21 || dcnt > 21)
                cmp = (pcnt > 21) ? LOSE : WIN;
            else if (pcnt == dcnt)
                cmp = (bj(i)) ? BJ : PUSH;
            else
                cmp = (pcnt > dcnt) ? (bj(i) ? BJ : WIN) : LOSE;
            switch (cmp)
                {
            case WIN:
                Hand[i].amt += Hand[i].bet;
                Hand[DEALER].amt -= Hand[i].bet;
                ptr = " WIN ";
                break;
            case BJ:
                Hand[i].amt += Hand[i].bet + (Hand[i].bet / 2);
                Hand[DEALER].amt -= (Hand[i].bet + Hand[i].bet / 2);
                ptr = " WIN ";
                break;
            case PUSH:
                ptr = " PUSH ";
                break;
            case LOSE:
                Hand[DEALER].amt += Hand[i].bet;
                Hand[i].amt -= Hand[i].bet;
                ptr = " LOSE ";
                break;
                }
            dispbet(i);
            dispbet(DEALER);
            wrefresh(Hand[i].win); wrefresh(Hand[0].win);
            wstandout(Hand[i].win); mvwprintw(Hand[i].win, 2, 20, ptr);
            wstandend(Hand[i].win); wrefresh(Hand[i].win);
            }
        }
    promptfor("New deck...press return to continue...", ptr, 1, "", CLR_FLD);
    }
}
void
inithand(n)
short   n;
{

Hand[n].win = newwin((n == DEALER) ? 7 : 5,0,(n == DEALER) ? 0 : n * 5 + 2, 0);
Hand[n].amt = (DEALER == n ) ? 0 : 1000;
Hand[n].bet = 0L;
Hand[n].cnt = 0;
strcpy(Hand[n].name, getlogin());
strcpy(Hand[DEALER].name, "Dealer");
}
/*******************************************************************************
* shuffle will return a static pointer to an array of random shorts with a range
* of 0 to 52 * 'decks'. If 'decks' is > 10, then 10 decks will be returned.
* If 'decks' is < 1 then one shuffled deck will be returned.
*/
short   *
shuffle(decks)
short   decks;
{
static  short   deck[52 * 10 + 1];
        short   mark[52 * 10 + 1];
        short   card;
        short   cnt = 0;

memset(mark, '\0', sizeof(mark));
memset(deck, '\0', sizeof(deck));

decks = (decks < 1) ? 1 : ((decks > 10) ? 10 : decks);
while (cnt < 52 * decks)
    if ((card = (short) rand() % (52 * decks)) >= 0 && !mark[card])
        deck[cnt++] = card, mark[card] = 1;
deck[cnt] = -1;
return(deck);
}
/******************************************************************************
* cardtype will return a 3 character string that specifies which card 'card'
* represents. Character [1] of the returned string specifies the cardinality
* of 'card' and character [2] specifies the suit of 'card'.
*/
char *
cardtype(card)
short   card;
{
static  char    value[27] = " A 2 3 4 5 6 7 8 910 J Q K";
static  char    suit[4] = "SHCD";
static  char    type[4];

strncpy(type, &value[((card % 52) / 4) << 1], 2);
type[2] = suit[card & 3];
type[3] = 0;
return(type);
}

int
boxit(win, rows, cols, brow, bcol)
WINDOW  *win;
short   rows;
short   cols;
short   brow;
short   bcol;
{
    short   i;

wrefresh(win);
wattron(win, A_ALTCHARSET);
mvwaddch(win, brow, bcol, ACS_ULCORNER);
mvwaddch(win, brow+rows-1, bcol, ACS_LLCORNER);
for (i=bcol+1; i < bcol+cols-1; i++)
    {
    mvwaddch(win, brow, i, ACS_HLINE);
    mvwaddch(win, brow+rows-1, i, ACS_HLINE);
    }
mvwaddch(win, brow, i, ACS_URCORNER);
mvwaddch(win, brow+rows-1, i, ACS_LRCORNER);
for (i=brow+1; i < rows-1; i++)
    {
    mvwaddch(win, i, bcol, ACS_VLINE);
    mvwprintw(win, i, bcol+1, "%*.*s", cols-2, cols-2, "");
    mvwaddch(win, i, bcol+cols-1, ACS_VLINE);
    }
wrefresh(win);
wattroff(win, A_ALTCHARSET);
}

void
getcard(hand)
short   hand;
{
    char    c, getact();
    char    *sel, *cardtype();
    char    ctype1[5], ctype2[5];
    short   getcard();
    void    dispcard();
    void    dispbet();

if (Hand[hand].cnt < 2)
    {
    Hand[hand].cards[Hand[hand].cnt++] = nextcard();
    dispcard(hand, Hand[hand].cnt-1, (hand==DEALER&&Hand[0].cnt==2)?DOWN:UP);

    return;
    }
if (hand == DEALER)
    {
    dispcard(DEALER, 1, UP);
    while (GETCNT(DEALER) < 17 || getcnt(DEALER) < 0 && GETCNT(DEALER) == 17)
        {
        Hand[DEALER].cards[Hand[DEALER].cnt++] = nextcard();
        dispcard(DEALER, Hand[DEALER].cnt-1, UP);
        }
    return;
    }
if (GETCNT(hand) == 21)
    return;
strcpy(ctype1, cardtype(Hand[hand].cards[0]));
strcpy(ctype2, cardtype(Hand[hand].cards[1]));
sel = (Hands < MAX_HANDS && ctype1[1]==ctype2[1]) ? "HSDP0123 " : "HSD123 ";
if ((c = getact(hand, sel)) == 'S')
    return;
if (c == 'P')
    {
    Hands++;
    Hand[hand].cnt--;
    Hand[hand+1].bet = Hand[hand].bet;
    Hand[hand+1].cards[0] = Hand[hand].cards[1];
    Hand[hand+1].cnt = 1;
    Hand[hand+1].bet = Hand[hand].bet;
    Hand[hand+1].amt = 0L;
    dispbet(hand);
    disphand(hand + 1);
    dispcard(hand + 1, 0, UP);
    }
Hand[hand].cards[Hand[hand].cnt++] = nextcard();
dispcard(hand, Hand[hand].cnt-1, UP);
/*
if (GETCNT(hand) >= 21)
    return;
*/
if (c == 'P')
    {
    if (getact(hand, "^HSD123 ") == 'S')
        return;
    Hand[hand].cards[Hand[hand].cnt++] = nextcard();
    dispcard(hand, Hand[hand].cnt-1, UP);
    }
if (c == 'D')
    {
    Hand[hand].bet *= 2;
    dispbet(hand);
    dispbet(DEALER);
    return;
    }

while (GETCNT(hand) < 21)
    {
    if ((c=getact(hand, "^HS12 ")) == 'S')
        return;
    Hand[hand].cards[Hand[hand].cnt++] = nextcard();
    dispcard(hand, Hand[hand].cnt-1, UP);
    }
return;
}

short
getcnt(hand)
short   hand;
{
    char    *type;
    char    *strchr();
    short   cnt, acecnt;
    short   i;

for (i = 0, cnt = acecnt = 0; i < Hand[hand].cnt; i++)
    {
    type = cardtype(Hand[hand].cards[i]);
    if (strchr("KQJ0", type[1]))
        cnt += 10;
    else if (strchr("23456789", type[1]))
        cnt += type[1] - '0';
    else
        cnt += 11, acecnt++;
    }
while (acecnt--)
    if (cnt > 21)
        cnt -= 10;
return(acecnt>0 ? -cnt : cnt);
}

char
getact(hand, valact)
short   hand;
char    *valact;
{
    char    prompt[80];
    char    choice[2];
    char    sel[5];

strcpy(sel, "PHSD");
sprintf(prompt, "Enter choice %s (Hit, Stand", Hand[hand].name);
if (strchr(valact, 'D'))
    strcat(prompt, ", Double");
if (strchr(valact, 'P'))
    strcat(prompt, ", sPlit");
strcat(prompt, "): ");
strcpy(choice, "S");
promptfor(prompt, choice, 1, valact, MAP_UPPER|AUTO_RET);
if (strchr("0123", choice[0]))
    choice[0] = sel[choice[0] - '0'];
return(choice[0]);
}

short
getbet(hand)
short   hand;
{
    char    prompt[80];
    char    bet[9];
    int     atoi();

if (Hand[hand].bet > 500)
    Hand[hand].bet = 500;
sprintf(prompt, "Enter bet %s: ($2 - $500, 0 to quit) ", Hand[hand].name);
do
    {
    sprintf(bet, "%hd", Hand[hand].bet);
    promptfor(prompt, bet, 5, "0123456789", INIT_ONKEY);
    Hand[hand].bet = (long)atoi(bet);
    if (!Hand[hand].bet)
        return((short) 0);
    } while (Hand[hand].bet % 2 || Hand[hand].bet > 500);
dispbet(hand);
return(Hand[hand].bet);
}

long
dispbet(hand)
short   hand;
{
    short   i;

if (hand == DEALER)
    for (i = 1, Hand[DEALER].bet = 0; i < Hands; i++)
        Hand[DEALER].bet += Hand[i].bet;

mvwprintw(Hand[hand].win,2,1,"stakes    : %5hd", Hand[hand].bet);
mvwprintw(Hand[hand].win,3,1,"credit    : %+5hd", Hand[hand].amt);
wrefresh(Hand[hand].win);
return(Hand[hand].bet);
}

void
dispcard(hand, card, up)
short   hand;
short   card;
short   up;
{
    short   bcol, val;
    short   bj();
    char    c,type[5];

bcol = 40 + (card - 2) * 3;
wstandout(Hand[hand].win);
boxit(Hand[hand].win, (short) 5, (short) 5, (short) 0, (short) bcol);
if (up)
    {
    strcpy(type, cardtype(Hand[hand].cards[card]));
    c = type[2]; type[2] = 0;
    mvwprintw(Hand[hand].win, 1, bcol+1, &type[(type[0] == ' ') ? 1 : 0]);
    mvwprintw(Hand[hand].win, 2, bcol+2, "%c", c);
    mvwprintw(Hand[hand].win, 3, bcol+2, type);
    wrefresh(Hand[hand].win);
    wstandend(Hand[hand].win);
    wrefresh(Hand[hand].win);
    mvwprintw(Hand[hand].win, 1, 13, "%5hd", (val = GETCNT(hand)));
    if (GETCNT(hand) > 21)
        mvwprintw(Hand[hand].win, 1, 21, "BUST ");
    if (bj(hand))
        mvwprintw(Hand[hand].win, 1, 21, "BLACKJACK! ");
    }
wstandend(Hand[hand].win);

wrefresh(Hand[hand].win);
}

void
disphand(hand)
short   hand;
{
    short   i;
for (i = 0; i < 5; i++)
    wmove(Hand[hand].win, i, 1), wclrtoeol(Hand[hand].win);
mvwprintw(Hand[hand].win, 1, 1, "%-10.10s: ", Hand[hand].name);
if (hand < 2)
    dispbet(hand);
wrefresh(Hand[hand].win);
}

void
promptfor(prompt, input, max, vchrs, flags)
char    *prompt;
char    *input;
short   max;
long    flags;
{
    char    *strchr();
wmove(Hand[DEALER].win, 5, 1);
while (*prompt)
    if (strchr(vchrs, *prompt) && *prompt != ' ')
        {
        wstandout(Hand[DEALER].win);
        waddch(Hand[DEALER].win, *prompt++);
        wstandend(Hand[DEALER].win);
        }
    else
        waddch(Hand[DEALER].win, *prompt++);
waddch(Hand[DEALER].win, ' ');

get_str(Hand[DEALER].win, input, max, vchrs, flags);
wmove(Hand[DEALER].win, 5, 1); wclrtoeol(Hand[DEALER].win);
wrefresh(Hand[DEALER].win);
}

short
nextcard()
{
return(Cards[Cur_card++]);
}

short
bj(hand)
short   hand;
{
if (GETCNT(hand) != 21)
    return((short)0);
if (Hand[hand].cnt == 2)
    return((short)1);
return((short)0);
}
/******************************************************************************
* get a string through curses...how many times has this been re-written?
* Returns:      key that caused get_str to return.
* Side Effects: _str_ holds a space padded array of characters entered by user.
*               _win_ will be changed by the contents of string
* Note: wattron before call to get_str for pretty display of input box...
*       noecho() should be enabled...
*/
short
get_str(win, str, max, vchrs, flags)
WINDOW  *win;           /* input window */
char    *str;           /* changed by side effect   */
short   max;            /* stop when this is reached    */
char    *vchrs;         /* valid keystrokes...  */
short   flags;          /* all kinds of options...  */
{

    short   sr, sc;     /* save row save column -restored on ret*/
    short   ofs =0;     /* current offset from beginning of str */
    short   ret =0;     /* return status != when return desired */
    short   c;          /* each input char is put into this var */
    short   first=TRUE; /* first time through the main loop?    */
    char    save[256];  /* max better be less than 256 chars    */

wrefresh(win);                          /* dump any changes */
getyx(win, sr, sc);                     /* get logical cursor location*/
if (flags & CLR_FLD)                    /* initialize field from beginning? */
    sprintf(str, "%*.*s", max, max, "");
strcpy(save, str);                      /* save in case of an ESC...    */
sprintf(str, "%-*.*s", max, max, save); /* left justified...pad for now...  */
if (~flags & NOECHO)                    /* if we want to echo string... */
    waddstr(win, str);
wmove(win, sr, sc+ofs);
while (!ret)
    {
    if (isprint((c = get_chr(win, vchrs, flags))))
        {                               /* clear the input string on first  */
        if (ofs == max)                 /* dont write over the terminating  */
            ofs--;                      /* null character...                */
        if (first && flags & INIT_ONKEY)/* clear on first printable char... */
            sprintf(str, "%*.*s", max, max, "");
        str[ofs++] = (char) c;
        if (ofs == max)                 /* if at end of string already...   */
            if (flags & AUTO_RET)       /*   and AUTO_RET flag ON then...   */
                ret = CR;               /*     pretend CR was pressed...    */
        }
    else
        switch (c)
            {
        case CR:
        case NL:
        case KEY_UP:
        case KEY_DOWN:
            ret = c;
            break;
        case ESC:
            strcpy(str, save);
            ret = c;
            break;
        case KEY_RIGHT:
            if (ofs < max)
                str[ofs++] = ' ';
            break;
        case KEY_LEFT:
        case BS:
            if (ofs)
                ofs--;
            break;
        default:
            ret = c;
            break;
            }
    if (~flags & NOECHO)                /* if NOECHO is OFF then disp str...*/
        mvwaddstr(win, sr, sc, str);    /* (display after each character)   */
    wmove(win, sr, sc+ofs);
    wrefresh(win);
    first = FALSE;                      /* we have been around the loop...  */
    }
if (flags & NOPAD)
    for (ofs = 0; ofs < max; ofs++)
        if (str[ofs] == ' ')
            c = ofs;
return(ret);
}

short
get_chr(win, vchrs, flags)
WINDOW  *win;
char    *vchrs;
short   flags;
{
    short   c;
wrefresh(win);
while ((c = wgetch(win)) == ERR)
    if (isprint(c) && strchr(vchrs, c))
        break;
    else if (flags & BEEP)
        beep();
if (flags & MAP_UPPER)
    if (c >= 'a' && c <= 'z')
        c = toupper(c);
if (flags & MAP_LOWER)
    if (c >= 'A' && c <= 'A')
        c = tolower(c);
return(c);

}

void
done()
{
mvprintw(19, 20, "Quit with %hd dollars",  Hand[1].amt);
mvprintw(20, 20, "Press any key to exit...");
refresh();
getch();

endwin();
exit(0);
}
---cut here---cut here---cut here---cut here---cut here---cut here---cut here--

-- 
 .--------------------------.  ...  |On the border of your mind lies a place
 |uunet!mcnc!unccvax!cs00chs|  (")  |where dreams and reality are one...I will 
 `--------------------------'-w-U-w-|take you there, for I am the subject...
 \%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\|the subject of your imagination. -Aldo Nova