patternscan: Fix crash when only a single game (or small) is fed in
[pachi/nmclean.git] / fbook.c
blob56278544f1ca491cc1d0e35cf0edc22f0027aea3
1 #include <assert.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
6 #define DEBUG
8 #include "board.h"
9 #include "debug.h"
10 #include "fbook.h"
11 #include "random.h"
14 static coord_t
15 coord_transform(struct board *b, coord_t coord, int i)
17 #define HASH_VMIRROR 1
18 #define HASH_HMIRROR 2
19 #define HASH_XYFLIP 4
20 if (i & HASH_VMIRROR) {
21 coord = coord_xy(b, coord_x(coord, b), board_size(b) - 1 - coord_y(coord, b));
23 if (i & HASH_HMIRROR) {
24 coord = coord_xy(b, board_size(b) - 1 - coord_x(coord, b), coord_y(coord, b));
26 if (i & HASH_XYFLIP) {
27 coord = coord_xy(b, coord_y(coord, b), coord_x(coord, b));
29 return coord;
32 /* Check if we can make a move along the fbook right away.
33 * Otherwise return pass. */
34 coord_t
35 fbook_check(struct board *board)
37 if (!board->fbook) return pass;
39 hash_t hi = board->hash;
40 coord_t cf = pass;
41 while (!is_pass(board->fbook->moves[hi & fbook_hash_mask])) {
42 if (board->fbook->hashes[hi & fbook_hash_mask] == board->hash) {
43 cf = board->fbook->moves[hi & fbook_hash_mask];
44 break;
46 hi++;
48 if (!is_pass(cf)) {
49 if (DEBUGL(1))
50 fprintf(stderr, "fbook match %"PRIhash":%"PRIhash"\n", board->hash, board->hash & fbook_hash_mask);
51 } else {
52 /* No match, also prevent further fbook usage
53 * until the next clear_board. */
54 if (DEBUGL(4))
55 fprintf(stderr, "fbook out %"PRIhash":%"PRIhash"\n", board->hash, board->hash & fbook_hash_mask);
56 fbook_done(board->fbook);
57 board->fbook = NULL;
59 return cf;
62 static struct fbook *fbcache;
64 struct fbook *
65 fbook_init(char *filename, struct board *b)
67 if (fbcache && fbcache->bsize == board_size(b)
68 && fbcache->handicap == b->handicap)
69 return fbcache;
71 FILE *f = fopen(filename, "r");
72 if (!f) {
73 perror(filename);
74 return NULL;
77 struct fbook *fbook = calloc(1, sizeof(*fbook));
78 fbook->bsize = board_size(b);
79 fbook->handicap = b->handicap;
80 /* We do not set handicap=1 in case of too low komi on purpose;
81 * we want to go with the no-handicap fbook for now. */
82 for (int i = 0; i < 1<<fbook_hash_bits; i++)
83 fbook->moves[i] = pass;
85 if (DEBUGL(1))
86 fprintf(stderr, "Loading opening fbook %s...\n", filename);
88 /* Scratch board where we lay out the sequence;
89 * one for each transposition. */
90 struct board *bs[8];
91 for (int i = 0; i < 8; i++) {
92 bs[i] = board_init(NULL);
93 board_resize(bs[i], fbook->bsize - 2);
96 char linebuf[1024];
97 while (fgets(linebuf, sizeof(linebuf), f)) {
98 char *line = linebuf;
99 linebuf[strlen(linebuf) - 1] = 0; // chop
101 /* Format of line is:
102 * BSIZE COORD COORD COORD... | COORD
103 * BSIZE/HANDI COORD COORD COORD... | COORD
104 * We descend up to |, then add the new node
105 * with value minimax(1000), forcing UCT to
106 * always pick that node immediately. */
107 int bsize = strtol(line, &line, 10);
108 if (bsize != fbook->bsize - 2)
109 continue;
110 int handi = 0;
111 if (*line == '/') {
112 line++;
113 handi = strtol(line, &line, 10);
115 if (handi != fbook->handicap)
116 continue;
117 while (isspace(*line)) line++;
119 for (int i = 0; i < 8; i++) {
120 board_clear(bs[i]);
121 bs[i]->last_move.color = S_WHITE;
124 while (*line != '|') {
125 coord_t *c = str2coord(line, fbook->bsize);
127 for (int i = 0; i < 8; i++) {
128 coord_t coord = coord_transform(b, *c, i);
129 struct move m = { .coord = coord, .color = stone_other(bs[i]->last_move.color) };
130 int ret = board_play(bs[i], &m);
131 assert(ret >= 0);
134 coord_done(c);
135 while (!isspace(*line)) line++;
136 while (isspace(*line)) line++;
139 line++;
140 while (isspace(*line)) line++;
142 /* In case of multiple candidates, pick one with
143 * exponentially decreasing likelihood. */
144 while (strchr(line, ' ') && fast_random(2)) {
145 line = strchr(line, ' ');
146 while (isspace(*line)) line++;
147 // fprintf(stderr, "<%s> skip to %s\n", linebuf, line);
150 coord_t *c = str2coord(line, fbook->bsize);
151 for (int i = 0; i < 8; i++) {
152 coord_t coord = coord_transform(b, *c, i);
153 #if 0
154 char conflict = is_pass(fbook->moves[bs[i]->hash & fbook_hash_mask]) ? '+' : 'C';
155 if (conflict == 'C')
156 for (int j = 0; j < i; j++)
157 if (bs[i]->hash == bs[j]->hash)
158 conflict = '+';
159 if (conflict == 'C') {
160 hash_t hi = bs[i]->hash;
161 while (!is_pass(fbook->moves[hi & fbook_hash_mask]) && fbook->hashes[hi & fbook_hash_mask] != bs[i]->hash)
162 hi++;
163 if (fbook->hashes[hi & fbook_hash_mask] == bs[i]->hash)
164 hi = 'c';
166 fprintf(stderr, "%c %"PRIhash":%"PRIhash" (<%d> %s)\n", conflict,
167 bs[i]->hash & fbook_hash_mask, bs[i]->hash, i, linebuf);
168 #endif
169 hash_t hi = bs[i]->hash;
170 while (!is_pass(fbook->moves[hi & fbook_hash_mask]) && fbook->hashes[hi & fbook_hash_mask] != bs[i]->hash)
171 hi++;
172 fbook->moves[hi & fbook_hash_mask] = coord;
173 fbook->hashes[hi & fbook_hash_mask] = bs[i]->hash;
174 fbook->movecnt++;
176 coord_done(c);
179 for (int i = 0; i < 8; i++) {
180 board_done(bs[i]);
183 fclose(f);
185 if (!fbook->movecnt) {
186 /* Empty book is not worth the hassle. */
187 fbook_done(fbook);
188 return NULL;
191 struct fbook *fbold = fbcache;
192 fbcache = fbook;
193 if (fbold)
194 fbook_done(fbold);
196 return fbook;
199 void fbook_done(struct fbook *fbook)
201 if (fbook != fbcache)
202 free(fbook);