DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T t

⟦c9708103f⟧ TextFile

    Length: 19603 (0x4c93)
    Types: TextFile
    Names: »turn.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/General/Cubes/turn.c« 

TextFile

/*	vi:set sw=4 ts=4: */
#ifndef	lint
static char	sccsid[] = "@(#)turn.c 5.1 (G.M. Paris) 89/01/22";
#endif	lint

/*
**
**	cubes 5.1  Copyright 1989 Gregory M. Paris
**		Permission granted to redistribute on a no charge basis.
**		All other rights are reserved.
**
*/

#include	<stdio.h>
#include	<syslog.h>
#include	<strings.h>
#include	<ctype.h>
#include	"cubes.h"

extern boolean	inprogress;
extern int		winscore;
extern int		turnnum;
extern player	plr[];
extern char	   *histfmtplr();
extern long		histavgplr();
static boolean	reallyquit();
extern boolean	isproxy();

/*
**	turn: do a single player's turn
**		Return turn score or 0 if player left game.
**		If points were thrown away, return them as negative.
*/
turn(c)
register int	c;
{
	diceset		dice, hold;
	int			winsqu;

	/*
	**	Announce who's up to the world.
	*/
	if(annturn(c) < 0)
		return 0;				/* left game */

	/*
	**	Reset the dice to their initial state.
	*/
	initdice(&dice);
	showdice(c, &dice);
	if(plr[c].p_stat == Active && isproxy(c) == False) {
		if(pfirst(c) == -2) {		/* timeout */
			pickproxy(c);			/* auto-autopilot */
			(void) tellstatus(c);	/* inform player */
		}
	}
	if(plr[c].p_stat == Computer || isproxy(c) == True)
		if(cfirst(c) < 0)
			return 0;			/* left game */
	if(plr[c].p_stat == Inactive)
		return 0;				/* quit */

	winsqu = 0;
	for(;;) {
		/*
		**	Check for asynchronous requests from other players.
		**	Then roll the dice and evaluate the roll.
		*/
		async(c);
		rolldice(&dice);
		evaluate(&dice);

		/*
		**	If Nothing rolled, this turn is over with no points scored.
		**	We jump to the end of the routine for final processing.
		*/
		if(dice.d_best == Nothing)
			goto gotnothing;

		/*
		**	Display the current state of the dice.
		**	Score the points we'd have were we to hold now.
		*/
		showdice(c, &dice);
		hold = dice;
		scoredice(&hold);

		/*
		**	Trying to properly assess when points have been squandered,
		**	we adjust the value of winsqu here.  This value represents
		**	points that could have been held with before crossing the
		**	winscore threshold.  If we've already crossed winscore,
		**	we don't adjust this value.
		*/
		if(plr[c].p_score + hold.d_pts_turn < winscore)
			if(hold.d_pts_turn > winsqu)
				winsqu = hold.d_pts_turn;

		/*
		**	If something was scored on the roll, then we ask the player what
		**	to do about it.  We must re-evaluate and redisplay the roll after
		**	asking since the scoring combinations may have changed.  We check
		**	for Nothing scored as a precaution, but that situation is not
		**	allowed by cquery or pquery.  Adjust point values.
		*/
		if(plr[c].p_stat == Active && isproxy(c) == False) {
			if(pquery(c, &dice) == -2) {		/* timeout */
				pickproxy(c);					/* auto-autopilot */
				(void) tellstatus(c);			/* inform player */
			}
		}
		if(plr[c].p_stat == Computer || isproxy(c) == True)
			cquery(c, &dice);
		if(plr[c].p_stat == Inactive)
			return 0;							/* quit */
		evaluate(&dice);
		scoredice(&dice);
		if(hold.d_pts_max > dice.d_pts_max)		/* discarded some points */
			dice.d_pts_max = hold.d_pts_max;	/* reflect that fact */
		if(dice.d_best == Nothing) {
			syslog(LOG_WARNING, "turn: %s `%s' tried to roll all scoring dice",
				plr[c].p_stat == Computer ? "Computer" : "Player",
				plr[c].p_name);
			goto gotnothing;
		}

		/*
		**	If d_again is False, the player has chosen to hold.
		**	Leave the loop and do the hold processing at routine end.
		*/
		if(dice.d_again == False) 
			break;

		/*
		**	Otherwise, reset and redisplay dice prior to next roll.
		*/
		pickup(&dice);
		showdice(c, &dice);
	}

	/*
	**	We get here when dice.d_again is False.  Don't sleep or hesistate
	**	here because it disrupts the rhythm of the game.  There's not much
	**	new information at this point anyway.
	*/
	strcpy(dice.d_mesg, "held");
	showdice(c, &dice);

	/*
	**	If not onboard yet, mark p_onboard True.  The [cp]query
	**	routines should enforce ONBOARD, but note any anomaly.
	*/
	if(plr[c].p_onboard == False) {
		if(dice.d_pts_turn < ONBOARD)
			syslog(LOG_WARNING, "turn: %s held %d getting onboard",
				plr[c].p_name, dice.d_pts_turn);
		plr[c].p_onboard = True;
	}

	/*
	**	If we reach winscore with less than OFFBOARD points,
	**	note the condition.  OFFBOARD should be enforced by
	**	the [cp]query routines.
	*/
	if(plr[c].p_score < winscore && dice.d_pts_turn < OFFBOARD)
		if(plr[c].p_score + dice.d_pts_turn >= winscore)
			syslog(LOG_WARNING, "turn: %s held %d getting offboard",
				plr[c].p_name, dice.d_pts_turn);

	/*
	**	Turn points are added to the player's score.
	**	The player may have discarded some points and not
	**	won them back.  If so, add those points to p_squander.
	*/
	plr[c].p_score += dice.d_pts_turn;
	if(dice.d_pts_turn < dice.d_pts_max)
		plr[c].p_squander += dice.d_pts_max - dice.d_pts_turn;
	return dice.d_pts_turn;				/* normal */

gotnothing:
	/*
	**	We get here if we rolled a nothing.  Score the dice.
	*/
	scoredice(&dice);

	/*
	**	If we aren't on board and didn't get ONBOARD points
	**	during the turn, we don't count any points as squandered,
	**	since the player had no choice.
	*/
	if(plr[c].p_onboard == False && dice.d_pts_max < ONBOARD)
		dice.d_pts_max = 0;

	/*
	**	If the max points during the turn would have put us over,
	**	but we didn't get OFFBOARD points, we don't count the
	**	points as squandered, since the player had little choice.
	**	This isn't always correct, since the player might have been
	**	able to hold before crossing winscore.  We account for this
	**	situation by making use of the value of winsqu, which is
	**	the maximum number of points that could have been held
	**	before going over the winscore threshold.
	*/
	if(plr[c].p_score < winscore && dice.d_pts_max < OFFBOARD)
		if(plr[c].p_score + dice.d_pts_max >= winscore)
			dice.d_pts_max = winsqu;

	/*
	**	Now that d_pts_max has been adjusted, we can show the dice.
	*/
	showdice(c, &dice);
	sleep(2);

	/*
	**	Squandered d_pts_max, no points gained.
	*/
	plr[c].p_squander += dice.d_pts_max;
	return -dice.d_pts_max;		/* normal */
}

/*
**	keephelp: long help message for M_KEEP prompt
XXX		We assume there's only seven lines of help window.
*/
static char	   *keephelp[] = {
  "{list}   Roll       roll non-scoring and listed dice (synonyms=[rt]{list})",
  "k{list}  Keep       roll all but the dice listed (synonyms=[,s]{list})",
  "h        Hold       don't roll again, save points scored (synonyms=[.nq])",
  "m,a,b    Ranks      display ranking info for you, or above or below you",
  "p{num}   Player     display ranking info for player {num} in roster",
  "x        Autopilot  engage the autopilot (in case of interruptions)",
  "e        Exit       leave the game early (not recommended)",
  (char *)0	/* end marker */
};

/*
**	pquery: ask the player what to do next
**
**	choices are:
**		Exit			leave the game
**		Autopilot (x)	engage the autopilot
**		Hold/Quit		end turn (no more rolling)
**		Keep/Save		score named dice and roll again
**		Roll/Throw		throw named dice (scoring those left)
**		Myrank			display player's ranking info
**		Aboveme			display ranking of next better player
**		Belowme			display ranking of next worse player
*/
pquery(c, pd)
int					c;
register diceset   *pd;
{
	register int	n, d;
	int				stay, rankq, errs;
	boolean			error;
	char			msgbuf[MESGLEN];
	char			cmd[32], digits[32];
	diceset			save, temp;

	/*
	**	If we got passed a nothing roll then we wonder why we are here
	**	and return with a little comment.
	*/
	if(pd->d_best == Nothing) {
		syslog(LOG_DEBUG, "pquery: got a nothing roll");
		if(simp(c, M_INFO, "You rolled a big zip.") < 0)
			return -1;
		return 0;
	}

	/*
	**	Calculate the number of points if we hold now.
	*/
	temp = *pd;
	scoredice(&temp);
	stay = temp.d_pts_turn;

#define	MAXRANKQ	3				/* limit abuse of rank queries */
#define	MAXERRS		(2 * MAXRANKQ)	/* limit abuse of error recovery */
	rankq = errs = 0;
	save = *pd;			/* save a copy of the dice */
	do {
		error = False;	/* no error yet */
		*pd = save;		/* get back the saved version */

		/*
		**	Send prompt.  Read and parse response.
		**
		**	We've got to prevent a player from causing everybody else
		**	to time out by giving three minutes worth of erroneous
		**	responses.  If we've reached MAXERRS, give a message
		**	and do the default action.
		*/
		if(errs < MAXERRS) {
			sprintf(msgbuf, "%d %d saved, %d showing; command? [rkh%sxe?]\r\n",
				M_KEEP, pd->d_pts_turn, stay - pd->d_pts_turn,
				rankq < MAXRANKQ ? "mabp" : "");
			switch(dialogue(c, msgbuf, sizeof msgbuf)) {
			case -2: msgbuf[0] = '\0'; break;	/* timeout, do default */
			case -1: return -1;					/* left game */
			}
		} else {
			if(++plr[c].p_timeouts >= MAXTIMO) {
				(void) simp(c, M_DOWN, "Too many errors -- goodbye!");
				oldplayer(c);
				return -1;
			}
			if(simp(c, M_ARGE, "Too many errors -- doing default.") < 0)
				return -1;
			msgbuf[0] = '\0';
		}
		cmd[0] = '\0', digits[0] = '\0';
		(void) sscanf(msgbuf, "%[^0123456789]%[0123456789]", cmd, digits);

		switch(cmd[0]) {
		case 'e': case 'E':		/* Exit -- leave game */
			if(reallyquit(c) == True)
				return -1;
			error = True;
			break;

		case 'x': case 'X':		/* enter autopilot (temporary proxy) mode */
			return -2;			/* pretend timeout, let caller do work */
		
		case 'm': case 'M':		/* Myrank -- send player's ranking info */
			if(rankq >= MAXRANKQ)
				goto toomany;
			if(myrank(c, False) < 0)
				return -1;
			error = True;		/* not an error, but we must continue */
			break;

		case 'a': case 'A':		/* Aboveme -- send better player's ranking */
			if(rankq >= MAXRANKQ)
				goto toomany;
			if(aboveme(c, False) < 0)
				return -1;
			error = True;		/* not an error, but we must continue */
			break;

		case 'b': case 'B':		/* Belowme -- send worse player's ranking */
			if(rankq >= MAXRANKQ)
				goto toomany;
			if(belowme(c, False) < 0)
				return -1;
			error = True;		/* not an error, but we must continue */
			break;
		
		case 'p': case 'P':		/* Player: send info about numbered player */
			if(rankq >= MAXRANKQ)
				goto toomany;
			if(plrrank(c, atoi(digits)-1, False) < 0)
				return -1;
			error = True;		/* maybe not an error, but we must continue */
			break;

		case 'n': case 'N':		/* No -- don't roll again */
		case 'h': case 'H':		/* Hold -- hold at current score */
		case 'q': case 'Q':		/* Quit -- quit rolling */
		case '.':				/* Hold (for numeric keypads) */
			pd->d_again = False;		/* quit rolling */
			keepall(pd);				/* keep all scoring dice */
			temp = *pd;					/* temporary for score evaluation */
			scoredice(&temp);			/* calculate turn points */
			if(plr[c].p_onboard == False) {	/* not on board yet */
				if(temp.d_pts_turn < ONBOARD) {	/* threshold */
					sprintf(msgbuf,
						"%d You can't hold until you get %d points.\r\n",
						M_ARGE, ONBOARD);
					if(message(c, msgbuf) < 0)
						return -1;
					error = True;
					break;
				}
			} else if(plr[c].p_score + temp.d_pts_turn >= winscore
			  && plr[c].p_score < winscore) {	/* about to go off board */
				if(temp.d_pts_turn < OFFBOARD) {	/* threshold */
					sprintf(msgbuf,
						"%d You can't hold until you get %d points.\r\n",
						M_ARGE, OFFBOARD);
					if(message(c, msgbuf) < 0)
						return -1;
					error = True;
					break;
				}
			}
			return 0;					/* all done */

		case 'k': case 'K':		/* Keep -- keep only named dice */
		case 's': case 'S':		/* Save -- save only named dice */
		case ',':				/* Keep (komma, for numeric keypads) */
			pd->d_again = True;			/* roll again */
			if(digits[0] == '\0') {		/* no dice named */
				keepall(pd);			/* default is to keep all */
				return 0;				/* all done */
			}
			freeall(pd);				/* free all rolled dice */
			for(n = 0;digits[n] != '\0';++n) {
				if((d = digits[n] - '1') < 0 || d >= NDICE) {
					if(simp(c, M_ARGE, "Die number is out of range.") < 0)
						return -1;
					error = True;
					break;
				}
				if(pd->d_comb[d] == Nothing) {
					if(simp(c, M_ARGE, "You can keep only scoring dice.") < 0)
						return -1;
					error = True;
					break;
				}
				if(pd->d_comb[d] == Previous || pd->d_stat[d] != Free) {
					if(simp(c, M_ARGE, "You can keep only rolled dice.") < 0)
						return -1;
					error = True;
					break;
				}
				pd->d_stat[d] = Taken;
			}
			break;

		case '\0':				/* default command */
/*		case 'y': case 'Y':		/* Yes -- roll again */
		case 'r': case 'R':		/* Roll -- roll named dice and non-scoring */
		case 't': case 'T':		/* Throw -- throw named dice and non-scoring */
			pd->d_again = True;			/* roll again */
			if(digits[0] == '\0') {		/* no dice named */
				keepall(pd);			/* default is to keep all */
				return 0;				/* all done */
			}
			keepall(pd);				/* keep all rolled, scoring dice */
			for(n = 0;digits[n] != '\0';++n) {
				if((d = digits[n] - '1') < 0 || d >= NDICE) {
					if(simp(c, M_ARGE, "Die number is out of range.") < 0)
						return -1;
					error = True;
					break;
				}
				if(pd->d_comb[d] == Previous || pd->d_stat[d] == Held) {
					if(simp(c,M_ARGE,"You can't roll previously held dice.")<0)
						return -1;
					error = True;
					break;
				}
				if(pd->d_stat[d] != Free) {
					pd->d_stat[d] = Free;
					++pd->d_rolling;
				}
			}
			break;
		
		case '?':	/* help */
			if(multimesg(c, M_HELP, keephelp) < 0)
				return -1;
			error = True;
			break;

toomany:
		default:
			if(invalid(c) > 0)
				return -1;
			error = True;
			break;
		}

		/*
		**	If we've arrived here without error, then a subset of the rolled
		**	dice was chosen.  Get a temporary copy of the roll as it stands
		**	and evaluate it.  If nothing scored it's an error.
		*/
		if(error == False) {
			temp = *pd;
			evaluate(&temp);
			if(temp.d_best == Nothing) {
				if(simp(c, M_ARGE, "You must save a scoring combination.") < 0)
					return -1;
				error = True;
			}
		}
	} while(++errs, error == True);

	return 0;

#undef	MAXRANKQ
#undef	MAXERRS
}

/*
**	annturn: announce who's turn it is
*/
annturn(c)
int		c;
{
	char	msgbuf[MESGLEN];

	if(plr[c].p_stat != Computer) {
		sprintf(msgbuf,
			"%d Turn %d: player %d, you are up.\r\n", M_TURN, turnnum, c+1);
		if(message(c, msgbuf) < 0)
			return -1;
	}
	sprintf(msgbuf, "%d Turn %d: player %d, %s, is up.\r\n",
		M_TURN, turnnum, c+1, plr[c].p_name);
	(void) announce(c, msgbuf);
	return 0;
}

/*
**	statchar: map die status to a single character
*/
statchar(s)
diestat	s;
{
	switch(s) {
	case Free:		return 'f';
	case Held:		return 'h';
	case Taken:		return 't';
	case Rolled:	return 'r';
	default:		return '?';
	}
}

/*
**	facechar: map die face to a single character
*/
facechar(f)
int		f;
{
	return (f == BADFACE) ? '*' : (f + '0');
}

/*
**	combchar: map die combination to a single character
*/
combchar(c)
combination	c;
{
	switch(c) {
	case Previous:			return 'p';
	case Nothing:			return 'n';
	case Joker:				return 'j';
	case Five:				return '5';
	case Ace:				return '1';
	case Three_of_a_kind:	return 't';
	case Small_straight:	return 'l';	/* l for little */
	case Four_of_a_kind:	return 'f';
	case Asm_straight:		return 'b';	/* b for built */
	case Straight:			return 's';
	case All_of_a_kind:		return 'a';
	default:				return '?';
	}
}

/*
**	showdice: present the current status of the dice to all players
*/
showdice(c, pd)
int					c;
register diceset   *pd;
{
	register int	d, cc;
	char			stats[NDICE+1];
	char			faces[NDICE+1];
	char			combs[NDICE+1];
	char			msgbuf[MESGLEN+4*NDICE+32];

	for(d = 0;d < NDICE;++d) {
		stats[d] = statchar(pd->d_stat[d]);
		faces[d] = facechar(pd->d_face[d]);
		combs[d] = combchar(pd->d_comb[d]);
	}
	stats[d] = faces[d] = combs[d] = '\0';

	sprintf(msgbuf, "%d %d %s %s %s %d %d %d %d %s\r\n",
		M_DICE, c, stats, faces, combs,
		pd->d_pts_roll, pd->d_pts_dice, pd->d_pts_turn, pd->d_pts_max,
		pd->d_mesg);

	for(cc = 0;cc < PLAYERS;++cc) {
		switch(plr[cc].p_stat) {
		case Active:
		case Waiting:
		case Watching:
			(void) message(cc, msgbuf);
			break;
		}
	}
}

/*
**	rfsthelp: long help message for M_RFST prompt
*/
static char	   *rfsthelp[] = {
  "<return>  Roll       roll the dice (synonyms=[yrt])",
  "m         Myrank     display your ranking info",
  "x         Autopilot  engage the autopilot (in case of interruptions)",
  "e         Exit       leave the game early (not recommended)",
  (char *)0	/* end marker */
};

/*
**	pfirst: query player for first roll of dice
*/
pfirst(c)
int		c;
{
	char	msgbuf[MESGLEN];
	int		n, errs;

#define	MAXERRS	3	/* limit abuse */
	for(errs = 0;;++errs) {
		if(errs < MAXERRS) {
			sprintf(msgbuf, "%d Ready to roll? [ymxe?]\r\n", M_RFST);
			if((n = dialogue(c, msgbuf, sizeof msgbuf)) < 0)
				return n;
		} else {
			if(++plr[c].p_timeouts >= MAXTIMO) {
				(void) simp(c, M_ARGE, "Too many errors -- goodbye!");
				oldplayer(c);
				return -1;
			}
			if(simp(c, M_ARGE, "Too much tomfoolery -- rolling!") < 0)
				return -1;
			return 0;	/* roll */
		}

		switch(msgbuf[0]) {
		case '\0':				/* default */
		case 'y': case 'Y':		/* Yes */
		case 'r': case 'R':		/* Roll */
			return 0;
		case 'e': case 'E':		/* Exit */
			if(reallyquit(c) == True)
				return -1;
			break;
		case 'x': case 'X':		/* autopilot */
			return -2;			/* pretend timeout */
		case 'm': case 'M':		/* Myrank */
			if(myrank(c, False) < 0)
				return -1;
			break;
		case '?':				/* Help */
			if(multimesg(c, M_HELP, rfsthelp) < 0)
				return -1;
			break;
		default:
			if(invalid(c) < 0)
				return -1;
			break;
		}
	}
#undef	MAXERRS
}

/*
**	reallyquit: does player really want to quit?
*/
static boolean
reallyquit(c)
int		c;
{
	int			half;
	boolean		wise	= True;
	char		msgbuf[128];

	if(inprogress == True && plr[c].p_stat == Active) {
		if(plr[c].p_onboard == True)
			wise = False;
		else if((half = histavgplr(c)) > 0) {
			half = (half * winscore) / (2 * WINSCORE);
			if(plr[c].p_score > half
			|| winscore - highscore(c, (int *)0) > half - plr[c].p_score)
				wise = False;
		}
	}

	sprintf(msgbuf, "%d %s [ny]\r\n", M_RFST,
		wise == True ? "Do you really want to quit?"
		: "It seems unwise to quit now.  Are you sure?");
	if(dialogue(c, msgbuf, sizeof msgbuf) < 0)
		return True;

	if(msgbuf[0] == 'y' || msgbuf[0] == 'Y') {
		(void) simp(c, M_DOWN, "Quitters never win.");
		oldplayer(c);
		return True;
	}

	return False;
}

/*
**	myrank: look up player's rank in history then broadcast it
*/
myrank(c, isasync)
boolean	isasync;
{
	char	msgbuf[MESGLEN];

	if(isasync == False) {
		sprintf(msgbuf, "%d rank: %s\r\n", M_INFO, histfmtplr(c, 0, False));
		announce(c, msgbuf);
	}
	if(plr[c].p_stat == Computer)
		return 0;
	sprintf(msgbuf, "%d rank: %s\r\n", M_INFO, histfmtplr(c, 0, True));
	return message(c, msgbuf);
}

/*
**	aboveme: look up player's rank in history
**		then broadcast the one directly below it
*/
aboveme(c, isasync)
boolean	isasync;
{
	char	msgbuf[MESGLEN];

	sprintf(msgbuf, "%d rank: %s\r\n", M_INFO, histfmtplr(c, -1, True));
	if(isasync == True)
		return message(c, msgbuf);
	announce(-1, msgbuf);
	return 0;
}

/*
**	belowme: look up player's rank in history
**		then broadcast the one directly below it
*/
belowme(c, isasync)
boolean	isasync;
{
	char	msgbuf[MESGLEN];

	sprintf(msgbuf, "%d rank: %s\r\n", M_INFO, histfmtplr(c, 1, True));
	if(isasync == True)
		return message(c, msgbuf);
	announce(-1, msgbuf);
	return 0;
}

/*
**	plrrank: look up player p's rank in history
**		then broadcast it, using the player's moniker
*/
/*ARGSUSED*/
plrrank(c, p, isasync)
boolean	isasync;
{
	char	msgbuf[MESGLEN];

	sprintf(msgbuf, "%d rank: %s\r\n", M_INFO, histfmtplr(p, 0, False));
	if(isasync == True)
		return message(c, msgbuf);
	announce(-1, msgbuf);
	return 0;
}