[net.sources.games] revised network blackjack game

jon@msunix.UUCP (Jonathan Hue) (07/24/86)

Here is a revised version of the network blackjack game.  It has a few
bug fixes and a few new features, including a new man page.  As before,
this only works on 4.[23] BSD UNIX and Ultrix.  See the README and
CHANGES files for more information.  Complaints get sent to me,
jon@msunix.UUCP.



"If we did it like everyone else,	  Jonathan Hue
what would distinguish us from		  Via Visuals Inc.
every other company in Silicon Valley?"	  sun!sunncal\
						      >!leadsv!msunix!jon
"A profit?"				amdcad!cae780/


-------------------------------cut here--------------------------------
#!/bin/sh
: "This is a shell archive, meaning:                              "
: "1. Remove everything above the #! /bin/sh line.                "
: "2. Save the resulting test in a file.                          "
: "3. Execute the file with /bin/sh (not csh) to create the files:"
: "	CHANGES"
: "	Makefile"
: "	README"
: "	bj.6"
: "	bj.c"
: "	dealer.c"
: "	defs.h"
: "	hand.c"
: "	opensock.c"
: "	sockets.c"
: "	sockio.c"
: "	table.c"
: "This archive created:  Wed Jul 23 22:53:17 PDT 1986 "
echo file: CHANGES
sed 's/^X//' >CHANGES << 'END-of-CHANGES'
Xdivide by zero error in del_players found
Xadded dealer average statistic to del_players
Xplayers hand turned up when busts
X-arrows option
Xbyteswap in sockio.c
END-of-CHANGES
echo file: Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
X# If you can edit /etc/services, define ROOTPRIV, otherwise
X# edit defs.h for an unused internet port address (PORT = xxxx)
X# If you want all the cards up, define BARNEYS
X#
XDEFS = -DROOTPRIV
X
XCFLAGS = -g $(DEFS)
X
Xall:	bj dealer
X
Xbj:		bj.o sockets.o sockio.o
X	cc $(CFLAGS) -o bj bj.o sockets.o sockio.o -lcurses -ltermlib
X
Xdealer:		dealer.o opensock.o sockio.o table.o hand.o
X	cc $(CFLAGS) -o dealer dealer.o opensock.o sockio.o table.o hand.o
X
Xbj.o process.o sockets.o sockio.o:	defs.h
X
Xdealer.o opensock.o table.o hand.o:	defs.h
X
Xpitboss.o:	defs.h
X
Xclean:	
X	rm *.o bj dealer
X
Xlint:	lintbj lintdealer
X
Xlintbj:
X	lint $(DEFS) bj.c sockets.c sockio.c
X
Xlintdealer:
X	lint $(DEFS) dealer.c opensock.c sockio.c table.c hand.c
END-of-Makefile
echo file: README
sed 's/^X//' >README << 'END-of-README'
XThis is the new network blackjack game.  I've fixed a couple bugs,
Xadded some statistic reporting, and added an arrows/inverse
Xoption.  I do not have access to a network with both Suns and
XVAXes so I do not know if the game can be played between a Sun and
Xa VAX, although I do know that it works on a network of Suns and on
Xa network of VAXes.  The old game definitely would not work between
Xa Sun and a VAX because of byte order problems (I think).  I've been
Xinformed that dealer needs to be setuid root on an Ultrix machine
X'cause DEC made some syscall privilidged or something like that.  
XOh yeah, I fixed the man page so it reflects the -arrows and hostname
Xarguments that bj accepts.
X
X
X----------------OLD README FOLLOWS------------------
X
XThere's not much to this.  If you can edit /etc/services, then define
XROOTPRIV in the Makefile, otherwise don't.  Add a line like:
X
Xblackjack	1025/tcp	dealer		# network blackjack
X
Xto /etc/services if you defined ROOTPRIV, otherwise edit defs.h and define
XPORT to be an unused port address.  Define BARNEYS if you want to play
Xwith all cards face up like they do on the second floor of Barneys in
XSouth Lake Tahoe.  Then type make, and if everything
Xworks, fire up the server in the background (dealer &), and attach to
Xit with bj.  Please mail bug reports to me (jon@msunix.UUCP).  No
Xcomplaints about code quality!  If anyone wants to make it work under
Xthe inetd, have fun.  I wasted about two hours last night trying and
Xgave up.  I wrote a program called "pitboss" which listened for udp
Xpackets at another port address, and forked dealer, and modified bj
Xto connect to pitboss first, but I couldn't get it to work.  Who
Xcares?
X
XThis program is known to work on a Sun-2/120 w/Sun 2.0, a Sun-2/130
Xw/Sun 3.0, and a DEC VAX-11/785 w/4.2 BSD.  Masscomps will need
Xwork, but it shouldn't be too bad.  Non-4.[23]BSD boxes will have
Xto have all the network code rewritten, serves you right.  You
Xcould probably extract a single user bj out of it, though.  Have
Xfun.
X
X
X
X"If we did it like everyone else,	  Jonathan Hue
Xwhat would distinguish us from		  Via Visuals Inc.
Xevery other company in Silicon Valley?"	  sun!sunncal\
X						      >!leadsv!msunix!jon
X"A profit?"				amdcad!cae780/
END-of-README
echo file: bj.6
sed 's/^X//' >bj.6 << 'END-of-bj.6'
X.TH BJ 6 86/07/13
X.SH NAME
Xbj \- network blackjack game
X.SH SYNOPSIS
X.B
Xbj [hostname] [-arrows]
X.br
Xdealer
X.SH DESCRIPTION
XThe program \fIbj\fP is the client portion of a network blackjack game,
Xthe program \fIdealer\fP is the server portion.  The game allows up to
Xsix players to play blackjack against the dealer, and the game may
Xspan machines.  The networking code uses stream sockets in the
Xinternet family.  The dealer runs as a daemon in the background
Xand may be started with the command /usr/games/dealer&.  Once the
Xdealer is running, players may attach with the command /usr/games/bj.
X.PP
XThe game will display the players currently in the game, and will allow
Xyou to select which seat you wish to sit at.  Once seated, you bet
Xby typing in the amount you want to bet, and the cards are dealt.  The
Xtop line of the display is a status line, informing each player of other
Xplayer's activities and prompts from the dealer.  While a player's hand
Xis being processed, the following commands are allowed:
X.TP
Xspace
Xstand
X.TP
Xh
XTake another card.
X.TP
Xq
XQuit after this hand.
X.TP
Xs
XSplit a pair.
X.TP
Xd
XDouble down.
X.PP
XIf the dealer has an ace showing, each player is asked if they want insurance.
XInsurance pays 2 to 1 if the dealer has blackjack, and thus insurance costs half
Xwhat the player's bet is.  Blackjack pays 3 to 2.  Once you have initially bet,
Xyou can repeat your last bet by simply hitting return, rather than typing it
Xin again.  Beware - this amount is actually the last amount that you won or
Xlost, watch out if you just doubled down and lost, your bet is twice what you
Xthink it is.  The display shows the player and the machine they are logged
Xin to, along with their bet and how much they are up or down.  You can set
Xan environment variable called BJ if you want to change your name, but there
Xisn't really enough room to display a name like "Amarillo Slim".
X.SH OPTIONS
X.TP
Xhostname
XIf the dealer server does not reside on the machine bj is running on, you
Xmust provide the name of the host which dealer is running on.
X.TP
X-arrows
XNormally the current player is highlighted in inverse video.  This is not
Xalways easy to read, especially on Sun consoles.  The -arrows option
Xhighlights the current player by placing "-->" next to their name.
X.SH FILES
X.TP
X/usr/games/bj
X.TP
X/usr/games/dealer
X.TP
X/etc/services
X.TP
X/etc/hosts
X.SH BUGS
XProbably lots.  Undocumented features, too.  (Or is that redundant)  Look
Xthrough the sources.  Go away.
END-of-bj.6
echo file: bj.c
sed 's/^X//' >bj.c << 'END-of-bj.c'
X#include <curses.h>
X#include "defs.h"
X
X/*
X * This implements the player (client) portion of blackjack.  It's
X * pretty stupid, basically a big case statement.  It listens to
X * the dealer (server), and updates the screen, getting input from
X * the player when necessary.  Here are the commands it sends and
X * receives:
X *
X * commands to client:
X *
X * Pnplayername		add player playername as player n
X * Mstring		display string as a message
X * Rstring		display string and get single char response
X * rstring		display string and get string response
X * Cnhstring		display string as cards for player n hand h
X *
X * commands to server:
X *
X * H			hit with another card
X * S			stand
X * T			split
X * D			double down
X * Q			quit
X * (plus plain old string)
X */
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
X	char buf[SLEN], host[20], pbuf[SLEN];
X	int s, make_con();		/* socket I talk and listen on */
X	void send_name();
X	char *gets();
X	int i, arrows = 0, gothost = 0;
X	static char blank[] = "                                   ";
X	int x, y, standoutflag = 0;		/* display stuff */
X
X	(void) signal(SIGINT, SIG_IGN);
X	(void) signal(SIGQUIT, SIG_IGN);
X	if (argc > 3)  {
X		(void) fprintf(stderr, "usage: %s [hostname] [-arrows]\n", *argv);
X		exit(1);
X	}
X	for (i = 1; i < argc; i++)
X		if (!strcmp(*++argv, "-arrows"))
X			arrows = 1;
X		else  {
X			if (gothost)  {
X				fputs("usage: bj [hostname] [-arrows]\n", stderr);
X				exit(1);
X			}
X			gothost = 1;
X			(void) strcpy(host, *argv);
X		}
X	if (!gothost)
X		(void) gethostname(host, SLEN);	/* host is this machine */
X	s = make_con(host);
X	send_name(s);			/* send name, get player info */
X	initscr();			/* curses stuff */
X	noecho();
X	crmode();
X	refresh();
X	for (;;)  {
X		sockread(s, buf);	/* get command from dealer */
X		switch(buf[0])  {
X		case 'P':		/* display player names */
X			if (buf[1] == 'S')
X				if (arrows)  {
X					(void) strcpy(pbuf, "-->");
X					(void) strcat(pbuf, buf + 3);
X				}
X				else  {
X					(void) strcpy(pbuf, buf + 3);
X					standoutflag = 1;
X				}
X			else
X				(void) strcpy(pbuf, buf + 3);
X			switch(buf[2])  {
X			case '0':
X				x = 5;
X				y = 55;
X				break;
X			case '1':
X				x = 11;
X				y = 50;
X				break;
X			case '2':
X				x = 17;
X				y = 45;
X				break;
X			case '3':
X				x = 17;
X				y = 10;
X				break;
X			case '4':
X				x = 11;
X				y = 5;
X				break;
X			case '5':
X				x = 5;
X				y = 0;
X				break;
X			case '6':
X				x = 2;
X				y = 30;
X				(void) strcpy(pbuf, "Dealer");
X				break;
X			}
X			mvaddstr(x, y, blank);
X			if (standoutflag)
X				standout();
X			mvaddstr(x, y, pbuf);
X			refresh();
X			standend();
X			standoutflag = 0;
X			break;
X		case 'M':		/* display a message */
X			move(0, 0);
X			clrtoeol();
X			mvaddstr(0, 0, buf + 1);
X			refresh();
X			sleep(1);	/* delete or increase for different delay reading messages */
X			break;
X		case 'r':		/* display a message, then read a string */
X			move(0, 0);
X			clrtoeol();
X			mvaddstr(0, 0, buf + 1);
X			refresh();
X			echo();
X			nocrmode();
X			(void) gets(buf);	/* getstr() screws up tty bits */
X			sockwrite(s, buf);
X			noecho();
X			crmode();
X			break;
X		case 'R':		/* display a message, then read a char */
X			move(0,0);
X			clrtoeol();
X			mvaddstr(0, 0, buf + 1);
X			refresh();
X			*buf = getch();
X			buf[1] = '\0';
X			sockwrite(s, buf);
X			break;
X		case 'C':		/* display a players hand */
X			switch(buf[1])  {
X			case '0':	/* the different players */
X				move(6, 55);
X				break;
X			case '1':
X				move(12, 50);
X				break;
X			case '2':
X				move(18, 45);
X				break;
X			case '3':
X				move(18, 10);
X				break;
X			case '4':
X				move(12, 5);
X				break;
X			case '5':
X				move(6, 0);
X				break;
X			case '6':
X				move(3, 30);
X			}
X			/* this places the hands under the player name in the proper row */
X			move(stdscr->_cury + buf[2] - '0', stdscr->_curx);
X			if (buf[1] == '6')
X				addstr(buf + 3);
X			else  {
X				printw("#%c: ", buf[2] + 1);
X				addstr(buf + 3);
X			}
X			refresh();
X			break;
X		case 'c':		/* clear screen */
X			clear();
X			refresh();
X			break;
X		case 'Q':		/* quit */
X			endwin();
X			exit(0);
X		default:
X			puts("Error");
X			puts(buf);
X		}
X		(void) fflush(stdout);
X	}
X}
END-of-bj.c
echo file: dealer.c
sed 's/^X//' >dealer.c << 'END-of-dealer.c'
X#include "defs.h"
X
Xint s;						/* main socket */
Xint numplayer();
Xstruct playent player[NPLAYERS];		/* players in the game */
Xstruct sockaddr_in sockaddr;
Xint deck[] = {
X	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
X	13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
X	26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
X	39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
X};
Xint card = 0;
Xint dealerhand[11];				/* dealers hand */
Xint needtoshuffle = 1;
Xint dealercard;					/* # of cards dealer has */
Xint lastdealt;					/* first card dealt from this hand - for shuffling during a hand */
Xvoid insurance(), proc_insurance(), tellall(), tell2all(), process();
Xvoid shuffle(), betting(), process_dealer(), deal(), show_deal(), who_won();
Xvoid opensock(), showdeal();
X
Xmain()
X{
X	int i;
X	int readfds;			/* main socket bit mask */
X	struct timeval t;		/* zero timeout for selects */
X	int news;			/* new fd */
X	char buf[80], pname[40];
X	struct sockaddr_in sockad;
X	int ssize;			/* to make accept happy */
X	int fderr;
X	void closesock(), del_players();
X	int getchair();
X	int newp;			/* temp socket of new player */
X	struct playent *p;
X	long random();
X
X	opensock();
X	srandom(getpid());
X	t.tv_sec = t.tv_usec = 0;
X	for (i = 0; i < NPLAYERS; i++)
X		player[i].socket = -1;
X	for(;;)  {
X		readfds = 1 <<  s;
X/*
X * wait for players to show up
X */
X		if (!numplayer())  {
X			if (select(WIDTH, &readfds, (int *) 0, (int *) 0, (struct timeval *) 0) == -1)  {
X				perror("select");
X				exit(1);
X			}
X		}
X		del_players();
X		readfds = 1 <<  s;
X		while(fderr = select(WIDTH, &readfds, (int *) 0, (int *) 0, &t))  {
X			if (fderr == -1)  {
X				perror("select");
X				exit(1);
X			}
X/*
X *  add whoever's waiting
X */
X			if ((news = accept(s, sockad, &ssize)) == -1)  {
X				perror("accept");
X				exit(1);
X			}
X			sockread(news, pname);
X/*
X * tell new player who's playing
X */
X			for (i = 0, p = player; i < NPLAYERS; i++, p++)  {
X				if (p->occupied)
X					(void) sprintf(buf, "Ps%1d%s $%1d (%c%1d)", i, p->name, p->bet[0], p->cash >= 0 ? '+' : '-', abs(p->cash));
X				else
X					(void) sprintf(buf, "Ps%1dEmpty", i);
X				sockwrite(news, buf);
X			}
X			if ((newp = getchair(news)) == -1)  {
X				sockwrite(news, "DSorry, game full");
X				(void) close(news);
X			}
X			else  {
X/*
X * add player and initialize things
X */
X				player[newp].occupied = TRUE;
X				player[newp].socket = news;
X				player[newp].cash = 0;
X				player[newp].bet[0] = 0;
X				player[newp].won = 0;
X				player[newp].lost = 0;
X				player[newp].push = 0;
X				player[newp].ctot = 0;
X				player[newp].dcnt = 0;
X				player[newp].dtot = 0;
X				player[newp].nobust = 0;
X				(void) strcpy(player[newp].name, pname);
X				(void) sprintf(buf, "MWelcome to network blackjack, you are player #%d", newp + 1);
X				sockwrite(news, buf);
X			}
X			readfds = 1 << s;
X		}
X		show_players();
X		if (needtoshuffle || (card < (numplayer() * 3) + 3))
X			shuffle(0);
X		betting();
X		show_players();
X		deal();
X		showdeal();
X/*
X * offer insurance if dealer shows an ace
X */
X		if (!(dealerhand[1] % 13))  {
X			insurance();
X			proc_insurance();
X		}
X/*
X * let people play if dealer doesn't have blackjack
X */
X		if ((handval(dealerhand, 2) & CARDMASK) != 21)
X			for (i = 0, p = player; i < NPLAYERS; i++, p++)
X				if (p->occupied)  {
X					(void) sprintf(buf, "PS%1d%s $%1d (%c%1d)", i, p->name, p->bet[0], p->cash >= 0 ? '+' : '-', abs(p->cash));
X					tellall(buf);
X					process(i);
X					(void) sprintf(buf, "Ps%1d%s $%1d (%c%1d)", i, p->name, p->bet[0], p->cash >= 0 ? '+' : '-', abs(p->cash));
X					tellall(buf);
X				}
X		process_dealer();
X		who_won();
X		tellall("RHit any key to continue");
X		readall();
X		tellall("c");
X	}
X
X}
X
X/*
X * Get a char from everyone (hit space to return or something like that )
X */
Xreadall()
X{
X	int i;
X	char buf[5];
X	struct playent *p;
X
X	for (i = 0, p = player; i < NPLAYERS; i++, p++)
X		if (p->occupied)
X			sockread(p->socket, buf);
X}
X
Xshow_players()
X/*
X * show everyone who's playing
X */
X{
X	int i;
X	struct playent *p;
X	char buf[40];
X
X	for (i = 0, p = player; i < NPLAYERS; i++, p++)  {
X		if (p->occupied)
X			(void) sprintf(buf, "Ps%1d%s $%1d (%c%1d)", i, p->name, p->bet[0], p->cash >= 0 ? '+' : '-', abs(p->cash));
X		else
X			(void) sprintf(buf, "Ps%1dEmpty", i);
X		tellall(buf);
X	}
X	(void) sprintf(buf, "Ps%1d", NPLAYERS);
X	tellall(buf);
X}
END-of-dealer.c
echo file: defs.h
sed 's/^X//' >defs.h << 'END-of-defs.h'
X#include <sys/time.h>
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <sys/file.h>
X#include <netinet/in.h>
X#include <stdio.h>
X#include <netdb.h>
X#include <signal.h>
X#include <strings.h>
X
X#ifndef ROOTPRIV		/* internet port # to use if not using	*/
X#define PORT 1027		/* /etc/services			*/
X#endif
X
X#ifndef TRUE
X#define TRUE 1
X#endif
X
X#ifndef FALSE
X#define FALSE 0
X#endif
X
X#define NPLAYERS 6		/* max # players */
X#define SLEN 40			/* short string */
X#define PROTO "tcp"		/* protocol */
X#define SERVICE "blackjack"	/* official service name */
X#define WIDTH 32		/* # file descriptors */
X#define SOFT17 0x80		/* bit set for soft 17 */
X#define CARDMASK 0x7f		/* bit mask for players */
X#define C_ALLUP 0		/* all cards up (at end of hand, splitting) */
X#define C_2DOWNRESTUP 1		/* two down, rest up (other player's hand) */
X#define C_2UP1DOWN 2		/* two up, one down (doubling down) */
X#define C_1DOWN1UP 3		/* one down, one up (dealer) */
X#define C_1UP1DOWN 4		/* one up, one down (splitting aces) */
X
X
Xtypedef struct playent  {
X	int socket;		/* socket for player */
X	int cards[4][11];	/* players hand */
X	int quitting;		/* TRUE when player is going to leave */
X	int bet[4];		/* amount player is betting */
X	int occupied;		/* TRUE when a player is sitting here */
X	char name[SLEN];	/* players name */
X	int nhands;		/* # hands player has */
X	int ncards[4];		/* # cards in each hand */
X	int insured;		/* TRUE when player is insured */
X	int cash;		/* amount of cash player has */
X	int won, lost, push;	/* # hands won, lost, pushed */
X	int ctot, nobust;	/* running total of non-bust hands */
X	int dtot, dcnt;		/* running total for dealer since player started */
X};
X
Xvoid sockread(), sockwrite();
Xchar *sprintf();
END-of-defs.h
echo file: hand.c
sed 's/^X//' >hand.c << 'END-of-hand.c'
X#include "defs.h"
X
Xextern struct playent player[];
Xextern int deck[];
Xextern int card;
Xextern int dealerhand[];
Xextern int dealercard;
Xextern int lastdealt;
Xvoid show_cards(), show_hand(), tellall();
X
X/*
X * Sends string s1 to the current player (recipient), and s2 to
X * all the other players
X */
Xvoid tell2all(recipient, s1, s2)
Xint recipient;				/* player who gets s1 */
Xchar *s1, *s2;				/* s1 usually hand with all cards showing, other guys see s2 */
X{
X	int i;
X	struct playent *p;
X
X	for (i = 0, p = player; i < NPLAYERS; i++, p++)
X		if (p->occupied)
X			if (i == recipient)
X				sockwrite(p->socket, s1);
X			else
X				sockwrite(p->socket, s2);
X}
X
X/*
X * Process the hands of player i.  Handles hitting, doubing down, splitting,
X * standing, busting, updating screen.
X */
Xvoid process(i)
Xint i;				/* player to process */
X{
X	struct playent *p;
X	int hand;		/* which hand of maximum of 4 */
X	int code;		/* controls display of hand	*/
X	int quit;		/* TRUE when single hand is done */
X	char buf[SLEN];
X	int value;		/* value of players hand */
X
X	p = &player[i];
X/*
X * Player got dealt a blackjack
X */
X	if ((handval(&p->cards[0][0], 2) & CARDMASK) == 21)  {
X		sockwrite(p->socket, "MBlackjack!  You win!");
X		p->ncards[0] = 2;
X		return;
X	}
X	code = C_2DOWNRESTUP;
X	for (hand = 0; hand < p->nhands; hand++)  {
X		p->ncards[hand] = (hand == 0) ? 2 : 1;
X		quit = 0;
X		while (!quit && ((value = (handval(&p->cards[hand][0], p->ncards[hand]) & CARDMASK)) < 21))  {
X			if (p->ncards[hand] == 1)  {
X/*
X * Player just split, so make sure he has two cards.  Make sure this hand
X * is face up, because doubling down could have set the previous hand to
X * two up one down.
X */
X				p->cards[hand][p->ncards[hand]++] = deal_it(lastdealt);
X				code = C_ALLUP;
X				show_cards(i, hand, &p->cards[hand][0], p->ncards[hand], code);
X				if ((handval(&p->cards[hand][0], 2) & CARDMASK) == 21)
X					sockwrite(p->socket, "MBlackjack!  You win!");
X				continue;
X			}
X			sockwrite(p->socket, "R(h)it ( )stand (s)plit (d)ouble down (q)uit?");
X			sockread(p->socket, buf);
X			switch(buf[0])  {
X/*
X * Player is quitting after finishing this hand
X */
X			case 'q' :
X			case 'Q' :
X				sockwrite(p->socket, "MQuitting...");
X				p->quitting = TRUE;
X				break;
X/*
X * Player is hitting.
X */
X			case 'h' :
X			case 'H' :
X				p->cards[hand][p->ncards[hand]++] = deal_it(lastdealt);
X				show_cards(i, hand, &p->cards[hand][0], p->ncards[hand], code);
X				break;
X/*
X * Player is splitting hand
X */
X			case 's' :
X			case 'S' :
X				if (((p->cards[hand][0] % 13) != (p->cards[hand][1] % 13)) || (p->ncards[hand] != 2))  {
X					sockwrite(p->socket, "MCan't split that");
X					break;
X				}
X/*
X * Split the hand up and adjust hand, # of cards, and bet.
X */
X				p->ncards[hand] = 1;
X				p->ncards[p->nhands] = 1;
X				p->cards[p->nhands][0] = p->cards[hand][1];
X				p->bet[p->nhands] = p->bet[hand];
X				if (!(p->cards[hand][0] % 13))  {
X					p->nhands++;
X					p->ncards[0]++;
X/*
X * Player just split aces, so deal him two more cards face down, and
X * don't let him play anymore.  Tell him if he gets blackjack now.
X */
X					p->ncards[1]++;
X					p->cards[0][1] = deal_it(lastdealt);
X					p->cards[1][1] = deal_it(lastdealt);
X					code = C_1UP1DOWN;
X					show_cards(i, 0, &p->cards[0][0], p->ncards[0], code);
X					show_cards(i, 1, &p->cards[1][0], p->ncards[1], code);
X					return;
X					
X				}
X/*
X * When player splits, cards are all up.  I am told that when you split,
X * the new hand becomes the LAST hand, and that you finish each hand
X * before making sure each hand has two cards.  If this were not the case
X * you would need a code for each hand.
X */
X				code = C_ALLUP;
X				show_cards(i, hand, &p->cards[hand][0], p->ncards[hand], code);
X				show_cards(i, p->nhands, &p->cards[p->nhands][0], p->ncards[p->nhands], code);
X				p->nhands++;
X				break;
X/*
X * Player is standing.
X */
X			case ' ' :
X				quit = 1;
X				break;
X/*
X * Player is doubling down.
X */
X			case 'd' :
X			case 'D' :
X				code = C_2UP1DOWN;
X				if (((value != 10) && (value != 11)) || (p->ncards[hand] != 2))  {
X					sockwrite(p->socket, "MCan't double down on that");
X					break;
X				}
X				quit = 1;
X				p->cards[hand][p->ncards[hand]++] = deal_it(lastdealt);
X				p->bet[hand] *= 2;
X				show_cards(i, hand, &p->cards[hand][0], p->ncards[hand], code);
X				break;
X			}
X		}
X/*
X * Player busted, so he throws his cards down in disgust.
X */
X		if ((handval(&p->cards[hand][0], p->ncards[hand]) & CARDMASK) > 21)  {
X			show_cards(i, hand, &p->cards[hand][0], p->ncards[hand], C_ALLUP);
X			(void) sprintf(buf, "M%s busts", p->name);
X			tell2all(i, "MYou bust", buf);
X		}
X	}
X
X}
X
X/*
X * Play for the dealer.  Hit soft 17.
X */
Xvoid process_dealer()
X{
X	char buf[40];
X	int value;
X
X	(void) sprintf(buf, "PS%1d", NPLAYERS);
X	tellall(buf);
X	show_hand(NPLAYERS, 0, dealerhand, dealercard, C_ALLUP, buf);
X	tellall(buf);
X	while ((((value = handval(dealerhand, dealercard)) & CARDMASK) < 17) || (value & SOFT17))  {
X		dealerhand[dealercard++] = deal_it(lastdealt);
X		show_hand(NPLAYERS, 0, dealerhand, dealercard, C_ALLUP, buf);
X		tellall(buf);
X	}
X	(void) sprintf(buf, "Ps%1d", NPLAYERS);
X	tellall(buf);
X}
X
X/*
X * Figure out who won and pay them off.
X */
Xvoid who_won()
X{
X	int i, j;
X	struct playent *p;
X	char buf[40], playerbuf[40], otherbuf[40];
X	int dealertot, playertot;
X
X	dealertot = handval(dealerhand, dealercard) & CARDMASK;
X	for (i = 0, p = player; i < NPLAYERS; i++, p++)  {
X		if (!p->occupied)
X			continue;
X		for (j = 0; j < p->nhands; j++)  {
X			if (dealertot <= 21)  {
X				p->dtot += dealertot;
X				p->dcnt++;
X			}
X			show_hand(i, j, &p->cards[j][0], p->ncards[j], C_ALLUP, buf);
X			tellall(buf);
X			playertot = handval(&p->cards[j][0], p->ncards[j]) & CARDMASK;
X			if (playertot < 21)  {
X				p->ctot += playertot;
X				p->nobust++;
X			}
X/*
X * You lose
X */
X			if (((dealertot > playertot) && (dealertot <= 21)) || (playertot > 21))  {
X				(void) sprintf(otherbuf, "M%s loses hand #%d (%1d)", p->name, j + 1, -p->bet[j]);
X				(void) sprintf(playerbuf, "MYou lose hand #%d (%1d)", j + 1, -p->bet[j]);
X				tell2all(i, playerbuf, otherbuf);
X				p->cash -= p->bet[j];
X				p->lost++;
X			}
X/*
X * You win.  Make sure you don't lose when you have blackjack and dealer
X * has 21.
X */
X			else if ((playertot > dealertot) || (dealertot > 21) || (((playertot == 21) && (p->ncards[j] == 2)) &&  !((dealertot == 21) && (dealercard == 2))))  {
X/*
X * Blackjack pays 3 to 2.  *= doesn't work.
X */
X				if ((playertot == 21) && (p->ncards[j] == 2))
X					p->bet[j] = p->bet[j] * 1.5;
X				(void) sprintf(otherbuf, "M%s wins hand #%d (+%1d)", p->name, j + 1, p->bet[j]);
X				(void) sprintf(playerbuf, "MYou win hand #%d (+%1d)", j + 1, p->bet[j]);
X				tell2all(i, playerbuf, otherbuf);
X				p->cash += p->bet[j];
X				p->won++;
X			}
X/*
X * You push
X */
X			else   {
X				(void) sprintf(otherbuf, "M%s pushes hand #%d", p->name, j + 1);
X				(void) sprintf(playerbuf, "MYou push hand #%d", j + 1);
X				tell2all(i, playerbuf, otherbuf);
X				p->push++;
X			}
X		}
X	}
X}
X
X/*
X * Handle insurance betting
X */
Xvoid insurance()
X{
X	int i;
X	struct playent *p;
X	char buf[40], ans[40];
X
X	for (i = 0, p = player; i < NPLAYERS; i++, p++)
X		if (p->occupied)  {
X			(void) sprintf(buf, "PS%1d%s $%1d (%c%1d)", i, p->name, p->bet[0], p->cash >= 0 ? '+' : '-', abs(p->cash));
X			tellall(buf);
X			sockwrite(p->socket, "RInsurance?");
X			sockread(p->socket, ans);
X			if ((*ans == 'y') || (*ans =='Y'))
X				p->insured = TRUE;
X			else
X				p->insured = FALSE;
X			(void) sprintf(buf, "Ps%1d%s $%1d (%c%1d)", i, p->name, p->bet[0], p->cash >= 0 ? '+' : '-', abs(p->cash));
X			tellall(buf);
X		}
X}
X
X/*
X * Process insurance bets.  Insurance pays 2 to 1.
X */
Xvoid proc_insurance()
X{
X	int i;
X	struct playent *p;
X	char playerbuf[40], otherbuf[40];
X	int dealertot;
X
X	dealertot = handval(dealerhand, 2) & CARDMASK;
X	for(i = 0, p = player; i < NPLAYERS; i++, p++)  {
X		if (!p->occupied || !p->insured)
X			continue;
X		if (dealertot == 21)  {
X			(void) sprintf(otherbuf, "M%s wins insurance bet (+%1d)", p->name, p->bet[0]);
X			(void) sprintf(playerbuf, "MYou win insurance bet (+%1d)", p->bet[0]);
X			p->cash += p->bet[0];
X			tell2all(i, playerbuf, otherbuf);
X		}
X		else  {
X			(void) sprintf(otherbuf, "M%s loses insurance bet (%1d)", p->name, (int) -(p->bet[0] * 0.5));
X			(void) sprintf(playerbuf, "MYou lose insurance bet (%1d)", (int) -(p->bet[0] * 0.5));
X			p->cash -= p->bet[0] * 0.5;
X			tell2all(i, playerbuf, otherbuf);
X		}
X	}
X}
X
X
X/*
X * Handle betting.
X */
Xvoid betting()
X{
X	int i;
X	struct playent *p;
X	char buf[40];
X
X	for (i = 0, p = player; i < NPLAYERS; i++, p++)
X		if (p->occupied)  {
X			(void) sprintf(buf, "PS%1d%s $%1d (%c%1d)", i, p->name, p->bet[0], p->cash >= 0 ? '+' : '-', abs(p->cash));
X			tellall(buf);
X			sockwrite(p->socket, "rBet? ");
X			sockread(p->socket, buf);
X/*
X * A null line repeats the last bet.
X */
X			if (strlen(buf))
X				(void) sscanf(buf, "%d", &p->bet[0]);
X			(void) sprintf(buf, "Ps%1d%s $%1d (%c%1d)", i, p->name, p->bet[0], p->cash >= 0 ? '+' : '-', abs(p->cash));
X			tellall(buf);
X		}
X}
END-of-hand.c
echo file: opensock.c
sed 's/^X//' >opensock.c << 'END-of-opensock.c'
X#include "defs.h"
X
Xextern int s;
Xextern struct sockaddr_in sockaddr;
X
X/*
X * Open main socket and set s to it.
X */
Xvoid opensock()
X{
X	struct hostent *host;
X#ifdef ROOTPRIV
X	struct servent *dealer;
X#endif
X	char hostnm[SLEN];
X
X	(void) gethostname(hostnm, SLEN);
X	if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)  {
X		perror("socket");
X		exit(1);
X	}
X	if ((host = gethostbyname(hostnm)) == NULL)  {
X		perror("gethostbyname");
X		exit(1);
X	}
X	bzero((char *) &sockaddr, sizeof (sockaddr));
X	bcopy(host->h_addr, (char *) &sockaddr.sin_addr, host->h_length);
X	sockaddr.sin_family = AF_INET;
X#ifdef ROOTPRIV
X	if ((dealer = getservbyname(SERVICE, PROTO)) == NULL)  {
X		fputs("blackjack: service not found\n", stderr);
X		exit(1);
X	}
X	sockaddr.sin_port = htons(dealer->s_port);
X#else
X	sockaddr.sin_port = htons(PORT);
X#endif
X/*
X * Allow reuse of local addresses.  Speeds up reinvocation of dealer.
X */
X	(void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) 0, 0);
X	if (bind(s, (char *) &sockaddr, sizeof(sockaddr)) < 0)  {
X		perror("bind");
X		exit(1);
X	}
X	if ((listen(s, 5)) == -1)  {
X		perror("listen");
X		exit(1);
X	}
X}
END-of-opensock.c
echo file: sockets.c
sed 's/^X//' >sockets.c << 'END-of-sockets.c'
X#include "defs.h"
X
Xchar *getenv();
X
X/*
X * Make connection to host running the blackjack dealer server,
X * return fd of new socket.
X */
Xint make_con(servhost)
Xchar *servhost;				/* name of host running server */
X{
X	int s;
X	struct hostent *host;
X	struct servent *dealer;
X	struct sockaddr_in sockaddr;
X
X	if ((host = gethostbyname(servhost)) == NULL)  {
X		perror("gethostbyname");
X		exit(1);
X	}
X	if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)  {
X		perror("socket");
X		exit(1);
X	}
X	bzero((char *) &sockaddr, sizeof (sockaddr));
X	bcopy(host->h_addr, (char *) &sockaddr.sin_addr, host->h_length);
X	sockaddr.sin_family = AF_INET;
X#ifdef ROOTPRIV
X	if ((dealer = getservbyname(SERVICE, PROTO)) == NULL)  {
X		fputs("blackjack: service not found\n", stderr);
X		exit(1);
X	}
X	sockaddr.sin_port = htons(dealer->s_port);
X#else
X	sockaddr.sin_port = htons(PORT);
X#endif
X	if (connect(s, (char *) &sockaddr, sizeof(sockaddr)) < 0)  {
X		perror("connect to dealer");
X		exit(1);
X	}
X	return(s);
X}
X
X/*
X * Send host your name and machine (name@machine)
X */
Xvoid send_name(s)
Xint s;					/* socket to talk to host on */
X{
X	char *name, host[SLEN], buf[SLEN];
X
X	if ((name = getenv("BJ")) == NULL)
X		if ((name = getenv("NAME")) == NULL)
X			name = getenv("USER");
X	(void) gethostname(host, SLEN);
X	(void) sprintf(buf, "%s@%s", name, host);
X	sockwrite(s, buf);
X}
END-of-sockets.c
echo file: sockio.c
sed 's/^X//' >sockio.c << 'END-of-sockio.c'
X#include <stdio.h>
X/*
X * Routines to send and receive on sockets.  Four bytes of length are
X * sent, followed by the null terminated string.
X *
X */
Xvoid sockread(s, buf)
Xint s;				/* socket to talk on */
Xchar *buf;			/* string to send */
X{
X	int nbytes;
X
X	(void) read(s, (char *) &nbytes, sizeof(int));
X	nbytes = htons(nbytes);
X	(void) read(s, buf, nbytes);
X}
X
X
Xvoid sockwrite(s, buf)
Xint s;				/* socket to talk on */
Xchar *buf;			/* string to read on */
X{
X	int nbytes;
X
X	nbytes = htons(strlen(buf) + 1);
X	(void) write(s, (char *) &nbytes, sizeof(int));
X	(void) write(s, buf, nbytes);
X}
END-of-sockio.c
echo file: table.c
sed 's/^X//' >table.c << 'END-of-table.c'
X#include "defs.h"
X
Xextern struct playent player[];
Xextern int deck[];
Xextern int card;
Xextern int dealerhand[];
Xextern int dealercard;
Xextern int needtoshuffle;
Xextern int lastdealt;
X
Xvoid tell2all();
X
X/*
X * Mark player as unoccupied and close file descriptor.
X */
Xvoid del_players()
X{
X	int i, total;
X	struct playent *p;
X	char buf[80];
X	float davg, pavg;;
X
X	for (i = 0, p = player; i < NPLAYERS; i++, p++)
X		if (p->quitting)  {
X			davg = (p->dcnt) ? ((float) p->dtot) / p->dcnt : 0;
X			total = p->lost + p->won + p->push;
X			pavg = (p->nobust) ? ((float) p->ctot) / p->nobust : 0;
X			sockwrite(p->socket, "MThank you for playing");
X			(void) sprintf(buf, "MYou played %d hands -  won %d lost %d pushed %d - %s $%d avg %3.1f davg %3.1f\n", total, p->won, p->lost, p->push, (p->cash >= 0) ? "up" : "down", abs(p->cash), pavg, davg);
X			sockwrite(p->socket, buf);
X			sockwrite(p->socket, "Q");
X			p->quitting = FALSE;
X			p->occupied = FALSE;
X			(void) close(p->socket);
X			p->socket = -1;
X		}
X}
X
X/*
X * Tell player which seats are empty and let him choose one.
X */
Xint getchair(s)
Xint s;				/* socket to talk to new player on */
X{
X	int i, len;
X	struct playent *p;
X	char buf[80], tmp[10];
X
X	(void) strcpy(buf, "RThe following seats are empty:");
X	len = strlen(buf);
X	for (i = 0, p = player; i < NPLAYERS; i++, p++)
X		if (!p->occupied)  {
X			(void) sprintf(tmp, " %d", i + 1);
X			(void) strcat(buf, tmp);
X		}
X	if (len == strlen(buf))
X		return(-1);
X	(void) strcat(buf, ".  Choose one: ");
X	do  {
X		sockwrite(s, buf);
X		sockread(s, tmp);
X		(void) sscanf(tmp, "%d", &i);
X	}  while ((--i < 0) || (i > (NPLAYERS - 1)) || (player[i].occupied));
X	return(i);
X}
X
X/*
X * Send everyone the string buf
X */
Xvoid tellall(buf)
Xchar *buf;
X{
X	int i;
X	struct playent *p;
X
X	for (i = 0, p = player; i < NPLAYERS; i++, p++)
X		if (p->occupied)
X			sockwrite(p->socket, buf);
X}
X
X/*
X * Return # of players in game.
X */
Xint numplayer()
X{
X	int i, nump = 0;
X	struct playent *p;
X
X	for (i = 0, p = player; i < NPLAYERS; i++, p++)
X		if (p->occupied)
X			nump++;
X	return(nump);
X}
X
X/*
X * Shuffle from the top of the deck (card 51) to the card topcard
X * (the first card of the last deal, or 0).  This way you can
X * shuffle just a portion of the deck if you run out of cards
X * during a hand (just like in Tahoe).  topcard is zero if the
X * whole deck is to be shuffled (i.e. you know you don't have
X * enough cards).
X */
Xvoid shuffle(topcard)
Xint topcard;				/* first card of last deal */
X{
X	int pos, i, swap, save;
X	long random();
X
X	save = topcard;
X	for (i = 51; i >= topcard; i--)  {
X		pos = ((((float) (random() & 0x7FFFFFFF)) / 0x7FFFFFFF) * (51 - topcard)) + topcard;
X		swap = deck[pos];
X		deck[pos] = deck[i];
X		deck[i] = swap;
X	}
X	tellall("Mshuffling deck");
X	card = 51;
X	needtoshuffle = FALSE;
X/*
X * If we are only shuffling part of the deck, make sure we shuffle
X * it the next time through.
X */
X	if (save)
X		needtoshuffle = TRUE;
X}
X
X/*
X * Deal everyone two cards.
X */
Xvoid deal()
X{
X	struct playent *p;
X	int i, j;
X
X	lastdealt = card + 1;
X	for (i = 0; i < 2; i++)  {
X		for (j = 0, p = player; j < NPLAYERS; j++, p++)
X			if (p->occupied)  {
X				p->cards[0][i] = deck[card--];
X				p->insured = FALSE;
X				p->nhands = 1;
X				p->ncards[0] = 2;
X			}
X		dealerhand[i] = deck[card--];
X	}
X	dealercard = 2;
X}
X
X/*
X * Append card i (i.e. 2J KH 10S) to end of string buf.
X */
Xvoid cardval(i, buf)
Xint i;					/* card */
Xchar *buf;				/* buffer to append to */
X{
X	int suit, cval;
X	static char *allsuit[] = {"C ", "D ", "H ", "S "};
X	static char *allval[] =
X		{"A", "2", "3", "4", "5", "6", "7", "8", "9",
X		"10", "J", "Q", "K"};
X
X	suit = i / 13;
X	cval = i % 13;
X	(void) strcat(buf, allval[cval]);
X	(void) strcat(buf, allsuit[suit]);
X}
X
X/*
X * Convert players hand to a string based upon code.
X */
Xvoid show_hand(player, hand, cards, ncards, code, buf)
Xint player;				/* player # */
Xint hand;				/* hand # */
Xint *cards;				/* pointer to cards of hand */
Xint ncards;				/* # cards of this hand */
Xint code;				/* controls display of hand */
Xchar *buf;				/* buf to write on */
X{
X	char playerid[2], handid[2];
X	int i;
X
X	(void) strcpy(buf, "C");
X	playerid[0] = player + '0';
X	playerid[1] = '\0';
X	(void) strcat(buf, playerid);
X	handid[0] = hand + '0';
X	handid[1] = '\0';
X	(void) strcat(buf, handid);
X
X#ifdef BARNEYS
X	code = C_ALLUP;
X#endif
X
X	switch(code)  {
X	case C_ALLUP:				/* all cards up */
X		for (i = 0; i < ncards; i++)
X			cardval(cards[i], buf);
X		break;
X	case C_2DOWNRESTUP:			/* 2 down, rest up */
X		(void) strcat(buf, "?? ?? ");
X		for (i = 2; i < ncards; i++)
X			cardval(cards[i], buf);
X		break;
X	case C_2UP1DOWN:			/* 2 up, 1 down */
X		cardval(cards[0], buf);
X		cardval(cards[1], buf);
X		(void) strcat(buf, "?? ");
X		break;
X	case C_1DOWN1UP:			/* 1 down, 1 up */
X		(void) strcat(buf, "?? ");
X		cardval(cards[1], buf);
X		break;
X	case C_1UP1DOWN:			/* 1 up, 1 down */
X		cardval(cards[0], buf);
X		(void) strcat(buf, "?? ");
X		break;
X	}
X}
X
X/*
X * Return the value of a hand.  Set hi bit if it's soft 17.
X */
Xint handval(cards, ncards)
Xint *cards, ncards;			/* array of cards and the # of them */
X{
X	int soft = 0, val = 0, i;
X	static int cardvalue[] = {11, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10};
X
X	for (i = 0; i < ncards; i ++)  {
X		val += cardvalue[cards[i] % 13];
X		if (!(cards[i] % 13))
X			soft++;
X	}
X	if (soft && (val > 21))
X		while (soft--)  {
X			val -= 10;
X			if (val <= 21)
X				break;
X		}
X	if (soft && (val == 17))
X		return(SOFT17 && val);
X	else
X		return(val);
X}
X
X/*
X * Show the deal to all the players.
X */
Xvoid showdeal()
X{
X	int i;
X	char playerbuf[40], otherbuf[40];
X	char dbuf[40];
X	struct playent *p;
X
X	show_hand(NPLAYERS, 0, dealerhand, 2, C_1DOWN1UP, dbuf);
X	for (i = 0, p = player; i < NPLAYERS; i++, p++)
X		if (p->occupied)  {
X/*
X * Show player his hand.
X */
X			show_hand(i, 0, (int *) p->cards, 2, C_ALLUP, playerbuf);
X/*
X * Show everyone else two ??s
X */
X			show_hand(i, 0, (int *) p->cards, 2, C_2DOWNRESTUP, otherbuf);
X			tell2all(i, playerbuf, otherbuf);
X			sockwrite(p->socket, dbuf);
X		}
X}
X
X/*
X * Show all players a given hand.  Player gets to see his own cards,
X * everyone else's view is based on code.  If BARNEYS is defined, all
X * cards are dealt face up.
X */
Xvoid show_cards(playernum, hand, cards, ncards, code)
Xint playernum;				/* player number */
Xint hand;				/* hand number */
Xint *cards;				/* array of cards */
Xint ncards;				/* number of cards */
Xint code;				/* controls display of hand */
X{
X	char playerbuf[40], otherbuf[40];
X
X	show_hand(playernum, hand, cards, ncards, code, otherbuf);
X	show_hand(playernum, hand, cards, ncards, C_ALLUP, playerbuf);
X	tell2all(playernum, playerbuf, otherbuf);
X}
X
X/*
X * Hand out cards.  This routine is used so that we can shuffle
X * if we run out of cards.
X */
Xint deal_it(lastdealt)
Xint lastdealt;				/* the first card of the last deal */
X{
X	if (card < 0)
X		shuffle(lastdealt);
X	return(deck[card--]);
X}
END-of-table.c
exit