1 |
3 |
sumanta.ch |
|
2 |
|
|
/*
|
3 |
|
|
|
4 |
|
|
connectk -- a program to play the connect-k family of games
|
5 |
|
|
Copyright (C) 2007 Michael Levin
|
6 |
|
|
|
7 |
|
|
This program is free software; you can redistribute it and/or
|
8 |
|
|
modify it under the terms of the GNU General Public License
|
9 |
|
|
as published by the Free Software Foundation; either version 2
|
10 |
|
|
of the License, or (at your option) any later version.
|
11 |
|
|
|
12 |
|
|
This program is distributed in the hope that it will be useful,
|
13 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 |
|
|
GNU General Public License for more details.
|
16 |
|
|
|
17 |
|
|
You should have received a copy of the GNU General Public License
|
18 |
|
|
along with this program; if not, write to the Free Software
|
19 |
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
20 |
|
|
|
21 |
|
|
*/
|
22 |
|
|
|
23 |
|
|
/* Some definitions in case glib is not included */
|
24 |
|
|
#ifndef TRUE
|
25 |
|
|
#define TRUE 1
|
26 |
|
|
#define FALSE 0
|
27 |
|
|
#define NULL ((void*)0)
|
28 |
|
|
#endif
|
29 |
|
|
#ifndef __G_TYPES_H__
|
30 |
|
|
typedef int gboolean;
|
31 |
|
|
typedef int gsize;
|
32 |
|
|
#endif
|
33 |
|
|
|
34 |
|
|
/*
|
35 |
|
|
* Options
|
36 |
|
|
*/
|
37 |
|
|
|
38 |
|
|
/* These are boolean options the user can toggle through the "Options" menu.
|
39 |
|
|
Do not modify them directly as the "Options" menu will not reflect your
|
40 |
|
|
changes. You can add more options in connectk.c */
|
41 |
|
|
extern int opt_pause_ai, /* Pause AI to inspect the board */
|
42 |
|
|
opt_det_ai, /* No randomness */
|
43 |
|
|
opt_print_u, /* Print utility after every move */
|
44 |
|
|
opt_debug_dfsc, /* Print out debug messages related to the DFS
|
45 |
|
|
cache */
|
46 |
|
|
opt_debug_thread, /* Print messages related to thread and mutex function */
|
47 |
|
|
opt_mark_log, /* Take log of weights before marking */
|
48 |
|
|
opt_mark_norm, /* Normalize to the largest weight */
|
49 |
|
|
opt_debug_stage, /* Debug priority stages */
|
50 |
|
|
opt_grayscale; /* Use grayscale rendering for print outs */
|
51 |
|
|
|
52 |
|
|
/*
|
53 |
|
|
* Utility
|
54 |
|
|
*/
|
55 |
|
|
|
56 |
|
|
#ifdef _EFISTDARG_H_
|
57 |
|
|
char *nvav(int *plen, const char *fmt, va_list va);
|
58 |
|
|
#endif
|
59 |
|
|
char *nva(int *plen, const char *fmt, ...);
|
60 |
|
|
char *va(const char *fmt, ...);
|
61 |
|
|
/* The va family of functions simplify string processing by allowing
|
62 |
|
|
printf-style substitution with any string-accepting function.
|
63 |
|
|
|
64 |
|
|
For example:
|
65 |
|
|
window_status(va("1 + 2 = %d", 1 + 2));
|
66 |
|
|
|
67 |
|
|
nva provides additional functionality by outputting the length of the
|
68 |
|
|
formatted string into the integer pointed to by plen. nvav accepts a variable
|
69 |
|
|
argument indirectly. */
|
70 |
|
|
|
71 |
|
|
void window_status(const char *msg);
|
72 |
|
|
/* Sets the status bar text of the main window */
|
73 |
|
|
|
74 |
|
|
/*
|
75 |
|
|
* Allocation Chain
|
76 |
|
|
*/
|
77 |
|
|
|
78 |
|
|
typedef struct AllocChain {
|
79 |
|
|
gboolean free;
|
80 |
|
|
/* Is this object unallocated? */
|
81 |
|
|
|
82 |
|
|
unsigned int id;
|
83 |
|
|
/* Each object has a unique id */
|
84 |
|
|
|
85 |
|
|
struct AllocChain *next;
|
86 |
|
|
/* Next object in the chain */
|
87 |
|
|
} AllocChain;
|
88 |
|
|
|
89 |
|
|
typedef AllocChain *(*AllocFunc)(AllocChain *old_ac);
|
90 |
|
|
|
91 |
|
|
AllocChain *achain_new(AllocChain **root, AllocFunc af);
|
92 |
|
|
void achain_free(AllocChain *ac);
|
93 |
|
|
void achain_copy(const AllocChain *src, AllocChain *dest, gsize mem);
|
94 |
|
|
|
95 |
|
|
/*
|
96 |
|
|
* Game state
|
97 |
|
|
*/
|
98 |
|
|
|
99 |
|
|
/* We limit the maximum values of these variables; note that these are not
|
100 |
|
|
arbitrary limits and should be modified with care */
|
101 |
|
|
#define MAX_BOARD_SIZE 59
|
102 |
|
|
#define MAX_CONNECT_K 12
|
103 |
|
|
#define MAX_PLACE_P 12
|
104 |
|
|
#define MAX_START_Q 6
|
105 |
|
|
#define MAX_DEPTH 9
|
106 |
|
|
#define MAX_BRANCH 32
|
107 |
|
|
|
108 |
|
|
enum {
|
109 |
|
|
PIECE_ERROR = -1,
|
110 |
|
|
/* Error pieces form a one tile deep border around the board */
|
111 |
|
|
|
112 |
|
|
PIECE_NONE = 0,
|
113 |
|
|
PIECE_BLACK,
|
114 |
|
|
PIECE_WHITE,
|
115 |
|
|
/* Empty and played pieces */
|
116 |
|
|
|
117 |
|
|
PIECES,
|
118 |
|
|
/* Total number of normal pieces (2) */
|
119 |
|
|
|
120 |
|
|
PIECE_SEARCHED,
|
121 |
|
|
PIECE_SEARCHED_MAX = PIECE_SEARCHED + MAX_DEPTH,
|
122 |
|
|
/* Markers used by the search system */
|
123 |
|
|
|
124 |
|
|
PIECE_THREAT0,
|
125 |
|
|
PIECE_MARKER = PIECE_THREAT0,
|
126 |
|
|
/* These threat markers are usable by the AIs */
|
127 |
|
|
};
|
128 |
|
|
typedef int PIECE;
|
129 |
|
|
|
130 |
|
|
#define MAX_THREAT (INT_MAX - PIECE_THREAT0)
|
131 |
|
|
/* Highest value a threat marker can have */
|
132 |
|
|
|
133 |
|
|
#define PIECE_THREAT(n) (PIECE_THREAT0 + (n))
|
134 |
|
|
/* This marker represents a threat n-turns (of that player) away */
|
135 |
|
|
|
136 |
|
|
#define piece_empty(p) ((p) == PIECE_NONE || (p) >= PIECES)
|
137 |
|
|
/* Checks if a piece is an empty or a marker */
|
138 |
|
|
|
139 |
|
|
typedef unsigned int PLAYER;
|
140 |
|
|
/* Type for AIs, this is the index of the AI entry in ai.c */
|
141 |
|
|
|
142 |
|
|
typedef unsigned int BCOORD;
|
143 |
|
|
/* Type for board coordinates */
|
144 |
|
|
|
145 |
|
|
typedef struct Board {
|
146 |
|
|
AllocChain ac;
|
147 |
|
|
/* Allocation chain must be the first member */
|
148 |
|
|
|
149 |
|
|
unsigned int moves_left;
|
150 |
|
|
/* How many moves the current player has left */
|
151 |
|
|
|
152 |
|
|
struct Board *parent;
|
153 |
|
|
/* The board preceeding this one in history */
|
154 |
|
|
|
155 |
|
|
gboolean won;
|
156 |
|
|
BCOORD win_x1, win_y1, win_x2, win_y2;
|
157 |
|
|
/* On won boards, used to indicate where the winning line is */
|
158 |
|
|
|
159 |
|
|
PIECE turn;
|
160 |
|
|
/* Whose turn it is on this board */
|
161 |
|
|
|
162 |
|
|
BCOORD move_x, move_y;
|
163 |
|
|
/* The move to the next Board in history */
|
164 |
|
|
|
165 |
|
|
PIECE data[];
|
166 |
|
|
} Board;
|
167 |
|
|
/* The board structure represents the state of the game board. Do NOT preserve
|
168 |
|
|
board pointers across games. */
|
169 |
|
|
|
170 |
|
|
extern AllocChain *board_root;
|
171 |
|
|
extern gsize board_mem;
|
172 |
|
|
/* Variables for the allocation chain */
|
173 |
|
|
|
174 |
|
|
extern Board *board;
|
175 |
|
|
/* This is the current board. Do NOT modify it, that's cheating. :) */
|
176 |
|
|
|
177 |
|
|
extern int board_size, board_stride, move_no, connect_k, place_p, start_q;
|
178 |
|
|
/* Board size (for all boards), moves in the game, connect_k to win, place_p
|
179 |
|
|
moves at a time, black has start_q moves on the first move; do NOT modify
|
180 |
|
|
these directly! */
|
181 |
|
|
|
182 |
|
|
const char *bcoords_to_string(BCOORD x, BCOORD y);
|
183 |
|
|
const char *bcoord_to_alpha(BCOORD c);
|
184 |
|
|
/* Return a static string representing a board coordinate pair */
|
185 |
|
|
|
186 |
|
|
void string_to_bcoords(const char *string, BCOORD *x, BCOORD *y);
|
187 |
|
|
/* Attempts to convert a string to board coordinates */
|
188 |
|
|
|
189 |
|
|
AllocChain *board_alloc(AllocChain *data);
|
190 |
|
|
#define board_new() ((Board*)achain_new(&board_root, board_alloc))
|
191 |
|
|
#define board_free(b) achain_free((AllocChain*)b)
|
192 |
|
|
/* Boards are dynamically allocated and must be free'd */
|
193 |
|
|
|
194 |
|
|
#define board_copy(from, to) achain_copy((AllocChain*)from, (AllocChain*)to,\
|
195 |
|
|
board_mem)
|
196 |
|
|
/* Overwrite one board with another */
|
197 |
|
|
|
198 |
|
|
void board_clean(Board *b);
|
199 |
|
|
/* Remove all threat markers from a board */
|
200 |
|
|
|
201 |
|
|
int threat_count(const Board *b, PIECE player);
|
202 |
|
|
/* Gives the number of threats on a board for the current player */
|
203 |
|
|
|
204 |
|
|
Board *board_at(unsigned int move);
|
205 |
|
|
/* Returns a static pointer to a board in the history at move */
|
206 |
|
|
|
207 |
|
|
gboolean count_pieces(const Board *b, BCOORD x, BCOORD y, PIECE type,
|
208 |
|
|
int dx, int dy, PIECE *out);
|
209 |
|
|
/* Count consecutive pieces of type starting from (x, y) in the direction given
|
210 |
|
|
by (dx, dy); includes (x, y) itself in the count and outputs the final
|
211 |
|
|
piece to out if it is not NULL */
|
212 |
|
|
|
213 |
|
|
gboolean check_win_full(const Board *b, BCOORD x, BCOORD y,
|
214 |
|
|
BCOORD *x1, BCOORD *y1, BCOORD *x2, BCOORD *y2);
|
215 |
|
|
#define check_win(b, x, y) check_win_full(b, x, y, 0, 0, 0, 0)
|
216 |
|
|
/* Returns non-zero if placing a piece of type at (x, y) on the current board
|
217 |
|
|
will result in a win for that player. The start and end coordinates of the
|
218 |
|
|
winning line will be placed in (x1, y1) and (x2, y2). */
|
219 |
|
|
|
220 |
|
|
static inline PIECE piece_at(const Board *b, BCOORD x, BCOORD y)
|
221 |
|
|
{
|
222 |
|
|
return b->data[(y + 1) * board_stride + x + 1];
|
223 |
|
|
}
|
224 |
|
|
/* Returns the piece at (x, y) on board b. If the coordinates are out of range,
|
225 |
|
|
this function will return PIECE_ERROR. */
|
226 |
|
|
|
227 |
|
|
char piece_to_char(PIECE piece);
|
228 |
|
|
/* Returns a one character representation of a piece (e.g. 'W', 'B', etc) */
|
229 |
|
|
|
230 |
|
|
const char *piece_to_string(PIECE piece);
|
231 |
|
|
/* Returns a static string representation of a piece (e.g. "White" etc) */
|
232 |
|
|
|
233 |
|
|
static inline void place_piece_type(Board *b, BCOORD x, BCOORD y, PIECE type)
|
234 |
|
|
{
|
235 |
|
|
b->data[(y + 1) * board_stride + x + 1] = type;
|
236 |
|
|
}
|
237 |
|
|
#define place_piece(b, x, y) place_piece_type(b, x, y, (b)->turn)
|
238 |
|
|
#define place_threat(b, x, y, n) place_piece_type(b, x, y, PIECE_THREAT(n))
|
239 |
|
|
/* Places a piece on board b, overwriting any piece that was previously in that
|
240 |
|
|
place */
|
241 |
|
|
|
242 |
|
|
#define other_player(p) ((p) == PIECE_BLACK ? PIECE_WHITE : PIECE_BLACK)
|
243 |
|
|
/* Invert a player piece */
|
244 |
|
|
|
245 |
|
|
/*
|
246 |
|
|
* Move arrays
|
247 |
|
|
*/
|
248 |
|
|
|
249 |
|
|
/* Some guideline values for move weights: */
|
250 |
|
|
#define AIW_MAX INT_MAX /* largest weight value */
|
251 |
|
|
#define AIW_MIN INT_MIN /* smallest weight value */
|
252 |
|
|
#define AIW_WIN AIW_MAX /* this move wins the game */
|
253 |
|
|
#define AIW_DEFEND (AIW_WIN - 2) /* defends from an opponent win */
|
254 |
|
|
#define AIW_NONE 0 /* does nothing */
|
255 |
|
|
#define AIW_DRAW AIW_NONE /* draw game */
|
256 |
|
|
#define AIW_LOSE (-AIW_WIN) /* this move loses the game */
|
257 |
|
|
#define AIW_THREAT_MAX 262144 /* value of an immediate threat */
|
258 |
|
|
|
259 |
|
|
typedef int AIWEIGHT;
|
260 |
|
|
/* Type for AI move weights (utilities) */
|
261 |
|
|
|
262 |
|
|
typedef struct {
|
263 |
|
|
AIWEIGHT weight;
|
264 |
|
|
BCOORD x, y;
|
265 |
|
|
} AIMove;
|
266 |
|
|
/* AIs return an array filled with these */
|
267 |
|
|
|
268 |
|
|
typedef struct AIMoves {
|
269 |
|
|
AllocChain ac;
|
270 |
|
|
/* Allocation chain must be the first member */
|
271 |
|
|
|
272 |
|
|
unsigned int len;
|
273 |
|
|
/* Number of members in data */
|
274 |
|
|
|
275 |
|
|
AIWEIGHT utility;
|
276 |
|
|
/* A composite utility value set by some AIs when producing a moves
|
277 |
|
|
list */
|
278 |
|
|
|
279 |
|
|
AIMove data[];
|
280 |
|
|
/* Array of AIMove structures */
|
281 |
|
|
} AIMoves;
|
282 |
|
|
/* An array type for holding move lists */
|
283 |
|
|
|
284 |
|
|
AllocChain *aimoves_alloc(AllocChain *data);
|
285 |
|
|
#define aimoves_new() ((AIMoves*)achain_new(&aimoves_root, aimoves_alloc))
|
286 |
|
|
#define aimoves_free(m) achain_free((AllocChain*)(m))
|
287 |
|
|
/* Move arrays are dynamically allocated and must be free'd */
|
288 |
|
|
|
289 |
|
|
#define aimoves_copy(from, to) achain_copy((AllocChain*)(from),\
|
290 |
|
|
(AllocChain*)(to), aimoves_mem)
|
291 |
|
|
/* Overwrite one array with another */
|
292 |
|
|
|
293 |
|
|
void aimoves_add(AIMoves *moves, const AIMove *aim);
|
294 |
|
|
/* Add an AIMove to an AIMoves array; move weights will be added to existing
|
295 |
|
|
weights */
|
296 |
|
|
|
297 |
|
|
void aimoves_append(AIMoves *moves, const AIMove *aim);
|
298 |
|
|
#define aimoves_set aimoves_append
|
299 |
|
|
/* Add an AIMove to an AIMoves array; existing moves weights will be
|
300 |
|
|
overwritten */
|
301 |
|
|
|
302 |
|
|
int aimoves_choose(AIMoves *moves, AIMove *move);
|
303 |
|
|
/* Will choose one of the best moves from a GArray of AIMove structures at
|
304 |
|
|
random. Returns non-zero if a move was chosen or zero if a move could not
|
305 |
|
|
be chosen for some reason. */
|
306 |
|
|
|
307 |
|
|
int aimoves_compare(const void *a, const void *b);
|
308 |
|
|
/* A comparison function for sorting move lists by weight */
|
309 |
|
|
|
310 |
|
|
void aimoves_crop(AIMoves *moves, unsigned int n);
|
311 |
|
|
/* Reduce a moves list to the top-n by weight */
|
312 |
|
|
|
313 |
|
|
void aimoves_concat(AIMoves *m1, const AIMoves *m2);
|
314 |
|
|
/* Concatenates m2 to m1 without checking for duplicates */
|
315 |
|
|
|
316 |
|
|
AIMoves *aimoves_dup(const AIMoves *moves);
|
317 |
|
|
/* Duplicate a GArray of moves */
|
318 |
|
|
|
319 |
|
|
int aimoves_find(const AIMoves *moves, BCOORD x, BCOORD y);
|
320 |
|
|
/* Returns the index of (x, y) if it is in moves or -1 otherwise */
|
321 |
|
|
|
322 |
|
|
void aimoves_range(AIMoves *moves, AIWEIGHT *min, AIWEIGHT *max);
|
323 |
|
|
/* Find the smallest and largest weight in the move array */
|
324 |
|
|
|
325 |
|
|
void aimoves_merge(AIMoves *m1, const AIMoves *m2);
|
326 |
|
|
/* Merges m2 into m1, the highest weight is used for duplicates */
|
327 |
|
|
|
328 |
|
|
void aimoves_print(const AIMoves *moves);
|
329 |
|
|
/* Prints out an array of moves */
|
330 |
|
|
|
331 |
|
|
void aimoves_remove(AIMoves *moves, BCOORD x, BCOORD y);
|
332 |
|
|
/* Remove an AIMove from a GArray of AIMoves */
|
333 |
|
|
|
334 |
|
|
void aimoves_remove_index_fast(AIMoves *moves, int i);
|
335 |
|
|
/* Remove a move from the list by overwriting it by the last move and
|
336 |
|
|
decrementing the length */
|
337 |
|
|
|
338 |
|
|
void aimoves_shuffle(AIMoves *moves);
|
339 |
|
|
/* Shuffle a list of moves */
|
340 |
|
|
|
341 |
|
|
void aimoves_sort(AIMoves *moves);
|
342 |
|
|
/* Sort a list of moves by descending weight */
|
343 |
|
|
|
344 |
|
|
void aimoves_subtract(AIMoves *m1, const AIMoves *m2);
|
345 |
|
|
/* Subtracts members of m2 from m1; O(n^2) */
|
346 |
|
|
|
347 |
|
|
extern AllocChain *aimoves_root;
|
348 |
|
|
extern gsize aimoves_mem;
|
349 |
|
|
/* Allocation chain variables */
|
350 |
|
|
|
351 |
|
|
const char *aiw_to_string(AIWEIGHT w);
|
352 |
|
|
/* Convert a weight to a string representation */
|
353 |
|
|
|
354 |
|
|
char *aimove_to_string(const AIMove *move);
|
355 |
|
|
/* Convert a move to a string representation */
|
356 |
|
|
|
357 |
|
|
/*
|
358 |
|
|
* AI helper functions
|
359 |
|
|
*/
|
360 |
|
|
|
361 |
|
|
extern int ai_stop;
|
362 |
|
|
/* If this variable is non-zero, the system is trying to stop the AI thread
|
363 |
|
|
and your AI should exit. Do not set this yourself. */
|
364 |
|
|
|
365 |
|
|
typedef AIMoves *(*AIFunc)(const Board *b);
|
366 |
|
|
/* AIs are defined as functions that output an unsorted, weighted list of board
|
367 |
|
|
coordinates for an arbitrary board. To create an AI in a file other than
|
368 |
|
|
ai.c, add a prototype of the function here and in ai.c. */
|
369 |
|
|
|
370 |
|
|
AIMoves *enum_top_n(const Board *b, int n);
|
371 |
|
|
/* Returns an array containing the top n moves according to the utility
|
372 |
|
|
function */
|
373 |
|
|
|
374 |
|
|
AIMoves *enum_adjacent(const Board *b, int dist);
|
375 |
|
|
/* Enumerate empty tiles at most dist away from some other piece on the board */
|
376 |
|
|
|
377 |
|
|
AIMoves *ai_marks(const Board *b, PIECE min);
|
378 |
|
|
/* Fills a moves list with tiles marked at least PIECE_THREAT(min) */
|
379 |
|
|
|
380 |
|
|
/*
|
381 |
|
|
* AI
|
382 |
|
|
*/
|
383 |
|
|
|
384 |
|
|
/* This table holds the information about all of the AIs in the program. Each
|
385 |
|
|
has a short and long description. The short description will be used for
|
386 |
|
|
the command line interface and the long description appears in the UI menu.
|
387 |
|
|
Each AI has an associated AIFunc which outputs a move for the current
|
388 |
|
|
board. */
|
389 |
|
|
typedef struct AI {
|
390 |
|
|
char *s_desc, *l_desc;
|
391 |
|
|
AIFunc func;
|
392 |
|
|
} AI;
|
393 |
|
|
|
394 |
|
|
AIMoves *ai_sequences(const Board *b);
|
395 |
|
|
/* The square of the number of pieces in a window */
|
396 |
|
|
|
397 |
|
|
AIMoves *ai_mesh(const Board *b);
|
398 |
|
|
/* The board as a mesh weighed down by the pieces */
|
399 |
|
|
|
400 |
|
|
AIMoves *ai_serial(const Board *b);
|
401 |
|
|
/* The move comes from serial port */
|
402 |
|
|
|
403 |
|
|
AIMoves *ai_monte_carlo(const Board *b);
|
404 |
|
|
/* Chooses the best move based on which one wins the most random games */
|
405 |
|
|
|
406 |
|
|
AIMoves *ai_random(const Board *b);
|
407 |
|
|
/* Plays in a random tile */
|
408 |
|
|
|
409 |
|
|
AIMoves *ai_adjacent(const Board *b);
|
410 |
|
|
/* Plays in a random tile adjacent to any piece on the board */
|
411 |
|
|
|
412 |
|
|
AIMoves *ai_windows(const Board *b);
|
413 |
|
|
/* Plays in the best defensive position */
|
414 |
|
|
|
415 |
|
|
AIMoves *ai_utility(const Board *b);
|
416 |
|
|
AIMoves *ai_dfs_utility(const Board *b);
|
417 |
|
|
/* Utility function */
|
418 |
|
|
|
419 |
|
|
AIMoves *ai_threats(const Board *b);
|
420 |
|
|
AIMoves *ai_priority(const Board *b);
|
421 |
|
|
/* Multi-level threats */
|