/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* This is GNU Go, a Go program. Contact gnugo@gnu.org, or see *
* http://www.gnu.org/software/gnugo/ for more information. *
* Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, *
* 2008 and 2009 by the Free Software Foundation. *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation - version 3 or *
* (at your option) any later version. *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License in file COPYING for more details. *
* You should have received a copy of the GNU General Public *
* License along with this program; if not, write to the Free *
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
* Boston, MA 02111, USA. *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int do_aftermath_genmove(int color
,
int under_control
[BOARDMAX
],
int do_capture_dead_stones
);
all_own_neighbors_inessential(int pos
, int color
)
if (board
[pos
+ delta
[k
]] == color
&& DRAGON2(pos
+ delta
[k
]).safety
!= INESSENTIAL
&& (DRAGON2(pos
+ delta
[k
]).safety
!= ALIVE
|| DRAGON2(pos
+ delta
[k
]).owl_status
!= DEAD
))
/* Does a move by color at pos make one of the neighboring points into
static int make_solid_eye(int pos
, int color
)
for (k
= 0; k
< 4; k
++) {
int eyepos
= pos
+ delta
[k
];
if (board
[eyepos
] == EMPTY
|| (board
[eyepos
] == OTHER_COLOR(color
)
&& countlib(eyepos
) == 1)) {
/* For a solid one-point eye all four neighbors must be own
* stones. But one is about to be played so we need three in the
* center, two on the edge and one in the corner.
* We also need a sufficient number of own diagonals; three in
* the center, two on the edge, and one in the corner.
* Notice that the same numbers are needed for both neighbors
* and diagonals and if we start counting at 2 in the corner and
* at 1 on the edge, we need to reach 3 everywhere on the board.
int own_neighbors
= is_edge_vertex(pos
) + is_corner_vertex(pos
);
int own_diagonals
= own_neighbors
;
for (r
= 0; r
< 8; r
++) {
if (board
[eyepos
+ delta
[r
]] == color
) {
if (own_neighbors
== 3 && own_diagonals
>= 3)
/* External interface to do_aftermath_genmove().
* If the suggested move turns out not to be allowed we just return
* pass. This is not ideal but also not a big deal. If
* do_aftermath_genmove() is ever redesigned that would be a good time
* to integrate allowed_moves.
aftermath_genmove(int color
, int do_capture_dead_stones
,
int allowed_moves
[BOARDMAX
])
int move
= do_aftermath_genmove(color
, NULL
, do_capture_dead_stones
);
if (move
!= PASS_MOVE
&& allowed_moves
&& !allowed_moves
[move
])
/* Generate a move to definitely settle the position after the game
* has been finished. The purpose of this is to robustly determine
* life and death status and to distinguish between life in seki and
* The strategy is basically to turn all own living stones into
* invincible ones and remove from the board all dead opponent stones.
* Stones which cannot be removed, nor turned invincible, are alive in
* If do_capture_dead_stones is 0, opponent stones are not necessarily
* removed from the board. This happens if they become unconditionally
* Moves are generated in the following order of priority:
* -1. Play a move which is listed as a replacement for an
* unconditionally meaningless move. This is guaranteed to extend
* the unconditionally settled part of the board. Only do this if
* the meaningless move is not connected through open space to an
* 0. Play edge liberties in certain positions. This is not really
* necessary, but often it can simplify the tactical and strategical
* reading substantially, making subsequent moves faster to generate.
* 1a. Capture an opponent string in atari and adjacent to own invincible
* string. Moves leading to ko or snapback are excluded.
* 1b. If do_capture_dead_stones, play a non-self-atari move adjacent
* to an unconditionally dead opponent string.
* 1c. If do_capture_dead_stones, play a liberty of an opponent string
* where the liberty is adjacent to own invincible string.
* 2. Extend an invincible string to a liberty of an opponent string.
* 3. Connect a non-invincible string to an invincible string.
* 4. Extend an invincible string towards an opponent string or an own
* 5. Split a big eyespace of an alive own dragon without invincible
* strings into smaller pieces. Do not play self-atari here.
* 6. Play a liberty of a dead opponent dragon.
* Steps 2--4 are interleaved to try to optimize the efficiency of the
* moves. In step 5 too, efforts are made to play efficient moves. By
* efficient we here mean moves which are effectively settling the
* position and simplify the tactical and strategical reading for
* Steps 1--4 are guaranteed to be completely safe. Step 0 and 5
* should also be risk-free. Step 6 on the other hand definitely
* isn't. Consider for example this position:
* In order to remove the O stones, it is necessary to play on one of
* the inner liberties, but one of them lets O live. Thus we have to
* check carefully for blunders at this step.
* Update: Step 0 is only safe against blunders if care is taken not
* to get into a shortage of liberties.
* Step 5 also has some risks. Consider this position:
* Playing at * allows X to make seki.
* Before calling this function it is mandatory to call genmove() or
* genmove_conservative(). For this function to be meaningful, the
* genmove() call should return pass.
do_aftermath_genmove(int color
,
int under_control
[BOARDMAX
],
int do_capture_dead_stones
)
int other
= OTHER_COLOR(color
);
float owl_hotspot
[BOARDMAX
];
float reading_hotspot
[BOARDMAX
];
int closest_opponent
= NO_MOVE
;
int closest_own
= NO_MOVE
;
owl_hotspots(owl_hotspot
);
reading_hotspots(reading_hotspot
);
/* As a preparation we compute a distance map to the invincible strings. */
for (pos
= BOARDMIN
; pos
< BOARDMAX
; pos
++) {
else if (board
[pos
] == color
&& worm
[pos
].invincible
)
else if (!do_capture_dead_stones
&& worm
[pos
].unconditional_status
== DEAD
)
&& worm
[pos
].unconditional_status
== ALIVE
)))
for (pos
= BOARDMIN
; pos
< BOARDMAX
; pos
++) {
if (ON_BOARD(pos
) && distance
[pos
] == -1) {
for (k
= 0; k
< 4; k
++) {
int pos2
= pos
+ delta
[k
];
if ((d
== 0 || board
[pos2
] == EMPTY
)
&& distance
[pos2
] == d
) {
if (d
> 0 && board
[pos
] == other
) {
if (closest_opponent
== NO_MOVE
)
else if (d
> 0 && board
[pos
] == color
) {
if (closest_own
== NO_MOVE
)
else if (board
[pos
] == EMPTY
) {
} while (something_found
);
for (pos
= BOARDMIN
; pos
< BOARDMAX
; pos
++) {
else if (distance
[pos
] == -1)
if (debug
& DEBUG_AFTERMATH
) {
for (m
= 0; m
< board_size
; m
++) {
for (n
= 0; n
< board_size
; n
++) {
fprintf(stderr
, "%2d", distance
[pos
]);
else if (distance
[pos
] == 0) {
else if (board
[pos
] == BLACK
)
else if (board
[pos
] == BLACK
)
gprintf("Closest opponent %1m", closest_opponent
);
if (closest_opponent
!= NO_MOVE
)
gprintf(", distance %d\n", distance
[closest_opponent
]);
gprintf("Closest own %1m", closest_own
);
if (closest_own
!= NO_MOVE
)
gprintf(", distance %d\n", distance
[closest_own
]);
for (pos
= BOARDMIN
; pos
< BOARDMAX
; pos
++) {
&& unconditionally_meaningless_move(pos
, color
, &replacement_move
)
&& replacement_move
!= NO_MOVE
) {
DEBUG(DEBUG_AFTERMATH
, "Replacement move for %1m at %1m\n", pos
,
/* Case 0. This is a special measure to avoid a certain kind of
* tactical reading inefficiency.
* Here we play on edge liberties in the configuration
* to stop X from "leaking" out along the edge. Sometimes this can
* save huge amounts of tactical reading for later moves.
best_scoring_move
= NO_MOVE
;
for (pos
= BOARDMIN
; pos
< BOARDMAX
; pos
++) {
libs
= approxlib(pos
, color
, 3, NULL
);
if (is_self_atari(pos
, other
))
for (k
= 0; k
< 4; k
++) {
int right
= delta
[(k
+1)%4];
&& board
[pos
+ dir
] == color
&& board
[pos
+ dir
+ right
] == other
&& board
[pos
+ dir
- right
] == other
&& (libs
> countlib(pos
+ dir
)
&& libs
== countlib(pos
+ dir
)))
&& (DRAGON2(pos
+ dir
).safety
== INVINCIBLE
|| DRAGON2(pos
+ dir
).safety
== STRONGLY_ALIVE
)) {
int this_score
= 20 * (owl_hotspot
[pos
] + reading_hotspot
[pos
]);
if (this_score
> best_score
) {
if (best_scoring_move
!= NO_MOVE
&& safe_move(best_scoring_move
, color
) == WIN
) {
DEBUG(DEBUG_AFTERMATH
, "Closing edge at %1m\n", best_scoring_move
);
return best_scoring_move
;
for (pos
= BOARDMIN
; pos
< BOARDMAX
; pos
++) {
&& worm
[pos
].unconditional_status
!= DEAD
&& ((ON_BOARD(SOUTH(pos
)) && distance
[SOUTH(pos
)] == 0)
|| (ON_BOARD(WEST(pos
)) && distance
[WEST(pos
)] == 0)
|| (ON_BOARD(NORTH(pos
)) && distance
[NORTH(pos
)] == 0)
|| (ON_BOARD(EAST(pos
)) && distance
[EAST(pos
)] == 0))) {
/* Make sure we don't play into a ko or a (proper) snapback. */
if (countstones(pos
) > 1 || !is_self_atari(lib
, color
)) {
/* Case 1b. Play liberties of unconditionally dead stones, but never
* self-atari. For efficiency against stubborn opponents, we want to
* split up the empty space as much as possible. Therefore we look
* among this class of moves for one with a maximum number of
* adjacent empty spaces and opponent stones.
if (do_capture_dead_stones
) {
best_scoring_move
= NO_MOVE
;
for (pos
= BOARDMIN
; pos
< BOARDMAX
; pos
++) {
/* Look at empty points which are connectable to some invincible
* string through empty space.
for (k
= 0; k
< 4; k
++) {
int pos2
= pos
+ delta
[k
];
if (board
[pos2
] == EMPTY
)
else if (board
[pos2
] == other
&& worm
[pos2
].unconditional_status
== DEAD
) {
&& this_score
> best_score
&& !is_self_atari(pos
, color
)) {
return best_scoring_move
;
if (do_capture_dead_stones
) {
for (pos
= BOARDMIN
; pos
< BOARDMAX
; pos
++) {
&& has_neighbor(pos
, other
)) {
if (closest_opponent
!= NO_MOVE
|| closest_own
!= NO_MOVE
) {
if (closest_own
== NO_MOVE
&& closest_opponent
!= NO_MOVE
&& distance
[closest_opponent
] < distance
[closest_own
]))
/* if we're about to play at distance 1, try to optimize the move. */
if (distance
[move
] == 2) {
signed char mx
[BOARDMAX
];
memset(mx
, 0, sizeof(mx
));
best_scoring_move
= move
;
for (pos
= BOARDMIN
; pos
< BOARDMAX
; pos
++) {
if (!ON_BOARD(pos
) || distance
[pos
] != 1)
for (k
= 0; k
< 4; k
++) {
int pos2
= pos
+ delta
[k
];
else if (board
[pos2
] == EMPTY
)
else if (mx
[pos2
] == mark
)
if (board
[pos2
] == color
) {
if (countstones(pos2
) > 2)
if (countstones(pos2
) > 4)
int deltalib
= (approxlib(pos
, other
, MAXLIBS
, NULL
)
mark_string(pos2
, mx
, mark
);
if (is_suicide(pos
, other
))
gprintf("Score %1m = %d\n", pos
, score
);
if (move_ok
&& score
> best_score
) {
move
= best_scoring_move
;
while (distance
[move
] > 1) {
for (k
= 0; k
< 4; k
++) {
int pos2
= move
+ delta
[k
];
&& distance
[pos2
] == distance
[move
] - 1) {
* If we reach here, either all strings of a dragon are invincible
* or no string is. Next we try to make alive dragons invincible by
* splitting big eyes into smaller ones. Our strategy is to search
* for an empty vertex with as many eye points as possible adjacent
* and with at least one alive but not invincible stone adjacent or
for (pos
= BOARDMIN
; pos
< BOARDMAX
; pos
++) {
int eyespace_neighbors
= 0;
int opponent_dragons
= 0;
if (board
[pos
] != EMPTY
|| distance
[pos
] != -1)
/* Do not play self-atari here. */
if (is_self_atari(pos
, color
))
memset(mx
, 0, sizeof(mx
));
for (k
= 0; k
< 8; k
++) {
int pos2
= pos
+ delta
[k
];
if (board
[pos2
] == EMPTY
) {
if (board
[pos2
] == other
) {
int origin
= dragon
[pos2
].origin
;
if (dragon
[pos2
].status
== ALIVE
) {
if (!mx
[origin
] && dragon
[pos2
].status
== DEAD
) {
&& countstones(pos2
) >= 3)
if (k
< 4 && countlib(pos2
) == 1)
else if (board
[pos2
] == color
) {
if (safety
== UNKNOWN
&& dragon
[pos2
].status
== ALIVE
)
if (DRAGON2(pos2
).safety
== INVINCIBLE
)
int apos
= worm
[pos2
].origin
;
if (countstones(apos
) == 1)
&& approxlib(pos
, color
, 5, NULL
) < countlib(apos
))
if (countlib(apos
) <= 2) {
for (r
= 0; r
< 4; r
++) {
if (board
[apos
+d
] == other
&& dragon
[apos
+d
].status
== DEAD
)
else if (board
[apos
+d
] == EMPTY
&& !is_self_atari(apos
+d
, other
))
if (approxlib(pos
, color
, 3, NULL
) > 2) {
if (safety
== DEAD
|| safety
== UNKNOWN
|| eyespace_neighbors
== 0
|| (own_neighbors
+ own_diagonals
) == 0)
/* Big bonus for making a small solid eye while splitting the
* eyespace. Don't bother optimizing for making two solid eyes,
* unconditional replacement moves (case -1) will take care of
* Additional bonus if adjacent to an opponent dragon and we are
* asked to remove all dead opponent stones.
if (eyespace_neighbors
>= 2)
if (make_solid_eye(pos
, color
)) {
if (do_capture_dead_stones
&& opponent_dragons
> 0)
score
[pos
] = 4 * eyespace_neighbors
+ bonus
;
if (safety
== INVINCIBLE
) {
score
[pos
] += own_neighbors
;
score
[pos
] += own_diagonals
;
if (own_worms
> 1 && eyespace_neighbors
>= 1)
score
[pos
] += 10 + 5 * (own_worms
- 2);
else if (eyespace_neighbors
> 2)
score
[pos
] += own_diagonals
;
if (opponent_dragons
> 1)
score
[pos
] += 10 * (opponent_dragons
- 1);
int owl_hotspot_bonus
= (int) (20.0 * owl_hotspot
[pos
]);
int reading_hotspot_bonus
= (int) (20.0 * reading_hotspot
[pos
]);
int hotspot_bonus
= owl_hotspot_bonus
+ reading_hotspot_bonus
;
/* Don't allow the hotspot bonus to turn a positive score into
if (score
[pos
] > 0 && score
[pos
] + hotspot_bonus
<= 0)
hotspot_bonus
= 1 - score
[pos
];
score
[pos
] += hotspot_bonus
;
if (1 && (debug
& DEBUG_AFTERMATH
))
gprintf("Score %1M = %d (hotspot bonus %d + %d)\n", pos
, score
[pos
],
owl_hotspot_bonus
, reading_hotspot_bonus
);
if (is_ko(pos
, color
, NULL
))
score
[pos
] = (score
[pos
] + 1) / 2;
for (pos
= BOARDMIN
; pos
< BOARDMAX
; pos
++) {
if (ON_BOARD(pos
) && score
[pos
] > best_score
) {
if (is_illegal_ko_capture(move
, color
)
|| !safe_move(move
, color
)
|| (DRAGON2(bb
).safety
!= INVINCIBLE
&& DRAGON2(bb
).safety
!= STRONGLY_ALIVE
&& owl_does_defend(move
, bb
, NULL
) != WIN
)
|| (!confirm_safety(move
, color
, NULL
, NULL
))) {
/* If we're getting short of liberties, we must be more careful.
* Check that no adjacent string or dragon gets more alive by
int libs
= approxlib(move
, color
, 5, NULL
);
for (k
= 0; k
< 4; k
++) {
if (board
[move
+ delta
[k
]] == color
&& countlib(move
+ delta
[k
]) > libs
)
if (trymove(move
, color
, "aftermath-B", move
+ delta
[k
])) {
neighbors
= chainlinks(move
, adjs
);
for (r
= 0; r
< neighbors
; r
++) {
if (worm
[adjs
[r
]].attack_codes
[0] != 0
&& (find_defense(adjs
[r
], NULL
)
> worm
[adjs
[r
]].defense_codes
[0])) {
"Blunder: %1m becomes tactically safer after %1m\n",
for (r
= 0; r
< neighbors
&& move_ok
; r
++) {
if (dragon
[adjs
[r
]].status
== DEAD
&& !owl_does_attack(move
, adjs
[r
], NULL
)) {
"Blunder: %1m becomes more alive after %1m\n",
DEBUG(DEBUG_AFTERMATH
, "Splitting eyespace at %1m\n", move
);
* Finally we try to play on liberties of remaining DEAD opponent
* dragons, carefully checking against mistakes.
for (pos
= BOARDMIN
; pos
< BOARDMAX
; pos
++) {
if (board
[pos
] != EMPTY
|| distance
[pos
] != -1)
for (k
= 0; k
< 8; k
++) {
int pos2
= pos
+ delta
[k
];
&& dragon
[pos2
].status
!= ALIVE
&& dragon
[pos2
].status
!= UNKNOWN
&& (do_capture_dead_stones
|| worm
[pos2
].unconditional_status
!= DEAD
)
&& DRAGON2(pos2
).safety
!= INESSENTIAL
) {
if (k
< 4 || all_own_neighbors_inessential(pos
, color
)) {
/* At this point, (pos) is a move that potentially may capture
* a dead opponent string at (target).
if (!trymove(pos
, color
, "aftermath-A", target
))
/* It is frequently necessary to sacrifice own stones in order
* to force the opponent's stones to be removed from the board,
* e.g. by adding stones to fill up a nakade shape. However, we
* should only play into a self atari if the sacrificed stones
* are classified as INESSENTIAL. Thus it would be ok for O to
* try a self atari in this position:
for (k
= 0; k
< 4; k
++) {
if (board
[pos
+ delta
[k
]] == color
&& DRAGON2(pos
+ delta
[k
]).safety
!= INESSENTIAL
) {
/* Copy the potential move to (move). */
/* If the move is a self atari, but that isn't okay, try to
* recursively find a backfilling move which later makes the
* potential move possible.
while (countlib(pos
) == 1) {
if (!trymove(move
, color
, "aftermath-B", target
))
/* Make sure that the potential move really isn't a self
* atari. In the case of a move found after backfilling this
* could happen (because the backfilling moves happened to
* capture some stones). The position of the move may even be
if (!self_atari_ok
&& (board
[move
] != EMPTY
|| is_self_atari(move
, color
)))
/* Consult the owl code to determine whether the considered move
* really is effective. Blunders should be detected here.
if (owl_does_attack(move
, target
, NULL
) == WIN
) {
/* If we have an adjacent own dragon, which is not inessential,
* verify that it remains safe.
if (cc
!= NO_MOVE
&& !owl_does_defend(move
, cc
, NULL
)) {
owl_analyze_semeai_after_move(move
, color
, target
, cc
,
&resulta
, &resultb
, NULL
, 1, NULL
, 1);
/* If we don't allow self atari, also call confirm safety to
* avoid setting up combination attacks.
if (!self_atari_ok
&& !confirm_safety(move
, color
, NULL
, NULL
))
DEBUG(DEBUG_AFTERMATH
, "Filling opponent liberty at %1m\n", move
);
* In very rare cases it turns out we need yet another pass. An
* example is this position:
* Here the X stones are found tactically dead and therefore the
* corner O stones have been amalgamated with the surrounding
* stones. Since the previous case only allows sacrificing
* INESSENTIAL stones, it fails to take X off the board.
* The solution is to look for tactically attackable opponent stones
* that still remain on the board but should be removed.
for (pos
= BOARDMIN
; pos
< BOARDMAX
; pos
++) {
&& (worm
[pos
].unconditional_status
== UNKNOWN
|| do_capture_dead_stones
)
&& (DRAGON2(pos
).safety
== DEAD
|| DRAGON2(pos
).safety
== TACTICALLY_DEAD
)
&& worm
[pos
].attack_codes
[0] != 0
&& !is_illegal_ko_capture(worm
[pos
].attack_points
[0], color
)) {
DEBUG(DEBUG_AFTERMATH
, "Tactically attack %1m at %1m\n",
pos
, worm
[pos
].attack_points
[0]);
return worm
[pos
].attack_points
[0];
/* This is a substitute for genmove_conservative() which only does
* what is required when doing the aftermath. Notice though that this
* generates an "ordinary" move, in contrast to aftermath_genmove().
* Usually this should turn up a pass, but when it doesn't it's
* important not to miss the move.
reduced_genmove(int color
)
/* no move is found yet. */
/* Prepare pattern matcher and reading code. */
/* Find out information about the worms and dragons. */
examine_position(EXAMINE_ALL
, 1);
/* The score will be used to determine when we are safely
* ahead. So we want the most conservative score.
our_score
= -white_score
;
* Ok, information gathering is complete. Now start to find some moves!
/* Pick up moves that we know of already. */
collect_move_reasons(color
);
/* Look for combination attacks and defenses against them. */
/* Review the move reasons and estimate move values. */
if (review_move_reasons(&move
, &value
, color
, 0.0, our_score
, NULL
, 0))
TRACE("Move generation likes %1m with value %f\n", move
, value
);
/* If no move is found then pass. */
TRACE("reduced_genmove() recommends %1m with value %f\n", move
, value
);
/* Preliminary function for playing through the aftermath. */
do_play_aftermath(int color
, struct aftermath_data
*a
,
SGFTree
*aftermath_sgftree
)
int color_to_play
= color
;
DEBUG(DEBUG_AFTERMATH
, "The aftermath starts.\n");
/* Disable computing worm and owl threats. */
disable_threat_computation
= 1;
/* Disable matching of endgame patterns. */
disable_endgame_patterns
= 1;
while (pass
< 2 && moves
< board_size
* board_size
) {
int reading_nodes
= get_reading_node_counter();
int owl_nodes
= get_owl_node_counter();
move
= reduced_genmove(color_to_play
);
int save_verbose
= verbose
;
move
= do_aftermath_genmove(color_to_play
,
(color_to_play
== WHITE
?
a
->white_control
: a
->black_control
),
play_move(move
, color_to_play
);
sgftreeAddPlay(aftermath_sgftree
, color_to_play
, I(move
), J(move
));
DEBUG(DEBUG_AFTERMATH
, "%d %C move %1m (nodes %d, %d total %d, %d)\n",
movenum
, color_to_play
, move
, get_owl_node_counter() - owl_nodes
,
get_reading_node_counter() - reading_nodes
,
get_owl_node_counter(), get_reading_node_counter());
color_to_play
= OTHER_COLOR(color_to_play
);
/* Reenable worm and dragon threats and endgame patterns. */
disable_threat_computation
= 0;
disable_endgame_patterns
= 0;
static struct aftermath_data aftermath
;
play_aftermath(int color
, SGFTree
*aftermath_sgftree
)
struct board_state saved_board
;
struct aftermath_data
*a
= &aftermath
;
static int current_board
[BOARDMAX
];
static int current_color
= EMPTY
;
gg_assert(color
== BLACK
|| color
== WHITE
);
if (current_color
!= color
) {
for (pos
= BOARDMIN
; pos
< BOARDMAX
; pos
++) {
if (ON_BOARD(pos
) && board
[pos
] != current_board
[pos
]) {
current_board
[pos
] = board
[pos
];
/* If this is exactly the same position as the one we analyzed the
* last time, the content of the aftermath struct is up to date.
a
->white_captured
= white_captured
;
a
->black_captured
= black_captured
;
store_board(&saved_board
);
do_play_aftermath(color
, a
, aftermath_sgftree
);
restore_board(&saved_board
);
for (pos
= BOARDMIN
; pos
< BOARDMAX
; pos
++) {
if (a
->black_control
[pos
]) {
if (board
[pos
] == WHITE
) {
a
->final_status
[pos
] = DEAD
;
else if (board
[pos
] == EMPTY
) {
a
->final_status
[pos
] = BLACK_TERRITORY
;
a
->final_status
[pos
] = ALIVE
;
else if (a
->white_control
[pos
]) {
if (board
[pos
] == BLACK
) {
a
->final_status
[pos
] = DEAD
;
else if (board
[pos
] == EMPTY
) {
a
->final_status
[pos
] = WHITE_TERRITORY
;
a
->final_status
[pos
] = ALIVE
;
a
->final_status
[pos
] = DAME
;
a
->final_status
[pos
] = ALIVE_IN_SEKI
;
if (debug
& DEBUG_AFTERMATH
) {
gprintf("White captured: %d\n", a
->white_captured
);
gprintf("Black captured: %d\n", a
->black_captured
);
gprintf("White prisoners: %d\n", a
->white_prisoners
);
gprintf("Black prisoners: %d\n", a
->black_prisoners
);
gprintf("White territory: %d\n", a
->white_territory
);
gprintf("Black territory: %d\n", a
->black_territory
);
gprintf("White area: %d\n", a
->white_area
);
gprintf("Black area: %d\n", a
->black_area
);
aftermath_compute_score(int color
, SGFTree
*tree
)
struct aftermath_data
*a
= &aftermath
;
play_aftermath(color
, tree
);
return (a
->white_territory
/* Report the final status of a vertex on the board.
* Possible results are ALIVE, DEAD, ALIVE_IN_SEKI, WHITE_TERRITORY,
* BLACK_TERRITORY, and DAME.
aftermath_final_status(int color
, int pos
)
play_aftermath(color
, NULL
);
return aftermath
.final_status
[pos
];