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
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.
32 #include "cfg_board.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];
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();
60 if (saved_nodes
[thread
] != NULL
) {
61 ret
= saved_nodes
[thread
];
62 saved_nodes
[thread
] = saved_nodes
[thread
]->next
;
64 ret
= malloc(sizeof(group
));
67 flog_crit("cfg", "system out of memory");
74 static void just_delloc_group(
77 int thread
= omp_get_thread_num();
78 g
->next
= saved_nodes
[thread
];
79 saved_nodes
[thread
] = g
;
82 static void delloc_group(
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(
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;
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
];
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(
146 assert(neighbors_side
[m
].count
< 5);
147 assert(neighbors_diag
[m
].count
< 5);
148 assert(cb
->p
[m
] > 0);
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];
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(
187 for (u8 i
= 0; i
< g
->neighbors_count
; ++i
) {
188 if (g
->neighbors
[i
] == n
->stones
.coord
[0]) {
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(
201 u8 mask
= (1 << (m
% 8));
203 if ((g
->ls
[m
/ 8] & mask
) == 0) {
204 g
->ls
[m
/ 8] |= mask
;
207 if (m
< g
->liberties_min_coord
) {
208 g
->liberties_min_coord
= m
;
213 static void add_liberty_unchecked(
217 u8 mask
= (1 << (m
% 8));
218 g
->ls
[m
/ 8] |= mask
;
221 if (m
< g
->liberties_min_coord
) {
222 g
->liberties_min_coord
= m
;
226 static void rem_liberty_unchecked(
230 u8 mask
= (1 << (m
% 8));
231 g
->ls
[m
/ 8] &= ~mask
;
235 static void rem_neighbor(
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
--;
247 flog_crit("cfg", "CFG group neighbor not found");
250 static void unite_groups(
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
);
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
);
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(
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
);
333 group
* neighbors
[4];
336 if (!border_left
[m
]) {
337 if ((n
= cb
->g
[m
+ LEFT
]) == NULL
) {
338 add_liberty(cb
->g
[m
], m
+ LEFT
);
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
]);
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
);
356 for (u8 i
= 0; i
< neighbors_n
; ++i
) {
357 if (neighbors
[i
] == n
) {
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
]);
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
);
381 for (u8 i
= 0; i
< neighbors_n
; ++i
) {
382 if (neighbors
[i
] == n
) {
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
]);
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
);
406 for (u8 i
= 0; i
< neighbors_n
; ++i
) {
407 if (neighbors
[i
] == n
) {
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
]);
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.
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
*));
455 cb
->unique_groups_count
= 0;
457 for (move m
= 0; m
< TOTAL_BOARD_SIZ
; ++m
) {
458 cb
->empty
.coord
[cb
->empty
.count
] = m
;
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.
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
;
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
];
521 assert(verify_cfg_board(dst
));
525 static void add_liberties_to_neighbors(
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(
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
);
560 add_liberties_to_neighbors(cb
, m
, own
);
562 cb
->empty
.coord
[cb
->empty
.count
] = m
;
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
];
581 static void cfg_board_kill_group2(
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
);
596 add_liberties_to_neighbors(cb
, m
, own
);
598 cb
->empty
.coord
[cb
->empty
.count
] = m
;
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
];
617 static void cfg_board_kill_group3(
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
);
632 stones_removed
[m
] = true;
633 add_liberties_to_neighbors(cb
, m
, own
);
635 cb
->empty
.coord
[cb
->empty
.count
] = m
;
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
];
659 Apply a passing turn.
664 cb
->last_played
= PASS
;
665 cb
->last_eaten
= NONE
;
669 Assume play is legal and update the structure, capturing
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
;
685 move one_stone_captured
= NONE
;
689 add_stone(cb
, is_black
, m
);
691 u8 n8
= is_black
? cb
->white_neighbors4
[m
] : cb
->black_neighbors4
[m
];
693 if (!border_left
[m
]) {
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
]) {
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
;
735 cb
->last_eaten
= one_stone_captured
;
737 cb
->last_eaten
= NONE
;
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
) {
746 cb
->empty
.coord
[k
] = cb
->empty
.coord
[cb
->empty
.count
];
753 Assume play is legal and update the structure, capturing
755 Also updates a Zobrist hash value.
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
;
771 move one_stone_captured
= NONE
;
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
];
780 if (!border_left
[m
]) {
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
]) {
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
;
822 cb
->last_eaten
= one_stone_captured
;
824 cb
->last_eaten
= NONE
;
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
) {
833 cb
->empty
.coord
[k
] = cb
->empty
.coord
[cb
->empty
.count
];
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
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
;
861 move one_stone_captured
= NONE
;
865 add_stone(cb
, is_black
, m
);
867 u8 n8
= is_black
? cb
->white_neighbors4
[m
] : cb
->black_neighbors4
[m
];
869 if (!border_left
[m
]) {
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
]) {
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
;
911 cb
->last_eaten
= one_stone_captured
;
913 cb
->last_eaten
= NONE
;
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
) {
925 cb
->empty
.coord
[k
] = cb
->empty
.coord
[cb
->empty
.count
];
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
,
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
) {
966 static void cfg_board_give_neighbors_libs(
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
1002 const cfg_board
* cb
,
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
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
;
1028 Calculates the liberties after playing and the number of stones captured.
1030 RETURNS number of liberties after play
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) {
1044 return 4 - out_neighbors4
[m
];
1048 /* warning: some fields are not initialized because they're not used */
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];
1059 if (!border_left
[m
]) {
1060 n
= cb
->g
[m
+ LEFT
];
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
];
1071 add_liberty_unchecked(&g
, m
+ RIGHT
);
1072 } else if (n
->is_black
== is_black
) {
1074 for (u8 k
= 0; k
< neighbors_n
; ++k
) {
1075 if (neighbors
[k
] == n
) {
1082 neighbors
[neighbors_n
++] = n
;
1087 if (!border_top
[m
]) {
1090 add_liberty_unchecked(&g
, m
+ TOP
);
1091 } else if (n
->is_black
== is_black
) {
1093 for (u8 k
= 0; k
< neighbors_n
; ++k
)
1094 if (neighbors
[k
] == n
) {
1100 neighbors
[neighbors_n
++] = n
;
1105 if (!border_bottom
[m
]) {
1106 n
= cb
->g
[m
+ BOTTOM
];
1108 add_liberty_unchecked(&g
, m
+ BOTTOM
);
1109 } else if (n
->is_black
== is_black
) {
1111 for (u8 k
= 0; k
< neighbors_n
; ++k
)
1112 if (neighbors
[k
] == n
) {
1118 neighbors
[neighbors_n
++] = n
;
1123 u8 on4
= is_black
? cb
->white_neighbors4
[m
] : cb
->black_neighbors4
[m
];
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
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
);
1169 for (u8 k
= 0; k
< opt_neighbors_n
; ++k
) {
1170 if (opt_neighbors
[k
] == n
) {
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
]) {
1187 if (n
!= NULL
&& n
->is_black
!= is_black
&& n
->liberties
== 1) {
1188 add_liberty_unchecked(&g
, m
+ TOP
);
1191 for (u8 k
= 0; k
< opt_neighbors_n
; ++k
) {
1192 if (opt_neighbors
[k
] == n
) {
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
);
1213 for (u8 k
= 0; k
< opt_neighbors_n
; ++k
) {
1214 if (opt_neighbors
[k
] == n
) {
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
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
];
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.
1246 RETURNS 0 for illegal, 1 for placed in atari, 2 for safe to play
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) {
1265 /* warning: some fields are not initialized because they're not used */
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];
1278 if (!border_left
[m
]) {
1279 n
= cb
->g
[m
+ LEFT
];
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
);
1290 if (n
->stones
.count
> 1) {
1291 opt_neighbors
[opt_neighbors_n
++] = n
;
1296 if (!border_right
[m
]) {
1297 n
= cb
->g
[m
+ RIGHT
];
1300 add_liberty(&g
, m
+ RIGHT
);
1301 } else if (n
->is_black
== is_black
) {
1303 for (u8 k
= 0; k
< neighbors_n
; ++k
) {
1304 if (neighbors
[k
] == n
) {
1311 neighbors
[neighbors_n
++] = n
;
1312 add_group_liberties(&g
, n
);
1314 } else if (n
->liberties
== 1) {
1315 add_liberty_unchecked(&g
, m
+ RIGHT
);
1318 if (n
->stones
.count
> 1) {
1320 for (u8 k
= 0; k
< opt_neighbors_n
; ++k
) {
1321 if (opt_neighbors
[k
] == n
) {
1328 opt_neighbors
[opt_neighbors_n
++] = n
;
1334 if (!border_top
[m
]) {
1338 add_liberty(&g
, m
+ TOP
);
1339 } else if (n
->is_black
== is_black
) {
1341 for (u8 k
= 0; k
< neighbors_n
; ++k
) {
1342 if (neighbors
[k
] == n
) {
1349 neighbors
[neighbors_n
++] = n
;
1350 add_group_liberties(&g
, n
);
1352 } else if (n
->liberties
== 1) {
1353 add_liberty_unchecked(&g
, m
+ TOP
);
1356 if (n
->stones
.count
> 1) {
1358 for (u8 k
= 0; k
< opt_neighbors_n
; ++k
) {
1359 if (opt_neighbors
[k
] == n
) {
1366 opt_neighbors
[opt_neighbors_n
++] = n
;
1372 if (!border_bottom
[m
]) {
1373 n
= cb
->g
[m
+ BOTTOM
];
1376 add_liberty(&g
, m
+ BOTTOM
);
1377 } else if (n
->is_black
== is_black
) {
1379 for (u8 k
= 0; k
< neighbors_n
; ++k
) {
1380 if (neighbors
[k
] == n
) {
1387 neighbors
[neighbors_n
++] = n
;
1388 add_group_liberties(&g
, n
);
1390 } else if (n
->liberties
== 1) {
1391 add_liberty_unchecked(&g
, m
+ BOTTOM
);
1394 if (n
->stones
.count
> 1) {
1396 for (u8 k
= 0; k
< opt_neighbors_n
; ++k
) {
1397 if (opt_neighbors
[k
] == n
) {
1404 opt_neighbors
[opt_neighbors_n
++] = n
;
1410 if (g
.liberties
> 2) {
1414 for (u8 i
= 0; i
< opt_neighbors_n
; ++i
) {
1415 if (are_neighbors(cb
, opt_neighbors
[i
], neighbors
, neighbors_n
)) {
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.
1427 RETURNS 0 for illegal, 1 for placed in atari, 2 for safe to play
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) {
1443 /* warning: some fields are not initialized because they're not used */
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];
1456 if (!border_left
[m
]) {
1457 n
= cb
->g
[m
+ LEFT
];
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
];
1477 add_liberty(&g
, m
+ RIGHT
);
1478 } else if (n
->is_black
== is_black
) {
1480 for (u8 k
= 0; k
< neighbors_n
; ++k
) {
1481 if (neighbors
[k
] == n
) {
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) {
1496 for (u8 k
= 0; k
< opt_neighbors_n
; ++k
) {
1497 if (opt_neighbors
[k
] == n
) {
1504 opt_neighbors
[opt_neighbors_n
++] = n
;
1510 if (!border_top
[m
]) {
1514 add_liberty(&g
, m
+ TOP
);
1515 } else if (n
->is_black
== is_black
) {
1517 for (u8 k
= 0; k
< neighbors_n
; ++k
) {
1518 if (neighbors
[k
] == n
) {
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) {
1533 for (u8 k
= 0; k
< opt_neighbors_n
; ++k
) {
1534 if (opt_neighbors
[k
] == n
) {
1541 opt_neighbors
[opt_neighbors_n
++] = n
;
1547 if (!border_bottom
[m
]) {
1548 n
= cb
->g
[m
+ BOTTOM
];
1551 add_liberty(&g
, m
+ BOTTOM
);
1552 } else if (n
->is_black
== is_black
) {
1554 for (u8 k
= 0; k
< neighbors_n
; ++k
) {
1555 if (neighbors
[k
] == n
) {
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) {
1570 for (u8 k
= 0; k
< opt_neighbors_n
; ++k
) {
1571 if (opt_neighbors
[k
] == n
) {
1578 opt_neighbors
[opt_neighbors_n
++] = n
;
1584 if (g
.liberties
> 2) {
1588 for (u8 i
= 0; i
< opt_neighbors_n
; ++i
) {
1589 if (are_neighbors(cb
, opt_neighbors
[i
], neighbors
, neighbors_n
)) {
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
,
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
];
1618 if (!border_left
[m
] && (n
= cb
->g
[m
+ LEFT
]) != NULL
&& n
->is_black
!= is_black
&& n
->liberties
== 1) {
1621 if (!border_right
[m
] && (n
= cb
->g
[m
+ RIGHT
]) != NULL
&& n
->is_black
!= is_black
&& n
->liberties
== 1) {
1624 if (!border_top
[m
] && (n
= cb
->g
[m
+ TOP
]) != NULL
&& n
->is_black
!= is_black
&& n
->liberties
== 1) {
1627 if (!border_bottom
[m
] && (n
= cb
->g
[m
+ BOTTOM
]) != NULL
&& n
->is_black
!= is_black
&& n
->liberties
== 1) {
1635 RETURNS true if play is valid (validating ko rule)
1638 const cfg_board
* cb
,
1642 assert(verify_cfg_board(cb
));
1643 if (cb
->p
[m
] != EMPTY
) {
1647 if (cb
->black_neighbors4
[m
] + cb
->white_neighbors4
[m
] + out_neighbors4
[m
] < 4) {
1651 if (ko_violation(cb
, m
)) {
1655 assert(cb
->g
[m
] == NULL
);
1658 if (!border_left
[m
]) {
1659 n
= cb
->g
[m
+ LEFT
];
1663 } else if (n
->is_black
== is_black
) {
1664 if (n
->liberties
!= 1) {
1667 } else if (n
->liberties
== 1) {
1672 if (!border_right
[m
]) {
1673 n
= cb
->g
[m
+ RIGHT
];
1677 } else if (n
->is_black
== is_black
) {
1678 if (n
->liberties
!= 1) {
1681 } else if (n
->liberties
== 1) {
1686 if (!border_top
[m
]) {
1691 } else if (n
->is_black
== is_black
) {
1692 if (n
->liberties
!= 1) {
1695 } else if (n
->liberties
== 1) {
1700 if (!border_bottom
[m
]) {
1701 n
= cb
->g
[m
+ BOTTOM
];
1705 } else if (n
->is_black
== is_black
) {
1706 if (n
->liberties
!= 1) {
1709 } else if (n
->liberties
== 1) {
1718 RETURNS true if play is valid (ignoring ko rule)
1720 bool can_play_ignoring_ko(
1721 const cfg_board
* cb
,
1725 assert(verify_cfg_board(cb
));
1726 if (cb
->p
[m
] != EMPTY
) {
1730 if (cb
->black_neighbors4
[m
] + cb
->white_neighbors4
[m
] + out_neighbors4
[m
] < 4) {
1734 assert(cb
->g
[m
] == NULL
);
1737 if (!border_left
[m
]) {
1738 n
= cb
->g
[m
+ LEFT
];
1742 } else if (n
->is_black
== is_black
) {
1743 if (n
->liberties
!= 1) {
1747 if (n
->liberties
== 1) {
1753 if (!border_right
[m
]) {
1754 n
= cb
->g
[m
+ RIGHT
];
1758 } else if (n
->is_black
== is_black
) {
1759 if (n
->liberties
!= 1) {
1763 if (n
->liberties
== 1) {
1769 if (!border_top
[m
]) {
1774 } else if (n
->is_black
== is_black
) {
1775 if (n
->liberties
!= 1) {
1779 if (n
->liberties
== 1) {
1785 if (!border_bottom
[m
]) {
1786 n
= cb
->g
[m
+ BOTTOM
];
1790 } else if (n
->is_black
== is_black
) {
1791 if (n
->liberties
!= 1) {
1795 if (n
->liberties
== 1) {
1806 Frees the structure information dynamically allocated (not the actual cfg_board
1809 void cfg_board_free(
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(
1824 const cfg_board
* cb
1827 board_to_string(s
, cb
->p
, cb
->last_played
, cb
->last_eaten
);
1828 fprintf(fp
, "\nBOARD\n%s", 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
);
1836 fprintf(fp
, " %3u", cb
->g
[m
]->stones
.count
);
1839 if (((m
+ 1) % BOARD_SIZ
) == 0) {
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
);
1849 fprintf(fp
, " %3u", cb
->g
[m
]->liberties
);
1852 if (((m
+ 1) % BOARD_SIZ
) == 0) {
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
);
1862 fprintf(fp
, " %3u", cb
->g
[m
]->unique_groups_idx
);
1865 if (((m
+ 1) % BOARD_SIZ
) == 0) {
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) {
1882 Verify the integrity of a CFG board structure.
1884 bool verify_cfg_board(
1885 const cfg_board
* cb
1888 fprintf(stderr
, "error: verify_cfg_board: null reference\n");
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");
1898 if (cb
->black_neighbors4
[m
] > 4) {
1899 fprintf(stderr
, "error: verify_cfg_board: illegal neighbor count (1)\n");
1903 if (cb
->white_neighbors4
[m
] > 4) {
1904 fprintf(stderr
, "error: verify_cfg_board: illegal neighbor count (2)\n");
1908 if (cb
->black_neighbors8
[m
] > 8) {
1909 fprintf(stderr
, "error: verify_cfg_board: illegal neighbor count (3)\n");
1913 if (cb
->white_neighbors8
[m
] > 8) {
1914 fprintf(stderr
, "error: verify_cfg_board: illegal neighbor count (4)\n");
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");
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");
1928 if ((cb
->p
[m
] == EMPTY
) != (cb
->g
[m
] == NULL
)) {
1929 fprintf(stderr
, "error: verify_cfg_board: mismatch between board and group\n");
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");
1941 if (g
->liberties
== 0) {
1942 fprintf(stderr
, "error: verify_cfg_board: zero number of liberties\n");
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");
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");
1956 if (g
->stones
.count
== 0) {
1957 fprintf(stderr
, "error: verify_cfg_board: illegal number of stones (0)\n");
1961 if (g
->stones
.count
> TOTAL_BOARD_SIZ
) {
1962 fprintf(stderr
, "error: verify_cfg_board: illegal number of stones\n");
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");
1974 if (g
->is_black
!= (cb
->p
[s
] == BLACK_STONE
)) {
1975 fprintf(stderr
, "error: verify_cfg_board: stone color mismatch\n");
1979 if (g
!= cb
->g
[s
]) {
1980 fprintf(stderr
, "error: verify_cfg_board: stone and links mismatch\n");
1985 if (g
->neighbors_count
> MAX_NEIGHBORS
) {
1986 fprintf(stderr
, "error: verify_cfg_board: illegal number of neighbors\n");
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");
2001 if (!is_board_move(cb
->last_eaten
) && cb
->last_eaten
!= NONE
) {
2002 fprintf(stderr
, "error: verify_cfg_board: illegal last eaten value\n");
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");
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
);
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");