static void board_init_data(struct board *board) { int size = board_size(board); board_setup(board); board_resize(board, size - 2 /* S_OFFBOARD margin */); /* Setup neighborhood iterators */ board->nei8[0] = -size - 1; // (-1,-1) board->nei8[1] = 1; board->nei8[2] = 1; board->nei8[3] = size - 2; // (-1,0) board->nei8[4] = 2; board->nei8[5] = size - 2; // (-1,1) board->nei8[6] = 1; board->nei8[7] = 1; board->dnei[0] = -size - 1; board->dnei[1] = 2; board->dnei[2] = size*2 - 2; board->dnei[3] = 2; /* Setup initial symmetry */ if (size % 2) { board->symmetry.d = 1; board->symmetry.x1 = board->symmetry.y1 = board_size(board) / 2; board->symmetry.x2 = board->symmetry.y2 = board_size(board) - 1; board->symmetry.type = SYM_FULL; } else { /* TODO: We do not handle board symmetry on boards * with no tengen yet. */ board->symmetry.d = 0; board->symmetry.x1 = board->symmetry.y1 = 1; board->symmetry.x2 = board->symmetry.y2 = board_size(board) - 1; board->symmetry.type = SYM_NONE; } /* Set up coordinate cache */ foreach_point(board) { board->coord[c][0] = c % board_size(board); board->coord[c][1] = c / board_size(board); } foreach_point_end; /* Draw the offboard margin */ int top_row = board_size2(board) - board_size(board); int i; for (i = 0; i < board_size(board); i++) board->b[i] = board->b[top_row + i] = S_OFFBOARD; for (i = 0; i <= top_row; i += board_size(board)) board->b[i] = board->b[board_size(board) - 1 + i] = S_OFFBOARD; foreach_point(board) { coord_t coord = c; if (board_at(board, coord) == S_OFFBOARD) continue; foreach_neighbor(board, c, { inc_neighbor_count_at(board, coord, board_at(board, c)); } ); } foreach_point_end;
struct fbook * fbook_init(char *filename, struct board *b) { if (fbcache && fbcache->bsize == board_size(b) && fbcache->handicap == b->handicap) return fbcache; FILE *f = fopen(filename, "r"); if (!f) { perror(filename); return NULL; } struct fbook *fbook = calloc(1, sizeof(*fbook)); fbook->bsize = board_size(b); fbook->handicap = b->handicap; /* We do not set handicap=1 in case of too low komi on purpose; * we want to go with the no-handicap fbook for now. */ for (int i = 0; i < 1<<fbook_hash_bits; i++) fbook->moves[i] = pass; if (DEBUGL(1)) fprintf(stderr, "Loading opening fbook %s...\n", filename); /* Scratch board where we lay out the sequence; * one for each transposition. */ struct board *bs[8]; for (int i = 0; i < 8; i++) { bs[i] = board_init(NULL); board_resize(bs[i], fbook->bsize - 2); } char linebuf[1024]; while (fgets(linebuf, sizeof(linebuf), f)) { char *line = linebuf; linebuf[strlen(linebuf) - 1] = 0; // chop /* Format of line is: * BSIZE COORD COORD COORD... | COORD * BSIZE/HANDI COORD COORD COORD... | COORD * We descend up to |, then add the new node * with value minimax(1000), forcing UCT to * always pick that node immediately. */ int bsize = strtol(line, &line, 10); if (bsize != fbook->bsize - 2) continue; int handi = 0; if (*line == '/') { line++; handi = strtol(line, &line, 10); } if (handi != fbook->handicap) continue; while (isspace(*line)) line++; for (int i = 0; i < 8; i++) { board_clear(bs[i]); bs[i]->last_move.color = S_WHITE; } while (*line != '|') { coord_t *c = str2coord(line, fbook->bsize); for (int i = 0; i < 8; i++) { coord_t coord = coord_transform(b, *c, i); struct move m = { .coord = coord, .color = stone_other(bs[i]->last_move.color) }; int ret = board_play(bs[i], &m); assert(ret >= 0); } coord_done(c); while (!isspace(*line)) line++; while (isspace(*line)) line++; } line++; while (isspace(*line)) line++; /* In case of multiple candidates, pick one with * exponentially decreasing likelihood. */ while (strchr(line, ' ') && fast_random(2)) { line = strchr(line, ' '); while (isspace(*line)) line++; // fprintf(stderr, "<%s> skip to %s\n", linebuf, line); } coord_t *c = str2coord(line, fbook->bsize); for (int i = 0; i < 8; i++) { coord_t coord = coord_transform(b, *c, i); #if 0 char conflict = is_pass(fbook->moves[bs[i]->hash & fbook_hash_mask]) ? '+' : 'C'; if (conflict == 'C') for (int j = 0; j < i; j++) if (bs[i]->hash == bs[j]->hash) conflict = '+'; if (conflict == 'C') { hash_t hi = bs[i]->hash; while (!is_pass(fbook->moves[hi & fbook_hash_mask]) && fbook->hashes[hi & fbook_hash_mask] != bs[i]->hash) hi++; if (fbook->hashes[hi & fbook_hash_mask] == bs[i]->hash) hi = 'c'; } fprintf(stderr, "%c %"PRIhash":%"PRIhash" (<%d> %s)\n", conflict, bs[i]->hash & fbook_hash_mask, bs[i]->hash, i, linebuf); #endif hash_t hi = bs[i]->hash; while (!is_pass(fbook->moves[hi & fbook_hash_mask]) && fbook->hashes[hi & fbook_hash_mask] != bs[i]->hash) hi++; fbook->moves[hi & fbook_hash_mask] = coord; fbook->hashes[hi & fbook_hash_mask] = bs[i]->hash; fbook->movecnt++; } coord_done(c); } for (int i = 0; i < 8; i++) { board_done(bs[i]); } fclose(f); if (!fbook->movecnt) { /* Empty book is not worth the hassle. */ fbook_done(fbook); return NULL; } struct fbook *fbold = fbcache; fbcache = fbook; if (fbold) fbook_done(fbold); return fbook; } void fbook_done(struct fbook *fbook) { if (fbook != fbcache) free(fbook); }
static void board_load(struct board *b, FILE *f, unsigned int size) { board_printed = false; board_resize(b, size); board_clear(b); for (int y = size - 1; y >= 0; y--) { char line[256]; if (!fgets(line, sizeof(line), f)) { fprintf(stderr, "Premature EOF.\n"); exit(EXIT_FAILURE); } line[strlen(line) - 1] = 0; // chomp if (strlen(line) != size * 2 - 1) { fprintf(stderr, "Line not %d char long: %s\n", size * 2 - 1, line); exit(EXIT_FAILURE); } for (unsigned int i = 0; i < size * 2; i++) { enum stone s; switch (line[i]) { case '.': s = S_NONE; break; case 'X': s = S_BLACK; break; case 'O': s = S_WHITE; break; default: fprintf(stderr, "Invalid stone '%c'\n", line[i]); exit(EXIT_FAILURE); } i++; if (line[i] != ' ' && i/2 < size - 1) { fprintf(stderr, "No space after stone %i: '%c'\n", i/2 + 1, line[i]); exit(EXIT_FAILURE); } if (s == S_NONE) continue; struct move m = { .color = s, .coord = coord_xy(b, i/2 + 1, y + 1) }; if (board_play(b, &m) < 0) { fprintf(stderr, "Failed to play %s %s\n", stone2str(s), coord2sstr(m.coord, b)); board_print(b, stderr); exit(EXIT_FAILURE); } } } int suicides = b->captures[S_BLACK] || b->captures[S_WHITE]; assert(!suicides); } static void set_ko(struct board *b, char *arg) { assert(isalpha(*arg)); struct move last; last.coord = str2scoord(arg, board_size(b)); last.color = board_at(b, last.coord); assert(last.color == S_BLACK || last.color == S_WHITE); b->last_move = last; /* Sanity checks */ group_t g = group_at(b, last.coord); assert(board_group_info(b, g).libs == 1); assert(group_stone_count(b, g, 2) == 1); coord_t lib = board_group_info(b, g).lib[0]; assert(board_is_eyelike(b, lib, last.color)); b->ko.coord = lib; b->ko.color = stone_other(last.color); }