2004-01-27 23:26:20 +03:00
|
|
|
/* $NetBSD: pickmove.c,v 1.11 2004/01/27 20:26:20 jsm Exp $ */
|
1997-01-03 04:35:23 +03:00
|
|
|
|
1996-12-28 21:44:55 +03:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1994
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
|
|
|
*
|
|
|
|
* This code is derived from software contributed to Berkeley by
|
|
|
|
* Ralph Campbell.
|
|
|
|
*
|
|
|
|
* 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.
|
2003-08-07 13:36:50 +04:00
|
|
|
* 3. Neither the name of the University nor the names of its contributors
|
1996-12-28 21:44:55 +03:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
1997-10-10 17:36:01 +04:00
|
|
|
#include <sys/cdefs.h>
|
1996-12-28 21:44:55 +03:00
|
|
|
#ifndef lint
|
1996-12-28 21:56:57 +03:00
|
|
|
#if 0
|
1996-12-28 21:44:55 +03:00
|
|
|
static char sccsid[] = "@(#)pickmove.c 8.2 (Berkeley) 5/3/95";
|
1996-12-28 21:56:57 +03:00
|
|
|
#else
|
2004-01-27 23:26:20 +03:00
|
|
|
__RCSID("$NetBSD: pickmove.c,v 1.11 2004/01/27 20:26:20 jsm Exp $");
|
1996-12-28 21:56:57 +03:00
|
|
|
#endif
|
1996-12-28 21:44:55 +03:00
|
|
|
#endif /* not lint */
|
|
|
|
|
1997-01-03 04:16:04 +03:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
1996-12-28 21:44:55 +03:00
|
|
|
#include <curses.h>
|
2004-01-27 23:26:20 +03:00
|
|
|
#include <limits.h>
|
1996-12-28 21:44:55 +03:00
|
|
|
|
|
|
|
#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] |= (1 << ((b) % BITS_PER_INT)))
|
|
|
|
#define BIT_CLR(a, b) ((a)[(b)/BITS_PER_INT] &= ~(1 << ((b) % BITS_PER_INT)))
|
|
|
|
#define BIT_TEST(a, b) ((a)[(b)/BITS_PER_INT] & (1 << ((b) % BITS_PER_INT)))
|
|
|
|
|
|
|
|
struct combostr *hashcombos[FAREA]; /* hash list for finding duplicates */
|
|
|
|
struct combostr *sortcombos; /* combos at higher levels */
|
|
|
|
int combolen; /* number of combos in sortcombos */
|
|
|
|
int nextcolor; /* color of next move */
|
|
|
|
int elistcnt; /* count of struct elist allocated */
|
|
|
|
int combocnt; /* count of struct combostr allocated */
|
|
|
|
int forcemap[MAPSZ]; /* map for blocking <1,x> combos */
|
|
|
|
int tmpmap[MAPSZ]; /* map for blocking <1,x> combos */
|
|
|
|
int nforce; /* count of opponent <1,x> combos */
|
|
|
|
|
1997-10-10 17:36:01 +04:00
|
|
|
int
|
1996-12-28 21:44:55 +03:00
|
|
|
pickmove(us)
|
|
|
|
int us;
|
|
|
|
{
|
1997-10-10 17:36:01 +04:00
|
|
|
struct spotstr *sp, *sp1, *sp2;
|
|
|
|
union comboval *Ocp, *Tcp;
|
|
|
|
int m;
|
1996-12-28 21:44:55 +03:00
|
|
|
|
|
|
|
/* first move is easy */
|
|
|
|
if (movenum == 1)
|
|
|
|
return (PT(K,10));
|
|
|
|
|
|
|
|
/* initialize all the board values */
|
|
|
|
for (sp = &board[PT(T,20)]; --sp >= &board[PT(A,1)]; ) {
|
|
|
|
sp->s_combo[BLACK].s = MAXCOMBO + 1;
|
|
|
|
sp->s_combo[WHITE].s = MAXCOMBO + 1;
|
|
|
|
sp->s_level[BLACK] = 255;
|
|
|
|
sp->s_level[WHITE] = 255;
|
|
|
|
sp->s_nforce[BLACK] = 0;
|
|
|
|
sp->s_nforce[WHITE] = 0;
|
|
|
|
sp->s_flg &= ~(FFLAGALL | MFLAGALL);
|
|
|
|
}
|
|
|
|
nforce = 0;
|
|
|
|
memset(forcemap, 0, sizeof(forcemap));
|
|
|
|
|
|
|
|
/* compute new values */
|
|
|
|
nextcolor = us;
|
|
|
|
scanframes(BLACK);
|
|
|
|
scanframes(WHITE);
|
|
|
|
|
|
|
|
/* find the spot with the highest value */
|
|
|
|
for (sp = sp1 = sp2 = &board[PT(T,19)]; --sp >= &board[PT(A,1)]; ) {
|
|
|
|
if (sp->s_occ != EMPTY)
|
|
|
|
continue;
|
|
|
|
if (debug && (sp->s_combo[BLACK].c.a == 1 ||
|
|
|
|
sp->s_combo[WHITE].c.a == 1)) {
|
|
|
|
sprintf(fmtbuf, "- %s %x/%d %d %x/%d %d %d", stoc(sp - board),
|
|
|
|
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);
|
|
|
|
dlog(fmtbuf);
|
|
|
|
}
|
|
|
|
/* pick the best black move */
|
|
|
|
if (better(sp, sp1, BLACK))
|
|
|
|
sp1 = sp;
|
|
|
|
/* pick the best white move */
|
|
|
|
if (better(sp, sp2, WHITE))
|
|
|
|
sp2 = sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debug) {
|
1997-10-10 17:36:01 +04:00
|
|
|
sprintf(fmtbuf, "B %s %x/%d %d %x/%d %d %d",
|
1996-12-28 21:44:55 +03:00
|
|
|
stoc(sp1 - board),
|
|
|
|
sp1->s_combo[BLACK].s, sp1->s_level[BLACK],
|
|
|
|
sp1->s_nforce[BLACK],
|
|
|
|
sp1->s_combo[WHITE].s, sp1->s_level[WHITE],
|
|
|
|
sp1->s_nforce[WHITE], sp1->s_wval);
|
|
|
|
dlog(fmtbuf);
|
1997-10-10 17:36:01 +04:00
|
|
|
sprintf(fmtbuf, "W %s %x/%d %d %x/%d %d %d",
|
1996-12-28 21:44:55 +03:00
|
|
|
stoc(sp2 - board),
|
|
|
|
sp2->s_combo[WHITE].s, sp2->s_level[WHITE],
|
|
|
|
sp2->s_nforce[WHITE],
|
|
|
|
sp2->s_combo[BLACK].s, sp2->s_level[BLACK],
|
|
|
|
sp2->s_nforce[BLACK], sp2->s_wval);
|
|
|
|
dlog(fmtbuf);
|
|
|
|
/*
|
|
|
|
* Check for more than one force that can't
|
|
|
|
* all be blocked with one move.
|
|
|
|
*/
|
|
|
|
sp = (us == BLACK) ? sp2 : sp1;
|
|
|
|
m = sp - board;
|
|
|
|
if (sp->s_combo[!us].c.a == 1 && !BIT_TEST(forcemap, m))
|
|
|
|
dlog("*** Can't be blocked");
|
|
|
|
}
|
|
|
|
if (us == BLACK) {
|
|
|
|
Ocp = &sp1->s_combo[BLACK];
|
|
|
|
Tcp = &sp2->s_combo[WHITE];
|
|
|
|
} else {
|
|
|
|
Tcp = &sp1->s_combo[BLACK];
|
|
|
|
Ocp = &sp2->s_combo[WHITE];
|
|
|
|
sp = sp1;
|
|
|
|
sp1 = sp2;
|
|
|
|
sp2 = sp;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Block their combo only if we have to (i.e., if they are one move
|
|
|
|
* away from completing a force and we don't have a force that
|
|
|
|
* we can complete which takes fewer moves to win).
|
|
|
|
*/
|
|
|
|
if (Tcp->c.a <= 1 && (Ocp->c.a > 1 ||
|
|
|
|
Tcp->c.a + Tcp->c.b < Ocp->c.a + Ocp->c.b))
|
|
|
|
return (sp2 - board);
|
|
|
|
return (sp1 - board);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return true if spot 'sp' is better than spot 'sp1' for color 'us'.
|
|
|
|
*/
|
1997-10-10 17:36:01 +04:00
|
|
|
int
|
1996-12-28 21:44:55 +03:00
|
|
|
better(sp, sp1, us)
|
Add use of `const' where appropriate to the games.
This merges in all such remaining changes from the Linux port of the
NetBSD games, except in hunt (where substantial changes from OpenBSD
need to be looked at).
Some such changes were previously covered in PRs bin/6041, bin/6146,
bin/6148, bin/6150, bin/6151, bin/6580, bin/6660, bin/7993, bin/7994,
bin/8039, bin/8057 and bin/8093.
1999-09-09 01:17:44 +04:00
|
|
|
const struct spotstr *sp;
|
|
|
|
const struct spotstr *sp1;
|
1996-12-28 21:44:55 +03:00
|
|
|
int us;
|
|
|
|
{
|
|
|
|
int them, s, s1;
|
|
|
|
|
|
|
|
if (sp->s_combo[us].s < sp1->s_combo[us].s)
|
|
|
|
return (1);
|
|
|
|
if (sp->s_combo[us].s != sp1->s_combo[us].s)
|
|
|
|
return (0);
|
|
|
|
if (sp->s_level[us] < sp1->s_level[us])
|
|
|
|
return (1);
|
|
|
|
if (sp->s_level[us] != sp1->s_level[us])
|
|
|
|
return (0);
|
|
|
|
if (sp->s_nforce[us] > sp1->s_nforce[us])
|
|
|
|
return (1);
|
|
|
|
if (sp->s_nforce[us] != sp1->s_nforce[us])
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
them = !us;
|
|
|
|
s = sp - board;
|
|
|
|
s1 = sp1 - board;
|
|
|
|
if (BIT_TEST(forcemap, s) && !BIT_TEST(forcemap, s1))
|
|
|
|
return (1);
|
|
|
|
if (!BIT_TEST(forcemap, s) && BIT_TEST(forcemap, s1))
|
|
|
|
return (0);
|
|
|
|
if (sp->s_combo[them].s < sp1->s_combo[them].s)
|
|
|
|
return (1);
|
|
|
|
if (sp->s_combo[them].s != sp1->s_combo[them].s)
|
|
|
|
return (0);
|
|
|
|
if (sp->s_level[them] < sp1->s_level[them])
|
|
|
|
return (1);
|
|
|
|
if (sp->s_level[them] != sp1->s_level[them])
|
|
|
|
return (0);
|
|
|
|
if (sp->s_nforce[them] > sp1->s_nforce[them])
|
|
|
|
return (1);
|
|
|
|
if (sp->s_nforce[them] != sp1->s_nforce[them])
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
if (sp->s_wval > sp1->s_wval)
|
|
|
|
return (1);
|
|
|
|
if (sp->s_wval != sp1->s_wval)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
#ifdef SVR4
|
|
|
|
return (rand() & 1);
|
|
|
|
#else
|
|
|
|
return (random() & 1);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int curcolor; /* implicit parameter to makecombo() */
|
|
|
|
int curlevel; /* implicit parameter to makecombo() */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan the sorted list of non-empty frames and
|
|
|
|
* update the minimum combo values for each empty spot.
|
|
|
|
* Also, try to combine frames to find more complex (chained) moves.
|
|
|
|
*/
|
1997-10-10 17:36:01 +04:00
|
|
|
void
|
1996-12-28 21:44:55 +03:00
|
|
|
scanframes(color)
|
|
|
|
int color;
|
|
|
|
{
|
1997-10-10 17:36:01 +04:00
|
|
|
struct combostr *cbp, *ecbp;
|
|
|
|
struct spotstr *sp;
|
|
|
|
union comboval *cp;
|
|
|
|
struct elist *ep, *nep;
|
|
|
|
int i, r, d, n;
|
1996-12-28 21:44:55 +03:00
|
|
|
union comboval cb;
|
|
|
|
|
|
|
|
curcolor = color;
|
|
|
|
|
|
|
|
/* check for empty list of frames */
|
|
|
|
cbp = sortframes[color];
|
|
|
|
if (cbp == (struct combostr *)0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* quick check for four in a row */
|
|
|
|
sp = &board[cbp->c_vertex];
|
|
|
|
cb.s = sp->s_fval[color][d = cbp->c_dir].s;
|
|
|
|
if (cb.s < 0x101) {
|
|
|
|
d = dd[d];
|
|
|
|
for (i = 5 + cb.c.b; --i >= 0; sp += d) {
|
|
|
|
if (sp->s_occ != EMPTY)
|
|
|
|
continue;
|
|
|
|
sp->s_combo[color].s = cb.s;
|
|
|
|
sp->s_level[color] = 1;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the minimum combo value for each spot in the frame
|
|
|
|
* and try making all combinations of two frames intersecting at
|
|
|
|
* an empty spot.
|
|
|
|
*/
|
|
|
|
n = combolen;
|
|
|
|
ecbp = cbp;
|
|
|
|
do {
|
|
|
|
sp = &board[cbp->c_vertex];
|
|
|
|
cp = &sp->s_fval[color][r = cbp->c_dir];
|
|
|
|
d = dd[r];
|
|
|
|
if (cp->c.b) {
|
|
|
|
/*
|
|
|
|
* Since this is the first spot of an open ended
|
|
|
|
* frame, we treat it as a closed frame.
|
|
|
|
*/
|
|
|
|
cb.c.a = cp->c.a + 1;
|
|
|
|
cb.c.b = 0;
|
|
|
|
if (cb.s < sp->s_combo[color].s) {
|
|
|
|
sp->s_combo[color].s = cb.s;
|
|
|
|
sp->s_level[color] = 1;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Try combining other frames that intersect
|
|
|
|
* at this spot.
|
|
|
|
*/
|
|
|
|
makecombo2(cbp, sp, 0, cb.s);
|
|
|
|
if (cp->s != 0x101)
|
|
|
|
cb.s = cp->s;
|
|
|
|
else if (color != nextcolor)
|
|
|
|
memset(tmpmap, 0, sizeof(tmpmap));
|
|
|
|
sp += d;
|
|
|
|
i = 1;
|
|
|
|
} else {
|
|
|
|
cb.s = cp->s;
|
|
|
|
i = 0;
|
|
|
|
}
|
|
|
|
for (; i < 5; i++, sp += d) { /* for each spot */
|
|
|
|
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 = sp - board;
|
|
|
|
BIT_SET(tmpmap, n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Try combining other frames that intersect
|
|
|
|
* at this spot.
|
|
|
|
*/
|
|
|
|
makecombo2(cbp, sp, i, cb.s);
|
|
|
|
}
|
|
|
|
if (cp->s == 0x101 && color != nextcolor) {
|
|
|
|
if (nforce == 0)
|
|
|
|
memcpy(forcemap, tmpmap, sizeof(tmpmap));
|
|
|
|
else {
|
1999-09-18 23:38:46 +04:00
|
|
|
for (i = 0; (unsigned int)i < MAPSZ; i++)
|
1996-12-28 21:44:55 +03:00
|
|
|
forcemap[i] &= tmpmap[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* mark frame as having been processed */
|
|
|
|
board[cbp->c_vertex].s_flg |= MFLAG << r;
|
|
|
|
} while ((cbp = cbp->c_next) != ecbp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to make new 3rd level combos, 4th level, etc.
|
|
|
|
* Limit the search depth early in the game.
|
|
|
|
*/
|
|
|
|
d = 2;
|
|
|
|
while (d <= ((movenum + 1) >> 1) && combolen > n) {
|
|
|
|
if (debug) {
|
|
|
|
sprintf(fmtbuf, "%cL%d %d %d %d", "BW"[color],
|
|
|
|
d, combolen - n, combocnt, elistcnt);
|
|
|
|
dlog(fmtbuf);
|
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
n = combolen;
|
|
|
|
addframes(d);
|
|
|
|
d++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* scan for combos at empty spots */
|
|
|
|
for (sp = &board[PT(T,20)]; --sp >= &board[PT(A,1)]; ) {
|
|
|
|
for (ep = sp->s_empty; ep; 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 = (struct elist *)0;
|
|
|
|
for (ep = sp->s_nempty; ep; 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 = (struct elist *)0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove old combos */
|
|
|
|
if ((cbp = sortcombos) != (struct combostr *)0) {
|
|
|
|
struct combostr *ncbp;
|
|
|
|
|
|
|
|
/* scan the list */
|
|
|
|
ecbp = cbp;
|
|
|
|
do {
|
|
|
|
ncbp = cbp->c_next;
|
|
|
|
free(cbp);
|
|
|
|
combocnt--;
|
|
|
|
} while ((cbp = ncbp) != ecbp);
|
|
|
|
sortcombos = (struct combostr *)0;
|
|
|
|
}
|
|
|
|
combolen = 0;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (combocnt) {
|
|
|
|
sprintf(fmtbuf, "scanframes: %c combocnt %d", "BW"[color],
|
|
|
|
combocnt);
|
|
|
|
dlog(fmtbuf);
|
|
|
|
whatsup(0);
|
|
|
|
}
|
|
|
|
if (elistcnt) {
|
|
|
|
sprintf(fmtbuf, "scanframes: %c elistcnt %d", "BW"[color],
|
|
|
|
elistcnt);
|
|
|
|
dlog(fmtbuf);
|
|
|
|
whatsup(0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute all level 2 combos of frames intersecting spot 'osp'
|
|
|
|
* within the frame 'ocbp' and combo value 's'.
|
|
|
|
*/
|
1997-10-10 17:36:01 +04:00
|
|
|
void
|
1996-12-28 21:44:55 +03:00
|
|
|
makecombo2(ocbp, osp, off, s)
|
|
|
|
struct combostr *ocbp;
|
|
|
|
struct spotstr *osp;
|
|
|
|
int off;
|
|
|
|
int s;
|
|
|
|
{
|
1997-10-10 17:36:01 +04:00
|
|
|
struct spotstr *fsp;
|
|
|
|
struct combostr *ncbp;
|
|
|
|
int f, r, d, c;
|
1996-12-28 21:44:55 +03:00
|
|
|
int baseB, fcnt, emask, bmask, n;
|
|
|
|
union comboval ocb, fcb;
|
|
|
|
struct combostr **scbpp, *fcbp;
|
|
|
|
|
|
|
|
/* try to combine a new frame with those found so far */
|
|
|
|
ocb.s = s;
|
|
|
|
baseB = ocb.c.a + ocb.c.b - 1;
|
|
|
|
fcnt = ocb.c.a - 2;
|
|
|
|
emask = fcnt ? ((ocb.c.b ? 0x1E : 0x1F) & ~(1 << off)) : 0;
|
|
|
|
for (r = 4; --r >= 0; ) { /* for each direction */
|
|
|
|
/* don't include frames that overlap in the same direction */
|
|
|
|
if (r == ocbp->c_dir)
|
|
|
|
continue;
|
|
|
|
d = dd[r];
|
|
|
|
/*
|
|
|
|
* Frame A combined with B is the same value as B combined with A
|
|
|
|
* so skip frames that have already been processed (MFLAG).
|
|
|
|
* Also skip blocked frames (BFLAG) and frames that are <1,x>
|
|
|
|
* since combining another frame with it isn't valid.
|
|
|
|
*/
|
|
|
|
bmask = (BFLAG | FFLAG | MFLAG) << r;
|
|
|
|
fsp = osp;
|
|
|
|
for (f = 0; f < 5; f++, fsp -= d) { /* for each frame */
|
|
|
|
if (fsp->s_occ == BORDER)
|
|
|
|
break;
|
|
|
|
if (fsp->s_flg & bmask)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* don't include frames of the wrong color */
|
|
|
|
fcb.s = fsp->s_fval[curcolor][r].s;
|
|
|
|
if (fcb.c.a >= MAXA)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the combo value for this frame.
|
|
|
|
* If this is the end point of the frame,
|
|
|
|
* use the closed ended value for the frame.
|
|
|
|
*/
|
1997-10-10 17:36:01 +04:00
|
|
|
if ((f == 0 && fcb.c.b) || fcb.s == 0x101) {
|
1996-12-28 21:44:55 +03:00
|
|
|
fcb.c.a++;
|
|
|
|
fcb.c.b = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* compute combo value */
|
|
|
|
c = fcb.c.a + ocb.c.a - 3;
|
|
|
|
if (c > 4)
|
|
|
|
continue;
|
|
|
|
n = fcb.c.a + fcb.c.b - 1;
|
|
|
|
if (baseB < n)
|
|
|
|
n = baseB;
|
|
|
|
|
|
|
|
/* make a new combo! */
|
|
|
|
ncbp = (struct combostr *)malloc(sizeof(struct combostr) +
|
|
|
|
2 * sizeof(struct combostr *));
|
1999-09-09 21:27:58 +04:00
|
|
|
if (ncbp == NULL)
|
|
|
|
panic("Out of memory!");
|
1996-12-28 21:44:55 +03:00
|
|
|
scbpp = (struct combostr **)(ncbp + 1);
|
|
|
|
fcbp = fsp->s_frame[r];
|
|
|
|
if (ocbp < fcbp) {
|
|
|
|
scbpp[0] = ocbp;
|
|
|
|
scbpp[1] = fcbp;
|
|
|
|
} else {
|
|
|
|
scbpp[0] = fcbp;
|
|
|
|
scbpp[1] = ocbp;
|
|
|
|
}
|
|
|
|
ncbp->c_combo.c.a = c;
|
|
|
|
ncbp->c_combo.c.b = 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 = osp - board;
|
|
|
|
ncbp->c_nframes = 2;
|
|
|
|
ncbp->c_dir = 0;
|
|
|
|
ncbp->c_frameindex = 0;
|
|
|
|
ncbp->c_flg = (ocb.c.b) ? C_OPEN_0 : 0;
|
|
|
|
if (fcb.c.b)
|
|
|
|
ncbp->c_flg |= C_OPEN_1;
|
|
|
|
ncbp->c_framecnt[0] = fcnt;
|
|
|
|
ncbp->c_emask[0] = emask;
|
|
|
|
ncbp->c_framecnt[1] = fcb.c.a - 2;
|
|
|
|
ncbp->c_emask[1] = ncbp->c_framecnt[1] ?
|
|
|
|
((fcb.c.b ? 0x1E : 0x1F) & ~(1 << f)) : 0;
|
|
|
|
combocnt++;
|
|
|
|
|
1997-10-10 17:36:01 +04:00
|
|
|
if ((c == 1 && debug > 1) || debug > 3) {
|
1996-12-28 21:44:55 +03:00
|
|
|
sprintf(fmtbuf, "%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]);
|
|
|
|
dlog(fmtbuf);
|
|
|
|
printcombo(ncbp, fmtbuf);
|
|
|
|
dlog(fmtbuf);
|
|
|
|
}
|
|
|
|
if (c > 1) {
|
|
|
|
/* record the empty spots that will complete this combo */
|
|
|
|
makeempty(ncbp);
|
|
|
|
|
|
|
|
/* add the new combo to the end of the list */
|
|
|
|
appendcombo(ncbp, curcolor);
|
|
|
|
} 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 /* DEBUG */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan the sorted list of frames and try to add a frame to
|
|
|
|
* combinations of 'level' number of frames.
|
|
|
|
*/
|
1997-10-10 17:36:01 +04:00
|
|
|
void
|
1996-12-28 21:44:55 +03:00
|
|
|
addframes(level)
|
|
|
|
int level;
|
|
|
|
{
|
1997-10-10 17:36:01 +04:00
|
|
|
struct combostr *cbp, *ecbp;
|
|
|
|
struct spotstr *sp, *fsp;
|
|
|
|
struct elist *ep, *nep;
|
|
|
|
int i, r, d;
|
1996-12-28 21:44:55 +03:00
|
|
|
struct combostr **cbpp, *pcbp;
|
|
|
|
union comboval fcb, cb;
|
|
|
|
|
|
|
|
curlevel = level;
|
|
|
|
|
|
|
|
/* scan for combos at empty spots */
|
|
|
|
i = curcolor;
|
|
|
|
for (sp = &board[PT(T,20)]; --sp >= &board[PT(A,1)]; ) {
|
|
|
|
for (ep = sp->s_empty; ep; ep = nep) {
|
|
|
|
cbp = ep->e_combo;
|
|
|
|
if (cbp->c_combo.s <= sp->s_combo[i].s) {
|
|
|
|
if (cbp->c_combo.s != sp->s_combo[i].s) {
|
|
|
|
sp->s_combo[i].s = cbp->c_combo.s;
|
|
|
|
sp->s_level[i] = cbp->c_nframes;
|
|
|
|
} else if (cbp->c_nframes < sp->s_level[i])
|
|
|
|
sp->s_level[i] = cbp->c_nframes;
|
|
|
|
}
|
|
|
|
nep = ep->e_next;
|
|
|
|
free(ep);
|
|
|
|
elistcnt--;
|
|
|
|
}
|
|
|
|
sp->s_empty = sp->s_nempty;
|
|
|
|
sp->s_nempty = (struct elist *)0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* try to add frames to the uncompleted combos at level curlevel */
|
|
|
|
cbp = ecbp = sortframes[curcolor];
|
|
|
|
do {
|
|
|
|
fsp = &board[cbp->c_vertex];
|
|
|
|
r = cbp->c_dir;
|
|
|
|
/* skip frames that are part of a <1,x> combo */
|
|
|
|
if (fsp->s_flg & (FFLAG << r))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't include <1,x> combo frames,
|
|
|
|
* treat it as a closed three in a row instead.
|
|
|
|
*/
|
|
|
|
fcb.s = fsp->s_fval[curcolor][r].s;
|
|
|
|
if (fcb.s == 0x101)
|
|
|
|
fcb.s = 0x200;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this is an open ended frame, use
|
|
|
|
* the combo value with the end closed.
|
|
|
|
*/
|
|
|
|
if (fsp->s_occ == EMPTY) {
|
|
|
|
if (fcb.c.b) {
|
|
|
|
cb.c.a = fcb.c.a + 1;
|
|
|
|
cb.c.b = 0;
|
|
|
|
} else
|
|
|
|
cb.s = fcb.s;
|
|
|
|
makecombo(cbp, fsp, 0, cb.s);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The next four spots are handled the same for both
|
|
|
|
* open and closed ended frames.
|
|
|
|
*/
|
|
|
|
d = dd[r];
|
|
|
|
sp = fsp + d;
|
|
|
|
for (i = 1; i < 5; i++, sp += d) {
|
|
|
|
if (sp->s_occ != EMPTY)
|
|
|
|
continue;
|
|
|
|
makecombo(cbp, sp, i, fcb.s);
|
|
|
|
}
|
|
|
|
} while ((cbp = cbp->c_next) != ecbp);
|
|
|
|
|
|
|
|
/* put all the combos in the hash list on the sorted list */
|
|
|
|
cbpp = &hashcombos[FAREA];
|
|
|
|
do {
|
|
|
|
cbp = *--cbpp;
|
|
|
|
if (cbp == (struct combostr *)0)
|
|
|
|
continue;
|
|
|
|
*cbpp = (struct combostr *)0;
|
|
|
|
ecbp = sortcombos;
|
|
|
|
if (ecbp == (struct combostr *)0)
|
|
|
|
sortcombos = cbp;
|
|
|
|
else {
|
|
|
|
/* append to sort list */
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute all level N combos of frames intersecting spot 'osp'
|
|
|
|
* within the frame 'ocbp' and combo value 's'.
|
|
|
|
*/
|
1997-10-10 17:36:01 +04:00
|
|
|
void
|
1996-12-28 21:44:55 +03:00
|
|
|
makecombo(ocbp, osp, off, s)
|
|
|
|
struct combostr *ocbp;
|
|
|
|
struct spotstr *osp;
|
|
|
|
int off;
|
|
|
|
int s;
|
|
|
|
{
|
1997-10-10 17:36:01 +04:00
|
|
|
struct combostr *cbp, *ncbp;
|
|
|
|
struct spotstr *sp;
|
|
|
|
struct elist *ep;
|
|
|
|
int n, c;
|
|
|
|
struct elist *nep;
|
1996-12-28 21:44:55 +03:00
|
|
|
struct combostr **scbpp;
|
1997-10-10 17:36:01 +04:00
|
|
|
int baseB, fcnt, emask, verts;
|
|
|
|
union comboval ocb;
|
1996-12-28 21:44:55 +03:00
|
|
|
struct ovlp_info vertices[1];
|
|
|
|
|
|
|
|
ocb.s = s;
|
|
|
|
baseB = ocb.c.a + ocb.c.b - 1;
|
|
|
|
fcnt = ocb.c.a - 2;
|
|
|
|
emask = fcnt ? ((ocb.c.b ? 0x1E : 0x1F) & ~(1 << off)) : 0;
|
|
|
|
for (ep = osp->s_empty; ep; ep = ep->e_next) {
|
|
|
|
/* check for various kinds of overlap */
|
|
|
|
cbp = ep->e_combo;
|
|
|
|
verts = checkframes(cbp, ocbp, osp, s, vertices);
|
|
|
|
if (verts < 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* check to see if this frame forms a valid loop */
|
|
|
|
if (verts) {
|
|
|
|
sp = &board[vertices[0].o_intersect];
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (sp->s_occ != EMPTY) {
|
|
|
|
sprintf(fmtbuf, "loop: %c %s", "BW"[curcolor],
|
|
|
|
stoc(sp - board));
|
|
|
|
dlog(fmtbuf);
|
|
|
|
whatsup(0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
* It is a valid loop if the intersection spot
|
|
|
|
* of the frame we are trying to attach is one
|
|
|
|
* of the completion spots of the combostr
|
|
|
|
* we are trying to attach the frame to.
|
|
|
|
*/
|
|
|
|
for (nep = sp->s_empty; nep; nep = nep->e_next) {
|
|
|
|
if (nep->e_combo == cbp)
|
|
|
|
goto fnd;
|
|
|
|
if (nep->e_combo->c_nframes < cbp->c_nframes)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* frame overlaps but not at a valid spot */
|
|
|
|
continue;
|
|
|
|
fnd:
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* compute the first half of the combo value */
|
|
|
|
c = cbp->c_combo.c.a + ocb.c.a - verts - 3;
|
|
|
|
if (c > 4)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* compute the second half of the combo value */
|
|
|
|
n = ep->e_fval.c.a + ep->e_fval.c.b - 1;
|
|
|
|
if (baseB < n)
|
|
|
|
n = baseB;
|
|
|
|
|
|
|
|
/* make a new combo! */
|
|
|
|
ncbp = (struct combostr *)malloc(sizeof(struct combostr) +
|
|
|
|
(cbp->c_nframes + 1) * sizeof(struct combostr *));
|
1999-09-09 21:27:58 +04:00
|
|
|
if (ncbp == NULL)
|
|
|
|
panic("Out of memory!");
|
1996-12-28 21:44:55 +03:00
|
|
|
scbpp = (struct combostr **)(ncbp + 1);
|
|
|
|
if (sortcombo(scbpp, (struct combostr **)(cbp + 1), ocbp)) {
|
|
|
|
free(ncbp);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
combocnt++;
|
|
|
|
|
|
|
|
ncbp->c_combo.c.a = c;
|
|
|
|
ncbp->c_combo.c.b = 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 = osp - board;
|
|
|
|
ncbp->c_nframes = cbp->c_nframes + 1;
|
|
|
|
ncbp->c_flg = ocb.c.b ? C_OPEN_1 : 0;
|
|
|
|
ncbp->c_frameindex = ep->e_frameindex;
|
|
|
|
/*
|
|
|
|
* Update the completion spot mask of the frame we
|
|
|
|
* are attaching 'ocbp' to so the intersection isn't
|
|
|
|
* listed twice.
|
|
|
|
*/
|
|
|
|
ncbp->c_framecnt[0] = ep->e_framecnt;
|
|
|
|
ncbp->c_emask[0] = ep->e_emask;
|
|
|
|
if (verts) {
|
|
|
|
ncbp->c_flg |= C_LOOP;
|
|
|
|
ncbp->c_dir = vertices[0].o_frameindex;
|
|
|
|
ncbp->c_framecnt[1] = fcnt - 1;
|
|
|
|
if (ncbp->c_framecnt[1]) {
|
|
|
|
n = (vertices[0].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] = vertices[0].o_off;
|
|
|
|
} else {
|
|
|
|
ncbp->c_dir = 0;
|
|
|
|
ncbp->c_framecnt[1] = fcnt;
|
|
|
|
ncbp->c_emask[1] = emask;
|
|
|
|
ncbp->c_voff[0] = ep->e_off;
|
|
|
|
}
|
|
|
|
|
1997-10-10 17:36:01 +04:00
|
|
|
if ((c == 1 && debug > 1) || debug > 3) {
|
1996-12-28 21:44:55 +03:00
|
|
|
sprintf(fmtbuf, "%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]);
|
|
|
|
dlog(fmtbuf);
|
|
|
|
printcombo(ncbp, fmtbuf);
|
|
|
|
dlog(fmtbuf);
|
|
|
|
}
|
|
|
|
if (c > 1) {
|
|
|
|
/* record the empty spots that will complete this combo */
|
|
|
|
makeempty(ncbp);
|
|
|
|
combolen++;
|
|
|
|
} else {
|
|
|
|
/* update board values */
|
|
|
|
updatecombo(ncbp, curcolor);
|
|
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (c == 1 && debug > 1 || debug > 4) {
|
|
|
|
markcombo(ncbp);
|
|
|
|
bdisp();
|
|
|
|
whatsup(0);
|
|
|
|
clearcombo(ncbp, 0);
|
|
|
|
}
|
|
|
|
#endif /* DEBUG */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MAXDEPTH 100
|
|
|
|
struct elist einfo[MAXDEPTH];
|
|
|
|
struct combostr *ecombo[MAXDEPTH]; /* separate from elist to save space */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add the combostr 'ocbp' to the empty spots list for each empty spot
|
|
|
|
* in 'ocbp' that will complete the combo.
|
|
|
|
*/
|
1997-10-10 17:36:01 +04:00
|
|
|
void
|
1996-12-28 21:44:55 +03:00
|
|
|
makeempty(ocbp)
|
|
|
|
struct combostr *ocbp;
|
|
|
|
{
|
|
|
|
struct combostr *cbp, *tcbp, **cbpp;
|
1997-10-10 17:36:01 +04:00
|
|
|
struct elist *ep, *nep;
|
1996-12-28 21:44:55 +03:00
|
|
|
struct spotstr *sp;
|
|
|
|
int s, d, m, emask, i;
|
|
|
|
int nframes;
|
|
|
|
|
|
|
|
if (debug > 2) {
|
|
|
|
sprintf(fmtbuf, "E%c ", "bw"[curcolor]);
|
|
|
|
printcombo(ocbp, fmtbuf + 3);
|
|
|
|
dlog(fmtbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* should never happen but check anyway */
|
|
|
|
if ((nframes = ocbp->c_nframes) >= MAXDEPTH)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The lower level combo can be pointed to by more than one
|
|
|
|
* higher level 'struct combostr' so we can't modify the
|
|
|
|
* lower level. Therefore, higher level combos store the
|
|
|
|
* real mask of the lower level frame in c_emask[0] and the
|
|
|
|
* frame number in c_frameindex.
|
|
|
|
*
|
|
|
|
* First we traverse the tree from top to bottom and save the
|
|
|
|
* connection info. Then we traverse the tree from bottom to
|
|
|
|
* top overwriting lower levels with the newer emask information.
|
|
|
|
*/
|
|
|
|
ep = &einfo[nframes];
|
|
|
|
cbpp = &ecombo[nframes];
|
1997-10-10 17:36:01 +04:00
|
|
|
for (cbp = ocbp; (tcbp = cbp->c_link[1]) != NULL;
|
|
|
|
cbp = cbp->c_link[0]) {
|
1996-12-28 21:44:55 +03:00
|
|
|
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];
|
|
|
|
|
|
|
|
/* now update the emask info */
|
|
|
|
s = 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_flg & C_LOOP) {
|
|
|
|
s++;
|
|
|
|
/*
|
|
|
|
* Account for the fact that this frame connects
|
|
|
|
* to a previous one (thus forming a loop).
|
|
|
|
*/
|
|
|
|
nep = &einfo[cbp->c_dir];
|
|
|
|
if (--nep->e_framecnt)
|
|
|
|
nep->e_emask &= ~(1 << cbp->c_voff[0]);
|
|
|
|
else
|
|
|
|
nep->e_emask = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We only need to update the emask values of "complete" loops
|
|
|
|
* to include the intersection spots.
|
|
|
|
*/
|
|
|
|
if (s && ocbp->c_combo.c.a == 2) {
|
|
|
|
/* process loops from the top down */
|
|
|
|
ep = &einfo[nframes];
|
|
|
|
do {
|
|
|
|
ep--;
|
|
|
|
cbp = ep->e_combo;
|
|
|
|
if (!(cbp->c_flg & C_LOOP))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the emask values to include the
|
|
|
|
* intersection spots.
|
|
|
|
*/
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check all the frames for completion spots */
|
|
|
|
for (i = 0, ep = einfo, cbpp = ecombo; i < nframes; i++, ep++, cbpp++) {
|
|
|
|
/* skip this frame if there are no incomplete spots in it */
|
|
|
|
if ((emask = ep->e_emask) == 0)
|
|
|
|
continue;
|
|
|
|
cbp = *cbpp;
|
|
|
|
sp = &board[cbp->c_vertex];
|
|
|
|
d = dd[cbp->c_dir];
|
|
|
|
for (s = 0, m = 1; s < 5; s++, sp += d, m <<= 1) {
|
|
|
|
if (sp->s_occ != EMPTY || !(emask & m))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* add the combo to the list of empty spots */
|
|
|
|
nep = (struct elist *)malloc(sizeof(struct elist));
|
1999-09-09 21:27:58 +04:00
|
|
|
if (nep == NULL)
|
|
|
|
panic("Out of memory!");
|
1996-12-28 21:44:55 +03:00
|
|
|
nep->e_combo = ocbp;
|
|
|
|
nep->e_off = s;
|
|
|
|
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) {
|
|
|
|
sprintf(fmtbuf, "e %s o%d i%d c%d m%x %x",
|
|
|
|
stoc(sp - board),
|
|
|
|
nep->e_off,
|
|
|
|
nep->e_frameindex,
|
|
|
|
nep->e_framecnt,
|
|
|
|
nep->e_emask,
|
|
|
|
nep->e_fval.s);
|
|
|
|
dlog(fmtbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sort by the number of frames in the combo */
|
|
|
|
nep->e_next = sp->s_nempty;
|
|
|
|
sp->s_nempty = nep;
|
|
|
|
elistcnt++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the board value based on the combostr.
|
|
|
|
* This is called only if 'cbp' is a <1,x> combo.
|
|
|
|
* We handle things differently depending on whether the next move
|
|
|
|
* would be trying to "complete" the combo or trying to block it.
|
|
|
|
*/
|
1997-10-10 17:36:01 +04:00
|
|
|
void
|
1996-12-28 21:44:55 +03:00
|
|
|
updatecombo(cbp, color)
|
|
|
|
struct combostr *cbp;
|
|
|
|
int color;
|
|
|
|
{
|
1997-10-10 17:36:01 +04:00
|
|
|
struct spotstr *sp;
|
|
|
|
struct combostr *tcbp;
|
|
|
|
int i, d;
|
1996-12-28 21:44:55 +03:00
|
|
|
int nframes, flg, s;
|
|
|
|
union comboval cb;
|
|
|
|
|
1997-10-10 17:36:01 +04:00
|
|
|
flg = 0;
|
1996-12-28 21:44:55 +03:00
|
|
|
/* save the top level value for the whole combo */
|
|
|
|
cb.c.a = cbp->c_combo.c.a;
|
|
|
|
nframes = cbp->c_nframes;
|
|
|
|
|
|
|
|
if (color != nextcolor)
|
|
|
|
memset(tmpmap, 0, sizeof(tmpmap));
|
|
|
|
|
1997-10-10 17:36:01 +04:00
|
|
|
for (; (tcbp = cbp->c_link[1]) != NULL; cbp = cbp->c_link[0]) {
|
1996-12-28 21:44:55 +03:00
|
|
|
flg = cbp->c_flg;
|
|
|
|
cb.c.b = cbp->c_combo.c.b;
|
|
|
|
if (color == nextcolor) {
|
|
|
|
/* update the board value for the vertex */
|
|
|
|
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 {
|
|
|
|
/* update the board values for each spot in frame */
|
|
|
|
sp = &board[s = tcbp->c_vertex];
|
|
|
|
d = dd[tcbp->c_dir];
|
|
|
|
i = (flg & C_OPEN_1) ? 6 : 5;
|
|
|
|
for (; --i >= 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* mark the frame as being part of a <1,x> combo */
|
|
|
|
board[tcbp->c_vertex].s_flg |= FFLAG << tcbp->c_dir;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (color != nextcolor) {
|
|
|
|
/* update the board values for each spot in frame */
|
|
|
|
sp = &board[s = cbp->c_vertex];
|
|
|
|
d = dd[cbp->c_dir];
|
|
|
|
i = (flg & C_OPEN_0) ? 6 : 5;
|
|
|
|
for (; --i >= 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 {
|
1999-09-18 23:38:46 +04:00
|
|
|
for (i = 0; (unsigned int)i < MAPSZ; i++)
|
1996-12-28 21:44:55 +03:00
|
|
|
forcemap[i] &= tmpmap[i];
|
|
|
|
}
|
|
|
|
nforce++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* mark the frame as being part of a <1,x> combo */
|
|
|
|
board[cbp->c_vertex].s_flg |= FFLAG << cbp->c_dir;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add combo to the end of the list.
|
|
|
|
*/
|
1997-10-10 17:36:01 +04:00
|
|
|
void
|
1996-12-28 21:44:55 +03:00
|
|
|
appendcombo(cbp, color)
|
|
|
|
struct combostr *cbp;
|
1999-09-09 01:45:25 +04:00
|
|
|
int color __attribute__((__unused__));
|
1996-12-28 21:44:55 +03:00
|
|
|
{
|
|
|
|
struct combostr *pcbp, *ncbp;
|
|
|
|
|
|
|
|
combolen++;
|
|
|
|
ncbp = sortcombos;
|
|
|
|
if (ncbp == (struct combostr *)0) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return zero if it is valid to combine frame 'fcbp' with the frames
|
|
|
|
* in 'cbp' and forms a linked chain of frames (i.e., a tree; no loops).
|
|
|
|
* Return positive if combining frame 'fcbp' to the frames in 'cbp'
|
|
|
|
* would form some kind of valid loop. Also return the intersection spots
|
|
|
|
* in 'vertices[]' beside the known intersection at spot 'osp'.
|
|
|
|
* Return -1 if 'fcbp' should not be combined with 'cbp'.
|
|
|
|
* 's' is the combo value for frame 'fcpb'.
|
|
|
|
*/
|
1997-10-10 17:36:01 +04:00
|
|
|
int
|
1996-12-28 21:44:55 +03:00
|
|
|
checkframes(cbp, fcbp, osp, s, vertices)
|
|
|
|
struct combostr *cbp;
|
|
|
|
struct combostr *fcbp;
|
|
|
|
struct spotstr *osp;
|
|
|
|
int s;
|
|
|
|
struct ovlp_info *vertices;
|
|
|
|
{
|
|
|
|
struct combostr *tcbp, *lcbp;
|
|
|
|
int i, n, mask, flg, verts, loop, index, fcnt;
|
|
|
|
union comboval cb;
|
|
|
|
u_char *str;
|
|
|
|
short *ip;
|
|
|
|
|
1997-10-10 17:36:01 +04:00
|
|
|
lcbp = NULL;
|
|
|
|
flg = 0;
|
|
|
|
|
1996-12-28 21:44:55 +03:00
|
|
|
cb.s = s;
|
|
|
|
fcnt = cb.c.a - 2;
|
|
|
|
verts = 0;
|
|
|
|
loop = 0;
|
|
|
|
index = cbp->c_nframes;
|
|
|
|
n = (fcbp - frames) * FAREA;
|
|
|
|
str = &overlap[n];
|
|
|
|
ip = &intersect[n];
|
|
|
|
/*
|
|
|
|
* i == which overlap bit to test based on whether 'fcbp' is
|
|
|
|
* an open or closed frame.
|
|
|
|
*/
|
|
|
|
i = cb.c.b ? 2 : 0;
|
1997-10-10 17:36:01 +04:00
|
|
|
for (; (tcbp = cbp->c_link[1]) != NULL;
|
|
|
|
lcbp = cbp, cbp = cbp->c_link[0]) {
|
1996-12-28 21:44:55 +03:00
|
|
|
if (tcbp == fcbp)
|
|
|
|
return (-1); /* fcbp is already included */
|
|
|
|
|
|
|
|
/* check for intersection of 'tcbp' with 'fcbp' */
|
|
|
|
index--;
|
|
|
|
mask = str[tcbp - frames];
|
|
|
|
flg = cbp->c_flg;
|
|
|
|
n = i + ((flg & C_OPEN_1) != 0);
|
|
|
|
if (mask & (1 << n)) {
|
|
|
|
/*
|
|
|
|
* The two frames are not independent if they
|
|
|
|
* both lie in the same line and intersect at
|
|
|
|
* more than one point.
|
|
|
|
*/
|
|
|
|
if (tcbp->c_dir == fcbp->c_dir && (mask & (0x10 << n)))
|
|
|
|
return (-1);
|
|
|
|
/*
|
|
|
|
* If this is not the spot we are attaching
|
|
|
|
* 'fcbp' to and it is a reasonable intersection
|
|
|
|
* spot, then there might be a loop.
|
|
|
|
*/
|
|
|
|
n = ip[tcbp - frames];
|
|
|
|
if (osp != &board[n]) {
|
|
|
|
/* check to see if this is a valid loop */
|
|
|
|
if (verts)
|
|
|
|
return (-1);
|
|
|
|
if (fcnt == 0 || cbp->c_framecnt[1] == 0)
|
|
|
|
return (-1);
|
|
|
|
/*
|
|
|
|
* Check to be sure the intersection is not
|
|
|
|
* one of the end points if it is an open
|
|
|
|
* ended frame.
|
|
|
|
*/
|
|
|
|
if ((flg & C_OPEN_1) &&
|
|
|
|
(n == tcbp->c_vertex ||
|
|
|
|
n == tcbp->c_vertex + 5 * dd[tcbp->c_dir]))
|
|
|
|
return (-1); /* invalid overlap */
|
|
|
|
if (cb.c.b &&
|
|
|
|
(n == fcbp->c_vertex ||
|
|
|
|
n == fcbp->c_vertex + 5 * dd[fcbp->c_dir]))
|
|
|
|
return (-1); /* invalid overlap */
|
|
|
|
|
|
|
|
vertices->o_intersect = n;
|
|
|
|
vertices->o_fcombo = cbp;
|
|
|
|
vertices->o_link = 1;
|
|
|
|
vertices->o_off = (n - tcbp->c_vertex) /
|
|
|
|
dd[tcbp->c_dir];
|
|
|
|
vertices->o_frameindex = index;
|
|
|
|
verts++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
n = i + ((flg & C_OPEN_0) != 0);
|
|
|
|
}
|
|
|
|
if (cbp == fcbp)
|
|
|
|
return (-1); /* fcbp is already included */
|
|
|
|
|
|
|
|
/* check for intersection of 'cbp' with 'fcbp' */
|
|
|
|
mask = str[cbp - frames];
|
|
|
|
if (mask & (1 << n)) {
|
|
|
|
/*
|
|
|
|
* The two frames are not independent if they
|
|
|
|
* both lie in the same line and intersect at
|
|
|
|
* more than one point.
|
|
|
|
*/
|
|
|
|
if (cbp->c_dir == fcbp->c_dir && (mask & (0x10 << n)))
|
|
|
|
return (-1);
|
|
|
|
/*
|
|
|
|
* If this is not the spot we are attaching
|
|
|
|
* 'fcbp' to and it is a reasonable intersection
|
|
|
|
* spot, then there might be a loop.
|
|
|
|
*/
|
|
|
|
n = ip[cbp - frames];
|
|
|
|
if (osp != &board[n]) {
|
|
|
|
/* check to see if this is a valid loop */
|
|
|
|
if (verts)
|
|
|
|
return (-1);
|
|
|
|
if (fcnt == 0 || lcbp->c_framecnt[0] == 0)
|
|
|
|
return (-1);
|
|
|
|
/*
|
|
|
|
* Check to be sure the intersection is not
|
|
|
|
* one of the end points if it is an open
|
|
|
|
* ended frame.
|
|
|
|
*/
|
|
|
|
if ((flg & C_OPEN_0) &&
|
|
|
|
(n == cbp->c_vertex ||
|
|
|
|
n == cbp->c_vertex + 5 * dd[cbp->c_dir]))
|
|
|
|
return (-1); /* invalid overlap */
|
|
|
|
if (cb.c.b &&
|
|
|
|
(n == fcbp->c_vertex ||
|
|
|
|
n == fcbp->c_vertex + 5 * dd[fcbp->c_dir]))
|
|
|
|
return (-1); /* invalid overlap */
|
|
|
|
|
|
|
|
vertices->o_intersect = n;
|
|
|
|
vertices->o_fcombo = lcbp;
|
|
|
|
vertices->o_link = 0;
|
|
|
|
vertices->o_off = (n - cbp->c_vertex) /
|
|
|
|
dd[cbp->c_dir];
|
|
|
|
vertices->o_frameindex = 0;
|
|
|
|
verts++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (verts);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Merge sort the frame 'fcbp' and the sorted list of frames 'cbpp' and
|
|
|
|
* store the result in 'scbpp'. 'curlevel' is the size of the 'cbpp' array.
|
|
|
|
* Return true if this list of frames is already in the hash list.
|
|
|
|
* Otherwise, add the new combo to the hash list.
|
|
|
|
*/
|
1997-10-10 17:36:01 +04:00
|
|
|
int
|
1996-12-28 21:44:55 +03:00
|
|
|
sortcombo(scbpp, cbpp, fcbp)
|
|
|
|
struct combostr **scbpp;
|
|
|
|
struct combostr **cbpp;
|
|
|
|
struct combostr *fcbp;
|
|
|
|
{
|
|
|
|
struct combostr **spp, **cpp;
|
|
|
|
struct combostr *cbp, *ecbp;
|
|
|
|
int n, inx;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (debug > 3) {
|
|
|
|
char *str;
|
|
|
|
|
|
|
|
sprintf(fmtbuf, "sortc: %s%c l%d", stoc(fcbp->c_vertex),
|
|
|
|
pdir[fcbp->c_dir], curlevel);
|
|
|
|
dlog(fmtbuf);
|
|
|
|
str = fmtbuf;
|
|
|
|
for (cpp = cbpp; cpp < cbpp + curlevel; cpp++) {
|
|
|
|
sprintf(str, " %s%c", stoc((*cpp)->c_vertex),
|
|
|
|
pdir[(*cpp)->c_dir]);
|
|
|
|
str += strlen(str);
|
|
|
|
}
|
|
|
|
dlog(fmtbuf);
|
|
|
|
}
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
|
|
/* first build the new sorted list */
|
|
|
|
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:
|
|
|
|
|
|
|
|
/* now check to see if this list of frames has already been seen */
|
|
|
|
cbp = hashcombos[inx = *scbpp - frames];
|
|
|
|
if (cbp == (struct combostr *)0) {
|
|
|
|
/*
|
|
|
|
* Easy case, this list hasn't been seen.
|
|
|
|
* Add it to the hash list.
|
|
|
|
*/
|
|
|
|
fcbp = (struct combostr *)
|
|
|
|
((char *)scbpp - sizeof(struct combostr));
|
|
|
|
hashcombos[inx] = fcbp;
|
|
|
|
fcbp->c_next = fcbp->c_prev = fcbp;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
ecbp = cbp;
|
|
|
|
do {
|
|
|
|
cbpp = (struct combostr **)(cbp + 1);
|
|
|
|
cpp = cbpp + n;
|
|
|
|
spp = scbpp + n;
|
|
|
|
cbpp++; /* first frame is always the same */
|
|
|
|
do {
|
|
|
|
if (*--spp != *--cpp)
|
|
|
|
goto next;
|
|
|
|
} while (cpp != cbpp);
|
|
|
|
/* we found a match */
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (debug > 3) {
|
|
|
|
char *str;
|
|
|
|
|
|
|
|
sprintf(fmtbuf, "sort1: n%d", n);
|
|
|
|
dlog(fmtbuf);
|
|
|
|
str = fmtbuf;
|
|
|
|
for (cpp = scbpp; cpp < scbpp + n; cpp++) {
|
|
|
|
sprintf(str, " %s%c", stoc((*cpp)->c_vertex),
|
|
|
|
pdir[(*cpp)->c_dir]);
|
|
|
|
str += strlen(str);
|
|
|
|
}
|
|
|
|
dlog(fmtbuf);
|
|
|
|
printcombo(cbp, fmtbuf);
|
|
|
|
dlog(fmtbuf);
|
|
|
|
str = fmtbuf;
|
|
|
|
cbpp--;
|
|
|
|
for (cpp = cbpp; cpp < cbpp + n; cpp++) {
|
|
|
|
sprintf(str, " %s%c", stoc((*cpp)->c_vertex),
|
|
|
|
pdir[(*cpp)->c_dir]);
|
|
|
|
str += strlen(str);
|
|
|
|
}
|
|
|
|
dlog(fmtbuf);
|
|
|
|
}
|
|
|
|
#endif /* DEBUG */
|
|
|
|
return (1);
|
|
|
|
next:
|
|
|
|
;
|
|
|
|
} while ((cbp = cbp->c_next) != ecbp);
|
|
|
|
/*
|
|
|
|
* This list of frames hasn't been seen.
|
|
|
|
* Add it to the hash list.
|
|
|
|
*/
|
|
|
|
ecbp = cbp->c_prev;
|
|
|
|
fcbp = (struct combostr *)((char *)scbpp - sizeof(struct combostr));
|
|
|
|
fcbp->c_next = cbp;
|
|
|
|
fcbp->c_prev = ecbp;
|
|
|
|
cbp->c_prev = fcbp;
|
|
|
|
ecbp->c_next = fcbp;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print the combo into string 'str'.
|
|
|
|
*/
|
1997-10-10 17:36:01 +04:00
|
|
|
void
|
1996-12-28 21:44:55 +03:00
|
|
|
printcombo(cbp, str)
|
|
|
|
struct combostr *cbp;
|
|
|
|
char *str;
|
|
|
|
{
|
|
|
|
struct combostr *tcbp;
|
|
|
|
|
|
|
|
sprintf(str, "%x/%d", cbp->c_combo.s, cbp->c_nframes);
|
|
|
|
str += strlen(str);
|
1997-10-10 17:36:01 +04:00
|
|
|
for (; (tcbp = cbp->c_link[1]) != NULL; cbp = cbp->c_link[0]) {
|
1996-12-28 21:44:55 +03:00
|
|
|
sprintf(str, " %s%c%x", stoc(tcbp->c_vertex), pdir[tcbp->c_dir],
|
|
|
|
cbp->c_flg);
|
|
|
|
str += strlen(str);
|
|
|
|
}
|
|
|
|
sprintf(str, " %s%c", stoc(cbp->c_vertex), pdir[cbp->c_dir]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
1997-10-10 17:36:01 +04:00
|
|
|
void
|
1996-12-28 21:44:55 +03:00
|
|
|
markcombo(ocbp)
|
|
|
|
struct combostr *ocbp;
|
|
|
|
{
|
|
|
|
struct combostr *cbp, *tcbp, **cbpp;
|
|
|
|
struct elist *ep, *nep, **epp;
|
|
|
|
struct spotstr *sp;
|
|
|
|
int s, d, m, i;
|
|
|
|
int nframes;
|
|
|
|
int r, n, flg, cmask, omask;
|
|
|
|
|
|
|
|
/* should never happen but check anyway */
|
|
|
|
if ((nframes = ocbp->c_nframes) >= MAXDEPTH)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The lower level combo can be pointed to by more than one
|
|
|
|
* higher level 'struct combostr' so we can't modify the
|
|
|
|
* lower level. Therefore, higher level combos store the
|
|
|
|
* real mask of the lower level frame in c_emask[0] and the
|
|
|
|
* frame number in c_frameindex.
|
|
|
|
*
|
|
|
|
* First we traverse the tree from top to bottom and save the
|
|
|
|
* connection info. Then we traverse the tree from bottom to
|
|
|
|
* top overwriting lower levels with the newer emask information.
|
|
|
|
*/
|
|
|
|
ep = &einfo[nframes];
|
|
|
|
cbpp = &ecombo[nframes];
|
|
|
|
for (cbp = ocbp; tcbp = cbp->c_link[1]; 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];
|
|
|
|
|
|
|
|
/* now update the emask info */
|
|
|
|
s = 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_flg & C_LOOP) {
|
|
|
|
s++;
|
|
|
|
/*
|
|
|
|
* Account for the fact that this frame connects
|
|
|
|
* to a previous one (thus forming a loop).
|
|
|
|
*/
|
|
|
|
nep = &einfo[cbp->c_dir];
|
|
|
|
if (--nep->e_framecnt)
|
|
|
|
nep->e_emask &= ~(1 << cbp->c_voff[0]);
|
|
|
|
else
|
|
|
|
nep->e_emask = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We only need to update the emask values of "complete" loops
|
|
|
|
* to include the intersection spots.
|
|
|
|
*/
|
|
|
|
if (s && ocbp->c_combo.c.a == 2) {
|
|
|
|
/* process loops from the top down */
|
|
|
|
ep = &einfo[nframes];
|
|
|
|
do {
|
|
|
|
ep--;
|
|
|
|
cbp = ep->e_combo;
|
|
|
|
if (!(cbp->c_flg & C_LOOP))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the emask values to include the
|
|
|
|
* intersection spots.
|
|
|
|
*/
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* mark all the frames with the completion spots */
|
|
|
|
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[s = cbp->c_dir];
|
|
|
|
cmask = CFLAG << s;
|
|
|
|
omask = (IFLAG | CFLAG) << s;
|
|
|
|
s = ep->e_fval.c.b ? 6 : 5;
|
|
|
|
for (; --s >= 0; sp += d, m >>= 1)
|
|
|
|
sp->s_flg |= (m & 1) ? omask : cmask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1997-10-10 17:36:01 +04:00
|
|
|
void
|
1996-12-28 21:44:55 +03:00
|
|
|
clearcombo(cbp, open)
|
|
|
|
struct combostr *cbp;
|
|
|
|
int open;
|
|
|
|
{
|
1997-10-10 17:36:01 +04:00
|
|
|
struct spotstr *sp;
|
1996-12-28 21:44:55 +03:00
|
|
|
struct combostr *tcbp;
|
|
|
|
int d, n, mask;
|
|
|
|
|
|
|
|
for (; tcbp = cbp->c_link[1]; cbp = cbp->c_link[0]) {
|
|
|
|
clearcombo(tcbp, cbp->c_flg & C_OPEN_1);
|
|
|
|
open = cbp->c_flg & C_OPEN_0;
|
|
|
|
}
|
|
|
|
sp = &board[cbp->c_vertex];
|
|
|
|
d = dd[n = cbp->c_dir];
|
|
|
|
mask = ~((IFLAG | CFLAG) << n);
|
|
|
|
n = open ? 6 : 5;
|
|
|
|
for (; --n >= 0; sp += d)
|
|
|
|
sp->s_flg &= mask;
|
|
|
|
}
|
|
|
|
|
1997-10-10 17:36:01 +04:00
|
|
|
int
|
1996-12-28 21:44:55 +03:00
|
|
|
list_eq(scbpp, cbpp, n)
|
|
|
|
struct combostr **scbpp;
|
|
|
|
struct combostr **cbpp;
|
|
|
|
int n;
|
|
|
|
{
|
|
|
|
struct combostr **spp, **cpp;
|
|
|
|
|
|
|
|
spp = scbpp + n;
|
|
|
|
cpp = cbpp + n;
|
|
|
|
do {
|
|
|
|
if (*--spp != *--cpp)
|
|
|
|
return (0);
|
|
|
|
} while (cpp != cbpp);
|
|
|
|
/* we found a match */
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
#endif /* DEBUG */
|