#include <sys/cdefs.h>
__RCSID("$NetBSD: pickmove.c,v 1.68 2022/05/29 22:03:29 rillig Exp $");
#include <stdlib.h>
#include <string.h>
#include <curses.h>
#include <limits.h>
#include "gomoku.h"
#define BITS_PER_INT (sizeof(int) * CHAR_BIT)
#define MAPSZ (BAREA / BITS_PER_INT)
#define BIT_SET(a, b) ((a)[(b)/BITS_PER_INT] |= (1U << ((b) % BITS_PER_INT)))
#define BIT_TEST(a, b) (((a)[(b)/BITS_PER_INT] & (1U << ((b) % BITS_PER_INT))) != 0)
struct overlap_info {
spot_index o_intersect;
u_char o_off;
u_char o_frameindex;
};
static struct combostr *hashcombos[FAREA];
static struct combostr *sortcombos;
static int combolen;
static player_color nextcolor;
static int elistcnt;
static int combocnt;
static unsigned int forcemap[MAPSZ];
static unsigned int tmpmap[MAPSZ];
static int nforce;
static bool better(spot_index, spot_index, player_color);
static void scanframes(player_color);
static void makecombo2(struct combostr *, struct spotstr *, u_char, u_short);
static void addframes(unsigned int);
static void makecombo(struct combostr *, struct spotstr *, u_char, u_short);
static void appendcombo(struct combostr *);
static void updatecombo(struct combostr *, player_color);
static void makeempty(struct combostr *);
static int checkframes(struct combostr *, struct combostr *, struct spotstr *,
u_short, struct overlap_info *);
static bool sortcombo(struct combostr **, struct combostr **, struct combostr *);
#if !defined(DEBUG)
static void printcombo(struct combostr *, char *, size_t);
#endif
spot_index
pickmove(player_color us)
{
if (game.nmoves == 0)
return PT((BSZ + 1) / 2, (BSZ + 1) / 2);
for (spot_index s = PT(BSZ, BSZ) + 1; s-- > PT(1, 1); ) {
struct spotstr *sp = &board[s];
sp->s_combo[BLACK].s = 0x601;
sp->s_combo[WHITE].s = 0x601;
sp->s_level[BLACK] = 255;
sp->s_level[WHITE] = 255;
sp->s_nforce[BLACK] = 0;
sp->s_nforce[WHITE] = 0;
sp->s_flags &= ~(FFLAGALL | MFLAGALL);
}
nforce = 0;
memset(forcemap, 0, sizeof(forcemap));
player_color them = us != BLACK ? BLACK : WHITE;
nextcolor = us;
scanframes(BLACK);
scanframes(WHITE);
spot_index os = PT(BSZ, BSZ);
spot_index ts = PT(BSZ, BSZ);
for (spot_index s = PT(BSZ, BSZ); s-- > PT(1, 1); ) {
const struct spotstr *sp = &board[s];
if (sp->s_occ != EMPTY)
continue;
if (debug > 0 &&
(sp->s_combo[BLACK].cv_force == 1 ||
sp->s_combo[WHITE].cv_force == 1)) {
debuglog("- %s %x/%d %d %x/%d %d %d",
stoc(s),
sp->s_combo[BLACK].s, sp->s_level[BLACK],
sp->s_nforce[BLACK],
sp->s_combo[WHITE].s, sp->s_level[WHITE],
sp->s_nforce[WHITE],
sp->s_wval);
}
if (better(s, os, us))
os = s;
if (better(s, ts, them))
ts = s;
}
if (debug > 0) {
spot_index bs = us == BLACK ? os : ts;
spot_index ws = us == BLACK ? ts : os;
const struct spotstr *bsp = &board[bs];
const struct spotstr *wsp = &board[ws];
debuglog("B %s %x/%d %d %x/%d %d %d",
stoc(bs),
bsp->s_combo[BLACK].s, bsp->s_level[BLACK],
bsp->s_nforce[BLACK],
bsp->s_combo[WHITE].s, bsp->s_level[WHITE],
bsp->s_nforce[WHITE], bsp->s_wval);
debuglog("W %s %x/%d %d %x/%d %d %d",
stoc(ws),
wsp->s_combo[WHITE].s, wsp->s_level[WHITE],
wsp->s_nforce[WHITE],
wsp->s_combo[BLACK].s, wsp->s_level[BLACK],
wsp->s_nforce[BLACK], wsp->s_wval);
if (board[ts].s_combo[them].cv_force == 1 &&
!BIT_TEST(forcemap, ts))
debuglog("*** Can't be blocked");
}
union comboval ocv = board[os].s_combo[us];
union comboval tcv = board[ts].s_combo[them];
if (tcv.cv_force <= 1 &&
!(ocv.cv_force <= 1 &&
tcv.cv_force + tcv.cv_win >= ocv.cv_force + ocv.cv_win))
return ts;
return os;
}
static bool
better(spot_index as, spot_index bs, player_color us)
{
const struct spotstr *asp = &board[as];
const struct spotstr *bsp = &board[bs];
if ( asp->s_combo[us].s != bsp->s_combo[us].s)
return asp->s_combo[us].s < bsp->s_combo[us].s;
if ( asp->s_level[us] != bsp->s_level[us])
return asp->s_level[us] < bsp->s_level[us];
if ( asp->s_nforce[us] != bsp->s_nforce[us])
return asp->s_nforce[us] > bsp->s_nforce[us];
player_color them = us != BLACK ? BLACK : WHITE;
if (BIT_TEST(forcemap, as) != BIT_TEST(forcemap, bs))
return BIT_TEST(forcemap, as);
if ( asp->s_combo[them].s != bsp->s_combo[them].s)
return asp->s_combo[them].s < bsp->s_combo[them].s;
if ( asp->s_level[them] != bsp->s_level[them])
return asp->s_level[them] < bsp->s_level[them];
if ( asp->s_nforce[them] != bsp->s_nforce[them])
return asp->s_nforce[them] > bsp->s_nforce[them];
if ( asp->s_wval != bsp->s_wval)
return asp->s_wval > bsp->s_wval;
return (random() & 1) != 0;
}
static player_color curcolor;
static unsigned int curlevel;
static bool
four_in_a_row(player_color color, spot_index s, direction r)
{
struct spotstr *sp = &board[s];
union comboval cb = { .s = sp->s_fval[color][r].s };
if (cb.s >= 0x101)
return false;
for (int off = 5 + cb.cv_win, d = dd[r]; off-- > 0; sp += d) {
if (sp->s_occ != EMPTY)
continue;
sp->s_combo[color].s = cb.s;
sp->s_level[color] = 1;
}
return true;
}
static void
scanframes(player_color color)
{
struct combostr *ecbp;
struct spotstr *sp;
union comboval *cp;
struct elist *nep;
int r, n;
union comboval cb;
curcolor = color;
struct combostr *cbp = sortframes[color];
if (cbp == NULL)
return;
if (four_in_a_row(color, cbp->c_vertex, cbp->c_dir))
return;
n = combolen;
ecbp = cbp;
do {
sp = &board[cbp->c_vertex];
cp = &sp->s_fval[color][r = cbp->c_dir];
int delta = dd[r];
u_char off;
if (cp->cv_win != 0) {
cb.cv_force = cp->cv_force + 1;
cb.cv_win = 0;
if (cb.s < sp->s_combo[color].s) {
sp->s_combo[color].s = cb.s;
sp->s_level[color] = 1;
}
makecombo2(cbp, sp, 0, cb.s);
if (cp->s != 0x101)
cb.s = cp->s;
else if (color != nextcolor)
memset(tmpmap, 0, sizeof(tmpmap));
sp += delta;
off = 1;
} else {
cb.s = cp->s;
off = 0;
}
for (; off < 5; off++, sp += delta) {
if (sp->s_occ != EMPTY)
continue;
if (cp->s < sp->s_combo[color].s) {
sp->s_combo[color].s = cp->s;
sp->s_level[color] = 1;
}
if (cp->s == 0x101) {
sp->s_nforce[color]++;
if (color != nextcolor) {
n = (spot_index)(sp - board);
BIT_SET(tmpmap, (spot_index)n);
}
}
makecombo2(cbp, sp, off, cb.s);
}
if (cp->s == 0x101 && color != nextcolor) {
if (nforce == 0)
memcpy(forcemap, tmpmap, sizeof(tmpmap));
else {
for (int i = 0; (unsigned int)i < MAPSZ; i++)
forcemap[i] &= tmpmap[i];
}
}
board[cbp->c_vertex].s_flags |= MFLAG << r;
} while ((cbp = cbp->c_next) != ecbp);
for (unsigned int level = 2;
level <= 1 + game.nmoves / 2 && combolen > n; level++) {
if (level >= 9)
break;
if (debug > 0) {
debuglog("%cL%u %d %d %d", "BW"[color],
level, combolen - n, combocnt, elistcnt);
refresh();
}
n = combolen;
addframes(level);
}
for (spot_index s = PT(BSZ, BSZ) + 1; s-- > PT(1, 1); ) {
sp = &board[s];
for (struct elist *ep = sp->s_empty; ep != NULL; ep = nep) {
cbp = ep->e_combo;
if (cbp->c_combo.s <= sp->s_combo[color].s) {
if (cbp->c_combo.s != sp->s_combo[color].s) {
sp->s_combo[color].s = cbp->c_combo.s;
sp->s_level[color] = cbp->c_nframes;
} else if (cbp->c_nframes < sp->s_level[color])
sp->s_level[color] = cbp->c_nframes;
}
nep = ep->e_next;
free(ep);
elistcnt--;
}
sp->s_empty = NULL;
for (struct elist *ep = sp->s_nempty; ep != NULL; ep = nep) {
cbp = ep->e_combo;
if (cbp->c_combo.s <= sp->s_combo[color].s) {
if (cbp->c_combo.s != sp->s_combo[color].s) {
sp->s_combo[color].s = cbp->c_combo.s;
sp->s_level[color] = cbp->c_nframes;
} else if (cbp->c_nframes < sp->s_level[color])
sp->s_level[color] = cbp->c_nframes;
}
nep = ep->e_next;
free(ep);
elistcnt--;
}
sp->s_nempty = NULL;
}
if ((cbp = sortcombos) != NULL) {
struct combostr *ncbp;
ecbp = cbp;
do {
ncbp = cbp->c_next;
free(cbp);
combocnt--;
} while ((cbp = ncbp) != ecbp);
sortcombos = NULL;
}
combolen = 0;
#ifdef DEBUG
if (combocnt != 0) {
debuglog("scanframes: %c combocnt %d", "BW"[color],
combocnt);
whatsup(0);
}
if (elistcnt != 0) {
debuglog("scanframes: %c elistcnt %d", "BW"[color],
elistcnt);
whatsup(0);
}
#endif
}
static void
makecombo2(struct combostr *ocbp, struct spotstr *osp, u_char off, u_short cv)
{
struct combostr *ncbp;
int baseB, fcnt, emask;
union comboval ocb, fcb;
struct combostr **scbpp, *fcbp;
char tmp[128];
ocb.s = cv;
baseB = ocb.cv_force + ocb.cv_win - 1;
fcnt = ocb.cv_force - 2;
emask = fcnt != 0 ? ((ocb.cv_win != 0 ? 0x1E : 0x1F) & ~(1 << off)) : 0;
for (direction r = 4; r-- > 0; ) {
if (r == ocbp->c_dir)
continue;
int d = dd[r];
int bmask = (BFLAG | FFLAG | MFLAG) << r;
struct spotstr *fsp = osp;
for (u_char f = 0; f < 5; f++, fsp -= d) {
if (fsp->s_occ == BORDER)
break;
if ((fsp->s_flags & bmask) != 0)
continue;
fcb.s = fsp->s_fval[curcolor][r].s;
if (fcb.cv_force >= 6)
continue;
if ((f == 0 && fcb.cv_win != 0) || fcb.s == 0x101) {
fcb.cv_force++;
fcb.cv_win = 0;
}
int c = fcb.cv_force + ocb.cv_force - 3;
if (c > 4)
continue;
int n = fcb.cv_force + fcb.cv_win - 1;
if (baseB < n)
n = baseB;
ncbp = (struct combostr *)malloc(sizeof(struct combostr) +
2 * sizeof(struct combostr *));
if (ncbp == NULL)
panic("Out of memory!");
scbpp = (void *)(ncbp + 1);
fcbp = &frames[fsp->s_frame[r]];
if (ocbp < fcbp) {
scbpp[0] = ocbp;
scbpp[1] = fcbp;
} else {
scbpp[0] = fcbp;
scbpp[1] = ocbp;
}
ncbp->c_combo.cv_force = c;
ncbp->c_combo.cv_win = n;
ncbp->c_link[0] = ocbp;
ncbp->c_link[1] = fcbp;
ncbp->c_linkv[0].s = ocb.s;
ncbp->c_linkv[1].s = fcb.s;
ncbp->c_voff[0] = off;
ncbp->c_voff[1] = f;
ncbp->c_vertex = (spot_index)(osp - board);
ncbp->c_nframes = 2;
ncbp->c_dir = 0;
ncbp->c_frameindex = 0;
ncbp->c_flags = ocb.cv_win != 0 ? C_OPEN_0 : 0;
if (fcb.cv_win != 0)
ncbp->c_flags |= C_OPEN_1;
ncbp->c_framecnt[0] = fcnt;
ncbp->c_emask[0] = emask;
ncbp->c_framecnt[1] = fcb.cv_force - 2;
ncbp->c_emask[1] = ncbp->c_framecnt[1] != 0 ?
((fcb.cv_win != 0 ? 0x1E : 0x1F) & ~(1 << f)) : 0;
combocnt++;
if ((c == 1 && debug > 1) || debug > 3) {
debuglog("%c c %d %d m %x %x o %d %d",
"bw"[curcolor],
ncbp->c_framecnt[0], ncbp->c_framecnt[1],
ncbp->c_emask[0], ncbp->c_emask[1],
ncbp->c_voff[0], ncbp->c_voff[1]);
printcombo(ncbp, tmp, sizeof(tmp));
debuglog("%s", tmp);
}
if (c > 1) {
makeempty(ncbp);
appendcombo(ncbp);
} else {
updatecombo(ncbp, curcolor);
free(ncbp);
combocnt--;
}
#ifdef DEBUG
if ((c == 1 && debug > 1) || debug > 5) {
markcombo(ncbp);
bdisp();
whatsup(0);
clearcombo(ncbp, 0);
}
#endif
}
}
}
static void
addframes(unsigned int level)
{
struct combostr *cbp, *ecbp;
struct spotstr *fsp;
struct elist *nep;
int r;
struct combostr **cbpp, *pcbp;
union comboval fcb, cb;
curlevel = level;
player_color c = curcolor;
for (spot_index s = PT(BSZ, BSZ) + 1; s-- > PT(1, 1); ) {
struct spotstr *sp = &board[s];
for (struct elist *ep = sp->s_empty; ep != NULL; ep = nep) {
cbp = ep->e_combo;
if (cbp->c_combo.s <= sp->s_combo[c].s) {
if (cbp->c_combo.s != sp->s_combo[c].s) {
sp->s_combo[c].s = cbp->c_combo.s;
sp->s_level[c] = cbp->c_nframes;
} else if (cbp->c_nframes < sp->s_level[c])
sp->s_level[c] = cbp->c_nframes;
}
nep = ep->e_next;
free(ep);
elistcnt--;
}
sp->s_empty = sp->s_nempty;
sp->s_nempty = NULL;
}
cbp = ecbp = sortframes[curcolor];
do {
fsp = &board[cbp->c_vertex];
r = cbp->c_dir;
if ((fsp->s_flags & (FFLAG << r)) != 0)
continue;
fcb.s = fsp->s_fval[curcolor][r].s;
if (fcb.s == 0x101)
fcb.s = 0x200;
if (fsp->s_occ == EMPTY) {
if (fcb.cv_win != 0) {
cb.cv_force = fcb.cv_force + 1;
cb.cv_win = 0;
} else
cb.s = fcb.s;
makecombo(cbp, fsp, 0, cb.s);
}
int d = dd[r];
struct spotstr *sp = fsp + d;
for (u_char off = 1; off < 5; off++, sp += d) {
if (sp->s_occ != EMPTY)
continue;
makecombo(cbp, sp, off, fcb.s);
}
} while ((cbp = cbp->c_next) != ecbp);
cbpp = &hashcombos[FAREA];
do {
cbp = *--cbpp;
if (cbp == NULL)
continue;
*cbpp = NULL;
ecbp = sortcombos;
if (ecbp == NULL)
sortcombos = cbp;
else {
pcbp = ecbp->c_prev;
pcbp->c_next = cbp;
ecbp->c_prev = cbp->c_prev;
cbp->c_prev->c_next = ecbp;
cbp->c_prev = pcbp;
}
} while (cbpp != hashcombos);
}
static void
makecombo(struct combostr *ocbp, struct spotstr *osp, u_char off, u_short cv)
{
struct combostr *cbp;
struct spotstr *sp;
struct combostr **scbpp;
int baseB, fcnt, emask, verts;
union comboval ocb;
struct overlap_info ovi;
char tmp[128];
ocb.s = cv;
baseB = ocb.cv_force + ocb.cv_win - 1;
fcnt = ocb.cv_force - 2;
emask = fcnt != 0 ? ((ocb.cv_win != 0 ? 0x1E : 0x1F) & ~(1 << off)) : 0;
for (struct elist *ep = osp->s_empty; ep != NULL; ep = ep->e_next) {
cbp = ep->e_combo;
verts = checkframes(cbp, ocbp, osp, cv, &ovi);
if (verts < 0)
continue;
if (verts > 0) {
sp = &board[ovi.o_intersect];
#ifdef DEBUG
if (sp->s_occ != EMPTY) {
debuglog("loop: %c %s", "BW"[curcolor],
stoc((spot_index)(sp - board)));
whatsup(0);
}
#endif
for (struct elist *nep = sp->s_empty;
nep != NULL; nep = nep->e_next) {
if (nep->e_combo == cbp)
goto fnd;
if (nep->e_combo->c_nframes < cbp->c_nframes)
break;
}
continue;
fnd:
;
}
int c = cbp->c_combo.cv_force + ocb.cv_force - verts - 3;
if (c > 4)
continue;
int n = ep->e_fval.cv_force + ep->e_fval.cv_win - 1;
if (baseB < n)
n = baseB;
struct combostr *ncbp = malloc(sizeof(struct combostr) +
(cbp->c_nframes + 1) * sizeof(struct combostr *));
if (ncbp == NULL)
panic("Out of memory!");
scbpp = (void *)(ncbp + 1);
if (sortcombo(scbpp, (void *)(cbp + 1), ocbp)) {
free(ncbp);
continue;
}
combocnt++;
ncbp->c_combo.cv_force = c;
ncbp->c_combo.cv_win = n;
ncbp->c_link[0] = cbp;
ncbp->c_link[1] = ocbp;
ncbp->c_linkv[1].s = ocb.s;
ncbp->c_voff[1] = off;
ncbp->c_vertex = (spot_index)(osp - board);
ncbp->c_nframes = cbp->c_nframes + 1;
ncbp->c_flags = ocb.cv_win != 0 ? C_OPEN_1 : 0;
ncbp->c_frameindex = ep->e_frameindex;
ncbp->c_framecnt[0] = ep->e_framecnt;
ncbp->c_emask[0] = ep->e_emask;
if (verts != 0) {
ncbp->c_flags |= C_LOOP;
ncbp->c_dir = ovi.o_frameindex;
ncbp->c_framecnt[1] = fcnt - 1;
if (ncbp->c_framecnt[1] != 0) {
n = (ovi.o_intersect - ocbp->c_vertex) / dd[ocbp->c_dir];
ncbp->c_emask[1] = emask & ~(1 << n);
} else
ncbp->c_emask[1] = 0;
ncbp->c_voff[0] = ovi.o_off;
} else {
ncbp->c_dir = 0;
ncbp->c_framecnt[1] = fcnt;
ncbp->c_emask[1] = emask;
ncbp->c_voff[0] = ep->e_off;
}
if ((c == 1 && debug > 1) || debug > 3) {
debuglog("%c v%d i%d d%d c %d %d m %x %x o %d %d",
"bw"[curcolor], verts, ncbp->c_frameindex, ncbp->c_dir,
ncbp->c_framecnt[0], ncbp->c_framecnt[1],
ncbp->c_emask[0], ncbp->c_emask[1],
ncbp->c_voff[0], ncbp->c_voff[1]);
printcombo(ncbp, tmp, sizeof(tmp));
debuglog("%s", tmp);
}
if (c > 1) {
makeempty(ncbp);
combolen++;
} else {
updatecombo(ncbp, curcolor);
}
#ifdef DEBUG
if ((c == 1 && debug > 1) || debug > 4) {
markcombo(ncbp);
bdisp();
whatsup(0);
clearcombo(ncbp, 0);
}
#endif
}
}
#define MAXDEPTH 100
static struct elist einfo[MAXDEPTH];
static struct combostr *ecombo[MAXDEPTH];
static void
makeempty(struct combostr *ocbp)
{
struct combostr *cbp, **cbpp;
struct elist *ep, *nep;
struct spotstr *sp;
int d, emask, i;
int nframes;
char tmp[128];
if (debug > 2) {
printcombo(ocbp, tmp, sizeof(tmp));
debuglog("E%c %s", "bw"[curcolor], tmp);
}
if ((nframes = ocbp->c_nframes) >= MAXDEPTH)
return;
ep = &einfo[nframes];
cbpp = &ecombo[nframes];
for (cbp = ocbp; cbp->c_link[1] != NULL; cbp = cbp->c_link[0]) {
ep--;
ep->e_combo = cbp;
*--cbpp = cbp->c_link[1];
ep->e_off = cbp->c_voff[1];
ep->e_frameindex = cbp->c_frameindex;
ep->e_fval.s = cbp->c_linkv[1].s;
ep->e_framecnt = cbp->c_framecnt[1];
ep->e_emask = cbp->c_emask[1];
}
cbp = ep->e_combo;
ep--;
ep->e_combo = cbp;
*--cbpp = cbp->c_link[0];
ep->e_off = cbp->c_voff[0];
ep->e_frameindex = 0;
ep->e_fval.s = cbp->c_linkv[0].s;
ep->e_framecnt = cbp->c_framecnt[0];
ep->e_emask = cbp->c_emask[0];
int n = 0;
for (i = 2, ep += 2; i < nframes; i++, ep++) {
cbp = ep->e_combo;
nep = &einfo[ep->e_frameindex];
nep->e_framecnt = cbp->c_framecnt[0];
nep->e_emask = cbp->c_emask[0];
if ((cbp->c_flags & C_LOOP) != 0) {
n++;
nep = &einfo[cbp->c_dir];
if (--nep->e_framecnt != 0)
nep->e_emask &= ~(1 << cbp->c_voff[0]);
else
nep->e_emask = 0;
}
}
if (n != 0 && ocbp->c_combo.cv_force == 2) {
ep = &einfo[nframes];
do {
ep--;
cbp = ep->e_combo;
if ((cbp->c_flags & C_LOOP) == 0)
continue;
nep = &einfo[cbp->c_dir];
nep->e_framecnt = 1;
nep->e_emask = 1 << cbp->c_voff[0];
ep->e_framecnt = 1;
ep->e_emask = 1 << ep->e_off;
ep = &einfo[ep->e_frameindex];
do {
ep->e_framecnt = 1;
ep->e_emask = 1 << ep->e_off;
ep = &einfo[ep->e_frameindex];
} while (ep > nep);
} while (ep != einfo);
}
for (i = 0, ep = einfo, cbpp = ecombo; i < nframes; i++, ep++, cbpp++) {
if ((emask = ep->e_emask) == 0)
continue;
cbp = *cbpp;
sp = &board[cbp->c_vertex];
d = dd[cbp->c_dir];
for (int off = 0, m = 1; off < 5; off++, sp += d, m <<= 1) {
if (sp->s_occ != EMPTY || (emask & m) == 0)
continue;
nep = (struct elist *)malloc(sizeof(struct elist));
if (nep == NULL)
panic("Out of memory!");
nep->e_combo = ocbp;
nep->e_off = off;
nep->e_frameindex = i;
if (ep->e_framecnt > 1) {
nep->e_framecnt = ep->e_framecnt - 1;
nep->e_emask = emask & ~m;
} else {
nep->e_framecnt = 0;
nep->e_emask = 0;
}
nep->e_fval.s = ep->e_fval.s;
if (debug > 2) {
debuglog("e %s o%d i%d c%d m%x %x",
stoc((spot_index)(sp - board)),
nep->e_off,
nep->e_frameindex,
nep->e_framecnt,
nep->e_emask,
nep->e_fval.s);
}
nep->e_next = sp->s_nempty;
sp->s_nempty = nep;
elistcnt++;
}
}
}
static void
updatecombo(struct combostr *cbp, player_color color)
{
struct combostr *tcbp;
union comboval cb;
int flags = 0;
cb.cv_force = cbp->c_combo.cv_force;
u_char nframes = cbp->c_nframes;
if (color != nextcolor)
memset(tmpmap, 0, sizeof(tmpmap));
for (; (tcbp = cbp->c_link[1]) != NULL; cbp = cbp->c_link[0]) {
flags = cbp->c_flags;
cb.cv_win = cbp->c_combo.cv_win;
if (color == nextcolor) {
struct spotstr *sp = &board[cbp->c_vertex];
sp->s_nforce[color]++;
if (cb.s <= sp->s_combo[color].s) {
if (cb.s != sp->s_combo[color].s) {
sp->s_combo[color].s = cb.s;
sp->s_level[color] = nframes;
} else if (nframes < sp->s_level[color])
sp->s_level[color] = nframes;
}
} else {
spot_index s = tcbp->c_vertex;
struct spotstr *sp = &board[s];
int d = dd[tcbp->c_dir];
int off = (flags & C_OPEN_1) != 0 ? 6 : 5;
for (; --off >= 0; sp += d, s += d) {
if (sp->s_occ != EMPTY)
continue;
sp->s_nforce[color]++;
if (cb.s <= sp->s_combo[color].s) {
if (cb.s != sp->s_combo[color].s) {
sp->s_combo[color].s = cb.s;
sp->s_level[color] = nframes;
} else if (nframes < sp->s_level[color])
sp->s_level[color] = nframes;
}
BIT_SET(tmpmap, s);
}
}
board[tcbp->c_vertex].s_flags |= FFLAG << tcbp->c_dir;
}
if (color != nextcolor) {
spot_index s = cbp->c_vertex;
struct spotstr *sp = &board[s];
int d = dd[cbp->c_dir];
int off = (flags & C_OPEN_0) != 0 ? 6 : 5;
for (; --off >= 0; sp += d, s += d) {
if (sp->s_occ != EMPTY)
continue;
sp->s_nforce[color]++;
if (cb.s <= sp->s_combo[color].s) {
if (cb.s != sp->s_combo[color].s) {
sp->s_combo[color].s = cb.s;
sp->s_level[color] = nframes;
} else if (nframes < sp->s_level[color])
sp->s_level[color] = nframes;
}
BIT_SET(tmpmap, s);
}
if (nforce == 0)
memcpy(forcemap, tmpmap, sizeof(tmpmap));
else {
for (int i = 0; (unsigned int)i < MAPSZ; i++)
forcemap[i] &= tmpmap[i];
}
nforce++;
}
board[cbp->c_vertex].s_flags |= FFLAG << cbp->c_dir;
}
static void
appendcombo(struct combostr *cbp)
{
struct combostr *pcbp, *ncbp;
combolen++;
ncbp = sortcombos;
if (ncbp == NULL) {
sortcombos = cbp;
cbp->c_next = cbp;
cbp->c_prev = cbp;
return;
}
pcbp = ncbp->c_prev;
cbp->c_next = ncbp;
cbp->c_prev = pcbp;
ncbp->c_prev = cbp;
pcbp->c_next = cbp;
}
static int
checkframes(struct combostr *cbp, struct combostr *fcbp, struct spotstr *osp,
u_short cv, struct overlap_info *ovi)
{
struct combostr *tcbp, *lcbp;
int ovbit, n, mask, flags, fcnt;
union comboval cb;
u_char *str;
lcbp = NULL;
flags = 0;
cb.s = cv;
fcnt = cb.cv_force - 2;
int verts = 0;
u_char myindex = cbp->c_nframes;
n = (frame_index)(fcbp - frames) * FAREA;
str = &overlap[n];
spot_index *ip = &intersect[n];
ovbit = cb.cv_win != 0 ? 2 : 0;
for (; (tcbp = cbp->c_link[1]) != NULL;
lcbp = cbp, cbp = cbp->c_link[0]) {
if (tcbp == fcbp)
return -1;
myindex--;
mask = str[tcbp - frames];
flags = cbp->c_flags;
n = ovbit + ((flags & C_OPEN_1) != 0 ? 1 : 0);
if ((mask & (1 << n)) != 0) {
if (tcbp->c_dir == fcbp->c_dir &&
(mask & (0x10 << n)) != 0)
return -1;
spot_index s = ip[tcbp - frames];
if (osp != &board[s]) {
if (verts != 0)
return -1;
if (fcnt == 0 || cbp->c_framecnt[1] == 0)
return -1;
if ((flags & C_OPEN_1) != 0 &&
(s == tcbp->c_vertex ||
s == tcbp->c_vertex + 5 * dd[tcbp->c_dir]))
return -1;
if (cb.cv_win != 0 &&
(s == fcbp->c_vertex ||
s == fcbp->c_vertex + 5 * dd[fcbp->c_dir]))
return -1;
ovi->o_intersect = s;
ovi->o_off =
(s - tcbp->c_vertex) / dd[tcbp->c_dir];
ovi->o_frameindex = myindex;
verts++;
}
}
n = ovbit + ((flags & C_OPEN_0) != 0 ? 1 : 0);
}
if (cbp == fcbp)
return -1;
mask = str[cbp - frames];
if ((mask & (1 << n)) != 0) {
if (cbp->c_dir == fcbp->c_dir && (mask & (0x10 << n)) != 0)
return -1;
spot_index s = ip[cbp - frames];
if (osp != &board[s]) {
if (verts != 0)
return -1;
if (fcnt == 0 || lcbp->c_framecnt[0] == 0)
return -1;
if ((flags & C_OPEN_0) != 0 &&
(s == cbp->c_vertex ||
s == cbp->c_vertex + 5 * dd[cbp->c_dir]))
return -1;
if (cb.cv_win != 0 &&
(s == fcbp->c_vertex ||
s == fcbp->c_vertex + 5 * dd[fcbp->c_dir]))
return -1;
ovi->o_intersect = s;
ovi->o_off = (s - cbp->c_vertex) / dd[cbp->c_dir];
ovi->o_frameindex = 0;
verts++;
}
}
return verts;
}
static bool
sortcombo(struct combostr **scbpp, struct combostr **cbpp,
struct combostr *fcbp)
{
struct combostr **spp, **cpp;
struct combostr *cbp, *ecbp;
int inx;
#ifdef DEBUG
if (debug > 3) {
char buf[128];
size_t pos;
debuglog("sortc: %s%c l%u", stoc(fcbp->c_vertex),
pdir[fcbp->c_dir], curlevel);
pos = 0;
for (cpp = cbpp; cpp < cbpp + curlevel; cpp++) {
snprintf(buf + pos, sizeof(buf) - pos, " %s%c",
stoc((*cpp)->c_vertex), pdir[(*cpp)->c_dir]);
pos += strlen(buf + pos);
}
debuglog("%s", buf);
}
#endif
unsigned int n = curlevel + 1;
spp = scbpp + n;
cpp = cbpp + curlevel;
do {
cpp--;
if (fcbp > *cpp) {
*--spp = fcbp;
do {
*--spp = *cpp;
} while (cpp-- != cbpp);
goto inserted;
}
*--spp = *cpp;
} while (cpp != cbpp);
*--spp = fcbp;
inserted:
cbp = hashcombos[inx = (frame_index)(*scbpp - frames)];
if (cbp == NULL) {
fcbp = (void *)((char *)scbpp - sizeof(struct combostr));
hashcombos[inx] = fcbp;
fcbp->c_next = fcbp->c_prev = fcbp;
return false;
}
ecbp = cbp;
do {
cbpp = (void *)(cbp + 1);
cpp = cbpp + n;
spp = scbpp + n;
cbpp++;
do {
if (*--spp != *--cpp)
goto next;
} while (cpp != cbpp);
#ifdef DEBUG
if (debug > 3) {
char buf[128];
size_t pos;
debuglog("sort1: n%u", n);
pos = 0;
for (cpp = scbpp; cpp < scbpp + n; cpp++) {
snprintf(buf + pos, sizeof(buf) - pos, " %s%c",
stoc((*cpp)->c_vertex),
pdir[(*cpp)->c_dir]);
pos += strlen(buf + pos);
}
debuglog("%s", buf);
printcombo(cbp, buf, sizeof(buf));
debuglog("%s", buf);
cbpp--;
pos = 0;
for (cpp = cbpp; cpp < cbpp + n; cpp++) {
snprintf(buf + pos, sizeof(buf) - pos, " %s%c",
stoc((*cpp)->c_vertex),
pdir[(*cpp)->c_dir]);
pos += strlen(buf + pos);
}
debuglog("%s", buf);
}
#endif
return true;
next:
;
} while ((cbp = cbp->c_next) != ecbp);
ecbp = cbp->c_prev;
fcbp = (void *)((char *)scbpp - sizeof(struct combostr));
fcbp->c_next = cbp;
fcbp->c_prev = ecbp;
cbp->c_prev = fcbp;
ecbp->c_next = fcbp;
return false;
}
#if !defined(DEBUG)
static
#endif
void
printcombo(struct combostr *cbp, char *buf, size_t max)
{
struct combostr *tcbp;
size_t pos = 0;
snprintf(buf + pos, max - pos, "%x/%d",
cbp->c_combo.s, cbp->c_nframes);
pos += strlen(buf + pos);
for (; (tcbp = cbp->c_link[1]) != NULL; cbp = cbp->c_link[0]) {
snprintf(buf + pos, max - pos, " %s%c%x",
stoc(tcbp->c_vertex), pdir[tcbp->c_dir], cbp->c_flags);
pos += strlen(buf + pos);
}
snprintf(buf + pos, max - pos, " %s%c",
stoc(cbp->c_vertex), pdir[cbp->c_dir]);
}
#ifdef DEBUG
void
markcombo(struct combostr *ocbp)
{
struct combostr *cbp, **cbpp;
struct elist *ep, *nep;
struct spotstr *sp;
int d, m, i;
int nframes;
int cmask, omask;
if ((nframes = ocbp->c_nframes) >= MAXDEPTH)
return;
ep = &einfo[nframes];
cbpp = &ecombo[nframes];
for (cbp = ocbp; cbp->c_link[1] != NULL; cbp = cbp->c_link[0]) {
ep--;
ep->e_combo = cbp;
*--cbpp = cbp->c_link[1];
ep->e_off = cbp->c_voff[1];
ep->e_frameindex = cbp->c_frameindex;
ep->e_fval.s = cbp->c_linkv[1].s;
ep->e_framecnt = cbp->c_framecnt[1];
ep->e_emask = cbp->c_emask[1];
}
cbp = ep->e_combo;
ep--;
ep->e_combo = cbp;
*--cbpp = cbp->c_link[0];
ep->e_off = cbp->c_voff[0];
ep->e_frameindex = 0;
ep->e_fval.s = cbp->c_linkv[0].s;
ep->e_framecnt = cbp->c_framecnt[0];
ep->e_emask = cbp->c_emask[0];
int n = 0;
for (i = 2, ep += 2; i < nframes; i++, ep++) {
cbp = ep->e_combo;
nep = &einfo[ep->e_frameindex];
nep->e_framecnt = cbp->c_framecnt[0];
nep->e_emask = cbp->c_emask[0];
if ((cbp->c_flags & C_LOOP) != 0) {
n++;
nep = &einfo[cbp->c_dir];
if (--nep->e_framecnt != 0)
nep->e_emask &= ~(1 << cbp->c_voff[0]);
else
nep->e_emask = 0;
}
}
if (n != 0 && ocbp->c_combo.cv_force == 2) {
ep = &einfo[nframes];
do {
ep--;
cbp = ep->e_combo;
if ((cbp->c_flags & C_LOOP) == 0)
continue;
nep = &einfo[cbp->c_dir];
nep->e_framecnt = 1;
nep->e_emask = 1 << cbp->c_voff[0];
ep->e_framecnt = 1;
ep->e_emask = 1 << ep->e_off;
ep = &einfo[ep->e_frameindex];
do {
ep->e_framecnt = 1;
ep->e_emask = 1 << ep->e_off;
ep = &einfo[ep->e_frameindex];
} while (ep > nep);
} while (ep != einfo);
}
for (i = 0, ep = einfo, cbpp = ecombo; i < nframes; i++, ep++, cbpp++) {
m = ep->e_emask;
cbp = *cbpp;
sp = &board[cbp->c_vertex];
d = dd[cbp->c_dir];
cmask = CFLAG << cbp->c_dir;
omask = (IFLAG | CFLAG) << cbp->c_dir;
int off = ep->e_fval.cv_win != 0 ? 6 : 5;
for (; --off >= 0; sp += d, m >>= 1)
sp->s_flags |= (m & 1) != 0 ? omask : cmask;
}
}
void
clearcombo(struct combostr *cbp, int open)
{
struct combostr *tcbp;
for (; (tcbp = cbp->c_link[1]) != NULL; cbp = cbp->c_link[0]) {
clearcombo(tcbp, cbp->c_flags & C_OPEN_1);
open = cbp->c_flags & C_OPEN_0;
}
struct spotstr *sp = &board[cbp->c_vertex];
int d = dd[cbp->c_dir];
int mask = ~((IFLAG | CFLAG) << cbp->c_dir);
int n = open != 0 ? 6 : 5;
for (; --n >= 0; sp += d)
sp->s_flags &= mask;
}
#endif