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 r

⟦bd03a2b3a⟧ TextFile

    Length: 16470 (0x4056)
    Types: TextFile
    Names: »risk.c«

Derivation

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

TextFile

/*	vi:set sw=4 ts=4: */
#ifndef	lint
static char	sccsid[] = "@(#)risk.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	"cubes.h"

extern boolean		jokermode;
extern int			winscore;
extern player		plr[];
extern boolean		iscomp();
extern boolean		isproxy();
extern boolean		iskamelion();
extern long			histavgplr();

/*
**	risk: values derived empirically using the avg program
**
**	Yes, you can calculate all this stuff, but what a pain.
**	In any case, this table is by no means a complete listing
**	of all possible conditions, probabilities, and expectations.
**	You could come up with a much more complicated table, but
**	to what end?  Some of computer players using this table
**	and quite simple strategies are able to best humans regularly.
**
**	Notes:
**		1. p_aok values for rolling NDICE are nonsense.
**		2. p_aok values cannot be used with all aces, fives, or jokers
**		   since they already count as scoring dice.
**		3. p_any and p_all for rolling 3, 2, and 1 can be averaged.
**		4. Values for rolling 4 are special, since avg is able to
**		   detect asmstr in that case.  Use only mix values.
*/
risk	joker_risktbl[NDICE+1]	= {
/*	       any       all       aok  mix aces fives jokers	*/
	{ 0.000000, 0.000000, 0.000000,   0,   0,   0,   0  },	/* roll 0 */
	{ 0.384615, 0.384615, 0.153846,  23, 406, 215, 715  },	/* roll 1 */
	{ 0.621656, 0.147983, 0.023669,  46, 105,  76,  99  },	/* roll 2 */
	{ 0.781163, 0.071125, 0.003641,  79,  86,  83,  83  },	/* roll 3 */
	{ 0.885755, 0.086400, 0.000560, 145, 145, 145, 145  },	/* roll 4 */
	{ 0.948547, 0.096702, 0.000086, 269, 269, 269, 269  },	/* roll 5 */
};

risk	std_risktbl[NDICE+1]	= {
/*	       any       all       aok  mix aces fives jokers	*/
	{ 0.000000, 0.000000, 0.000000,   0,   0,   0,   0  },	/* roll 0 */
	{ 0.333333, 0.333333, 0.166667,  25, 442, 233,   0  },	/* roll 1 */
	{ 0.555513, 0.110736, 0.027778,  50, 119,  84,   0  },	/* roll 2 */
	{ 0.722309, 0.055524, 0.004630,  86,  95,  91,   0  },	/* roll 3 */
	{ 0.842288, 0.095481, 0.000772, 161, 161, 161,   0  },	/* roll 4 */
	{ 0.922813, 0.099667, 0.000129, 309, 309, 309,   0  },	/* roll 5 */
};

#define	risktbl			(jokermode == True ? joker_risktbl : std_risktbl)

/*
**	cquery: computer rolling choice
*/
cquery(comp, pd)
int			comp;
diceset	   *pd;
{
	diceset				modi;		/* dice after re-rolling strategy */
	diceset				temp;		/* a temporary copy of the dice */
	int					stay;		/* points if we hold */
	int					need;		/* points needed beyond kept so far */
	int					xpct;		/* expectation value of next roll */
	boolean				canthold;	/* true if can't hold */

	/*
	**	Fake human-like hesitation for kamelion and proxies.
	**	Otherwise, just allow time for screen update.
	*/
	if(iskamelion(comp) == True || isproxy(comp) == True)
		hesitate(1800L, 3600L);
	else
		hesitate(1500L, 2000L);

	/*
	**	Mark Rolled dice as Taken, updating the value of d_rolling.
	*/
	keepall(pd);

	/*
	**	Determine any minimum number of points we need above those
	**	accumulated on previous rolls.  For now, we just worry about
	XXX	onboard and offboard conditions, though in the future we
	XXX	could also worry about stopping winners and setting minimums.
	*/
	if(plr[comp].p_onboard == False && pd->d_pts_turn < ONBOARD)
		need = ONBOARD - pd->d_pts_turn;
	else if(pd->d_pts_turn < OFFBOARD && plr[comp].p_score < winscore) {
		if(plr[comp].p_score + pd->d_pts_turn >= winscore)
			need = OFFBOARD - pd->d_pts_turn;
	} else
		need = 0;

	/*
	**	We get a copy of the dice and then use the computer's re-rolling
	**	strategy on it.  We can only do this once, because some players
	**	are using a fickle strategy that's different every time.  Set
	**	this copy of the dice up to roll again.
	*/
	modi = *pd;
	(*plr[comp].p_computer->c_strategy->s_func)(&modi, need);
	modi.d_again = True;

	/*
	**	Evaluate the number of points we'd have if we held.
	**	Set this copy of the dice up to hold.
	*/
	pd->d_again = False;
	temp = *pd;
	scoredice(&temp);
	stay = temp.d_pts_turn;

	/*
	**	Take care of conditions where we can't hold.
	*/
	if(plr[comp].p_onboard == False && stay < ONBOARD)
		canthold = True;
	else if(stay < OFFBOARD && plr[comp].p_score < winscore) {
		if(plr[comp].p_score + stay >= winscore)
			canthold = True;
	} else
		canthold = False;

	/*
	**	If we can't hold, use the diceset with the best expectation value.
	**	Maybe this decision should be made by the temperament routine,
	**	but they aren't set up with that assumption.  Still, this should
	**	be an improvement over the previous situation, where the modified
	**	diceset was always selected.
	*/	
	if(canthold == True) {
		temp = *pd; 
		stay = expect(&temp);	/* stay is expectation of original */
		temp = modi;
		xpct = expect(&temp);	/* xpct is expectation of modified */
		if(xpct >= stay)		/* prefer modified version */
			*pd = modi;			/* use modified diceset */
		pd->d_again = True;		/* in case it's the orignal */
		return;
	}

	/*
	**	Fake some indecision sometimes.  More rarely, call snoop to
	**	pretend to be a human interested in rankings.  The randint
	**	calls below work out to about one snoop per game for kamelion
	**	and proxies vs. about one snoop per two games for others
	**	(before checking value of stay, anyway).
	*/
	if(iscomp(comp) == False && randint(12) > 9) {
		int		thresh;
		switch(modi.d_rolling) {
		case 1: thresh = P_SMSTR; break;
		case 2: thresh = P_ASMSTR; break;
		case 3: thresh = P_3OKMULT * P_ACEMULT; break;
		case 4: thresh = P_4OKMULT * P_ACEMULT; break;
		default:thresh = P_AOKMULT * P_ACEMULT; break;
		}
		if(stay >= thresh) {
			hesitate(500L, 2000L);
			switch(randint(56)) {
			case 21: if(iskamelion(comp) == True) snoop(comp); break;
			case 14: if(isproxy(comp) == True) snoop(comp); break;
			case  7: snoop(comp); break;
			}
		}
	}
	
	/*
	**	Calculate the expectation value of rolling again.
	*/
	temp = modi;
	xpct = expect(&temp);

	/*
	**	Player's temperament will chose which diceset to use.
	**	If c_temper->t_func returns True, it means roll again.
	*/
	if((*plr[comp].p_computer->c_temper->t_func)(comp,stay,xpct,&modi) == True)
		*pd = modi;		/* modi is the roll-again diceset */
}

/*
**	sameface: all dice showing same face test
**		if scoring is False, considers only held dice
**		returns True if considered dice are all comb's
**		returns False in the case of no comb's or mixed faces
*/
boolean
sameface(pd, comb, scoring)
diceset		   *pd;
combination		comb;
boolean			scoring;
{
	register int		d;
	register boolean	all;
	int					face;

	switch(comb) {
	case Joker:	if(jokermode == False) return False; face = JOKER; break;
	case Five:	face = FIVE; break;
	case Ace:	face = ACE; break;
	default:	face = BADFACE; break;	/* non-scoring face */
	}

	if(scoring == False) {
		all = False;
		for(d = 0;d < NDICE;++d) {
			if(pd->d_comb[d] == Previous) {
				if(face == BADFACE) {
					switch(pd->d_face[d]) {
					case ACE: case FIVE: case JOKER: return False;
					default: face = pd->d_face[d]; break;
					}
				} else if(pd->d_face[d] != face)
					return False;
				all = True;
			}
		}
		return all;
	}

	all = False;
	for(d = 0;d < NDICE;++d) {
		switch(pd->d_comb[d]) {
		case Previous:
		case Three_of_a_kind:
		case Four_of_a_kind:
			if(face == BADFACE) {	/* comb == Nothing too */
				switch(pd->d_face[d]) {
				case ACE: case FIVE: case JOKER: return False;
				default: face = pd->d_face[d]; break;
				}
			} else if(pd->d_face[d] != face)
				return False;
			break;
		case All_of_a_kind:
			if(comb == Nothing) {
				switch(pd->d_face[d]) {
				case ACE: case FIVE: case JOKER: return False;
				default: return True;
				}
			}
			return (pd->d_face[d] == face) ? True : False;
		case Ace:		if(comb != Ace) return False; break;
		case Five:		if(comb != Five) return False; break;
		case Joker:		if(comb != Joker) return False; break;
		case Nothing:	continue;
		default:		return False;	/* scored but mixed faces */
		}
		all = True;
	}

	return all;
}

/*
**	expect: return expectation value of rolling dice as they stand
*/
expect(pd)
register diceset   *pd;
{
	int				d;
	boolean			allaces, allfives, alljokers, allofakind;
	risk		   *pr;
	diceset			temp;
	double			xpct;
	double			any, all;

	/*
	**	Get a copy of the dice and forget the location of the original.
	*/
	temp = *pd;
	pd = &temp;

	/*
	**	Four special conditions: all Aces, all Fives, all Jokers,
	**	or all of a kind (not in Aces, Fives, or Jokers).
	*/
	allaces = allfives = alljokers = allofakind = False;
	if(pd->d_rolling != 0 && pd->d_rolling != NDICE)
		if((allaces = sameface(pd, Ace, True)) == False)
			if((allfives = sameface(pd, Five, True)) == False)
				if(jokermode == False
				|| (alljokers = sameface(pd, Joker, True)) == False)
					allofakind = sameface(pd, Nothing, True);

	/*
	**	Point to the appropriate risk values.  First get the expected
	**	value of the next roll, accounting for whether we have all Aces,
	**	all Fives, all Jokers or a mix.
	*/
	pr = &risktbl[pd->d_rolling == 0 ? NDICE : pd->d_rolling];
	any = pr->r_p_any, all = pr->r_p_all;
	if(allaces == True)			xpct = pr->r_e_aces;
	else if(allfives == True)	xpct = pr->r_e_fives;
	else if(alljokers == True)	xpct = pr->r_e_jokers;
	else						xpct = pr->r_e_mix;

	scoredice(pd);				/* sets d_pts values */

	/*
	**	Add additional expectation due to getting All_of_a_kind
	**	in what normally would be considered non-scoring dice.
	*/
	if(allofakind == True) {
		any += pr->r_p_aok, all += pr->r_p_aok;
		for(d = 0;d < NDICE;++d) {
			if(pd->d_comb[d] != Nothing) {
				xpct += pr->r_p_aok
					* (P_AOKMULT * pd->d_face[d] - pd->d_pts_dice);
				break;
			}
		}
	}

#ifdef	ASMSTR
	/*
	**	Account for Asm_straight.  There are three basic ways to
	**	complete an assembled straight.  First is if an ace or five
	**	is held.  Second is if an ace and five are held.  Last is
	**	if a small straight is held, of which there are three varieties.
	*/
	switch(pd->d_rolling) {
	case ASMSTR-1:
		if(allaces == True) {
			/*
			**	To assemble here, what would be a Small_straight
			**	would need to be rolled.  That probability is
			**	already accounted for, but the expectation value
			**	of this combination is higher.
			*/
			xpct += (jokermode == True) ? 3 : 4;
		} else if(allfives == True) {
			/*
			**	To assemble here, a Small_straight or a 2346 is needed.
			**	The probability of the first is accounted for, the
			**	second is not.  Additional expectation from both.
			*/
			if(jokermode == True)
				any += 0.013445, all += 0.013445, xpct += 13;
			else
				any += 0.018519, all += 0.018519, xpct += 18;
		}
		break;
	case ASMSTR-2:
		if(allaces == False && allfives == False) {
			/*
			**	To assemble here, we need 234 which is unaccounted for
			**	and has additional expectation.
			*/
			if(jokermode == True)
				any += 0.021848, all += 0.021848, xpct += 13;
			else
				any += 0.027778, all += 0.027778, xpct += 16;
		}
		break;
	case 1:
		switch(heldsmall(pd)) {
		case ACE:	/* low die is an ACE */
			/*
			**	To assemble here, we need a FIVE.  The probability
			**	is accounted for, but the expectation is not.
			*/
			xpct += (jokermode == True) ? 46 : 50;
			break;
		case 2:		/* low die is a deuce */
			/*
			**	To assemble here, we need an ACE or a 6.  The
			**	first is accounted for, the second is not.  Both
			**	carry additional expectation.
			*/
			if(jokermode == True)
				any += 0.153846, all += 0.153846, xpct += 92;
			else
				any += 0.166667, all += 0.166667, xpct += 100;
			break;
		case 3:		/* low die is a three */
			/*
			**	To assemble here, we need a 2.  The probability
			**	and additional expectation are unaccounted for.
			*/
			if(jokermode == True)
				any += 0.153846, all += 0.153846, xpct += 53;
			else
				any += 0.166667, all += 0.166667, xpct += 58;
			break;
		default:	/* not holding small straight */
			break;
		}
		break;
	}
#endif	ASMSTR

	/*
	**	Add the value of keeping what we've got.
	*/
	xpct += any * pd->d_pts_turn;

	/*
	**	Add the value of getting to roll all dice again.
	**	This is probably the most faulty estimate due to the way
	**	we make use of r_e_mix and p_nothing * xpct.
	*/
	pr = &risktbl[NDICE];
	xpct = xpct + all * (pr->r_e_mix - (1.0 - pr->r_p_any) * xpct);

	return (int)xpct;
}

/*
**	ziprisk: return the probability of rolling nothing
**		assumes that rolling zero really means rolling all
*/
double
ziprisk(pd)
register diceset   *pd;
{
	double			any;

	/*
	**	Get the probability of anything assuming mixed dice held.
	*/
	any = risktbl[pd->d_rolling == 0 ? NDICE : pd->d_rolling].r_p_any;

	/*
	**	Check for all held/scoring dice having same (usu. non-scoring) face.
	**	We count both Previous and scoring dice.
	*/
	if(pd->d_rolling != 0 && pd->d_rolling != NDICE)
		if(sameface(pd, Nothing, True) == True)
			any += risktbl[pd->d_rolling].r_p_aok;

	return 1.0 - any;
}

/*
**	mayendearly: return True if the game may end prematurely
**		(no human player is onboard == True)
*/
boolean
mayendearly()
{
	register int	c;

	for(c = 0;c < PLAYERS;++c)
		if(plr[c].p_stat == Active && plr[c].p_onboard == True)
			return False;
	
	return True;
}

/*
**	cfirst: hesitation before first roll
*/
cfirst(comp)
int		comp;
{
	int		high, half;
	int		winneed, halfneed;

#define	QUIT	(winscore / 4)

	/*
	**	Fake human-like hesitation for kamelion and proxies.
	**	Otherwise, just allow time for screen update.
	*/
	if(iskamelion(comp) == True || isproxy(comp) == True)
		hesitate(1800L, 3600L);
	else
		hesitate(1500L, 2000L);

	/*
	**	If this player is not yet on board, and
	**	if this is not the COMP computer and also not a proxy player, and
	**	if this player is behind by more than QUIT points, and
	**	if the game is in little danger of ending prematurely, and
	**	if this player's score is less than half of its average score, and
	**	if this player needs more to reach half than the leader needs to win,
	**	then there is a probability of quitting that is initially 50%
	*/
	if(plr[comp].p_onboard == False) {
		if(iscomp(comp) == False && isproxy(comp) == False) {
			if((high = highscore(comp, (int *)0)) - plr[comp].p_score > QUIT) {
				hesitate(250L, 2000L);		/* fake consideration of quit */
				if(mayendearly() == False) {
					half = (winscore * histavgplr(comp)) / (2 * WINSCORE);
					if((halfneed = half - plr[comp].p_score) > 0) {
						if((winneed = winscore - high) < halfneed) {
							if(randint(2 * winneed) < halfneed) {
								oldplayer(comp);
								return -1;
							}
						}
					}
				}
			}
		}
	}

	/*
	**	If this is a proxy computer, sometimes do more hesitating.
	*/
	if(isproxy(comp) == True)
		hesitate(100L, 1500L);

	return 0;

#undef	QUIT
}

/*
**	snoop: make it look like a computer player is interested in rankings
**		It might be better to individualize this by putting calls
**		to the snooping routines in the temperament routines.  For now,
**		we'll just make some decisions based on computer temperament.
*/
snoop(comp)
{
	int				r;
	temper		   *ctemp;
	extern temper	te_robot, te_antsy, te_tracker, te_goalie;
	extern int		highscore(), closescore();

	ctemp = plr[comp].p_computer->c_temper;

	if(ctemp == &te_robot)
		return;
	
	if(ctemp == &te_antsy)
		r = peekhigh(comp, highscore);
	else if(ctemp == &te_tracker)
		r = peekhigh(comp, closescore);
	else if(ctemp == &te_goalie) {
		r = randint(3) == 1 ? myrank(comp, False) : peekhigh(comp, highscore);
	} else switch(randint(5)) {
		case 1: r = aboveme(comp, False); break;
		case 2: r = belowme(comp, False); break;
		case 3: r = peekhigh(comp, highscore); break;
		case 4: r = peekhuman(comp); break;
		default:r = myrank(comp, False); break;
	}

	/*
	**	Pretend to consider the ranking info.
	*/
	if(r >= 0)
		hesitate(1000L, 3000L);
}

/*
**	peekhigh: look at the high scorer's history
**		If there's a tie, don't bother.
*/
peekhigh(comp, track)
int		comp;
int	  (*track)();
{
	int		highc;

	(void) (*track)(comp, &highc);
	if(highc < 0)
		return -1;
	return plrrank(comp, highc, False);
}

/*
**	peekhuman: look at a random human's history
*/
peekhuman(comp)
{
	register int	c, pick;
	extern int		active, waiting, watching;

	pick = randint(active + waiting + watching);

	for(c = 0;c < PLAYERS;++c) {
		switch(plr[c].p_stat) {
		case Active:
		case Waiting:
		case Watching:
			if(--pick == 0)
				return plrrank(comp, c, False);
			break;
		}
	}

	return -1;	/* huh? */
}