makemove.c revision 1.22
1/*	$NetBSD: makemove.c,v 1.22 2022/05/27 20:35:58 rillig Exp $	*/
2
3/*
4 * Copyright (c) 1994
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Ralph Campbell.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36/*	@(#)makemove.c	8.2 (Berkeley) 5/3/95	*/
37__RCSID("$NetBSD: makemove.c,v 1.22 2022/05/27 20:35:58 rillig Exp $");
38
39#include "gomoku.h"
40
41		/* direction deltas */
42const int     dd[4] = {
43	MRIGHT, MRIGHT+MDOWN, MDOWN, MDOWN+MLEFT
44};
45
46static const int weight[5] = { 0, 1, 7, 22, 100 };
47
48static void update_overlap(struct spotstr *);
49
50/*
51 * Return values:
52 *	MOVEOK	everything is OK.
53 *	RESIGN	Player resigned.
54 *	ILLEGAL	Illegal move.
55 *	WIN	The winning move was just played.
56 *	TIE	The game is a tie.
57 */
58int
59makemove(int us, int mv)
60{
61	struct spotstr *sp, *fsp;
62	union comboval *cp;
63	struct spotstr *osp;
64	struct combostr *cbp, *cbp1;
65	union comboval *cp1;
66	int d, n;
67	int val, bmask;
68	bool space;
69
70	/* check for end of game */
71	if (mv == RESIGN)
72		return RESIGN;
73
74	/* check for illegal move */
75	sp = &board[mv];
76	if (sp->s_occ != EMPTY)
77		return ILLEGAL;
78
79	/* make move */
80	sp->s_occ = us;
81	movelog[nmoves++] = mv;
82
83	/*
84	 * FIXME: The last spot on the board can still complete a frame.
85	 *
86	 * Example game: A1 B1 C1 D1 E1 F1 G1 H1 J1 K1 L1 M1 N1 O1 P1 Q1 R1
87	 * S1 T1 A2 B2 C2 D2 E2 F2 G2 H2 J2 K2 L2 M2 N2 O2 P2 Q2 R2 S2 T2 A3
88	 * B3 C3 D3 E3 F3 G3 H3 J3 K3 L3 M3 N3 O3 P3 Q3 R3 S3 T3 A4 B4 C4 D4
89	 * E4 F4 G4 H4 J4 K4 L4 M4 N4 O4 P4 Q4 R4 S4 T4 B5 C5 D5 E5 F5 G5 H5
90	 * J5 K5 L5 M5 N5 O5 P5 Q5 R5 S5 T5 B6 C6 D6 E6 F6 G6 H6 J6 K6 L6 M6
91	 * N6 O6 P6 Q6 R6 S6 T6 B7 C7 D7 E7 F7 G7 H7 J7 K7 L7 M7 N7 O7 P7 Q7
92	 * R7 S7 T7 A8 B8 C8 D8 E8 F8 G8 H8 J8 K8 L8 M8 N8 O8 P8 Q8 R8 S8 T8
93	 * A9 B9 C9 D9 E9 F9 G9 H9 J9 K9 L9 M9 N9 O9 P9 Q9 R9 S9 T9 A10 B10
94	 * C10 D10 E10 F10 G10 H10 J10 K10 L10 M10 N10 O10 P10 Q10 R10 S10
95	 * T10 B11 C11 D11 E11 F11 G11 H11 J11 K11 L11 M11 N11 O11 P11 Q11
96	 * R11 S11 T11 B12 C12 D12 E12 F12 G12 H12 J12 K12 L12 M12 N12 O12
97	 * P12 Q12 R12 S12 T12 B13 C13 D13 E13 F13 G13 H13 J13 K13 L13 M13
98	 * N13 O13 P13 Q13 R13 S13 T13 A14 B14 C14 D14 E14 F14 G14 H14 J14
99	 * K14 L14 M14 N14 O14 P14 Q14 R14 S14 T14 A15 B15 C15 D15 E15 F15
100	 * G15 H15 J15 K15 L15 M15 N15 O15 P15 Q15 R15 S15 T15 A16 B16 C16
101	 * D16 E16 F16 G16 H16 J16 K16 L16 M16 N16 O16 P16 Q16 R16 S16 T16
102	 * B17 C17 D17 E17 F17 G17 H17 J17 K17 L17 M17 N17 O17 P17 Q17 R17
103	 * S17 T17 B18 C18 D18 E18 F18 G18 H18 J18 K18 L18 M18 N18 O18 P18
104	 * Q18 R18 S18 T18 A11 A5 A12 A6 A13 A7 A18 A17 A19 B19 C19 D19 E19
105	 * F19 G19 H19 J19 K19 L19 T19 M19 S19 N19 R19 O19 Q19
106	 *
107	 * Now P19 is the last remaining spot, and when Black plays there,
108	 * the frame L19-P19 is complete.
109	 */
110	if (nmoves == BSZ * BSZ)
111		return TIE;
112
113	/* compute new frame values */
114	sp->s_wval = 0;
115	osp = sp;
116	for (int r = 4; --r >= 0; ) {		/* for each direction */
117	    d = dd[r];
118	    fsp = osp;
119	    bmask = BFLAG << r;
120	    for (int f = 5; --f >= 0; fsp -= d) {	/* for each frame */
121		if (fsp->s_occ == BORDER)
122		    goto nextr;
123		if ((fsp->s_flags & bmask) != 0)
124		    continue;
125
126		/* remove this frame from the sorted list of frames */
127		cbp = fsp->s_frame[r];
128		if (cbp->c_next != NULL) {
129			if (sortframes[BLACK] == cbp)
130			    sortframes[BLACK] = cbp->c_next;
131			if (sortframes[WHITE] == cbp)
132			    sortframes[WHITE] = cbp->c_next;
133			cbp->c_next->c_prev = cbp->c_prev;
134			cbp->c_prev->c_next = cbp->c_next;
135		}
136
137		/* compute old weight value for this frame */
138		cp = &fsp->s_fval[BLACK][r];
139		if (cp->s <= 0x500)
140		    val = weight[5 - cp->cv_force - cp->cv_win];
141		else
142		    val = 0;
143		cp = &fsp->s_fval[WHITE][r];
144		if (cp->s <= 0x500)
145		    val += weight[5 - cp->cv_force - cp->cv_win];
146
147		/* compute new combo value for this frame */
148		sp = fsp;
149		space = sp->s_occ == EMPTY;
150		n = 0;
151		for (int i = 5; --i >= 0; sp += d) {	/* for each spot */
152		    if (sp->s_occ == us)
153			n++;
154		    else if (sp->s_occ == EMPTY)
155			sp->s_wval -= val;
156		    else {
157			/* this frame is now blocked, adjust values */
158			fsp->s_flags |= bmask;
159			fsp->s_fval[BLACK][r].s = 0x600;
160			fsp->s_fval[WHITE][r].s = 0x600;
161			while (--i >= 0) {
162			    sp += d;
163			    if (sp->s_occ == EMPTY)
164				sp->s_wval -= val;
165			}
166			goto nextf;
167		    }
168		}
169
170		/* check for game over */
171		if (n == 5)
172		    return(WIN);
173
174		/* compute new value & combo number for this frame & color */
175		fsp->s_fval[us != BLACK ? BLACK : WHITE][r].s = 0x600;
176		cp = &fsp->s_fval[us][r];
177		/* both ends open? */
178		if (space && sp->s_occ == EMPTY) {
179		    cp->cv_force = 4 - n;
180		    cp->cv_win = 1;
181		} else {
182		    cp->cv_force = 5 - n;
183		    cp->cv_win = 0;
184		}
185		val = weight[n];
186		sp = fsp;
187		for (int i = 5; --i >= 0; sp += d)	/* for each spot */
188		    if (sp->s_occ == EMPTY)
189			sp->s_wval += val;
190
191		/* add this frame to the sorted list of frames by combo value */
192		cbp1 = sortframes[us];
193		if (cbp1 == NULL)
194		    sortframes[us] = cbp->c_next = cbp->c_prev = cbp;
195		else {
196		    cp1 = &board[cbp1->c_vertex].s_fval[us][cbp1->c_dir];
197		    if (cp->s <= cp1->s) {
198			/* insert at the head of the list */
199			sortframes[us] = cbp;
200		    } else {
201			do {
202			    cbp1 = cbp1->c_next;
203			    cp1 = &board[cbp1->c_vertex].s_fval[us][cbp1->c_dir];
204			    if (cp->s <= cp1->s)
205				break;
206			} while (cbp1 != sortframes[us]);
207		    }
208		    cbp->c_next = cbp1;
209		    cbp->c_prev = cbp1->c_prev;
210		    cbp1->c_prev->c_next = cbp;
211		    cbp1->c_prev = cbp;
212		}
213
214	    nextf:
215		;
216	    }
217
218	    /* both ends open? */
219	    if (fsp->s_occ == EMPTY) {
220		cp = &fsp->s_fval[BLACK][r];
221		if (cp->cv_win != 0) {
222		    cp->cv_force++;
223		    cp->cv_win = 0;
224		}
225		cp = &fsp->s_fval[WHITE][r];
226		if (cp->cv_win != 0) {
227		    cp->cv_force++;
228		    cp->cv_win = 0;
229		}
230	    }
231
232	nextr:
233	    ;
234	}
235
236	update_overlap(osp);
237
238	return MOVEOK;
239}
240
241/*
242 * fix up the overlap array due to updating spot osp.
243 */
244static void
245update_overlap(struct spotstr *osp)
246{
247	struct spotstr *sp, *sp1, *sp2;
248	int d, d1, n;
249	int a, b, bmask, bmask1;
250	struct spotstr *esp;
251	u_char *str;
252
253	esp = NULL;
254	for (int r = 4; --r >= 0; ) {		/* for each direction */
255	    d = dd[r];
256	    sp1 = osp;
257	    bmask = BFLAG << r;
258	    for (int f = 0; f < 6; f++, sp1 -= d) {	/* for each frame */
259		if (sp1->s_occ == BORDER)
260		    break;
261		if ((sp1->s_flags & bmask) != 0)
262		    continue;
263		/*
264		 * Update all other frames that intersect the current one
265		 * to indicate whether they still overlap or not.
266		 * Since F1 overlap F2 == F2 overlap F1, we only need to
267		 * do the rows 0 <= r1 <= r. The r1 == r case is special
268		 * since the two frames can overlap at more than one point.
269		 */
270		str = &overlap[(a = (int)(sp1->s_frame[r] - frames)) * FAREA];
271		sp2 = sp1 - d;
272		for (int i = f + 1; i < 6; i++, sp2 -= d) {
273		    if (sp2->s_occ == BORDER)
274			break;
275		    if ((sp2->s_flags & bmask) != 0)
276			continue;
277		    /*
278		     * count the number of empty spots to see if there is
279		     * still an overlap.
280		     */
281		    n = 0;
282		    sp = sp1;
283		    for (b = i - f; b < 5; b++, sp += d) {
284			if (sp->s_occ == EMPTY) {
285			    esp = sp;	/* save the intersection point */
286			    n++;
287			}
288		    }
289		    b = (int)(sp2->s_frame[r] - frames);
290		    if (n == 0) {
291			if (sp->s_occ == EMPTY) {
292			    str[b] &= 0xA;
293			    overlap[b * FAREA + a] &= 0xC;
294			    intersect[a * FAREA + b] = n = (int)(sp - board);
295			    intersect[b * FAREA + a] = n;
296			} else {
297			    str[b] = 0;
298			    overlap[b * FAREA + a] = 0;
299			}
300		    } else if (n == 1) {
301			if (sp->s_occ == EMPTY) {
302			    str[b] &= 0xAF;
303			    overlap[b * FAREA + a] &= 0xCF;
304			} else {
305			    str[b] &= 0xF;
306			    overlap[b * FAREA + a] &= 0xF;
307			}
308			intersect[a * FAREA + b] = n = (int)(esp - board);
309			intersect[b * FAREA + a] = n;
310		    }
311		    /* else no change, still multiple overlap */
312		}
313
314		/* the other directions can only intersect at spot osp */
315		for (int r1 = r; --r1 >= 0; ) {
316		    d1 = dd[r1];
317		    bmask1 = BFLAG << r1;
318		    sp = osp;
319		    for (int i = 6; --i >= 0; sp -= d1) { /* for each spot */
320			if (sp->s_occ == BORDER)
321			    break;
322			if ((sp->s_flags & bmask1) != 0)
323			    continue;
324			b = (int)(sp->s_frame[r1] - frames);
325			str[b] = 0;
326			overlap[b * FAREA + a] = 0;
327		    }
328		}
329	    }
330	}
331}
332