fbook caching: Store last loaded fbook and reuse it if it matches current board
[pachi/json.git] / fbook.c
bloba525381ee1ec4783ed4fe6863bb9421749314cd3
1 #include <assert.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
6 #include "board.h"
7 #include "debug.h"
8 #include "fbook.h"
9 #include "random.h"
12 static coord_t
13 coord_transform(struct board *b, coord_t coord, int i)
15 #define HASH_VMIRROR 1
16 #define HASH_HMIRROR 2
17 #define HASH_XYFLIP 4
18 if (i & HASH_VMIRROR) {
19 coord = coord_xy(b, coord_x(coord, b), board_size(b) - 1 - coord_y(coord, b));
21 if (i & HASH_HMIRROR) {
22 coord = coord_xy(b, board_size(b) - 1 - coord_x(coord, b), coord_y(coord, b));
24 if (i & HASH_XYFLIP) {
25 coord = coord_xy(b, coord_y(coord, b), coord_x(coord, b));
27 return coord;
30 /* Check if we can make a move along the fbook right away.
31 * Otherwise return pass. */
32 coord_t
33 fbook_check(struct board *board)
35 if (!board->fbook) return pass;
37 hash_t hi = board->hash;
38 coord_t cf = pass;
39 while (!is_pass(board->fbook->moves[hi & fbook_hash_mask])) {
40 if (board->fbook->hashes[hi & fbook_hash_mask] == board->hash) {
41 cf = board->fbook->moves[hi & fbook_hash_mask];
42 break;
44 hi++;
46 if (!is_pass(cf)) {
47 if (DEBUGL(1))
48 fprintf(stderr, "fbook match %"PRIhash":%"PRIhash"\n", board->hash, board->hash & fbook_hash_mask);
49 } else {
50 /* No match, also prevent further fbook usage
51 * until the next clear_board. */
52 if (DEBUGL(4))
53 fprintf(stderr, "fbook out %"PRIhash":%"PRIhash"\n", board->hash, board->hash & fbook_hash_mask);
54 fbook_done(board->fbook);
55 board->fbook = NULL;
57 return cf;
60 static struct fbook *fbcache;
62 struct fbook *
63 fbook_init(char *filename, struct board *b)
65 if (fbcache && fbcache->bsize == board_size(b)
66 && fbcache->handicap == b->handicap)
67 return fbcache;
69 FILE *f = fopen(filename, "r");
70 if (!f) {
71 perror(filename);
72 return NULL;
75 struct fbook *fbook = calloc(1, sizeof(*fbook));
76 fbook->bsize = board_size(b);
77 fbook->handicap = b->handicap;
78 /* We do not set handicap=1 in case of too low komi on purpose;
79 * we want to go with the no-handicap fbook for now. */
80 for (int i = 0; i < 1<<fbook_hash_bits; i++)
81 fbook->moves[i] = pass;
83 if (DEBUGL(1))
84 fprintf(stderr, "Loading opening fbook %s...\n", filename);
86 /* Scratch board where we lay out the sequence;
87 * one for each transposition. */
88 struct board *bs[8];
89 for (int i = 0; i < 8; i++) {
90 bs[i] = board_init(NULL);
91 board_resize(bs[i], fbook->bsize - 2);
94 char linebuf[1024];
95 while (fgets(linebuf, sizeof(linebuf), f)) {
96 char *line = linebuf;
97 linebuf[strlen(linebuf) - 1] = 0; // chop
99 /* Format of line is:
100 * BSIZE COORD COORD COORD... | COORD
101 * BSIZE/HANDI COORD COORD COORD... | COORD
102 * We descend up to |, then add the new node
103 * with value minimax(1000), forcing UCT to
104 * always pick that node immediately. */
105 int bsize = strtol(line, &line, 10);
106 if (bsize != fbook->bsize - 2)
107 continue;
108 int handi = 0;
109 if (*line == '/') {
110 line++;
111 handi = strtol(line, &line, 10);
113 if (handi != fbook->handicap)
114 continue;
115 while (isspace(*line)) line++;
117 for (int i = 0; i < 8; i++) {
118 board_clear(bs[i]);
119 bs[i]->last_move.color = S_WHITE;
122 while (*line != '|') {
123 coord_t *c = str2coord(line, fbook->bsize);
125 for (int i = 0; i < 8; i++) {
126 coord_t coord = coord_transform(b, *c, i);
127 struct move m = { .coord = coord, .color = stone_other(bs[i]->last_move.color) };
128 int ret = board_play(bs[i], &m);
129 assert(ret >= 0);
132 coord_done(c);
133 while (!isspace(*line)) line++;
134 while (isspace(*line)) line++;
137 line++;
138 while (isspace(*line)) line++;
140 /* In case of multiple candidates, pick one with
141 * exponentially decreasing likelihood. */
142 while (strchr(line, ' ') && fast_random(2)) {
143 line = strchr(line, ' ');
144 while (isspace(*line)) line++;
145 // fprintf(stderr, "<%s> skip to %s\n", linebuf, line);
148 coord_t *c = str2coord(line, fbook->bsize);
149 for (int i = 0; i < 8; i++) {
150 coord_t coord = coord_transform(b, *c, i);
151 #if 0
152 char conflict = is_pass(fbook->moves[bs[i]->hash & fbook_hash_mask]) ? '+' : 'C';
153 if (conflict == 'C')
154 for (int j = 0; j < i; j++)
155 if (bs[i]->hash == bs[j]->hash)
156 conflict = '+';
157 if (conflict == 'C') {
158 hash_t hi = bs[i]->hash;
159 while (!is_pass(fbook->moves[hi & fbook_hash_mask]) && fbook->hashes[hi & fbook_hash_mask] != bs[i]->hash)
160 hi++;
161 if (fbook->hashes[hi & fbook_hash_mask] == bs[i]->hash)
162 hi = 'c';
164 fprintf(stderr, "%c %"PRIhash":%"PRIhash" (<%d> %s)\n", conflict,
165 bs[i]->hash & fbook_hash_mask, bs[i]->hash, i, linebuf);
166 #endif
167 hash_t hi = bs[i]->hash;
168 while (!is_pass(fbook->moves[hi & fbook_hash_mask]) && fbook->hashes[hi & fbook_hash_mask] != bs[i]->hash)
169 hi++;
170 fbook->moves[hi & fbook_hash_mask] = coord;
171 fbook->hashes[hi & fbook_hash_mask] = bs[i]->hash;
173 coord_done(c);
176 for (int i = 0; i < 8; i++) {
177 board_done(bs[i]);
180 fclose(f);
182 struct fbook *fbold = fbcache;
183 fbcache = fbook;
184 if (fbold)
185 fbook_done(fbold);
187 return fbook;
190 void fbook_done(struct fbook *fbook)
192 if (fbook != fbcache)
193 free(fbook);