/*	$NetBSD: move.c,v 1.13 2012/10/13 19:39:57 dholland Exp $	*/

/*
 * Copyright (c) 1980, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)move.c	8.1 (Berkeley) 5/31/93";
#else
__RCSID("$NetBSD: move.c,v 1.13 2012/10/13 19:39:57 dholland Exp $");
#endif
#endif /* not lint */

#include <assert.h>

#include "back.h"
#include "backlocal.h"

#ifdef DEBUG
FILE   *trace;
static char tests[20];
#endif

struct BOARD {			/* structure of game position */
	int     b_board[26];	/* board position */
	int     b_in[2];	/* men in */
	int     b_off[2];	/* men off */
	int     b_st[4], b_fn[4];	/* moves */

	struct BOARD *b_next;	/* forward queue pointer */
};

static struct BOARD *freeq = 0;
static struct BOARD *checkq = 0;

 /* these variables are values for the candidate move */
static int ch;			/* chance of being hit */
static int op;			/* computer's open men */
static int pt;			/* comp's protected points */
static int em;			/* farthest man back */
static int frc;			/* chance to free comp's men */
static int frp;			/* chance to free pl's men */

 /* these values are the values for the move chosen (so far) */
static int chance;		/* chance of being hit */
static int openmen;		/* computer's open men */
static int points;		/* comp's protected points */
static int endman;		/* farthest man back */
static int barmen;		/* men on bar */
static int menin;		/* men in inner table */
static int menoff;		/* men off board */
static int oldfrc;		/* chance to free comp's men */
static int oldfrp;		/* chance to free pl's men */

static int cp[5];		/* candidate start position */
static int cg[5];		/* candidate finish position */

static int race;		/* game reduced to a race */


static int bcomp(struct BOARD *, struct BOARD *);
static struct BOARD *bsave(struct move *);
static void binsert(struct move *, struct BOARD *);
static void boardcopy(struct move *, struct BOARD *);
static void makefree(struct BOARD *);
static void mvcheck(struct move *, struct BOARD *, struct BOARD *);
static struct BOARD *nextfree(void);
static void trymove(struct move *, int, int);
static void pickmove(struct move *);
static void movcmp(struct move *);
static int movegood(void);


/* zero if first move */
void
move(struct move *mm, int okay)
{
	int     i;		/* index */
	int     l;		/* last man */

	l = 0;
	if (okay) {
		/* see if comp should double */
		if (gvalue < 64 && dlast != cturn && dblgood()) {
			writel(*Colorptr);
			dble();	/* double */
			/* return if declined */
			if (cturn != 1 && cturn != -1)
				return;
		}
		roll(mm);
	}
	race = 0;
	for (i = 0; i < 26; i++) {
		if (board[i] < 0)
			l = i;
	}
	for (i = 0; i < l; i++) {
		if (board[i] > 0)
			break;
	}
	if (i == l)
		race = 1;

	/* print roll */
	if (tflag)
		curmove(cturn == -1 ? 18 : 19, 0);
	writel(*Colorptr);
	writel(" rolls ");
	writec(mm->D0 + '0');
	writec(' ');
	writec(mm->D1 + '0');
	/* make tty interruptable while thinking */
	if (tflag)
		cline();
	fixtty(&noech);

	/* find out how many moves */
	mm->mvlim = movallow(mm);
	if (mm->mvlim == 0) {
		writel(" but cannot use it.\n");
		nexturn();
		fixtty(&raw);
		return;
	}
	/* initialize */
	for (i = 0; i < 4; i++)
		cp[i] = cg[i] = 0;

	/* strategize */
	trymove(mm, 0, 0);
	pickmove(mm);

	/* print move */
	writel(" and moves ");
	for (i = 0; i < mm->mvlim; i++) {
		if (i > 0)
			writec(',');
		wrint(mm->p[i] = cp[i]);
		writec('-');
		wrint(mm->g[i] = cg[i]);
		makmove(mm, i);

		/*
		 * This assertion persuades gcc 4.5 that the loop
		 * doesn't result in signed overflow of i. mvlim
		 * isn't, or at least shouldn't be, changed by makmove
		 * at all.
		 */
		assert(mm->mvlim >= 0 && mm->mvlim <= 5);
	}
	writec('.');

	/* print blots hit */
	if (tflag)
		curmove(20, 0);
	else
		writec('\n');
	for (i = 0; i < mm->mvlim; i++)
		if (mm->h[i])
			wrhit(mm->g[i]);
	/* get ready for next move */
	nexturn();
	if (!okay) {
		buflush();
		sleep(3);
	}
	fixtty(&raw);		/* no more tty interrupt */
}

/* 	mvnum   == number of move (rel zero) */
/* 	swapped == see if swapped also tested */
static void
trymove(struct move *mm, int mvnum, int swapped)
{
	int     pos;		/* position on board */
	int     rval;		/* value of roll */

	/* if recursed through all dice values, compare move */
	if (mvnum == mm->mvlim) {
		binsert(mm, bsave(mm));
		return;
	}
	/* make sure dice in always same order */
	if (mm->d0 == swapped)
		mswap(mm);
	/* choose value for this move */
	rval = mm->dice[mvnum != 0];

	/* find all legitimate moves */
	for (pos = bar; pos != home; pos += cturn) {
		/* fix order of dice */
		if (mm->d0 == swapped)
			mswap(mm);
		/* break if stuck on bar */
		if (board[bar] != 0 && pos != bar)
			break;
		/* on to next if not occupied */
		if (board[pos] * cturn <= 0)
			continue;
		/* set up arrays for move */
		mm->p[mvnum] = pos;
		mm->g[mvnum] = pos + rval * cturn;
		if (mm->g[mvnum] * cturn >= home) {
			if (*offptr < 0)
				break;
			mm->g[mvnum] = home;
		}
		/* try to move */
		if (makmove(mm, mvnum))
			continue;
		else
			trymove(mm, mvnum + 1, 2);
		/* undo move to try another */
		backone(mm, mvnum);
	}

	/* swap dice and try again */
	if ((!swapped) && mm->D0 != mm->D1)
		trymove(mm, 0, 1);
}

static struct BOARD *
bsave(struct move *mm)
{
	int     i;		/* index */
	struct BOARD *now;	/* current position */

	now = nextfree();	/* get free BOARD */

	/* store position */
	for (i = 0; i < 26; i++)
		now->b_board[i] = board[i];
	now->b_in[0] = in[0];
	now->b_in[1] = in[1];
	now->b_off[0] = off[0];
	now->b_off[1] = off[1];
	for (i = 0; i < mm->mvlim; i++) {
		now->b_st[i] = mm->p[i];
		now->b_fn[i] = mm->g[i];
	}
	return (now);
}

/* new == item to insert */
static void
binsert(struct move *mm, struct BOARD *new)
{
	struct BOARD *qp = checkq;	/* queue pointer */
	int     result;		/* comparison result */

	if (qp == 0) {		/* check if queue empty */
		checkq = qp = new;
		qp->b_next = 0;
		return;
	}
	result = bcomp(new, qp);	/* compare to first element */
	if (result < 0) {	/* insert in front */
		new->b_next = qp;
		checkq = new;
		return;
	}
	if (result == 0) {	/* duplicate entry */
		mvcheck(mm, qp, new);
		makefree(new);
		return;
	}
	while (qp->b_next != 0) {/* traverse queue */
		result = bcomp(new, qp->b_next);
		if (result < 0) {	/* found place */
			new->b_next = qp->b_next;
			qp->b_next = new;
			return;
		}
		if (result == 0) {	/* duplicate entry */
			mvcheck(mm, qp->b_next, new);
			makefree(new);
			return;
		}
		qp = qp->b_next;
	}
	/* place at end of queue */
	qp->b_next = new;
	new->b_next = 0;
}

static int
bcomp(struct BOARD *a, struct BOARD *b)
{
	int    *aloc = a->b_board;	/* pointer to board a */
	int    *bloc = b->b_board;	/* pointer to board b */
	int     i;		/* index */
	int     result;		/* comparison result */

	for (i = 0; i < 26; i++) {	/* compare boards */
		result = cturn * (aloc[i] - bloc[i]);
		if (result)
			return (result);	/* found inequality */
	}
	return (0);		/* same position */
}

static void
mvcheck(struct move *mm, struct BOARD *incumbent, struct BOARD *candidate)
{
	int     i;
	int     result;

	for (i = 0; i < mm->mvlim; i++) {
		result = cturn * (candidate->b_st[i] - incumbent->b_st[i]);
		if (result > 0)
			return;
		if (result < 0)
			break;
	}
	if (i == mm->mvlim)
		return;
	for (i = 0; i < mm->mvlim; i++) {
		incumbent->b_st[i] = candidate->b_st[i];
		incumbent->b_fn[i] = candidate->b_fn[i];
	}
}

void
makefree(struct BOARD *dead)
{
	dead->b_next = freeq;	/* add to freeq */
	freeq = dead;
}

static struct BOARD *
nextfree(void)
{
	struct BOARD *new;

	if (freeq == 0) {
		new = (struct BOARD *) calloc(1, sizeof(struct BOARD));
		if (new == 0) {
			writel("\nOut of memory\n");
			getout(0);
		}
	} else {
		new = freeq;
		freeq = freeq->b_next;
	}

	new->b_next = 0;
	return (new);
}

static void
pickmove(struct move *mm)
{
	/* current game position */
	struct BOARD *now = bsave(mm);
	struct BOARD *next;	/* next move */

#ifdef DEBUG
	if (trace == NULL)
		trace = fopen("bgtrace", "w");
	fprintf(trace, "\nRoll:  %d %d%s\n", D0, D1, race ? " (race)" : "");
	fflush(trace);
#endif
	do {			/* compare moves */
		boardcopy(mm, checkq);
		next = checkq->b_next;
		makefree(checkq);
		checkq = next;
		movcmp(mm);
	} while (checkq != 0);

	boardcopy(mm, now);
}

static void
boardcopy(struct move *mm, struct BOARD *s)
{
	int     i;		/* index */

	for (i = 0; i < 26; i++)
		board[i] = s->b_board[i];
	for (i = 0; i < 2; i++) {
		in[i] = s->b_in[i];
		off[i] = s->b_off[i];
	}
	for (i = 0; i < mm->mvlim; i++) {
		mm->p[i] = s->b_st[i];
		mm->g[i] = s->b_fn[i];
	}
}

static void
movcmp(struct move *mm)
{
	int     i;

#ifdef DEBUG
	if (trace == NULL)
		trace = fopen("bgtrace", "w");
#endif

	odds(0, 0, 0);
	if (!race) {
		ch = op = pt = 0;
		for (i = 1; i < 25; i++) {
			if (board[i] == cturn)
				ch = canhit(i, 1);
			op += abs(bar - i);
		}
		for (i = bar + cturn; i != home; i += cturn)
			if (board[i] * cturn > 1)
				pt += abs(bar - i);
		frc = freemen(bar) + trapped(bar, cturn);
		frp = freemen(home) + trapped(home, -cturn);
	}
	for (em = bar; em != home; em += cturn)
		if (board[em] * cturn > 0)
			break;
	em = abs(home - em);
#ifdef DEBUG
	fputs("Board: ", trace);
	for (i = 0; i < 26; i++)
		fprintf(trace, " %d", board[i]);
	if (race)
		fprintf(trace, "\n\tem = %d\n", em);
	else
		fprintf(trace,
		    "\n\tch = %d, pt = %d, em = %d, frc = %d, frp = %d\n",
		    ch, pt, em, frc, frp);
	fputs("\tMove: ", trace);
	for (i = 0; i < mvlim; i++)
		fprintf(trace, " %d-%d", p[i], g[i]);
	fputs("\n", trace);
	fflush(trace);
	strcpy(tests, "");
#endif
	if ((cp[0] == 0 && cg[0] == 0) || movegood()) {
#ifdef DEBUG
		fprintf(trace, "\t[%s] ... wins.\n", tests);
		fflush(trace);
#endif
		for (i = 0; i < mm->mvlim; i++) {
			cp[i] = mm->p[i];
			cg[i] = mm->g[i];
		}
		if (!race) {
			chance = ch;
			openmen = op;
			points = pt;
			endman = em;
			barmen = abs(board[home]);
			oldfrc = frc;
			oldfrp = frp;
		}
		menin = *inptr;
		menoff = *offptr;
	}
#ifdef DEBUG
	else {
		fprintf(trace, "\t[%s] ... loses.\n", tests);
		fflush(trace);
	}
#endif
}

static int
movegood(void)
{
	int     n;

	if (*offptr == 15)
		return (1);
	if (menoff == 15)
		return (0);
	if (race) {
#ifdef DEBUG
		strcat(tests, "o");
#endif
		if (*offptr - menoff)
			return (*offptr > menoff);
#ifdef DEBUG
		strcat(tests, "e");
#endif
		if (endman - em)
			return (endman > em);
#ifdef DEBUG
		strcat(tests, "i");
#endif
		if (menin == 15)
			return (0);
		if (*inptr == 15)
			return (1);
#ifdef DEBUG
		strcat(tests, "i");
#endif
		if (*inptr - menin)
			return (*inptr > menin);
		return (rnum(2));
	} else {
		n = barmen - abs(board[home]);
#ifdef DEBUG
		strcat(tests, "c");
#endif
		if (abs(chance - ch) + 25 * n > rnum(150))
			return (n ? (n < 0) : (ch < chance));
#ifdef DEBUG
		strcat(tests, "o");
#endif
		if (*offptr - menoff)
			return (*offptr > menoff);
#ifdef DEBUG
		strcat(tests, "o");
#endif
		if (abs(openmen - op) > 7 + rnum(12))
			return (openmen > op);
#ifdef DEBUG
		strcat(tests, "b");
#endif
		if (n)
			return (n < 0);
#ifdef DEBUG
		strcat(tests, "e");
#endif
		if (abs(endman - em) > rnum(2))
			return (endman > em);
#ifdef DEBUG
		strcat(tests, "f");
#endif
		if (abs(frc - oldfrc) > rnum(2))
			return (frc < oldfrc);
#ifdef DEBUG
		strcat(tests, "p");
#endif
		if (abs(n = pt - points) > rnum(4))
			return (n > 0);
#ifdef DEBUG
		strcat(tests, "i");
#endif
		if (*inptr - menin)
			return (*inptr > menin);
#ifdef DEBUG
		strcat(tests, "f");
#endif
		if (abs(frp - oldfrp) > rnum(2))
			return (frp > oldfrp);
		return (rnum(2));
	}
}
