/* $NetBSD: makemove.c,v 1.43 2022/06/19 10:23:48 rillig Exp $ */ /* * 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. * 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 /* @(#)makemove.c 8.2 (Berkeley) 5/3/95 */ __RCSID("$NetBSD: makemove.c,v 1.43 2022/06/19 10:23:48 rillig Exp $"); #include "gomoku.h" const int dd[4] = { /* direction deltas */ 1, /* right */ -(BSZ + 1) + 1, /* down + right */ -(BSZ + 1), /* down */ -(BSZ + 1) - 1 /* down + left */ }; static const int weight[5] = { 0, 1, 7, 22, 100 }; static void update_overlap(spot_index); static bool is_tie(void) { for (int y = 1; y <= BSZ; y++) for (int x = 1; x <= BSZ; x++) if (board[PT(x, y)].s_wval != 0) return false; return true; } static void sortframes_remove(struct combostr *cbp) { if (cbp->c_next == NULL) return; if (sortframes[BLACK] == cbp) sortframes[BLACK] = cbp->c_next; if (sortframes[WHITE] == cbp) sortframes[WHITE] = cbp->c_next; cbp->c_next->c_prev = cbp->c_prev; cbp->c_prev->c_next = cbp->c_next; } static int old_weight_value(const struct spotstr *sp, direction r) { union comboval cb; int val = 0; if ((cb = sp->s_fval[BLACK][r]).s <= 0x500) val += weight[5 - cb.cv_force - cb.cv_win]; if ((cb = sp->s_fval[WHITE][r]).s <= 0x500) val += weight[5 - cb.cv_force - cb.cv_win]; return val; } /* * Return values: * MOVEOK everything is OK. * RESIGN Player resigned. * ILLEGAL Illegal move. * WIN The winning move was just played. * TIE The game is a tie. */ int makemove(player_color us, spot_index mv) { /* check for end of game */ if (mv == RESIGN) return RESIGN; /* check for illegal move */ struct spotstr *sp = &board[mv]; if (sp->s_occ != EMPTY) return ILLEGAL; /* make move */ sp->s_occ = us; game.moves[game.nmoves++] = mv; /* compute new frame values */ sp->s_wval = 0; for (direction r = 4; r-- > 0; ) { int d = dd[r]; struct spotstr *fsp = &board[mv]; for (int f = 5; --f >= 0; fsp -= d) { /* for each frame */ if (fsp->s_occ == BORDER) goto nextr; if (is_blocked(fsp, r)) continue; struct combostr *cbp = &frames[fsp->s_frame[r]]; sortframes_remove(cbp); int val = old_weight_value(fsp, r); /* compute new combo value for this frame */ bool space = fsp->s_occ == EMPTY; int n = 0; sp = fsp; for (int off = 5; off-- > 0; sp += d) { /* for each spot */ if (sp->s_occ == us) n++; else if (sp->s_occ == EMPTY) sp->s_wval -= val; else { set_blocked(fsp, r); /* adjust values */ fsp->s_fval[BLACK][r].s = 0x600; fsp->s_fval[WHITE][r].s = 0x600; while (off-- > 0) { sp += d; if (sp->s_occ == EMPTY) sp->s_wval -= val; } goto nextf; } } /* check for game over */ if (n == 5) { game.win_spot = (spot_index)(fsp - board); game.win_dir = r; return WIN; } /* compute new value & combo number for this frame & color */ player_color them = us != BLACK ? BLACK : WHITE; fsp->s_fval[them][r].s = 0x600; union comboval *cp = &fsp->s_fval[us][r]; /* both ends open? */ if (space && sp->s_occ == EMPTY) { cp->cv_force = 4 - n; cp->cv_win = 1; } else { cp->cv_force = 5 - n; cp->cv_win = 0; } val = weight[n]; sp = fsp; for (int off = 5; off-- > 0; sp += d) /* for each spot */ if (sp->s_occ == EMPTY) sp->s_wval += val; /* add this frame to the sorted list of frames by combo value */ struct combostr *cbp1 = sortframes[us]; if (cbp1 == NULL) sortframes[us] = cbp->c_next = cbp->c_prev = cbp; else { union comboval *cp1 = &board[cbp1->c_vertex].s_fval[us][cbp1->c_dir]; if (cp->s <= cp1->s) { /* insert at the head of the list */ sortframes[us] = cbp; } else { do { cbp1 = cbp1->c_next; cp1 = &board[cbp1->c_vertex].s_fval[us][cbp1->c_dir]; if (cp->s <= cp1->s) break; } while (cbp1 != sortframes[us]); } cbp->c_next = cbp1; cbp->c_prev = cbp1->c_prev; cbp1->c_prev->c_next = cbp; cbp1->c_prev = cbp; } nextf: ; } /* both ends open? */ if (fsp->s_occ == EMPTY) { union comboval *cp = &fsp->s_fval[BLACK][r]; if (cp->cv_win != 0) { cp->cv_force++; cp->cv_win = 0; } cp = &fsp->s_fval[WHITE][r]; if (cp->cv_win != 0) { cp->cv_force++; cp->cv_win = 0; } } nextr: ; } update_overlap(mv); if (is_tie()) return TIE; return MOVEOK; } static void update_overlap_same_direction(spot_index s1, spot_index s2, frame_index a, int d, int off_minus_f, direction r) { /* * count the number of empty spots to see if there is * still an overlap. */ int n = 0; spot_index s = s1; spot_index es = 0; for (int b = off_minus_f; b < 5; b++, s += d) { if (board[s].s_occ == EMPTY) { es = s; /* save the intersection point */ n++; } } frame_index b = board[s2].s_frame[r]; if (n == 0) { if (board[s].s_occ == EMPTY) { overlap[a * FAREA + b] &= 0xA; overlap[b * FAREA + a] &= 0xC; intersect[a * FAREA + b] = s; intersect[b * FAREA + a] = s; } else { overlap[a * FAREA + b] = 0; overlap[b * FAREA + a] = 0; } } else if (n == 1) { if (board[s].s_occ == EMPTY) { overlap[a * FAREA + b] &= 0xAF; overlap[b * FAREA + a] &= 0xCF; } else { overlap[a * FAREA + b] &= 0xF; overlap[b * FAREA + a] &= 0xF; } intersect[a * FAREA + b] = es; intersect[b * FAREA + a] = es; } /* else no change, still multiple overlap */ } /* * The last move was at 'os', which is part of frame 'a'. There are 6 frames * with direction 'rb' that cross frame 'a' in 'os'. Since the spot 'os' * cannot be used as a double threat anymore, mark each of these crossing * frames as non-overlapping with frame 'a'. */ static void update_overlap_different_direction(spot_index os, frame_index a, direction rb) { int db = dd[rb]; for (int off = 0; off < 6; off++) { const struct spotstr *sp = &board[os - db * off]; if (sp->s_occ == BORDER) break; if (is_blocked(sp, rb)) continue; frame_index b = sp->s_frame[rb]; overlap[a * FAREA + b] = 0; overlap[b * FAREA + a] = 0; } } /* * fix up the overlap array according to the changed 'os'. */ static void update_overlap(spot_index os) { for (direction r = 4; r-- > 0; ) { int d = dd[r]; spot_index s1 = os; /* for each frame 'a' that contains the spot 'os' */ for (int f = 0; f < 6; f++, s1 -= d) { if (board[s1].s_occ == BORDER) break; if (is_blocked(&board[s1], r)) continue; /* * Update all other frames that intersect the current one * to indicate whether they still overlap or not. * Since F1 overlap F2 == F2 overlap F1, we only need to * do the rows 0 <= r1 <= r. The r1 == r case is special * since the two frames can overlap in more than one spot. */ frame_index a = board[s1].s_frame[r]; spot_index s2 = s1 - d; for (int off = f + 1; off < 6; off++, s2 -= d) { if (board[s2].s_occ == BORDER) break; if (is_blocked(&board[s2], r)) continue; update_overlap_same_direction(s1, s2, a, d, off - f, r); } /* the other directions can only intersect at spot 'os' */ for (direction rb = 0; rb < r; rb++) update_overlap_different_direction(os, a, rb); } } }