coord_xy_otf(): Add
[pachi.git] / board.c
bloba6d08d617a087eec25355577dd9478c3f187f829
1 #include <alloca.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
6 #include "board.h"
7 #include "debug.h"
8 #include "random.h"
10 int board_group_capture(struct board *board, int group);
12 bool random_pass = false;
15 #if 0
16 #define profiling_noinline __attribute__((noinline))
17 #else
18 #define profiling_noinline
19 #endif
21 #define gi_granularity 4
22 #define gi_allocsize(gids) ((1 << gi_granularity) + ((gids) >> gi_granularity) * (1 << gi_granularity))
25 static void
26 board_setup(struct board *b)
28 memset(b, 0, sizeof(*b));
30 struct move m = { pass, S_NONE };
31 b->last_move = b->ko = m;
34 struct board *
35 board_init(void)
37 struct board *b = malloc(sizeof(struct board));
38 board_setup(b);
39 return b;
42 struct board *
43 board_copy(struct board *b2, struct board *b1)
45 memcpy(b2, b1, sizeof(struct board));
47 int bsize = b2->size2 * sizeof(*b2->b);
48 int gsize = b2->size2 * sizeof(*b2->g);
49 int fsize = b2->size2 * sizeof(*b2->f);
50 int nsize = b2->size2 * sizeof(*b2->n);
51 int psize = b2->size2 * sizeof(*b2->p);
52 int hsize = b2->size2 * 2 * sizeof(*b2->h);
53 int lsize = b2->size2 * sizeof(*b2->l);
54 void *x = malloc(bsize + gsize + fsize + psize + nsize + hsize + lsize);
55 memcpy(x, b1->b, bsize + gsize + fsize + psize + nsize + hsize + lsize);
56 b2->b = x; x += bsize;
57 b2->g = x; x += gsize;
58 b2->f = x; x += fsize;
59 b2->p = x; x += psize;
60 b2->n = x; x += nsize;
61 b2->h = x; x += hsize;
62 b2->l = x; x += lsize;
64 return b2;
67 void
68 board_done_noalloc(struct board *board)
70 if (board->b) free(board->b);
73 void
74 board_done(struct board *board)
76 board_done_noalloc(board);
77 free(board);
80 void
81 board_resize(struct board *board, int size)
83 board->size = size + 2 /* S_OFFBOARD margin */;
84 board->size2 = board->size * board->size;
85 if (board->b)
86 free(board->b);
88 int bsize = board->size2 * sizeof(*board->b);
89 int gsize = board->size2 * sizeof(*board->g);
90 int fsize = board->size2 * sizeof(*board->f);
91 int nsize = board->size2 * sizeof(*board->n);
92 int psize = board->size2 * sizeof(*board->p);
93 int hsize = board->size2 * 2 * sizeof(*board->h);
94 int lsize = board->size2 * sizeof(*board->l);
95 void *x = malloc(bsize + gsize + fsize + psize + nsize + hsize + lsize);
96 memset(x, 0, bsize + gsize + fsize + psize + nsize + hsize + lsize);
97 board->b = x; x += bsize;
98 board->g = x; x += gsize;
99 board->f = x; x += fsize;
100 board->p = x; x += psize;
101 board->n = x; x += nsize;
102 board->h = x; x += hsize;
103 board->l = x; x += lsize;
106 void
107 board_clear(struct board *board)
109 int size = board->size;
111 board_done_noalloc(board);
112 board_setup(board);
113 board_resize(board, size - 2 /* S_OFFBOARD margin */);
115 /* Draw the offboard margin */
116 int top_row = board->size2 - board->size;
117 int i;
118 for (i = 0; i < board->size; i++)
119 board->b[i] = board->b[top_row + i] = S_OFFBOARD;
120 for (i = 0; i <= top_row; i += board->size)
121 board->b[i] = board->b[board->size - 1 + i] = S_OFFBOARD;
123 foreach_point(board) {
124 coord_t coord = c;
125 if (board_at(board, coord) == S_OFFBOARD)
126 continue;
127 foreach_neighbor(board, c, {
128 inc_neighbor_count_at(board, coord, board_at(board, c));
129 } );
130 } foreach_point_end;
132 /* First, pass is always a free position. */
133 board->f[board->flen++] = coord_raw(pass);
134 /* All positions are free! Except the margin. */
135 for (i = board->size; i < (board->size - 1) * board->size; i++)
136 if (i % board->size != 0 && i % board->size != board->size - 1)
137 board->f[board->flen++] = i;
139 /* Initialize zobrist hashtable. */
140 foreach_point(board) {
141 int max = (sizeof(hash_t) << history_hash_bits);
142 /* fast_random() is 16-bit only */
143 board->h[coord_raw(c) * 2] = ((hash_t) fast_random(max))
144 | ((hash_t) fast_random(max) << 16)
145 | ((hash_t) fast_random(max) << 32)
146 | ((hash_t) fast_random(max) << 48);
147 if (!board->h[coord_raw(c) * 2])
148 /* Would be kinda "oops". */
149 board->h[coord_raw(c) * 2] = 1;
150 /* And once again for white */
151 board->h[coord_raw(c) * 2 + 1] = ((hash_t) fast_random(max))
152 | ((hash_t) fast_random(max) << 16)
153 | ((hash_t) fast_random(max) << 32)
154 | ((hash_t) fast_random(max) << 48);
155 if (!board->h[coord_raw(c) * 2 + 1])
156 board->h[coord_raw(c) * 2 + 1] = 1;
157 } foreach_point_end;
161 void
162 board_print(struct board *board, FILE *f)
164 fprintf(f, "Move: % 3d Komi: %2.1f Captures B: %d W: %d\n ",
165 board->moves, board->komi,
166 board->captures[S_BLACK], board->captures[S_WHITE]);
167 int x, y;
168 char asdf[] = "ABCDEFGHJKLMNOPQRSTUVWXYZ";
169 for (x = 1; x < board->size - 1; x++)
170 fprintf(f, "%c ", asdf[x - 1]);
171 fprintf(f, "\n +-");
172 for (x = 1; x < board->size - 1; x++)
173 fprintf(f, "--");
174 fprintf(f, "+\n");
175 for (y = board->size - 2; y >= 1; y--) {
176 fprintf(f, "%2d | ", y);
177 for (x = 1; x < board->size - 1; x++) {
178 if (coord_x(board->last_move.coord, board) == x && coord_y(board->last_move.coord, board) == y)
179 fprintf(f, "%c)", stone2char(board_atxy(board, x, y)));
180 else
181 fprintf(f, "%c ", stone2char(board_atxy(board, x, y)));
183 if (DEBUGL(6)) {
184 fprintf(f, "| ");
185 for (x = 1; x < board->size - 1; x++) {
186 fprintf(f, "%d ", group_atxy(board, x, y));
189 fprintf(f, "|\n");
191 fprintf(f, " +-");
192 for (x = 1; x < board->size - 1; x++)
193 fprintf(f, "--");
194 fprintf(f, "+\n\n");
198 /* Update board hash with given coordinate. */
199 static void profiling_noinline
200 board_hash_update(struct board *board, coord_t coord, enum stone color)
202 board->hash ^= board->h[(color == S_BLACK ? board->size2 : 0) + coord_raw(coord)];
203 if (DEBUGL(8))
204 fprintf(stderr, "board_hash_update(%d,%d,%d) ^ %llx -> %llx\n", color, coord_x(coord, board), coord_y(coord, board), board->h[color * coord_raw(coord)], board->hash);
207 /* Commit current board hash to history. */
208 static void profiling_noinline
209 board_hash_commit(struct board *board)
211 if (DEBUGL(8))
212 fprintf(stderr, "board_hash_commit %llx\n", board->hash);
213 if (likely(board->history_hash[board->hash & history_hash_mask]) == 0) {
214 board->history_hash[board->hash & history_hash_mask] = board->hash;
215 } else {
216 hash_t i = board->hash;
217 while (board->history_hash[i & history_hash_mask]) {
218 if (board->history_hash[i & history_hash_mask] == board->hash) {
219 if (DEBUGL(5))
220 fprintf(stderr, "SUPERKO VIOLATION noted at %d,%d\n",
221 coord_x(board->last_move.coord, board), coord_y(board->last_move.coord, board));
222 board->superko_violation = true;
223 return;
225 i++;
227 board->history_hash[i & history_hash_mask] = board->hash;
232 void
233 board_handicap_stone(struct board *board, int x, int y, FILE *f)
235 struct move m;
236 m.color = S_BLACK;
237 coord_xy(m.coord, x, y, board);
239 board_play(board, &m);
241 char *str = coord2str(m.coord, board);
242 if (DEBUGL(1))
243 fprintf(stderr, "choosing handicap %s (%d,%d)\n", str, x, y);
244 fprintf(f, "%s ", str);
245 free(str);
248 void
249 board_handicap(struct board *board, int stones, FILE *f)
251 int margin = 3 + (board->size >= 13);
252 int min = margin;
253 int mid = board->size / 2;
254 int max = board->size - 1 - margin;
255 const int places[][2] = {
256 { min, min }, { max, max }, { max, min }, { min, max },
257 { min, mid }, { max, mid },
258 { mid, min }, { mid, max },
259 { mid, mid },
262 board->handicap = stones;
264 if (stones == 5 || stones == 7) {
265 board_handicap_stone(board, mid, mid, f);
266 stones--;
269 int i;
270 for (i = 0; i < stones; i++)
271 board_handicap_stone(board, places[i][0], places[i][1], f);
275 /* This is a low-level routine that doesn't maintain consistency
276 * of all the board data structures. Use board_group_capture() from
277 * your code. */
278 static void
279 board_remove_stone(struct board *board, coord_t c)
281 enum stone color = board_at(board, c);
282 board_at(board, c) = S_NONE;
283 group_at(board, c) = 0;
284 board_hash_update(board, c, color);
286 /* Increase liberties of surrounding groups */
287 coord_t coord = c;
288 foreach_neighbor(board, coord, {
289 dec_neighbor_count_at(board, c, color);
290 board_group_libs(board, group_at(board, c))++;
293 if (DEBUGL(6))
294 fprintf(stderr, "pushing free move [%d]: %d,%d\n", board->flen, coord_x(c, board), coord_y(c, board));
295 board->f[board->flen++] = coord_raw(c);
299 static void profiling_noinline
300 add_to_group(struct board *board, int gid, coord_t prevstone, coord_t coord)
302 board_group_libs(board, gid) += immediate_liberty_count(board, coord);
304 group_at(board, coord) = gid;
305 groupnext_at(board, coord) = groupnext_at(board, prevstone);
306 groupnext_at(board, prevstone) = coord_raw(coord);
308 if (DEBUGL(8))
309 fprintf(stderr, "add_to_group: added (%d,%d ->) %d,%d (-> %d,%d) to group %d - libs %d\n",
310 coord_x(prevstone, board), coord_y(prevstone, board),
311 coord_x(coord, board), coord_y(coord, board),
312 groupnext_at(board, coord) % board->size, groupnext_at(board, coord) / board->size,
313 gid, board_group_libs(board, gid));
316 static void profiling_noinline
317 merge_groups(struct board *board, group_t group_to, group_t group_from)
319 if (DEBUGL(7))
320 fprintf(stderr, "board_play_raw: merging groups %d(%d) -> %d(%d)\n",
321 group_from, board_group_libs(board, group_from),
322 group_to, board_group_libs(board, group_to));
324 coord_t last_in_group;
325 foreach_in_group(board, group_from) {
326 last_in_group = c;
327 group_at(board, c) = group_to;
328 } foreach_in_group_end;
329 groupnext_at(board, last_in_group) = groupnext_at(board, group_to);
330 groupnext_at(board, group_to) = group_from;
332 board_group_libs(board, group_to) += board_group_libs(board, group_from);
334 if (DEBUGL(7))
335 fprintf(stderr, "board_play_raw: merged group: %d(%d)\n",
336 group_to, board_group_libs(board, group_to));
339 static group_t profiling_noinline
340 new_group(struct board *board, coord_t coord)
342 group_t gid = coord_raw(coord);
343 board_group_libs(board, gid) = immediate_liberty_count(board, coord);
345 group_at(board, coord) = gid;
346 groupnext_at(board, coord) = 0;
348 if (DEBUGL(8))
349 fprintf(stderr, "new_group: added %d,%d to group %d - libs %d\n",
350 coord_x(coord, board), coord_y(coord, board),
351 gid, board_group_libs(board, gid));
353 return gid;
356 /* We played on a place with at least one liberty. We will become a member of
357 * some group for sure. */
358 static int profiling_noinline
359 board_play_outside(struct board *board, struct move *m, int f)
361 coord_t coord = m->coord;
362 enum stone color = m->color;
363 enum stone other_color = stone_other(color);
364 int gid = 0;
366 board->f[f] = board->f[--board->flen];
367 if (DEBUGL(6))
368 fprintf(stderr, "popping free move [%d->%d]: %d\n", board->flen, f, board->f[f]);
369 board_at(board, coord) = color;
371 foreach_neighbor(board, coord, {
372 enum stone ncolor = board_at(board, c);
373 group_t ngroup = group_at(board, c);
375 inc_neighbor_count_at(board, c, color);
377 board_group_libs(board, ngroup)--;
378 if (DEBUGL(7))
379 fprintf(stderr, "board_play_raw: reducing libs for group %d: libs %d\n",
380 ngroup, board_group_libs(board, ngroup));
382 if (ncolor == color && ngroup != gid) {
383 if (gid <= 0) {
384 gid = ngroup;
385 add_to_group(board, gid, c, coord);
386 } else {
387 merge_groups(board, gid, ngroup);
389 } else if (ncolor == other_color) {
390 if (unlikely(board_group_captured(board, ngroup)))
391 board_group_capture(board, ngroup);
395 if (unlikely(gid <= 0))
396 gid = new_group(board, coord);
398 board->last_move = *m;
399 board->moves++;
400 board_hash_update(board, coord, color);
401 struct move ko = { pass, S_NONE };
402 board->ko = ko;
404 return gid;
407 /* We played in an eye-like shape. Either we capture at least one of the eye
408 * sides in the process of playing, or return -1. */
409 static int profiling_noinline
410 board_play_in_eye(struct board *board, struct move *m, int f)
412 coord_t coord = m->coord;
413 enum stone color = m->color;
414 /* Check ko: Capture at a position of ko capture one move ago */
415 if (unlikely(color == board->ko.color && coord_eq(coord, board->ko.coord))) {
416 if (DEBUGL(5))
417 fprintf(stderr, "board_check: ko at %d,%d color %d\n", coord_x(coord, board), coord_y(coord, board), color);
418 return -1;
419 } else if (DEBUGL(6)) {
420 fprintf(stderr, "board_check: no ko at %d,%d,%d - ko is %d,%d,%d\n",
421 color, coord_x(coord, board), coord_y(coord, board),
422 board->ko.color, coord_x(board->ko.coord, board), coord_y(board->ko.coord, board));
425 struct move ko = { pass, S_NONE };
427 board->f[f] = board->f[--board->flen];
428 if (DEBUGL(6))
429 fprintf(stderr, "popping free move [%d->%d]: %d\n", board->flen, f, board->f[f]);
431 int captured_groups = 0;
433 foreach_neighbor(board, coord, {
434 group_t group = group_at(board, c);
436 board_group_libs(board, group)--;
437 if (DEBUGL(7))
438 fprintf(stderr, "board_play_raw: reducing libs for group %d: libs %d\n",
439 group, board_group_libs(board, group));
441 if (unlikely(board_group_captured(board, group))) {
442 captured_groups++;
443 if (board_group_capture(board, group) == 1) {
444 /* If we captured multiple groups at once,
445 * we can't be fighting ko so we don't need
446 * to check for that. */
447 ko.color = stone_other(color);
448 ko.coord = c;
449 if (DEBUGL(5))
450 fprintf(stderr, "guarding ko at %d,%d,%d\n", ko.color, coord_x(ko.coord, board), coord_y(ko.coord, board));
455 if (likely(captured_groups == 0)) {
456 if (DEBUGL(5)) {
457 if (DEBUGL(6))
458 board_print(board, stderr);
459 fprintf(stderr, "board_check: one-stone suicide\n");
462 foreach_neighbor(board, coord, {
463 board_group_libs(board, group_at(board, c))++;
464 if (DEBUGL(7))
465 fprintf(stderr, "board_play_raw: restoring libs for group %d: libs %d\n",
466 group_at(board, c), board_group_libs(board, group_at(board, c)));
469 coord_t c = coord;
470 if (DEBUGL(6))
471 fprintf(stderr, "pushing free move [%d]: %d,%d\n", board->flen, coord_x(c, board), coord_y(c, board));
472 board->f[board->flen++] = coord_raw(c);
473 return -1;
476 foreach_neighbor(board, coord, {
477 inc_neighbor_count_at(board, c, color);
480 board_at(board, coord) = color;
482 board->last_move = *m;
483 board->moves++;
484 board_hash_update(board, coord, color);
485 board_hash_commit(board);
486 board->ko = ko;
488 return new_group(board, coord);
491 static int __attribute__((flatten))
492 board_play_f(struct board *board, struct move *m, int f)
494 if (DEBUGL(7)) {
495 fprintf(stderr, "board_play(): ---- Playing %d,%d\n", coord_x(m->coord, board), coord_y(m->coord, board));
497 if (likely(!board_is_eyelike(board, &m->coord, stone_other(m->color)))) {
498 /* NOT playing in an eye. Thus this move has to succeed. (This
499 * is thanks to New Zealand rules. Otherwise, multi-stone
500 * suicide might fail.) */
501 int gid = board_play_outside(board, m, f);
502 if (unlikely(board_group_captured(board, gid))) {
503 board_group_capture(board, gid);
505 board_hash_commit(board);
506 return 0;
507 } else {
508 return board_play_in_eye(board, m, f);
513 board_play(struct board *board, struct move *m)
515 if (unlikely(is_pass(m->coord) || is_resign(m->coord)))
516 return 0;
518 int f;
519 for (f = 0; f < board->flen; f++)
520 if (board->f[f] == coord_raw(m->coord))
521 return board_play_f(board, m, f);
523 if (DEBUGL(7))
524 fprintf(stderr, "board_check: stone exists\n");
525 return -1;
529 static inline bool
530 board_try_random_move(struct board *b, enum stone color, coord_t *coord, int f)
532 coord_raw(*coord) = b->f[f];
533 if (is_pass(*coord))
534 return random_pass;
535 struct move m = { *coord, color };
536 if (DEBUGL(6))
537 fprintf(stderr, "trying random move %d: %d,%d\n", f, coord_x(*coord, b), coord_y(*coord, b));
538 return (!board_is_one_point_eye(b, coord, color) /* bad idea to play into one, usually */
539 && board_play_f(b, &m, f) >= 0);
542 void
543 board_play_random(struct board *b, enum stone color, coord_t *coord)
545 int base = fast_random(b->flen);
546 coord_pos(*coord, base, b);
547 if (likely(board_try_random_move(b, color, coord, base)))
548 return;
550 int f;
551 for (f = base + 1; f < b->flen; f++)
552 if (board_try_random_move(b, color, coord, f))
553 return;
554 for (f = 0; f < base; f++)
555 if (board_try_random_move(b, color, coord, f))
556 return;
558 *coord = pass;
562 bool
563 board_is_eyelike(struct board *board, coord_t *coord, enum stone eye_color)
565 return (neighbor_count_at(board, *coord, eye_color) + neighbor_count_at(board, *coord, S_OFFBOARD)) == 4;
568 bool
569 board_is_one_point_eye(struct board *board, coord_t *coord, enum stone eye_color)
571 enum stone color_diag_libs[S_MAX] = {0, 0, 0, 0};
573 if (likely(neighbor_count_at(board, *coord, eye_color) + neighbor_count_at(board, *coord, S_OFFBOARD) < 4)) {
574 return false;
577 /* XXX: We attempt false eye detection but we will yield false
578 * positives in case of http://senseis.xmp.net/?TwoHeadedDragon :-( */
580 foreach_diag_neighbor(board, *coord) {
581 color_diag_libs[(enum stone) board_at(board, c)]++;
582 } foreach_diag_neighbor_end;
583 color_diag_libs[stone_other(eye_color)] += !!color_diag_libs[S_OFFBOARD];
584 return likely(color_diag_libs[stone_other(eye_color)] < 2);
587 enum stone
588 board_get_one_point_eye(struct board *board, coord_t *coord)
590 if (board_is_one_point_eye(board, coord, S_WHITE))
591 return S_WHITE;
592 else if (board_is_one_point_eye(board, coord, S_BLACK))
593 return S_BLACK;
594 else
595 return S_NONE;
599 int profiling_noinline
600 board_group_capture(struct board *board, int group)
602 int stones = 0;
604 foreach_in_group(board, group) {
605 board->captures[stone_other(board_at(board, c))]++;
606 board_remove_stone(board, c);
607 stones++;
608 } foreach_in_group_end;
610 return stones;
613 bool
614 board_group_in_atari(struct board *board, group_t group, coord_t *lastlib)
616 /* First rule out obvious fakes. */
617 if (!group || board_group_libs(board, group) > 4)
618 return false;
619 coord_t base_stone = group;
620 if (immediate_liberty_count(board, base_stone) > 1)
621 return false;
623 int libs = 0;
625 bool watermark[board->size2];
626 memset(watermark, 0, sizeof(watermark));
628 foreach_in_group(board, group) {
629 coord_t coord = c;
630 foreach_neighbor(board, coord, {
631 if (likely(watermark[coord_raw(c)]))
632 continue;
633 watermark[coord_raw(c)] = true;
634 if (unlikely(board_at(board, c) == S_NONE)) {
635 libs++;
636 if (unlikely(libs > 1))
637 return false;
638 *lastlib = c;
640 } );
641 } foreach_in_group_end;
643 return libs == 1;
647 /* Chinese counting */
648 float
649 board_official_score(struct board *board)
651 int scores[S_MAX];
652 memset(scores, 0, sizeof(scores));
654 enum { GC_DUNNO, GC_ALIVE, GC_DEAD } gcache[board->size * board->size + 1];
655 memset(gcache, 0, sizeof(gcache));
657 foreach_point(board) {
658 enum stone color = board_at(board, c);
659 group_t g = group_at(board, c);
660 if (g > 0) {
661 /* There is a complication: There can be some dead
662 * stones that could not have been removed because
663 * they are in enemy territory and we can't suicide.
664 * At least we know they are in atari. */
665 if (gcache[g] == GC_DUNNO) {
666 coord_t x;
667 gcache[g] = board_group_in_atari(board, g, &x) == 1 ? GC_DEAD : GC_ALIVE;
669 if (gcache[g] == GC_ALIVE)
670 scores[color]++;
671 else
672 scores[stone_other(color)]++;
673 /* XXX: But we still miss the one empty opponent's point. */
675 } else if (color == S_NONE) {
676 /* TODO: Count multi-point eyes */
677 color = board_get_one_point_eye(board, &c);
678 scores[color]++;
680 } foreach_point_end;
682 return board->komi + scores[S_WHITE] - scores[S_BLACK];
685 float
686 board_fast_score(struct board *board)
688 int scores[S_MAX];
689 memset(scores, 0, sizeof(scores));
691 foreach_point(board) {
692 enum stone color = board_at(board, c);
693 if (color == S_NONE)
694 color = board_get_one_point_eye(board, &c);
695 scores[color]++;
696 // fprintf(stderr, "%d, %d ++%d = %d\n", coord_x(c, board), coord_y(c, board), color, scores[color]);
697 } foreach_point_end;
699 return board->komi + board->handicap + scores[S_WHITE] - scores[S_BLACK];