Initial commit of GNU Go v3.8.
[sgk-go] / engine / endgame.c
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* 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. *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "gnugo.h"
#include "liberty.h"
static void endgame_analyze_worm_liberties(int pos, int color);
static void endgame_find_backfilling_dame(int str, int color);
static int endgame_find_liberties(int str, int *essential_liberties,
int essential_libs[MAXLIBS],
int *inessential_liberties,
int inessential_libs[MAXLIBS],
int *false_eye_liberties,
int false_eye_libs[MAXLIBS]);
/* Generate endgame moves. These are typically moves in settled positions,
* they aren't worth many points. Currently, we generate such moves using
* patterns in endgames.db and this algorithmic move generator. It is only
* called when no move of value higher than 6.0 has been found on board.
*/
void
endgame(int color)
{
int pos;
TRACE("\nEndgame move generator tries to look for additional moves...\n");
/* Try to generate some moves using endgame_analyze_worm_liberties(). See
* the description of that function to find what moves it generates.
*/
for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
/* We are only interested in alive, but not invincible worms which are
* parts of alive dragons. That is, the position must be stable.
*/
if (IS_STONE(board[pos])
&& worm[pos].origin == pos
&& dragon[pos].status == ALIVE
&& !worm[pos].invincible
&& !worm[pos].inessential
&& worm[pos].attack_codes[0] == 0) {
endgame_analyze_worm_liberties(pos, color);
endgame_find_backfilling_dame(pos, color);
}
}
}
/* This function handles two cases of endgame moves. Consider these two
* positions (from endgame:301,302 and endgame:801,802 respectively):
*
* OOOOOOO XXXXXO.|
* O.XXX.O X.O.O*.|
* OOX.XXO X.OOOX.|
* .O*X.OX XXXXOX.|
* .OXX..X X..XOOO|
* .OOOXX. XXXXXXX|
*
* The two marked with `*' moves are worth one point in gote each (for
* both colors). The first one is obvious - once black runs short on
* liberties, he'll have to defend in his own eyespace, wasting one
* point. In the second position, although black sacrifices one point
* by playing in white's territory, he forces white to eventually
* capture the black string, losing three points. However, white has
* to play at `*' sooner or later if black doesn't take that vertex, so
* the move is worth 3 - 1 - 1 = 1 point only, not two.
*
* This function is able to find such moves. The algorithm is based on
* finding so called "inessential liberties". These are defined as
* liberties, which satisfy five conditions:
*
* 1) they are not within an eye (not in someone's territory),
* 2) all their adjacent worms and dragons are alive,
* 3) they have adjacent worms of both colors,
* 4) they have no other adjacent worms of the same color as the worm
* under consideration,
* 5) they are safe to fill with stones of other than the worm's color.
*
* Such liberties are supposed to never become territory (they can't become
* an additional eye for the worm under consideration), the worm cannot
* connect to something via such a liberty and they will (or at least can)
* eventually be filled by either of the players.
*
* FIXME: This function can probably be improved to handle more cases.
*/
static void
endgame_analyze_worm_liberties(int pos, int color)
{
int k;
int worm_color = board[pos];
int other = OTHER_COLOR(worm_color);
int essential_liberties;
int essential_libs[MAXLIBS];
int inessential_liberties;
int inessential_libs[MAXLIBS];
int false_eye_liberties;
int false_eye_libs[MAXLIBS];
int num_attacks;
int num_attacks2;
int attacks[MAXLIBS];
int defenses[MAXLIBS];
int apos;
int value;
if (!endgame_find_liberties(pos, &essential_liberties, essential_libs,
&inessential_liberties, inessential_libs,
&false_eye_liberties, false_eye_libs))
return;
apos = NO_MOVE;
num_attacks = 0;
/* Now, try to predict the final state of the position. We fill all
* inessential liberties by stones of other than the current worm's
* color. This is just a guess, we'll have to check the results later.
*/
for (k = 0; k < inessential_liberties; k++) {
if (!safe_move(inessential_libs[k], other)
|| !trymove(inessential_libs[k], other, "endgame", pos))
break;
}
/* If we haven't eaten the worm accidentally, look if any attacks on the
* worm have appeared.
*/
if (k == inessential_liberties && board[pos] != EMPTY) {
/* Try to look for moves as in position 1. If the worm still has
* more than one liberty, try to play on every essential liberty
* and see if an attack appears.
*/
if (countlib(pos) > 1) {
for (k = 0; k < essential_liberties; k++) {
int lib = essential_libs[k];
if (safe_move(lib, worm_color) && safe_move(lib, other)
&& trymove(lib, other, "endgame", pos)) {
if (attack(pos, NULL) != 0) {
int dpos;
if (find_defense(pos, &dpos) && is_proper_eye_space(dpos)) {
int i;
/* If the attack cannot be defended against by playing on
* another essential liberty, filling a pure false eye (an
* eye which can't become territory) or capturing an opponent
* string in atari, keep it for now.
*/
for (i = 0; i < essential_liberties; i++) {
if (i != k && essential_libs[i] != dpos
&& does_defend(essential_libs[i], pos))
break;
}
if (i == essential_liberties) {
for (i = 0; i < false_eye_liberties; i++) {
if (does_defend(false_eye_libs[i], pos))
break;
}
if (i == false_eye_liberties) {
int adj[MAXCHAIN];
int adjs;
adjs = chainlinks2(pos, adj, 1);
for (i = 0; i < adjs; i++) {
int lib2;
findlib(adj[i], 1, &lib2);
if (lib2 != dpos && !is_proper_eye_space(lib2)
&& does_defend(lib2, pos))
break;
}
if (i == adjs) {
attacks[num_attacks] = lib;
defenses[num_attacks] = dpos;
num_attacks++;
}
}
}
}
}
popgo();
}
}
}
else if (essential_liberties > 0) {
/* If the only remaining liberty is essential, it is an attack. */
attacks[num_attacks] = essential_libs[0];
defenses[num_attacks] = NO_MOVE;
num_attacks++;
}
/* Try to find moves as in position 2. */
if (attack(pos, &apos) != 0) {
if (is_proper_eye_space(apos)) {
/* The attack point is in someone's eye (must be an eye which the worm
* bounds). This looks promising. If this attack cannot be averted by
* playing on an essential liberty, keep it for further analyzis.
*/
for (k = 0; k < essential_liberties; k++) {
if (does_defend(essential_libs[k], pos)) {
apos = NO_MOVE;
break;
}
}
if (apos != NO_MOVE && worm_color == color && !does_defend(apos, pos))
apos = NO_MOVE;
}
else
apos = NO_MOVE;
}
}
else {
/* We were unable to fill all the liberties. Modify
* `inessential_liberties' in order to undo the right number of
* moves.
*/
inessential_liberties = k;
}
/* Undo all the moves made to fill inessential liberties. */
for (k = 0; k < inessential_liberties; k++)
popgo();
ASSERT1(stackp == 0, pos);
num_attacks2 = 0;
for (k = 0; k < num_attacks; k++) {
/* These moves must be safe for the other color, otherwise they are
* pointless. Note that checks for safety on previous step were not
* sufficient since we had additional stones on board then.
*/
if (safe_move(attacks[k], other)) {
if (defenses[k] != NO_MOVE) {
int i;
/* Consider this position:
*
* .X...OO The move at `*' satisfies the conditions above.
* .X*OO.O However, it is pointless, since black has a miai
* X.OX..O move at `a' to force white to play `b'. That is,
* XXObOOO no matter if white plays `*' or `a', black takes
* .XXaOXO the other point and white has to fill `b'. So, if
* ...XXXX there is a point, adjacent to defense point, safe
* for "other" color, we discard the attack.
*
* Also, in some positions, defense point is adjacent to worm
* inessential liberty. In such cases we discard the attack too.
*/
for (i = 0; i < 4; i++) {
int pos2 = defenses[k] + delta[i];
if (board[pos2] == EMPTY) {
int m;
if (!is_proper_eye_space(pos2) && safe_move(pos2, other))
break;
for (m = 0; m < inessential_liberties; m++) {
if (inessential_libs[m] == pos2)
break;
}
if (m < inessential_liberties)
break;
}
}
/* If this is not the case, the attack is kept for the final trial. */
if (i == 4)
attacks[num_attacks2++] = attacks[k];
}
else {
/* This must be the only attack (filling all inessential liberties
* gives an atari).
*/
ASSERT1(num_attacks == 1, pos);
attacks[num_attacks2++] = attacks[k];
}
}
}
value = 0;
if (apos != NO_MOVE) {
/* We use the number of string's liberties minus 2 as the value of
* the move. Minus 2 is explained in the comment before the
* function. In some rare cases the value may differ, but this
* should be a good guess.
*/
value = accuratelib(apos, other, MAXLIBS, NULL) - 2;
}
/* If we haven't found anything interesting or have already dropped it,
* there is no point trying more moves, so we return now.
*/
if (value <= 0 && num_attacks2 == 0)
return;
/* We filled the liberties with stones of "other" color. That could lead to
* some strange attacks, since inessential liberties are not always really
* inessential (see trevorb:320 and trevorb:940 for examples where this step
* is necessary). Now we fill the liberties with stones of the same color as
* the current worm. If the results remain unchanged, then we can probably
* trust them.
*/
for (k = 0; k < inessential_liberties; k++) {
if (!trymove(inessential_libs[k], worm_color, "endgame", pos))
break;
}
/* GNU Go currently doesn't allow suicide, but let's assume it does. */
if (k == inessential_liberties && board[pos] != EMPTY) {
if (countlib(pos) > 1) {
for (k = 0; k < num_attacks2; k++) {
if (trymove(attacks[k], other, "endgame", pos)) {
if (attack(pos, NULL) != 0) {
TRACE(" endgame move with territorial value %d.0 found at %1m\n",
1, attacks[k]);
add_expand_territory_move(attacks[k]);
/* FIXME: We just guess the value here. Find a way to calculate it
* (more) precisely.
*/
set_minimum_territorial_value(attacks[k], 1.0);
}
popgo();
}
}
}
else if (essential_liberties > 0 && essential_libs[0] == attacks[0]) {
TRACE(" endgame move with territorial value %d.0 found at %1m\n",
1, attacks[k]);
add_expand_territory_move(attacks[0]);
/* FIXME: We just guess the value here. Find a way to calculate it
* (more) precisely.
*/
set_minimum_territorial_value(attacks[0], 1.0);
}
if (value > 0 && does_attack(apos, pos)) {
TRACE(" endgame move with territorial value %d.0 found at %1m\n",
value, apos);
add_expand_territory_move(apos);
set_minimum_territorial_value(apos, (float) value);
}
}
else {
/* Don't undo moves we didn't play. */
inessential_liberties = k;
}
/* Undo all the moves made at the third step. */
for (k = 0; k < inessential_liberties; k++)
popgo();
ASSERT1(stackp == 0, pos);
}
/* A backfilling dame is a defense move, usually within potential own
* territory, which does not have to be played immediately but after
* outer liberties of some string have been filled. If those outer
* liberties are dame points (here inessential liberties), it is
* usually better to play the backfilling moves before filling the
* dame points. If nothing else it reduces the risk for making stupid
* blunders while filling dame.
*/
static void
endgame_find_backfilling_dame(int str, int color_to_move)
{
int k;
int color = board[str];
int other = OTHER_COLOR(color);
int essential_liberties;
int essential_libs[MAXLIBS];
int inessential_liberties;
int inessential_libs[MAXLIBS];
int false_eye_liberties;
int false_eye_libs[MAXLIBS];
int dpos;
int loop_again = 1;
int potential_moves[BOARDMAX];
int num_potential_moves = 0;
int move = NO_MOVE;
while (loop_again) {
loop_again = 0;
if (!endgame_find_liberties(str, &essential_liberties, essential_libs,
&inessential_liberties, inessential_libs,
&false_eye_liberties, false_eye_libs))
break;
for (k = 0; k < inessential_liberties; k++) {
if (!safe_move(inessential_libs[k], other)
|| !trymove(inessential_libs[k], other, "endgame", str))
continue;
increase_depth_values();
if (board[str] == EMPTY)
break;
if (attack_and_defend(str, NULL, NULL, NULL, &dpos)) {
if (worm[dpos].color == EMPTY) {
potential_moves[num_potential_moves] = dpos;
num_potential_moves++;
}
forced_backfilling_moves[dpos] = 1;
if (trymove(dpos, color, "endgame", str))
increase_depth_values();
loop_again = 1;
break;
}
}
}
while (stackp > 0) {
popgo();
decrease_depth_values();
}
for (k = num_potential_moves - 1; k >= 0; k--)
if (safe_move(potential_moves[k], color)) {
move = potential_moves[k];
TRACE(" backfilling dame found at %1m for string %1m\n", move, str);
if (color == color_to_move) {
add_expand_territory_move(move);
set_minimum_territorial_value(move, 0.1);
}
break;
}
}
/* Find liberties of the string str with various characteristics. See
* the comments above endgame_analyze_worm_liberties() for more
* information.
*/
static int
endgame_find_liberties(int str,
int *essential_liberties, int essential_libs[MAXLIBS],
int *inessential_liberties,
int inessential_libs[MAXLIBS],
int *false_eye_liberties, int false_eye_libs[MAXLIBS])
{
int liberties;
int libs[MAXLIBS];
int k;
ASSERT1(IS_STONE(board[str]), str);
*essential_liberties = 0;
*inessential_liberties = 0;
*false_eye_liberties = 0;
/* Find all string liberties. */
liberties = findlib(str, MAXLIBS, libs);
/* Loop over the liberties and find inessential and essential ones. The
* latter are defined as those, which are not inside an eye space, but
* don't otherwise qualify as inessential. If we find a non-alive (dead
* or critical) worm or dragon around, we stop looking for liberties and
* skip the current worm (position is unstable).
*/
for (k = 0; k < liberties; k++) {
int lib = libs[k];
if (!is_proper_eye_space(lib)) {
int i;
int essential = 0;
int found_other = 0;
for (i = 0; i < 4; i++) {
int pos = lib + delta[i];
if (!IS_STONE(board[pos]) || !IS_STONE(worm[pos].color))
continue;
if (worm[pos].attack_codes[0] != 0 || dragon[pos].status != ALIVE)
return 0;
if (board[pos] == board[str]) {
if (find_origin(pos) != find_origin(str))
essential = 1;
}
else
found_other = 1;
}
if (i < 4)
break;
if (found_other) {
if (essential)
essential_libs[(*essential_liberties)++] = lib;
else
inessential_libs[(*inessential_liberties)++] = lib;
}
else if (is_false_eye(half_eye, lib) && !false_eye_territory[lib])
false_eye_libs[(*false_eye_liberties)++] = lib;
}
}
return 1;
}
/*
* Local Variables:
* tab-width: 8
* c-basic-offset: 2
* End:
*/