12 #define profiling_noinline __attribute__((noinline))
14 #define profiling_noinline
17 #define gi_granularity 4
18 #define gi_allocsize(gids) ((1 << gi_granularity) + ((gids) >> gi_granularity) * (1 << gi_granularity))
22 board_quick_cmp(struct board
*b1
, struct board
*b2
)
24 if (b1
->size
!= b2
->size
||
25 b1
->size2
!= b2
->size2
||
26 b1
->bits2
!= b2
->bits2
||
27 b1
->captures
[S_BLACK
] != b2
->captures
[S_BLACK
] ||
28 b1
->captures
[S_WHITE
] != b2
->captures
[S_WHITE
] ||
29 b1
->moves
!= b2
->moves
) {
30 fprintf(stderr
, "differs in main vars\n");
33 if (move_cmp(&b1
->last_move
, &b2
->last_move
) ||
34 move_cmp(&b1
->last_move2
, &b2
->last_move2
)) {
35 fprintf(stderr
, "differs in last_move\n");
38 if (move_cmp(&b1
->ko
, &b2
->ko
) ||
39 move_cmp(&b1
->last_ko
, &b2
->last_ko
) ||
40 b1
->last_ko_age
!= b2
->last_ko_age
) {
41 fprintf(stderr
, "differs in ko\n");
45 int bsize
= board_size2(b1
) * sizeof(*b1
->b
);
46 int gsize
= board_size2(b1
) * sizeof(*b1
->g
);
47 //int fsize = board_size2(b1) * sizeof(*b1->f);
48 int nsize
= board_size2(b1
) * sizeof(*b1
->n
);
49 int psize
= board_size2(b1
) * sizeof(*b1
->p
);
50 //int hsize = board_size2(b1) * 2 * sizeof(*b1->h);
51 int gisize
= board_size2(b1
) * sizeof(*b1
->gi
);
52 //int csize = board_size2(board) * sizeof(*b1->c);
53 //int ssize = board_size2(board) * sizeof(*b1->spathash);
54 //int p3size = board_size2(board) * sizeof(*b1->pat3);
55 //int tsize = board_size2(board) * sizeof(*b1->t);
56 //int tqsize = board_size2(board) * sizeof(*b1->t);
58 //int cdsize = board_size2(b1) * sizeof(*b1->coord);
60 if (memcmp(b1
->b
, b2
->b
, bsize
)) {
61 fprintf(stderr
, "differs in b\n"); return 1; }
62 if (memcmp(b1
->g
, b2
->g
, gsize
)) {
63 fprintf(stderr
, "differs in g\n"); return 1; }
64 if (memcmp(b1
->n
, b2
->n
, nsize
)) {
65 fprintf(stderr
, "differs in n\n"); return 1; }
66 if (memcmp(b1
->p
, b2
->p
, psize
)) {
67 fprintf(stderr
, "differs in p\n"); return 1; }
68 if (memcmp(b1
->gi
, b2
->gi
, gisize
)) {
69 fprintf(stderr
, "differs in gi\n"); return 1; }
76 board_group_find_extra_libs(struct board
*board
, group_t group
, struct group
*gi
, coord_t avoid
)
78 /* Add extra liberty from the board to our liberty list. */
79 unsigned char watermark
[board_size2(board
) / 8];
80 memset(watermark
, 0, sizeof(watermark
));
81 #define watermark_get(c) (watermark[c >> 3] & (1 << (c & 7)))
82 #define watermark_set(c) watermark[c >> 3] |= (1 << (c & 7))
84 for (int i
= 0; i
< GROUP_KEEP_LIBS
- 1; i
++)
85 watermark_set(gi
->lib
[i
]);
88 foreach_in_group(board
, group
) {
90 foreach_neighbor(board
, coord2
, {
91 if (board_at(board
, c
) + watermark_get(c
) != S_NONE
)
94 gi
->lib
[gi
->libs
++] = c
;
95 if (unlikely(gi
->libs
>= GROUP_KEEP_LIBS
))
98 } foreach_in_group_end
;
104 check_libs_consistency(struct board
*board
, group_t g
)
108 board_group_addlib(struct board
*board
, group_t group
, coord_t coord
)
111 fprintf(stderr
, "Group %d[%s] %d: Adding liberty %s\n",
112 group_base(group
), coord2sstr(group_base(group
), board
),
113 board_group_info(board
, group
).libs
, coord2sstr(coord
, board
));
116 check_libs_consistency(board
, group
);
118 struct group
*gi
= &board_group_info(board
, group
);
119 //bool onestone = group_is_onestone(board, group);
120 if (gi
->libs
< GROUP_KEEP_LIBS
) {
121 for (int i
= 0; i
< GROUP_KEEP_LIBS
; i
++) {
123 /* Seems extra branch just slows it down */
127 if (unlikely(gi
->lib
[i
] == coord
))
130 gi
->lib
[gi
->libs
++] = coord
;
133 check_libs_consistency(board
, group
);
138 board_group_rmlib(struct board
*board
, group_t group
, coord_t coord
)
141 fprintf(stderr
, "Group %d[%s] %d: Removing liberty %s\n",
142 group_base(group
), coord2sstr(group_base(group
), board
),
143 board_group_info(board
, group
).libs
, coord2sstr(coord
, board
));
146 struct group
*gi
= &board_group_info(board
, group
);
147 //bool onestone = group_is_onestone(board, group);
148 for (int i
= 0; i
< GROUP_KEEP_LIBS
; i
++) {
150 /* Seems extra branch just slows it down */
154 if (likely(gi
->lib
[i
] != coord
))
158 gi
->lib
[i
] = gi
->lib
[--gi
->libs
];
159 gi
->lib
[gi
->libs
] = 0;
161 check_libs_consistency(board
, group
);
163 /* Postpone refilling lib[] until we need to. */
164 assert(GROUP_REFILL_LIBS
> 1);
165 if (gi
->libs
> GROUP_REFILL_LIBS
)
167 if (gi
->libs
== GROUP_REFILL_LIBS
)
168 board_group_find_extra_libs(board
, group
, gi
, coord
);
173 /* This is ok even if gi->libs < GROUP_KEEP_LIBS since we
174 * can call this multiple times per coord. */
175 check_libs_consistency(board
, group
);
180 /* This is a low-level routine that doesn't maintain consistency
181 * of all the board data structures. */
183 board_remove_stone(struct board
*board
, group_t group
, coord_t c
)
185 enum stone color
= board_at(board
, c
);
186 board_at(board
, c
) = S_NONE
;
187 group_at(board
, c
) = 0;
189 /* Increase liberties of surrounding groups */
191 foreach_neighbor(board
, coord
, {
192 dec_neighbor_count_at(board
, c
, color
);
193 group_t g
= group_at(board
, c
);
195 board_group_addlib(board
, g
, coord
);
199 static int profiling_noinline
200 board_group_capture(struct board
*board
, group_t group
)
204 foreach_in_group(board
, group
) {
205 board
->captures
[stone_other(board_at(board
, c
))]++;
206 board_remove_stone(board
, group
, c
);
208 } foreach_in_group_end
;
210 struct group
*gi
= &board_group_info(board
, group
);
211 assert(gi
->libs
== 0);
212 memset(gi
, 0, sizeof(*gi
));
217 static void profiling_noinline
218 add_to_group(struct board
*board
, group_t group
, coord_t prevstone
, coord_t coord
)
220 group_at(board
, coord
) = group
;
221 groupnext_at(board
, coord
) = groupnext_at(board
, prevstone
);
222 groupnext_at(board
, prevstone
) = coord
;
224 foreach_neighbor(board
, coord
, {
225 if (board_at(board
, c
) == S_NONE
)
226 board_group_addlib(board
, group
, c
);
230 fprintf(stderr
, "add_to_group: added (%d,%d ->) %d,%d (-> %d,%d) to group %d\n",
231 coord_x(prevstone
, board
), coord_y(prevstone
, board
),
232 coord_x(coord
, board
), coord_y(coord
, board
),
233 groupnext_at(board
, coord
) % board_size(board
), groupnext_at(board
, coord
) / board_size(board
),
237 static void profiling_noinline
238 merge_groups(struct board
*board
, group_t group_to
, group_t group_from
, struct board_undo
*u
)
241 fprintf(stderr
, "board_play_raw: merging groups %d -> %d\n",
242 group_base(group_from
), group_base(group_to
));
243 struct group
*gi_from
= &board_group_info(board
, group_from
);
244 struct group
*gi_to
= &board_group_info(board
, group_to
);
245 // bool onestone_from = group_is_onestone(board, group_from);
246 // bool onestone_to = group_is_onestone(board, group_to);
249 fprintf(stderr
,"---- (froml %d, tol %d)\n", gi_from
->libs
, gi_to
->libs
);
251 if (gi_to
->libs
< GROUP_KEEP_LIBS
) {
252 for (int i
= 0; i
< gi_from
->libs
; i
++) {
253 for (int j
= 0; j
< gi_to
->libs
; j
++)
254 if (gi_to
->lib
[j
] == gi_from
->lib
[i
])
256 gi_to
->lib
[gi_to
->libs
++] = gi_from
->lib
[i
];
257 if (gi_to
->libs
>= GROUP_KEEP_LIBS
)
263 //if (gi_to->libs == 1) {
264 // coord_t lib = board_group_info(board, group_to).lib[0];
267 coord_t last_in_group
;
268 foreach_in_group(board
, group_from
) {
270 group_at(board
, c
) = group_to
;
271 } foreach_in_group_end
;
273 u
->merged
[++u
->nmerged_tmp
].last
= last_in_group
;
274 groupnext_at(board
, last_in_group
) = groupnext_at(board
, group_base(group_to
));
275 groupnext_at(board
, group_base(group_to
)) = group_base(group_from
);
276 memset(gi_from
, 0, sizeof(struct group
));
279 fprintf(stderr
, "board_play_raw: merged group: %d\n",
280 group_base(group_to
));
284 static group_t profiling_noinline
285 new_group(struct board
*board
, coord_t coord
)
287 group_t group
= coord
;
288 struct group
*gi
= &board_group_info(board
, group
);
289 foreach_neighbor(board
, coord
, {
290 if (board_at(board
, c
) == S_NONE
)
291 /* board_group_addlib is ridiculously expensive for us */
292 #if GROUP_KEEP_LIBS < 4
293 if (gi
->libs
< GROUP_KEEP_LIBS
)
295 gi
->lib
[gi
->libs
++] = c
;
298 group_at(board
, coord
) = group
;
299 groupnext_at(board
, coord
) = 0;
301 check_libs_consistency(board
, group
);
304 fprintf(stderr
, "new_group: added %d,%d to group %d\n",
305 coord_x(coord
, board
), coord_y(coord
, board
),
312 undo_save_merge(struct board
*b
, struct board_undo
*u
, group_t g
, coord_t c
)
314 if (g
== u
->merged
[0].group
|| g
== u
->merged
[1].group
||
315 g
== u
->merged
[2].group
|| g
== u
->merged
[3].group
)
318 int i
= u
->nmerged
++;
321 u
->merged
[i
].group
= g
;
322 u
->merged
[i
].last
= 0; // can remove
323 u
->merged
[i
].info
= board_group_info(b
, g
);
327 undo_save_enemy(struct board
*b
, struct board_undo
*u
, group_t g
)
329 if (g
== u
->enemies
[0].group
|| g
== u
->enemies
[1].group
||
330 g
== u
->enemies
[2].group
|| g
== u
->enemies
[3].group
)
333 int i
= u
->nenemies
++;
334 u
->enemies
[i
].group
= g
;
335 u
->enemies
[i
].info
= board_group_info(b
, g
);
338 coord_t
*stones
= u
->enemies
[i
].stones
;
339 if (board_group_info(b
, g
).libs
<= 1) { // Will be captured
340 foreach_in_group(b
, g
) {
342 } foreach_in_group_end
;
349 undo_save_group_info(struct board
*b
, coord_t coord
, enum stone color
, struct board_undo
*u
)
351 u
->next_at
= groupnext_at(b
, coord
);
353 foreach_neighbor(b
, coord
, {
354 group_t g
= group_at(b
, c
);
356 if (board_at(b
, c
) == color
)
357 undo_save_merge(b
, u
, g
, c
);
358 else if (board_at(b
, c
) == stone_other(color
))
359 undo_save_enemy(b
, u
, g
);
364 undo_save_suicide(struct board
*b
, coord_t coord
, enum stone color
, struct board_undo
*u
)
366 foreach_neighbor(b
, coord
, {
367 if (board_at(b
, c
) == color
) {
368 // Handle suicide as a capture ...
369 undo_save_enemy(b
, u
, group_at(b
, c
));
376 static inline group_t
377 play_one_neighbor(struct board
*board
,
378 coord_t coord
, enum stone color
, enum stone other_color
,
379 coord_t c
, group_t group
, struct board_undo
*u
)
381 enum stone ncolor
= board_at(board
, c
);
382 group_t ngroup
= group_at(board
, c
);
384 inc_neighbor_count_at(board
, c
, color
);
388 board_group_rmlib(board
, ngroup
, coord
);
390 fprintf(stderr
, "board_play_raw: reducing libs for group %d (%d:%d,%d)\n",
391 group_base(ngroup
), ncolor
, color
, other_color
);
393 if (ncolor
== color
&& ngroup
!= group
) {
396 add_to_group(board
, group
, c
, coord
);
398 merge_groups(board
, group
, ngroup
, u
);
400 } else if (ncolor
== other_color
) {
402 struct group
*gi
= &board_group_info(board
, ngroup
);
403 fprintf(stderr
, "testing captured group %d[%s]: ", group_base(ngroup
), coord2sstr(group_base(ngroup
), board
));
404 for (int i
= 0; i
< GROUP_KEEP_LIBS
; i
++)
405 fprintf(stderr
, "%s ", coord2sstr(gi
->lib
[i
], board
));
406 fprintf(stderr
, "\n");
408 if (unlikely(board_group_captured(board
, ngroup
)))
409 board_group_capture(board
, ngroup
);
415 /* We played on a place with at least one liberty. We will become a member of
416 * some group for sure. */
417 static group_t profiling_noinline
418 board_play_outside(struct board
*board
, struct move
*m
, struct board_undo
*u
)
420 coord_t coord
= m
->coord
;
421 enum stone color
= m
->color
;
422 enum stone other_color
= stone_other(color
);
425 undo_save_group_info(board
, coord
, color
, u
);
427 foreach_neighbor(board
, coord
, {
428 group
= play_one_neighbor(board
, coord
, color
, other_color
, c
, group
, u
);
431 board_at(board
, coord
) = color
;
432 if (unlikely(!group
))
433 group
= new_group(board
, coord
);
435 board
->last_move2
= board
->last_move
;
436 board
->last_move
= *m
;
438 struct move ko
= { pass
, S_NONE
};
445 /* We played in an eye-like shape. Either we capture at least one of the eye
446 * sides in the process of playing, or return -1. */
447 static int profiling_noinline
448 board_play_in_eye(struct board
*board
, struct move
*m
, struct board_undo
*u
)
450 coord_t coord
= m
->coord
;
451 enum stone color
= m
->color
;
452 /* Check ko: Capture at a position of ko capture one move ago */
453 if (unlikely(color
== board
->ko
.color
&& coord
== board
->ko
.coord
)) {
455 fprintf(stderr
, "board_check: ko at %d,%d color %d\n", coord_x(coord
, board
), coord_y(coord
, board
), color
);
457 } else if (DEBUGL(6)) {
458 fprintf(stderr
, "board_check: no ko at %d,%d,%d - ko is %d,%d,%d\n",
459 color
, coord_x(coord
, board
), coord_y(coord
, board
),
460 board
->ko
.color
, coord_x(board
->ko
.coord
, board
), coord_y(board
->ko
.coord
, board
));
463 struct move ko
= { pass
, S_NONE
};
465 int captured_groups
= 0;
467 foreach_neighbor(board
, coord
, {
468 group_t g
= group_at(board
, c
);
470 fprintf(stderr
, "board_check: group %d has %d libs\n",
471 g
, board_group_info(board
, g
).libs
);
472 captured_groups
+= (board_group_info(board
, g
).libs
== 1);
475 if (likely(captured_groups
== 0)) {
478 board_print(board
, stderr
);
479 fprintf(stderr
, "board_check: one-stone suicide\n");
485 undo_save_group_info(board
, coord
, color
, u
);
488 coord_t cap_at
= pass
;
489 foreach_neighbor(board
, coord
, {
490 inc_neighbor_count_at(board
, c
, color
);
491 group_t group
= group_at(board
, c
);
495 board_group_rmlib(board
, group
, coord
);
497 fprintf(stderr
, "board_play_raw: reducing libs for group %d\n",
500 if (board_group_captured(board
, group
)) {
501 ko_caps
+= board_group_capture(board
, group
);
506 ko
.color
= stone_other(color
);
507 ko
.coord
= cap_at
; // unique
509 board
->last_ko_age
= board
->moves
;
511 fprintf(stderr
, "guarding ko at %d,%s\n", ko
.color
, coord2sstr(ko
.coord
, board
));
514 board_at(board
, coord
) = color
;
515 group_t group
= new_group(board
, coord
);
517 board
->last_move2
= board
->last_move
;
518 board
->last_move
= *m
;
526 static int __attribute__((flatten
))
527 board_play_f(struct board
*board
, struct move
*m
, struct board_undo
*u
)
530 fprintf(stderr
, "board_play(%s): ---- Playing %d,%d\n", coord2sstr(m
->coord
, board
), coord_x(m
->coord
, board
), coord_y(m
->coord
, board
));
532 if (likely(!board_is_eyelike(board
, m
->coord
, stone_other(m
->color
)))) {
533 /* NOT playing in an eye. Thus this move has to succeed. (This
534 * is thanks to New Zealand rules. Otherwise, multi-stone
535 * suicide might fail.) */
536 group_t group
= board_play_outside(board
, m
, u
);
537 if (unlikely(board_group_captured(board
, group
))) {
538 undo_save_suicide(board
, m
->coord
, m
->color
, u
);
539 board_group_capture(board
, group
);
543 return board_play_in_eye(board
, m
, u
);
548 undo_init(struct board
*b
, struct move
*m
, struct board_undo
*u
)
550 // Paranoid uninitialized mem test
551 // memset(u, 0xff, sizeof(*u));
553 u
->last_move2
= b
->last_move2
;
555 u
->last_ko
= b
->last_ko
;
556 u
->last_ko_age
= b
->last_ko_age
;
559 u
->nmerged
= u
->nmerged_tmp
= u
->nenemies
= 0;
560 for (int i
= 0; i
< 4; i
++)
561 u
->merged
[i
].group
= u
->enemies
[i
].group
= 0;
566 board_quick_play_(struct board
*board
, struct move
*m
, struct board_undo
*u
)
568 undo_init(board
, m
, u
);
570 if (unlikely(is_pass(m
->coord
) || is_resign(m
->coord
))) {
571 struct move nomove
= { pass
, S_NONE
};
573 board
->last_move2
= board
->last_move
;
574 board
->last_move
= *m
;
578 if (likely(board_at(board
, m
->coord
) == S_NONE
))
579 return board_play_f(board
, m
, u
);
582 fprintf(stderr
, "board_check: stone exists\n");
587 board_quick_play(struct board
*board
, struct move
*m
, struct board_undo
*u
)
589 int r
= board_quick_play_(board
, m
, u
);
590 #ifdef BOARD_UNDO_CHECKS
597 /***********************************************************************************/
600 undo_merge(struct board
*b
, struct board_undo
*u
, struct move
*m
)
602 coord_t coord
= m
->coord
;
603 group_t group
= group_at(b
, coord
);
604 struct undo_merge
*merged
= u
->merged
;
606 // Others groups, in reverse order ...
607 for (int i
= u
->nmerged
- 1; i
> 0; i
--) {
608 group_t old_group
= merged
[i
].group
;
610 board_group_info(b
, old_group
) = merged
[i
].info
;
612 groupnext_at(b
, group_base(group
)) = groupnext_at(b
, merged
[i
].last
);
613 groupnext_at(b
, merged
[i
].last
) = 0;
616 printf("merged_group[%i]: (last: %s)", i
, coord2sstr(merged
[i
].last
, b
));
617 foreach_in_group(b
, old_group
) {
618 printf("%s ", coord2sstr(c
, b
));
619 } foreach_in_group_end
;
623 foreach_in_group(b
, old_group
) {
624 group_at(b
, c
) = old_group
;
625 } foreach_in_group_end
;
628 // Restore first group
629 groupnext_at(b
, u
->inserted
) = groupnext_at(b
, coord
);
630 board_group_info(b
, merged
[0].group
) = merged
[0].info
;
633 printf("merged_group[0]: ");
634 foreach_in_group(b
, merged
[0].group
) {
635 printf("%s ", coord2sstr(c
, b
));
636 } foreach_in_group_end
;
643 restore_enemies(struct board
*b
, struct board_undo
*u
, struct move
*m
)
645 enum stone color
= m
->color
;
646 enum stone other_color
= stone_other(m
->color
);
648 struct undo_enemy
*enemy
= u
->enemies
;
649 for (int i
= 0; i
< u
->nenemies
; i
++) {
650 group_t old_group
= enemy
[i
].group
;
652 board_group_info(b
, old_group
) = enemy
[i
].info
;
654 coord_t
*stones
= enemy
[i
].stones
;
655 for (int j
= 0; stones
[j
]; j
++) {
656 board_at(b
, stones
[j
]) = other_color
;
657 group_at(b
, stones
[j
]) = old_group
;
658 groupnext_at(b
, stones
[j
]) = stones
[j
+ 1];
660 foreach_neighbor(b
, stones
[j
], {
661 inc_neighbor_count_at(b
, c
, other_color
);
664 // Update liberties of neighboring groups
665 foreach_neighbor(b
, stones
[j
], {
666 if (board_at(b
, c
) != color
)
668 group_t g
= group_at(b
, c
);
669 if (g
== u
->merged
[0].group
|| g
== u
->merged
[1].group
|| g
== u
->merged
[2].group
|| g
== u
->merged
[3].group
)
671 board_group_rmlib(b
, g
, stones
[j
]);
678 board_undo_stone(struct board
*b
, struct board_undo
*u
, struct move
*m
)
680 coord_t coord
= m
->coord
;
681 enum stone color
= m
->color
;
683 * - put captures back
686 //printf("nmerged: %i\n", u->nmerged);
688 // Restore merged groups
691 else // Single stone group undo
692 memset(&board_group_info(b
, group_at(b
, coord
)), 0, sizeof(struct group
));
694 board_at(b
, coord
) = S_NONE
;
695 group_at(b
, coord
) = 0;
696 groupnext_at(b
, coord
) = u
->next_at
;
698 foreach_neighbor(b
, coord
, {
699 dec_neighbor_count_at(b
, c
, color
);
702 // Restore enemy groups
704 b
->captures
[color
] -= u
->captures
;
705 restore_enemies(b
, u
, m
);
710 restore_suicide(struct board
*b
, struct board_undo
*u
, struct move
*m
)
712 enum stone color
= m
->color
;
713 enum stone other_color
= stone_other(m
->color
);
715 struct undo_enemy
*enemy
= u
->enemies
;
716 for (int i
= 0; i
< u
->nenemies
; i
++) {
717 group_t old_group
= enemy
[i
].group
;
719 board_group_info(b
, old_group
) = enemy
[i
].info
;
721 coord_t
*stones
= enemy
[i
].stones
;
722 for (int j
= 0; stones
[j
]; j
++) {
723 board_at(b
, stones
[j
]) = other_color
;
724 group_at(b
, stones
[j
]) = old_group
;
725 groupnext_at(b
, stones
[j
]) = stones
[j
+ 1];
727 foreach_neighbor(b
, stones
[j
], {
728 inc_neighbor_count_at(b
, c
, other_color
);
731 // Update liberties of neighboring groups
732 foreach_neighbor(b
, stones
[j
], {
733 if (board_at(b
, c
) != color
)
735 group_t g
= group_at(b
, c
);
736 if (g
== u
->enemies
[0].group
|| g
== u
->enemies
[1].group
|| g
== u
->enemies
[2].group
|| g
== u
->enemies
[3].group
)
738 board_group_rmlib(b
, g
, stones
[j
]);
746 board_undo_suicide(struct board
*b
, struct board_undo
*u
, struct move
*m
)
748 coord_t coord
= m
->coord
;
749 enum stone other_color
= stone_other(m
->color
);
751 // Pretend it's capture ...
752 struct move m2
= { .coord
= m
->coord
, .color
= other_color
};
753 b
->captures
[other_color
] -= u
->captures
;
755 restore_suicide(b
, u
, &m2
);
759 board_at(b
, coord
) = S_NONE
;
760 group_at(b
, coord
) = 0;
761 groupnext_at(b
, coord
) = u
->next_at
;
763 foreach_neighbor(b
, coord
, {
764 dec_neighbor_count_at(b
, c
, m
->color
);
772 board_quick_undo(struct board
*b
, struct move
*m
, struct board_undo
*u
)
774 #ifdef BOARD_UNDO_CHECKS
778 b
->last_move
= b
->last_move2
;
779 b
->last_move2
= u
->last_move2
;
781 b
->last_ko
= u
->last_ko
;
782 b
->last_ko_age
= u
->last_ko_age
;
784 if (unlikely(is_pass(m
->coord
) || is_resign(m
->coord
)))
789 if (likely(board_at(b
, m
->coord
) == m
->color
))
790 board_undo_stone(b
, u
, m
);
791 else if (board_at(b
, m
->coord
) == S_NONE
)
792 board_undo_suicide(b
, u
, m
);
794 assert(0); /* Anything else doesn't make sense */