Minor changes
[matilda.git] / src / cfg_board.c
blob66882ab6c0c52c2d46ad1868b633813e640a9282
1 /*
2 A cfg_board structure is a common fate graph board representation that is used
3 for fast atari checking; because of this it is useful specially in heavy
4 playouts.
6 A cfg_board is built from a previous board structure, but the two are not
7 linked; i.e. changes in one don't reflect in the other.
9 Building and destroying (freeing) a cfg_board are costly operations that should
10 be used only if the cfg_board will be used in playing many turns. cfg_board
11 structures are partially dynamically created and as such cannot be simply
12 memcpied to reuse the same starting game point. Undo is also not supported.
14 Freed cfg_board information is kept in cache for fast access in the future; it
15 is best to first free previous instances before creating new ones, thus limiting
16 the size the cache has to have.
18 Just like in the rest of the source code, all functions are not thread safe
19 unless explicitly said so.
22 #include "config.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <assert.h>
28 #include <omp.h>
30 #include "alloc.h"
31 #include "board.h"
32 #include "cfg_board.h"
33 #include "flog.h"
34 #include "move.h"
35 #include "types.h"
36 #include "zobrist.h"
38 /* from board_constants */
39 extern u8 out_neighbors8[TOTAL_BOARD_SIZ];
40 extern u8 out_neighbors4[TOTAL_BOARD_SIZ];
41 extern move_seq neighbors_side[TOTAL_BOARD_SIZ];
42 extern move_seq neighbors_diag[TOTAL_BOARD_SIZ];
43 extern move_seq neighbors_3x3[TOTAL_BOARD_SIZ];
44 extern bool border_left[TOTAL_BOARD_SIZ];
45 extern bool border_right[TOTAL_BOARD_SIZ];
46 extern bool border_top[TOTAL_BOARD_SIZ];
47 extern bool border_bottom[TOTAL_BOARD_SIZ];
48 extern u8 active_bits_in_byte[256];
50 /* from zobrist */
51 extern u16 iv_3x3[TOTAL_BOARD_SIZ][TOTAL_BOARD_SIZ][3];
52 extern u16 initial_3x3_hash[TOTAL_BOARD_SIZ];
54 static group * saved_nodes[MAXIMUM_NUM_THREADS];
56 static group * alloc_group() {
57 int thread = omp_get_thread_num();
58 group * ret;
60 if (saved_nodes[thread] != NULL) {
61 ret = saved_nodes[thread];
62 saved_nodes[thread] = saved_nodes[thread]->next;
63 } else {
64 ret = malloc(sizeof(group));
66 if (ret == NULL) {
67 flog_crit("cfg", "system out of memory");
71 return ret;
74 static void just_delloc_group(
75 group * g
76 ) {
77 int thread = omp_get_thread_num();
78 g->next = saved_nodes[thread];
79 saved_nodes[thread] = g;
82 static void delloc_group(
83 cfg_board * cb,
84 group * g
85 ) {
86 cb->unique_groups_count--;
88 if (g->unique_groups_idx < cb->unique_groups_count) {
89 cb->unique_groups[g->unique_groups_idx] = cb->unique_groups[cb->unique_groups_count];
90 cb->g[cb->unique_groups[g->unique_groups_idx]]->unique_groups_idx = g->unique_groups_idx;
93 int thread = omp_get_thread_num();
94 g->next = saved_nodes[thread];
95 saved_nodes[thread] = g;
98 static void pos_set_occupied(
99 cfg_board * cb,
100 bool is_black,
101 move m
103 assert(neighbors_side[m].count < 5);
104 assert(neighbors_diag[m].count < 5);
105 assert(cb->p[m] > 0);
107 u8 idx = cb->p[m] - 1;
108 if (is_black) {
109 for (u8 k = 0; k < neighbors_side[m].count; ++k) {
110 move n = neighbors_side[m].coord[k];
111 cb->black_neighbors4[n]++;
112 cb->black_neighbors8[n]++;
114 cb->hash[n] += iv_3x3[n][m][idx];
117 for (u8 k = 0; k < neighbors_diag[m].count; ++k) {
118 move n = neighbors_diag[m].coord[k];
119 cb->black_neighbors8[n]++;
121 cb->hash[n] += iv_3x3[n][m][idx];
123 } else {
124 for (u8 k = 0; k < neighbors_side[m].count; ++k) {
125 move n = neighbors_side[m].coord[k];
126 cb->white_neighbors4[n]++;
127 cb->white_neighbors8[n]++;
129 cb->hash[n] += iv_3x3[n][m][idx];
132 for (u8 k = 0; k < neighbors_diag[m].count; ++k) {
133 move n = neighbors_diag[m].coord[k];
134 cb->white_neighbors8[n]++;
136 cb->hash[n] += iv_3x3[n][m][idx];
141 static void pos_set_free(
142 cfg_board * cb,
143 move m,
144 bool is_black
146 assert(neighbors_side[m].count < 5);
147 assert(neighbors_diag[m].count < 5);
148 assert(cb->p[m] > 0);
150 if (is_black) {
151 for (u8 k = 0; k < neighbors_side[m].count; ++k) {
152 move n = neighbors_side[m].coord[k];
153 cb->black_neighbors4[n]--;
154 cb->black_neighbors8[n]--;
156 cb->hash[n] ^= iv_3x3[n][m][cb->p[m] - 1];
159 for (u8 k = 0; k < neighbors_diag[m].count; ++k) {
160 move n = neighbors_diag[m].coord[k];
161 cb->black_neighbors8[n]--;
163 cb->hash[n] ^= iv_3x3[n][m][cb->p[m] - 1];
165 } else {
166 for (u8 k = 0; k < neighbors_side[m].count; ++k) {
167 move n = neighbors_side[m].coord[k];
168 cb->white_neighbors4[n]--;
169 cb->white_neighbors8[n]--;
171 cb->hash[n] ^= iv_3x3[n][m][cb->p[m] - 1];
174 for (u8 k = 0; k < neighbors_diag[m].count; ++k) {
175 move n = neighbors_diag[m].coord[k];
176 cb->white_neighbors8[n]--;
178 cb->hash[n] ^= iv_3x3[n][m][cb->p[m] - 1];
183 static void add_neighbor(
184 group * restrict g,
185 group * restrict n
187 for (u8 i = 0; i < g->neighbors_count; ++i) {
188 if (g->neighbors[i] == n->stones.coord[0]) {
189 return;
193 g->neighbors[g->neighbors_count++] = n->stones.coord[0];
194 n->neighbors[n->neighbors_count++] = g->stones.coord[0];
197 static void add_liberty(
198 group * g,
199 move m
201 u8 mask = (1 << (m % 8));
203 if ((g->ls[m / 8] & mask) == 0) {
204 g->ls[m / 8] |= mask;
205 g->liberties++;
207 if (m < g->liberties_min_coord) {
208 g->liberties_min_coord = m;
213 static void add_liberty_unchecked(
214 group * g,
215 move m
217 u8 mask = (1 << (m % 8));
218 g->ls[m / 8] |= mask;
219 g->liberties++;
221 if (m < g->liberties_min_coord) {
222 g->liberties_min_coord = m;
226 static void rem_liberty_unchecked(
227 group * g,
228 move m
230 u8 mask = (1 << (m % 8));
231 g->ls[m / 8] &= ~mask;
232 g->liberties--;
235 static void rem_neighbor(
236 group * restrict g,
237 const group * restrict to_remove
239 for (u8 j = 0; j < g->neighbors_count; ++j) {
240 if (g->neighbors[j] == to_remove->stones.coord[0]) {
241 g->neighbors[j] = g->neighbors[g->neighbors_count - 1];
242 g->neighbors_count--;
243 return;
247 flog_crit("cfg", "CFG group neighbor not found");
250 static void unite_groups(
251 cfg_board * cb,
252 group * restrict to_keep,
253 group * restrict to_replace
255 assert(to_keep != to_replace);
256 assert(to_keep->is_black == to_replace->is_black);
258 copy_moves(&to_keep->stones, &to_replace->stones);
260 for (move i = 0; i < to_replace->stones.count; ++i) {
261 move m = to_replace->stones.coord[i];
262 assert(cb->g[m] == to_replace);
263 cb->g[m] = to_keep;
266 for (u8 i = 0; i < to_replace->neighbors_count; ++i) {
267 add_neighbor(to_keep, cb->g[to_replace->neighbors[i]]);
268 rem_neighbor(cb->g[to_replace->neighbors[i]], to_replace);
271 if (to_replace->liberties == 0) {
272 delloc_group(cb, to_replace);
273 return;
276 u8 new_lib_count = 0;
277 for (u8 i = 0; i < LIB_BITMAP_SIZ; ++i) {
278 to_keep->ls[i] |= to_replace->ls[i];
279 new_lib_count += active_bits_in_byte[to_keep->ls[i]];
281 to_keep->liberties = new_lib_count;
283 if (to_replace->liberties_min_coord < to_keep->liberties_min_coord)
284 to_keep->liberties_min_coord = to_replace->liberties_min_coord;
285 delloc_group(cb, to_replace);
289 Adds a stone to the group information of a struct cfg_board.
290 Doesn't capture anything.
292 static void add_stone(
293 cfg_board * cb,
294 bool is_black,
295 move m
297 /* Create new stone group */
298 assert(cb->g[m] == NULL);
300 cb->g[m] = alloc_group();
301 cb->g[m]->is_black = is_black;
302 cb->g[m]->liberties = 0;
303 memset(cb->g[m]->ls, 0, LIB_BITMAP_SIZ);
304 cb->g[m]->liberties_min_coord = TOTAL_BOARD_SIZ;
305 cb->g[m]->neighbors_count = 0;
306 cb->g[m]->stones.count = 1;
307 cb->g[m]->stones.coord[0] = m;
309 cb->unique_groups[cb->unique_groups_count] = m;
310 cb->g[m]->unique_groups_idx = cb->unique_groups_count;
311 cb->unique_groups_count++;
313 /* Update neighbor stone counts */
314 pos_set_occupied(cb, is_black, m);
316 if (cb->black_neighbors4[m] + cb->white_neighbors4[m] == 0) {
317 if (!border_left[m]) {
318 add_liberty(cb->g[m], m + LEFT);
320 if (!border_right[m]) {
321 add_liberty(cb->g[m], m + RIGHT);
323 if (!border_top[m]) {
324 add_liberty(cb->g[m], m + TOP);
326 if (!border_bottom[m]) {
327 add_liberty(cb->g[m], m + BOTTOM);
329 return;
332 group * n;
333 group * neighbors[4];
334 u8 neighbors_n = 0;
336 if (!border_left[m]) {
337 if ((n = cb->g[m + LEFT]) == NULL) {
338 add_liberty(cb->g[m], m + LEFT);
339 } else {
340 neighbors[neighbors_n++] = n;
341 rem_liberty_unchecked(n, m);
343 if (n->is_black == is_black) {
344 unite_groups(cb, n, cb->g[m]);
345 } else {
346 add_neighbor(cb->g[m], n);
351 if (!border_right[m]) {
352 if ((n = cb->g[m + RIGHT]) == NULL) {
353 add_liberty(cb->g[m], m + RIGHT);
354 } else {
355 bool found = false;
356 for (u8 i = 0; i < neighbors_n; ++i) {
357 if (neighbors[i] == n) {
358 found = true;
359 break;
363 if (!found) {
364 neighbors[neighbors_n++] = n;
365 rem_liberty_unchecked(n, m);
367 if (n->is_black == is_black) {
368 unite_groups(cb, n, cb->g[m]);
369 } else {
370 add_neighbor(cb->g[m], n);
376 if (!border_top[m]) {
377 if ((n = cb->g[m + TOP]) == NULL) {
378 add_liberty(cb->g[m], m + TOP);
379 } else {
380 bool found = false;
381 for (u8 i = 0; i < neighbors_n; ++i) {
382 if (neighbors[i] == n) {
383 found = true;
384 break;
388 if (!found) {
389 neighbors[neighbors_n++] = n;
390 rem_liberty_unchecked(n, m);
392 if (n->is_black == is_black) {
393 unite_groups(cb, n, cb->g[m]);
394 } else {
395 add_neighbor(cb->g[m], n);
401 if (!border_bottom[m]) {
402 if ((n = cb->g[m + BOTTOM]) == NULL) {
403 add_liberty(cb->g[m], m + BOTTOM);
404 } else {
405 bool found = false;
406 for (u8 i = 0; i < neighbors_n; ++i) {
407 if (neighbors[i] == n) {
408 found = true;
409 break;
413 if (!found) {
414 neighbors[neighbors_n++] = n;
415 rem_liberty_unchecked(n, m);
417 if (n->is_black == is_black) {
418 unite_groups(cb, n, cb->g[m]);
419 } else {
420 add_neighbor(cb->g[m], n);
428 Tests if the two structures have the same board contents.
429 RETURNS true if the structures are equal in board contents
431 bool cfg_board_are_equal(
432 cfg_board * restrict a,
433 const board * restrict b
435 return memcmp(a->p, b->p, TOTAL_BOARD_SIZ) == 0 && a->last_played == b->last_played &&
436 a->last_eaten == b->last_eaten;
440 Initiliazes the data pointed to cb, to hold a valid (but empty) board.
442 void cfg_init_board(
443 cfg_board * cb
445 memset(cb->p, EMPTY, TOTAL_BOARD_SIZ);
446 cb->last_played = cb->last_eaten = NONE;
448 memcpy(cb->hash, initial_3x3_hash, TOTAL_BOARD_SIZ * sizeof(u16));
449 memset(cb->black_neighbors4, 0, TOTAL_BOARD_SIZ);
450 memset(cb->white_neighbors4, 0, TOTAL_BOARD_SIZ);
451 memset(cb->black_neighbors8, 0, TOTAL_BOARD_SIZ);
452 memset(cb->white_neighbors8, 0, TOTAL_BOARD_SIZ);
453 memset(cb->g, 0, TOTAL_BOARD_SIZ * sizeof(group *));
454 cb->empty.count = 0;
455 cb->unique_groups_count = 0;
457 for (move m = 0; m < TOTAL_BOARD_SIZ; ++m) {
458 cb->empty.coord[cb->empty.count] = m;
459 cb->empty.count++;
462 assert(verify_cfg_board(cb));
466 Converts a board structure into an cfg_board structure; the two are not linked;
467 changing one will not modify the other.
469 void cfg_from_board(
470 cfg_board * restrict dst,
471 const board * restrict src
473 memcpy(dst, src, sizeof(board));
474 memcpy(dst->hash, initial_3x3_hash, TOTAL_BOARD_SIZ * sizeof(u16));
475 memset(dst->black_neighbors4, 0, TOTAL_BOARD_SIZ);
476 memset(dst->white_neighbors4, 0, TOTAL_BOARD_SIZ);
477 memset(dst->black_neighbors8, 0, TOTAL_BOARD_SIZ);
478 memset(dst->white_neighbors8, 0, TOTAL_BOARD_SIZ);
479 memset(dst->g, 0, TOTAL_BOARD_SIZ * sizeof(group *));
480 dst->empty.count = 0;
481 dst->unique_groups_count = 0;
483 for (move m = 0; m < TOTAL_BOARD_SIZ; ++m) {
484 if (src->p[m] == EMPTY) {
485 dst->empty.coord[dst->empty.count] = m;
486 dst->empty.count++;
487 } else {
488 add_stone(dst, src->p[m] == BLACK_STONE, m);
492 assert(cfg_board_are_equal(dst, src));
493 assert(verify_cfg_board(dst));
497 Clones a CFG board into another, independent, instance.
499 void cfg_board_clone(
500 cfg_board * restrict dst,
501 const cfg_board * restrict src
503 /* copy most of the structure */
504 memcpy(dst, src, sizeof(cfg_board) - (TOTAL_BOARD_SIZ * sizeof(group *)));
505 memset(dst->g, 0, TOTAL_BOARD_SIZ * sizeof(group *));
507 for (u8 i = 0; i < src->unique_groups_count; ++i) {
508 /* copy group information */
509 group * g = alloc_group();
510 group * s = src->g[src->unique_groups[i]];
511 assert(s->unique_groups_idx == i);
512 memcpy(g, s, ((char *)&s->neighbors[s->neighbors_count]) - ((char *)s));
514 /* replace hard links to group information */
515 for (move j = 0; j < g->stones.count; ++j) {
516 move m = g->stones.coord[j];
517 dst->g[m] = g;
521 assert(verify_cfg_board(dst));
525 static void add_liberties_to_neighbors(
526 cfg_board * cb,
527 move m,
528 u8 own
530 if (!border_left[m] && cb->p[m + LEFT] == own) {
531 add_liberty(cb->g[m + LEFT], m);
534 if (!border_right[m] && cb->p[m + RIGHT] == own) {
535 add_liberty(cb->g[m + RIGHT], m);
538 if (!border_top[m] && cb->p[m + TOP] == own) {
539 add_liberty(cb->g[m + TOP], m);
542 if (!border_bottom[m] && cb->p[m + BOTTOM] == own) {
543 add_liberty(cb->g[m + BOTTOM], m);
547 static void cfg_board_kill_group(
548 cfg_board * cb,
549 group * g,
550 u8 own
552 move id = g->stones.coord[0];
554 for (move i = 0; i < g->stones.count; ++i) {
555 move m = g->stones.coord[i];
557 pos_set_free(cb, m, g->is_black);
558 cb->p[m] = EMPTY;
559 cb->g[m] = NULL;
560 add_liberties_to_neighbors(cb, m, own);
562 cb->empty.coord[cb->empty.count] = m;
563 cb->empty.count++;
566 for (u8 i = 0; i < g->neighbors_count; ++i) {
567 group * nei = cb->g[g->neighbors[i]];
569 for (u8 j = 0; j < nei->neighbors_count; ++j) {
570 if (nei->neighbors[j] == id) {
571 nei->neighbors_count--;
572 nei->neighbors[j] = nei->neighbors[nei->neighbors_count];
573 break;
578 delloc_group(cb, g);
581 static void cfg_board_kill_group2(
582 cfg_board * cb,
583 group * g,
584 u8 own,
585 u64 * zobrist_hash
587 move id = g->stones.coord[0];
589 for (move i = 0; i < g->stones.count; ++i) {
590 move m = g->stones.coord[i];
592 zobrist_update_hash(zobrist_hash, m, cb->p[m]);
593 pos_set_free(cb, m, g->is_black);
594 cb->p[m] = EMPTY;
595 cb->g[m] = NULL;
596 add_liberties_to_neighbors(cb, m, own);
598 cb->empty.coord[cb->empty.count] = m;
599 cb->empty.count++;
602 for (u8 i = 0; i < g->neighbors_count; ++i) {
603 group * nei = cb->g[g->neighbors[i]];
605 for (u8 j = 0; j < nei->neighbors_count; ++j) {
606 if (nei->neighbors[j] == id) {
607 nei->neighbors_count--;
608 nei->neighbors[j] = nei->neighbors[nei->neighbors_count];
609 break;
614 delloc_group(cb, g);
617 static void cfg_board_kill_group3(
618 cfg_board * cb,
619 group * g,
620 u8 own,
621 bool stones_removed[static TOTAL_BOARD_SIZ],
622 u8 rem_nei_libs[static LIB_BITMAP_SIZ]
624 move id = g->stones.coord[0];
626 for (move i = 0; i < g->stones.count; ++i) {
627 move m = g->stones.coord[i];
628 assert(cb->p[m] != EMPTY);
629 pos_set_free(cb, m, g->is_black);
630 cb->p[m] = EMPTY;
631 cb->g[m] = NULL;
632 stones_removed[m] = true;
633 add_liberties_to_neighbors(cb, m, own);
635 cb->empty.coord[cb->empty.count] = m;
636 cb->empty.count++;
639 for (u8 i = 0; i < g->neighbors_count; ++i) {
640 group * nei = cb->g[g->neighbors[i]];
642 for (u8 i = 0; i < LIB_BITMAP_SIZ; ++i) {
643 rem_nei_libs[i] |= nei->ls[i];
646 for (u8 j = 0; j < nei->neighbors_count; ++j) {
647 if (nei->neighbors[j] == id) {
648 nei->neighbors_count--;
649 nei->neighbors[j] = nei->neighbors[nei->neighbors_count];
650 break;
655 delloc_group(cb, g);
659 Apply a passing turn.
661 void just_pass(
662 cfg_board * cb
664 cb->last_played = PASS;
665 cb->last_eaten = NONE;
669 Assume play is legal and update the structure, capturing
670 accordingly.
672 void just_play(
673 cfg_board * cb,
674 bool is_black,
675 move m
677 assert(verify_cfg_board(cb));
678 assert(is_board_move(m));
679 assert(cb->p[m] == EMPTY);
680 assert(cb->g[m] == NULL);
682 u8 own = is_black ? BLACK_STONE : WHITE_STONE;
684 move captures = 0;
685 move one_stone_captured = NONE;
686 group * n;
688 cb->p[m] = own;
689 add_stone(cb, is_black, m);
691 u8 n8 = is_black ? cb->white_neighbors4[m] : cb->black_neighbors4[m];
692 if (n8 > 0) {
693 if (!border_left[m]) {
694 n = cb->g[m + LEFT];
696 if (n != NULL && n->is_black != is_black && n->liberties == 0) {
697 captures += n->stones.count;
698 cfg_board_kill_group(cb, n, own);
699 one_stone_captured = m + LEFT;
703 if (!border_right[m]) {
704 n = cb->g[m + RIGHT];
706 if (n != NULL && n->is_black != is_black && n->liberties == 0) {
707 captures += n->stones.count;
708 cfg_board_kill_group(cb, n, own);
709 one_stone_captured = m + RIGHT;
713 if (!border_top[m]) {
714 n = cb->g[m + TOP];
716 if (n != NULL && n->is_black != is_black && n->liberties == 0) {
717 captures += n->stones.count;
718 cfg_board_kill_group(cb, n, own);
719 one_stone_captured = m + TOP;
723 if (!border_bottom[m]) {
724 n = cb->g[m + BOTTOM];
726 if (n != NULL && n->is_black != is_black && n->liberties == 0) {
727 captures += n->stones.count;
728 cfg_board_kill_group(cb, n, own);
729 one_stone_captured = m + BOTTOM;
734 if (captures == 1) {
735 cb->last_eaten = one_stone_captured;
736 } else {
737 cb->last_eaten = NONE;
740 cb->last_played = m;
742 /* Remove position from list of empty intersections */
743 for (move k = 0; k < cb->empty.count; ++k) {
744 if (cb->empty.coord[k] == m) {
745 cb->empty.count--;
746 cb->empty.coord[k] = cb->empty.coord[cb->empty.count];
747 break;
753 Assume play is legal and update the structure, capturing
754 accordingly.
755 Also updates a Zobrist hash value.
757 void just_play2(
758 cfg_board * cb,
759 bool is_black,
760 move m,
761 u64 * zobrist_hash
763 assert(verify_cfg_board(cb));
764 assert(is_board_move(m));
765 assert(cb->p[m] == EMPTY);
766 assert(cb->g[m] == NULL);
768 u8 own = is_black ? BLACK_STONE : WHITE_STONE;
770 move captures = 0;
771 move one_stone_captured = NONE;
772 group * n;
774 cb->p[m] = own;
775 add_stone(cb, is_black, m);
776 zobrist_update_hash(zobrist_hash, m, own);
778 u8 n8 = is_black ? cb->white_neighbors4[m] : cb->black_neighbors4[m];
779 if (n8 > 0) {
780 if (!border_left[m]) {
781 n = cb->g[m + LEFT];
783 if (n != NULL && n->is_black != is_black && n->liberties == 0) {
784 captures += n->stones.count;
785 cfg_board_kill_group2(cb, n, own, zobrist_hash);
786 one_stone_captured = m + LEFT;
790 if (!border_right[m]) {
791 n = cb->g[m + RIGHT];
793 if (n != NULL && n->is_black != is_black && n->liberties == 0) {
794 captures += n->stones.count;
795 cfg_board_kill_group2(cb, n, own, zobrist_hash);
796 one_stone_captured = m + RIGHT;
800 if (!border_top[m]) {
801 n = cb->g[m + TOP];
803 if (n != NULL && n->is_black != is_black && n->liberties == 0) {
804 captures += n->stones.count;
805 cfg_board_kill_group2(cb, n, own, zobrist_hash);
806 one_stone_captured = m + TOP;
810 if (!border_bottom[m]) {
811 n = cb->g[m + BOTTOM];
813 if (n != NULL && n->is_black != is_black && n->liberties == 0) {
814 captures += n->stones.count;
815 cfg_board_kill_group2(cb, n, own, zobrist_hash);
816 one_stone_captured = m + BOTTOM;
821 if (captures == 1) {
822 cb->last_eaten = one_stone_captured;
823 } else {
824 cb->last_eaten = NONE;
827 cb->last_played = m;
829 /* Remove position from list of empty intersections */
830 for (move k = 0; k < cb->empty.count; ++k) {
831 if (cb->empty.coord[k] == m) {
832 cb->empty.count--;
833 cb->empty.coord[k] = cb->empty.coord[cb->empty.count];
834 break;
840 Assume play is legal and update the structure, capturing accordingly. Also
841 updates a stone difference and fills a matrix of captured stones and a bitmap of
842 liberties of neighbors of the captured groups. Does NOT clear the matrix and
843 bitmap.
845 void just_play3(
846 cfg_board * cb,
847 bool is_black,
848 move m,
849 d16 * stone_difference,
850 bool stones_removed[static TOTAL_BOARD_SIZ],
851 u8 rem_nei_libs[static LIB_BITMAP_SIZ]
853 assert(verify_cfg_board(cb));
854 assert(is_board_move(m));
855 assert(cb->p[m] == EMPTY);
856 assert(cb->g[m] == NULL);
858 u8 own = is_black ? BLACK_STONE : WHITE_STONE;
860 move captures = 0;
861 move one_stone_captured = NONE;
862 group * n;
864 cb->p[m] = own;
865 add_stone(cb, is_black, m);
867 u8 n8 = is_black ? cb->white_neighbors4[m] : cb->black_neighbors4[m];
868 if (n8 > 0) {
869 if (!border_left[m]) {
870 n = cb->g[m + LEFT];
872 if (n != NULL && n->is_black != is_black && n->liberties == 0) {
873 captures += n->stones.count;
874 cfg_board_kill_group3(cb, n, own, stones_removed, rem_nei_libs);
875 one_stone_captured = m + LEFT;
879 if (!border_right[m]) {
880 n = cb->g[m + RIGHT];
882 if (n != NULL && n->is_black != is_black && n->liberties == 0) {
883 captures += n->stones.count;
884 cfg_board_kill_group3(cb, n, own, stones_removed, rem_nei_libs);
885 one_stone_captured = m + RIGHT;
889 if (!border_top[m]) {
890 n = cb->g[m + TOP];
892 if (n != NULL && n->is_black != is_black && n->liberties == 0) {
893 captures += n->stones.count;
894 cfg_board_kill_group3(cb, n, own, stones_removed, rem_nei_libs);
895 one_stone_captured = m + TOP;
899 if (!border_bottom[m]) {
900 n = cb->g[m + BOTTOM];
902 if (n != NULL && n->is_black != is_black && n->liberties == 0) {
903 captures += n->stones.count;
904 cfg_board_kill_group3(cb, n, own, stones_removed, rem_nei_libs);
905 one_stone_captured = m + BOTTOM;
910 if (captures == 1) {
911 cb->last_eaten = one_stone_captured;
912 } else {
913 cb->last_eaten = NONE;
916 cb->last_played = m;
918 d16 stone_diff = 1 + captures;
919 *stone_difference += is_black ? stone_diff : -stone_diff;
921 /* Remove position from list of empty intersections */
922 for (move k = 0; k < cb->empty.count; ++k) {
923 if (cb->empty.coord[k] == m) {
924 cb->empty.count--;
925 cb->empty.coord[k] = cb->empty.coord[cb->empty.count];
926 break;
930 assert(verify_cfg_board(cb));
933 static void add_group_liberties(
934 group * restrict dst,
935 const group * restrict src
937 u8 new_lib_count = 0;
939 for (u8 i = 0; i < LIB_BITMAP_SIZ; ++i) {
940 dst->ls[i] |= src->ls[i];
941 new_lib_count += active_bits_in_byte[dst->ls[i]];
944 dst->liberties = new_lib_count;
947 static bool are_neighbors(
948 const cfg_board * cb,
949 group * g,
950 group ** neighbors,
951 u8 neighbors_n
953 for (u8 i = 0; i < g->neighbors_count; ++i) {
954 group * nei = cb->g[g->neighbors[i]];
956 for (u8 k = 0; k < neighbors_n; ++k) {
957 if (neighbors[k] == nei) {
958 return true;
963 return false;
966 static void cfg_board_give_neighbors_libs(
967 cfg_board * cb,
968 group * g,
969 group ** neighbors,
970 u8 neighbors_n
972 for (move i = 0; i < g->stones.count; ++i) {
973 move m = g->stones.coord[i];
975 for (u8 k = 0; k < neighbors_n; ++k) {
976 if (!border_left[m] && cb->g[m + LEFT] == neighbors[k]) {
977 add_liberty(cb->g[m + LEFT], m);
980 if (!border_right[m] && cb->g[m + RIGHT] == neighbors[k]) {
981 add_liberty(cb->g[m + RIGHT], m);
984 if (!border_top[m] && cb->g[m + TOP] == neighbors[k]) {
985 add_liberty(cb->g[m + TOP], m);
988 if (!border_bottom[m] && cb->g[m + BOTTOM] == neighbors[k]) {
989 add_liberty(cb->g[m + BOTTOM], m);
997 Detects one stone ko rule violations.
998 Doesn't test other types of legality.
999 RETURNS true if play is illegal due to ko
1001 bool ko_violation(
1002 const cfg_board * cb,
1003 move m
1005 assert(verify_cfg_board(cb));
1006 assert(is_board_move(m));
1008 return cb->last_eaten == m && cb->g[cb->last_played]->stones.count == 1 &&
1009 cb->g[cb->last_played]->liberties == 1;
1013 If ko is possible, returns the offending play.
1014 RETURNS position in ko, or NONE
1016 move get_ko_play(
1017 const cfg_board * cb
1019 if (is_board_move(cb->last_eaten) && cb->g[cb->last_played]->stones.count == 1 &&
1020 cb->g[cb->last_played]->liberties == 1) {
1021 return cb->last_eaten;
1024 return NONE;
1028 Calculates the liberties after playing and the number of stones captured.
1029 Does not test ko.
1030 RETURNS number of liberties after play
1032 u8 libs_after_play(
1033 cfg_board * cb,
1034 bool is_black,
1035 move m,
1036 move * caps
1038 assert(verify_cfg_board(cb));
1039 assert(cb->p[m] == EMPTY);
1040 assert(cb->g[m] == NULL);
1042 if (cb->black_neighbors4[m] + cb->white_neighbors4[m] == 0) {
1043 *caps = false;
1044 return 4 - out_neighbors4[m];
1047 group * n;
1048 /* warning: some fields are not initialized because they're not used */
1049 group g;
1050 g.liberties = 0;
1051 g.liberties_min_coord = TOTAL_BOARD_SIZ;
1052 memset(g.ls, 0, LIB_BITMAP_SIZ);
1053 add_liberty_unchecked(&g, m);
1055 /* list of same color neighbors */
1056 group * neighbors[4];
1057 u8 neighbors_n = 0;
1059 if (!border_left[m]) {
1060 n = cb->g[m + LEFT];
1061 if (n == NULL) {
1062 add_liberty_unchecked(&g, m + LEFT);
1063 } else if (n->is_black == is_black) {
1064 neighbors[neighbors_n++] = n;
1068 if (!border_right[m]) {
1069 n = cb->g[m + RIGHT];
1070 if (n == NULL) {
1071 add_liberty_unchecked(&g, m + RIGHT);
1072 } else if (n->is_black == is_black) {
1073 bool found = false;
1074 for (u8 k = 0; k < neighbors_n; ++k) {
1075 if (neighbors[k] == n) {
1076 found = true;
1077 break;
1081 if (!found) {
1082 neighbors[neighbors_n++] = n;
1087 if (!border_top[m]) {
1088 n = cb->g[m + TOP];
1089 if (n == NULL) {
1090 add_liberty_unchecked(&g, m + TOP);
1091 } else if (n->is_black == is_black) {
1092 bool found = false;
1093 for (u8 k = 0; k < neighbors_n; ++k)
1094 if (neighbors[k] == n) {
1095 found = true;
1096 break;
1099 if (!found) {
1100 neighbors[neighbors_n++] = n;
1105 if (!border_bottom[m]) {
1106 n = cb->g[m + BOTTOM];
1107 if (n == NULL) {
1108 add_liberty_unchecked(&g, m + BOTTOM);
1109 } else if (n->is_black == is_black) {
1110 bool found = false;
1111 for (u8 k = 0; k < neighbors_n; ++k)
1112 if (neighbors[k] == n) {
1113 found = true;
1114 break;
1117 if (!found) {
1118 neighbors[neighbors_n++] = n;
1123 u8 on4 = is_black ? cb->white_neighbors4[m] : cb->black_neighbors4[m];
1124 if (on4 == 0) {
1125 *caps = false;
1126 for (u8 k = 0; k < neighbors_n; ++k) {
1127 add_group_liberties(&g, neighbors[k]);
1130 return g.liberties - 1;
1134 Backup neighbor groups before being modified
1136 u8 neighbor_bak_ls[4][LIB_BITMAP_SIZ];
1137 u8 neighbor_bak_libs[4];
1138 for (u8 k = 0; k < neighbors_n; ++k) {
1139 memcpy(neighbor_bak_ls[k], neighbors[k]->ls, LIB_BITMAP_SIZ);
1140 neighbor_bak_libs[k] = neighbors[k]->liberties;
1145 First capture enemy stones, saving them for later
1147 move captured = 0;
1148 group * opt_neighbors[4];
1149 u8 opt_neighbors_n = 0;
1151 if (!border_left[m]) {
1152 n = cb->g[m + LEFT];
1154 if (n != NULL && n->is_black != is_black && n->liberties == 1) {
1155 add_liberty_unchecked(&g, m + LEFT);
1156 opt_neighbors[opt_neighbors_n++] = n;
1157 cfg_board_give_neighbors_libs(cb, n, neighbors, neighbors_n);
1158 captured += n->stones.count;
1162 if (!border_right[m]) {
1163 n = cb->g[m + RIGHT];
1165 if (n != NULL && n->is_black != is_black && n->liberties == 1) {
1166 add_liberty_unchecked(&g, m + RIGHT);
1168 bool found = false;
1169 for (u8 k = 0; k < opt_neighbors_n; ++k) {
1170 if (opt_neighbors[k] == n) {
1171 found = true;
1172 break;
1176 if (!found) {
1177 opt_neighbors[opt_neighbors_n++] = n;
1178 cfg_board_give_neighbors_libs(cb, n, neighbors, neighbors_n);
1179 captured += n->stones.count;
1184 if (!border_top[m]) {
1185 n = cb->g[m + TOP];
1187 if (n != NULL && n->is_black != is_black && n->liberties == 1) {
1188 add_liberty_unchecked(&g, m + TOP);
1190 bool found = false;
1191 for (u8 k = 0; k < opt_neighbors_n; ++k) {
1192 if (opt_neighbors[k] == n) {
1193 found = true;
1194 break;
1198 if (!found) {
1199 opt_neighbors[opt_neighbors_n++] = n;
1200 cfg_board_give_neighbors_libs(cb, n, neighbors, neighbors_n);
1201 captured += n->stones.count;
1206 if (!border_bottom[m]) {
1207 n = cb->g[m + BOTTOM];
1209 if (n != NULL && n->is_black != is_black && n->liberties == 1) {
1210 add_liberty_unchecked(&g, m + BOTTOM);
1212 bool found = false;
1213 for (u8 k = 0; k < opt_neighbors_n; ++k) {
1214 if (opt_neighbors[k] == n) {
1215 found = true;
1216 break;
1220 if (!found) {
1221 opt_neighbors[opt_neighbors_n++] = n;
1222 cfg_board_give_neighbors_libs(cb, n, neighbors, neighbors_n);
1223 captured += n->stones.count;
1229 Now with updated liberty counts, count liberties and restore group liberty
1230 states
1232 for (u8 k = 0; k < neighbors_n; ++k) {
1233 add_group_liberties(&g, neighbors[k]);
1234 memcpy(neighbors[k]->ls, neighbor_bak_ls[k], LIB_BITMAP_SIZ);
1235 neighbors[k]->liberties = neighbor_bak_libs[k];
1238 *caps = captured;
1239 return g.liberties - 1;
1243 Calculates if playing at the designated position is legal and safe.
1244 Also returns whether it would return in a capture.
1245 Does not test ko.
1246 RETURNS 0 for illegal, 1 for placed in atari, 2 for safe to play
1248 u8 safe_to_play2(
1249 cfg_board * cb,
1250 bool is_black,
1251 move m,
1252 bool * caps
1254 assert(verify_cfg_board(cb));
1255 assert(cb->p[m] == EMPTY);
1256 assert(cb->g[m] == NULL);
1258 if (cb->white_neighbors8[m] + cb->black_neighbors8[m] + out_neighbors8[m] < 3) {
1259 *caps = false;
1260 return 2;
1263 *caps = false;
1264 group * n;
1265 /* warning: some fields are not initialized because they're not used */
1266 group g;
1267 g.liberties = 0;
1268 g.liberties_min_coord = TOTAL_BOARD_SIZ;
1269 memset(g.ls, 0, LIB_BITMAP_SIZ);
1270 add_liberty_unchecked(&g, m);
1272 u8 probable_libs = 0;
1273 group * opt_neighbors[4];
1274 u8 opt_neighbors_n = 0;
1275 group * neighbors[4];
1276 u8 neighbors_n = 0;
1278 if (!border_left[m]) {
1279 n = cb->g[m + LEFT];
1281 if (n == NULL) {
1282 add_liberty_unchecked(&g, m + LEFT);
1283 } else if (n->is_black == is_black) {
1284 neighbors[neighbors_n++] = n;
1285 add_group_liberties(&g, n);
1286 } else if (n->liberties == 1) {
1287 add_liberty_unchecked(&g, m + LEFT);
1288 *caps = true;
1290 if (n->stones.count > 1) {
1291 opt_neighbors[opt_neighbors_n++] = n;
1296 if (!border_right[m]) {
1297 n = cb->g[m + RIGHT];
1299 if (n == NULL) {
1300 add_liberty(&g, m + RIGHT);
1301 } else if (n->is_black == is_black) {
1302 bool found = false;
1303 for (u8 k = 0; k < neighbors_n; ++k) {
1304 if (neighbors[k] == n) {
1305 found = true;
1306 break;
1310 if (!found) {
1311 neighbors[neighbors_n++] = n;
1312 add_group_liberties(&g, n);
1314 } else if (n->liberties == 1) {
1315 add_liberty_unchecked(&g, m + RIGHT);
1316 *caps = true;
1318 if (n->stones.count > 1) {
1319 bool found = false;
1320 for (u8 k = 0; k < opt_neighbors_n; ++k) {
1321 if (opt_neighbors[k] == n) {
1322 found = true;
1323 break;
1327 if (!found) {
1328 opt_neighbors[opt_neighbors_n++] = n;
1334 if (!border_top[m]) {
1335 n = cb->g[m + TOP];
1337 if (n == NULL) {
1338 add_liberty(&g, m + TOP);
1339 } else if (n->is_black == is_black) {
1340 bool found = false;
1341 for (u8 k = 0; k < neighbors_n; ++k) {
1342 if (neighbors[k] == n) {
1343 found = true;
1344 break;
1348 if (!found) {
1349 neighbors[neighbors_n++] = n;
1350 add_group_liberties(&g, n);
1352 } else if (n->liberties == 1) {
1353 add_liberty_unchecked(&g, m + TOP);
1354 *caps = true;
1356 if (n->stones.count > 1) {
1357 bool found = false;
1358 for (u8 k = 0; k < opt_neighbors_n; ++k) {
1359 if (opt_neighbors[k] == n) {
1360 found = true;
1361 break;
1365 if (!found) {
1366 opt_neighbors[opt_neighbors_n++] = n;
1372 if (!border_bottom[m]) {
1373 n = cb->g[m + BOTTOM];
1375 if (n == NULL) {
1376 add_liberty(&g, m + BOTTOM);
1377 } else if (n->is_black == is_black) {
1378 bool found = false;
1379 for (u8 k = 0; k < neighbors_n; ++k) {
1380 if (neighbors[k] == n) {
1381 found = true;
1382 break;
1386 if (!found) {
1387 neighbors[neighbors_n++] = n;
1388 add_group_liberties(&g, n);
1390 } else if (n->liberties == 1) {
1391 add_liberty_unchecked(&g, m + BOTTOM);
1392 *caps = true;
1394 if (n->stones.count > 1) {
1395 bool found = false;
1396 for (u8 k = 0; k < opt_neighbors_n; ++k) {
1397 if (opt_neighbors[k] == n) {
1398 found = true;
1399 break;
1403 if (!found) {
1404 opt_neighbors[opt_neighbors_n++] = n;
1410 if (g.liberties > 2) {
1411 return 2;
1414 for (u8 i = 0; i < opt_neighbors_n; ++i) {
1415 if (are_neighbors(cb, opt_neighbors[i], neighbors, neighbors_n)) {
1416 ++probable_libs;
1420 u8 libs = probable_libs + g.liberties - 1;
1421 return MIN(libs, 2);
1425 Calculates if playing at the designated position is legal and safe.
1426 Does not test ko.
1427 RETURNS 0 for illegal, 1 for placed in atari, 2 for safe to play
1429 u8 safe_to_play(
1430 cfg_board * cb,
1431 bool is_black,
1432 move m
1434 assert(verify_cfg_board(cb));
1435 assert(cb->p[m] == EMPTY);
1436 assert(cb->g[m] == NULL);
1438 if (cb->white_neighbors4[m] + cb->black_neighbors4[m] + out_neighbors4[m] < 3) {
1439 return 2;
1442 group * n;
1443 /* warning: some fields are not initialized because they're not used */
1444 group g;
1445 g.liberties = 0;
1446 g.liberties_min_coord = TOTAL_BOARD_SIZ;
1447 memset(g.ls, 0, LIB_BITMAP_SIZ);
1448 add_liberty_unchecked(&g, m);
1450 u8 probable_libs = 0;
1451 group * opt_neighbors[4];
1452 u8 opt_neighbors_n = 0;
1453 group * neighbors[4];
1454 u8 neighbors_n = 0;
1456 if (!border_left[m]) {
1457 n = cb->g[m + LEFT];
1459 if (n == NULL) {
1460 add_liberty_unchecked(&g, m + LEFT);
1461 } else if (n->is_black == is_black) {
1462 neighbors[neighbors_n++] = n;
1463 add_group_liberties(&g, n);
1464 } else if (n->liberties == 1) {
1465 add_liberty_unchecked(&g, m + LEFT);
1467 if (n->stones.count > 1) {
1468 opt_neighbors[opt_neighbors_n++] = n;
1473 if (!border_right[m]) {
1474 n = cb->g[m + RIGHT];
1476 if (n == NULL) {
1477 add_liberty(&g, m + RIGHT);
1478 } else if (n->is_black == is_black) {
1479 bool found = false;
1480 for (u8 k = 0; k < neighbors_n; ++k) {
1481 if (neighbors[k] == n) {
1482 found = true;
1483 break;
1487 if (!found) {
1488 neighbors[neighbors_n++] = n;
1489 add_group_liberties(&g, n);
1491 } else if (n->liberties == 1) {
1492 add_liberty_unchecked(&g, m + RIGHT);
1494 if (n->stones.count > 1) {
1495 bool found = false;
1496 for (u8 k = 0; k < opt_neighbors_n; ++k) {
1497 if (opt_neighbors[k] == n) {
1498 found = true;
1499 break;
1503 if (!found) {
1504 opt_neighbors[opt_neighbors_n++] = n;
1510 if (!border_top[m]) {
1511 n = cb->g[m + TOP];
1513 if (n == NULL) {
1514 add_liberty(&g, m + TOP);
1515 } else if (n->is_black == is_black) {
1516 bool found = false;
1517 for (u8 k = 0; k < neighbors_n; ++k) {
1518 if (neighbors[k] == n) {
1519 found = true;
1520 break;
1524 if (!found) {
1525 neighbors[neighbors_n++] = n;
1526 add_group_liberties(&g, n);
1528 } else if (n->liberties == 1) {
1529 add_liberty_unchecked(&g, m + TOP);
1531 if (n->stones.count > 1) {
1532 bool found = false;
1533 for (u8 k = 0; k < opt_neighbors_n; ++k) {
1534 if (opt_neighbors[k] == n) {
1535 found = true;
1536 break;
1540 if (!found) {
1541 opt_neighbors[opt_neighbors_n++] = n;
1547 if (!border_bottom[m]) {
1548 n = cb->g[m + BOTTOM];
1550 if (n == NULL) {
1551 add_liberty(&g, m + BOTTOM);
1552 } else if (n->is_black == is_black) {
1553 bool found = false;
1554 for (u8 k = 0; k < neighbors_n; ++k) {
1555 if (neighbors[k] == n) {
1556 found = true;
1557 break;
1561 if (!found) {
1562 neighbors[neighbors_n++] = n;
1563 add_group_liberties(&g, n);
1565 } else if (n->liberties == 1) {
1566 add_liberty_unchecked(&g, m + BOTTOM);
1568 if (n->stones.count > 1) {
1569 bool found = false;
1570 for (u8 k = 0; k < opt_neighbors_n; ++k) {
1571 if (opt_neighbors[k] == n) {
1572 found = true;
1573 break;
1577 if (!found) {
1578 opt_neighbors[opt_neighbors_n++] = n;
1584 if (g.liberties > 2) {
1585 return 2;
1588 for (u8 i = 0; i < opt_neighbors_n; ++i) {
1589 if (are_neighbors(cb, opt_neighbors[i], neighbors, neighbors_n)) {
1590 ++probable_libs;
1594 u8 libs = probable_libs + g.liberties - 1;
1595 return MIN(libs, 2);
1599 Tests if a play captures any opponent stone.
1600 RETURNS true if any opponent stone is captured
1602 bool caps_after_play(
1603 const cfg_board * cb,
1604 bool is_black,
1605 move m
1607 assert(verify_cfg_board(cb));
1608 assert(cb->p[m] == EMPTY);
1609 assert(cb->g[m] == NULL);
1611 u8 on4 = is_black ? cb->white_neighbors4[m] : cb->black_neighbors4[m];
1612 if (on4 == 0) {
1613 return false;
1616 group * n;
1618 if (!border_left[m] && (n = cb->g[m + LEFT]) != NULL && n->is_black != is_black && n->liberties == 1) {
1619 return true;
1621 if (!border_right[m] && (n = cb->g[m + RIGHT]) != NULL && n->is_black != is_black && n->liberties == 1) {
1622 return true;
1624 if (!border_top[m] && (n = cb->g[m + TOP]) != NULL && n->is_black != is_black && n->liberties == 1) {
1625 return true;
1627 if (!border_bottom[m] && (n = cb->g[m + BOTTOM]) != NULL && n->is_black != is_black && n->liberties == 1) {
1628 return true;
1631 return false;
1635 RETURNS true if play is valid (validating ko rule)
1637 bool can_play(
1638 const cfg_board * cb,
1639 bool is_black,
1640 move m
1642 assert(verify_cfg_board(cb));
1643 if (cb->p[m] != EMPTY) {
1644 return false;
1647 if (cb->black_neighbors4[m] + cb->white_neighbors4[m] + out_neighbors4[m] < 4) {
1648 return true;
1651 if (ko_violation(cb, m)) {
1652 return false;
1655 assert(cb->g[m] == NULL);
1656 group * n;
1658 if (!border_left[m]) {
1659 n = cb->g[m + LEFT];
1661 if (n == NULL) {
1662 return true;
1663 } else if (n->is_black == is_black) {
1664 if (n->liberties != 1) {
1665 return true;
1667 } else if (n->liberties == 1) {
1668 return true;
1672 if (!border_right[m]) {
1673 n = cb->g[m + RIGHT];
1675 if (n == NULL) {
1676 return true;
1677 } else if (n->is_black == is_black) {
1678 if (n->liberties != 1) {
1679 return true;
1681 } else if (n->liberties == 1) {
1682 return true;
1686 if (!border_top[m]) {
1687 n = cb->g[m + TOP];
1689 if (n == NULL) {
1690 return true;
1691 } else if (n->is_black == is_black) {
1692 if (n->liberties != 1) {
1693 return true;
1695 } else if (n->liberties == 1) {
1696 return true;
1700 if (!border_bottom[m]) {
1701 n = cb->g[m + BOTTOM];
1703 if (n == NULL) {
1704 return true;
1705 } else if (n->is_black == is_black) {
1706 if (n->liberties != 1) {
1707 return true;
1709 } else if (n->liberties == 1) {
1710 return true;
1714 return false;
1718 RETURNS true if play is valid (ignoring ko rule)
1720 bool can_play_ignoring_ko(
1721 const cfg_board * cb,
1722 bool is_black,
1723 move m
1725 assert(verify_cfg_board(cb));
1726 if (cb->p[m] != EMPTY) {
1727 return false;
1730 if (cb->black_neighbors4[m] + cb->white_neighbors4[m] + out_neighbors4[m] < 4) {
1731 return true;
1734 assert(cb->g[m] == NULL);
1735 group * n;
1737 if (!border_left[m]) {
1738 n = cb->g[m + LEFT];
1740 if (n == NULL) {
1741 return true;
1742 } else if (n->is_black == is_black) {
1743 if (n->liberties != 1) {
1744 return true;
1746 } else {
1747 if (n->liberties == 1) {
1748 return true;
1753 if (!border_right[m]) {
1754 n = cb->g[m + RIGHT];
1756 if (n == NULL) {
1757 return true;
1758 } else if (n->is_black == is_black) {
1759 if (n->liberties != 1) {
1760 return true;
1762 } else {
1763 if (n->liberties == 1) {
1764 return true;
1769 if (!border_top[m]) {
1770 n = cb->g[m + TOP];
1772 if (n == NULL) {
1773 return true;
1774 } else if (n->is_black == is_black) {
1775 if (n->liberties != 1) {
1776 return true;
1778 } else {
1779 if (n->liberties == 1) {
1780 return true;
1785 if (!border_bottom[m]) {
1786 n = cb->g[m + BOTTOM];
1788 if (n == NULL) {
1789 return true;
1790 } else if (n->is_black == is_black) {
1791 if (n->liberties != 1) {
1792 return true;
1794 } else {
1795 if (n->liberties == 1) {
1796 return true;
1801 return false;
1806 Frees the structure information dynamically allocated (not the actual cfg_board
1807 structure).
1809 void cfg_board_free(
1810 cfg_board * cb
1812 assert(verify_cfg_board(cb));
1814 for (u8 i = 0; i < cb->unique_groups_count; ++i) {
1815 just_delloc_group(cb->g[cb->unique_groups[i]]);
1820 Print structure information for debugging.
1822 void fprint_cfg_board(
1823 FILE * fp,
1824 const cfg_board * cb
1826 char * s = alloc();
1827 board_to_string(s, cb->p, cb->last_played, cb->last_eaten);
1828 fprintf(fp, "\nBOARD\n%s", s);
1829 release(s);
1831 fprintf(fp, "\nSTONES\n");
1832 for (move m = 0; m < TOTAL_BOARD_SIZ; ++m) {
1833 if (cb->g[m] == NULL) {
1834 fprintf(fp, " %c", EMPTY_STONE_CHAR);
1835 } else {
1836 fprintf(fp, " %3u", cb->g[m]->stones.count);
1839 if (((m + 1) % BOARD_SIZ) == 0) {
1840 fprintf(fp, "\n");
1844 fprintf(fp, "\nLIBERTIES\n");
1845 for (move m = 0; m < TOTAL_BOARD_SIZ; ++m) {
1846 if (cb->g[m] == NULL) {
1847 fprintf(fp, " %c", EMPTY_STONE_CHAR);
1848 } else {
1849 fprintf(fp, " %3u", cb->g[m]->liberties);
1852 if (((m + 1) % BOARD_SIZ) == 0) {
1853 fprintf(fp, "\n");
1857 fprintf(fp, "\nUNIQUES %u\n", cb->unique_groups_count);
1858 for (move m = 0; m < TOTAL_BOARD_SIZ; ++m) {
1859 if (cb->g[m] == NULL) {
1860 fprintf(fp, " %c", EMPTY_STONE_CHAR);
1861 } else {
1862 fprintf(fp, " %3u", cb->g[m]->unique_groups_idx);
1865 if (((m + 1) % BOARD_SIZ) == 0) {
1866 fprintf(fp, "\n");
1870 fprintf(fp, "\nHASHES %u\n", cb->unique_groups_count);
1871 for (move m = 0; m < TOTAL_BOARD_SIZ; ++m) {
1872 fprintf(fp, " %04x", cb->hash[m]);
1874 if (((m + 1) % BOARD_SIZ) == 0) {
1875 fprintf(fp, "\n");
1882 Verify the integrity of a CFG board structure.
1884 bool verify_cfg_board(
1885 const cfg_board * cb
1887 if (cb == NULL) {
1888 fprintf(stderr, "error: verify_cfg_board: null reference\n");
1889 return false;
1892 for (move m = 0; m < TOTAL_BOARD_SIZ; ++m) {
1893 if (cb->p[m] != EMPTY && cb->p[m] != BLACK_STONE && cb->p[m] != WHITE_STONE) {
1894 fprintf(stderr, "error: verify_cfg_board: illegal intersection color\n");
1895 return false;
1898 if (cb->black_neighbors4[m] > 4) {
1899 fprintf(stderr, "error: verify_cfg_board: illegal neighbor count (1)\n");
1900 return false;
1903 if (cb->white_neighbors4[m] > 4) {
1904 fprintf(stderr, "error: verify_cfg_board: illegal neighbor count (2)\n");
1905 return false;
1908 if (cb->black_neighbors8[m] > 8) {
1909 fprintf(stderr, "error: verify_cfg_board: illegal neighbor count (3)\n");
1910 return false;
1913 if (cb->white_neighbors8[m] > 8) {
1914 fprintf(stderr, "error: verify_cfg_board: illegal neighbor count (4)\n");
1915 return false;
1918 if (cb->black_neighbors4[m] + cb->white_neighbors4[m] + out_neighbors4[m] > 4) {
1919 fprintf(stderr, "error: verify_cfg_board: illegal total neighbor count (1)\n");
1920 return false;
1923 if (cb->black_neighbors8[m] + cb->white_neighbors8[m] + out_neighbors8[m] > 8) {
1924 fprintf(stderr, "error: verify_cfg_board: illegal total neighbor count (2)\n");
1925 return false;
1928 if ((cb->p[m] == EMPTY) != (cb->g[m] == NULL)) {
1929 fprintf(stderr, "error: verify_cfg_board: mismatch between board and group\n");
1930 return false;
1933 if (cb->g[m] != NULL) {
1934 group * g = cb->g[m];
1936 if (g->is_black != (cb->p[m] == BLACK_STONE)) {
1937 fprintf(stderr, "error: verify_cfg_board: group color mismatch\n");
1938 return false;
1941 if (g->liberties == 0) {
1942 fprintf(stderr, "error: verify_cfg_board: zero number of liberties\n");
1943 return false;
1946 if (cb->unique_groups[g->unique_groups_idx] != g->stones.coord[0]) {
1947 fprintf(stderr, "error: verify_cfg_board: unique groups linking error\n");
1948 return false;
1951 if (g->liberties > 0 && g->liberties_min_coord >= BOARD_SIZ * BOARD_SIZ) {
1952 fprintf(stderr, "error: verify_cfg_board: illegal value of 1st liberty\n");
1953 return false;
1956 if (g->stones.count == 0) {
1957 fprintf(stderr, "error: verify_cfg_board: illegal number of stones (0)\n");
1958 return false;
1961 if (g->stones.count > TOTAL_BOARD_SIZ) {
1962 fprintf(stderr, "error: verify_cfg_board: illegal number of stones\n");
1963 return false;
1966 for (move n = 0; n < g->stones.count; ++n) {
1967 move s = g->stones.coord[n];
1969 if (cb->p[s] == EMPTY) {
1970 fprintf(stderr, "error: verify_cfg_board: group actually empty\n");
1971 return false;
1974 if (g->is_black != (cb->p[s] == BLACK_STONE)) {
1975 fprintf(stderr, "error: verify_cfg_board: stone color mismatch\n");
1976 return false;
1979 if (g != cb->g[s]) {
1980 fprintf(stderr, "error: verify_cfg_board: stone and links mismatch\n");
1981 return false;
1985 if (g->neighbors_count > MAX_NEIGHBORS) {
1986 fprintf(stderr, "error: verify_cfg_board: illegal number of neighbors\n");
1987 return false;
1990 for (u8 n = 0; n < g->neighbors_count; ++n) {
1991 for (u8 k = 0; k < n; ++k) {
1992 if (g->neighbors[k] == g->neighbors[n]) {
1993 fprintf(stderr, "error: verify_cfg_board: neighbor mismatch\n");
1994 return false;
2001 if (!is_board_move(cb->last_eaten) && cb->last_eaten != NONE) {
2002 fprintf(stderr, "error: verify_cfg_board: illegal last eaten value\n");
2003 return false;
2006 if (!is_board_move(cb->last_played) && cb->last_played != NONE && cb->last_played != PASS) {
2007 fprintf(stderr, "error: verify_cfg_board: illegal last played value\n");
2008 return false;
2011 if (cb->empty.count > TOTAL_BOARD_SIZ) {
2012 fprintf(stderr, "error: verify_cfg_board: illegal number of empty points (%u)\n", cb->empty.count);
2013 return false;
2016 for (move m = 0; m < cb->empty.count; ++m) {
2017 if (!is_board_move(m)) {
2018 fprintf(stderr, "error: verify_cfg_board: illegal empty intersection value\n");
2019 return false;
2023 return true;