/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * 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: */