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