Initial commit of GNU Go v3.8.
[sgk-go] / engine / reading.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 <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "liberty.h"
#include "cache.h"
#include "gg_utils.h"
/* If nonzero, attack() and find_defense() write all results to
* stderr. Use this to if you have deviations in results, but cannot
* find where they come from.
*
* Redirect results to a file. Take dumps of two versions and
* (assuming GNU tools) do `sort -t= -s' on both. Then join the
* sorted dumps:
*
* join -t= sorted-first-dump sorted-second-dump \
* | sed -e "s/^[^=]\+=\([^=]\+\)=\1$//" | tr -s "\n" | tr = "\t" \
* | uniq
*
* to get a nice list of deviations. This is only meaningful if you
* dump results of a single test (or at least tests originating at a
* same position).
*/
#define DUMP_ALL_RESULTS 0
/* Size of array where candidate moves are stored. */
#define MAX_MOVES 50
/* Please notice that message had better be a fixed string. Only the
* pointer to it is saved and there is no attempt to free up any
* storage.
*/
#define ADD_CANDIDATE_MOVE(move, this_score, moves, this_message) \
do { \
int u; \
for (u = 0; u < (moves).num; u++) \
if ((moves).pos[u] == (move)) { \
(moves).score[u] += this_score; \
break; \
} \
if ((u == (moves).num) && ((moves).num < MAX_MOVES)) { \
(moves).pos[(moves).num] = move; \
(moves).score[(moves).num] = this_score; \
(moves).message[(moves).num] = this_message; \
(moves).num++; \
} \
} while (0)
#define REMOVE_CANDIDATE_MOVE(move, moves) \
do { \
int u; \
for (u = (moves).num_tried; u < (moves).num; u++) { \
if ((moves).pos[u] == (move)) { \
(moves).pos[u] = (moves).pos[(moves).num - 1]; \
(moves).score[u] = (moves).score[(moves).num - 1]; \
(moves).message[u] = (moves).message[(moves).num - 1]; \
(moves).num--; \
break; \
} \
} \
} while (0)
/* This macro checks whether the reported result is a loss, so we have won
* and can exit, or else if it is the best result so far.
* Note that SGFTRACE must have been setup.
*/
#define CHECK_RESULT(savecode, savemove, code, move_pos, move_ptr, \
trace_message) \
do { \
if (code == 0) { \
if (move_ptr) \
*(move_ptr) = (move_pos); \
SGFTRACE(move_pos, WIN, trace_message); \
return WIN; \
} \
else if (REVERSE_RESULT(code) > savecode) { \
savemove = move_pos; \
savecode = REVERSE_RESULT(code); \
} \
} while (0)
/* Reverse of CHECK_RESULT, for results passed from a helper function. */
#define CHECK_RESULT_UNREVERSED(savecode, savemove, code, move_pos, \
move_ptr, trace_message) \
CHECK_RESULT(savecode, savemove, REVERSE_RESULT(code), move_pos, \
move_ptr, trace_message)
#define RETURN_RESULT(savecode, savemove, move_ptr, trace_message) \
do { \
if (savecode) { \
if (move_ptr) \
*(move_ptr) = (savemove); \
SGFTRACE(savemove, savecode, trace_message); \
} \
else \
SGFTRACE(0, 0, NULL); \
return savecode; \
} while (0)
/* Play a collected batch of moves and see if any of them works. This
* is a defense version.
*/
#define DEFEND_TRY_MOVES(no_deep_branching, attack_hint) \
do { \
int k; \
\
for (k = moves.num_tried; k < moves.num; k++) { \
int ko_move; \
int dpos = moves.pos[k]; \
\
if (komaster_trymove(dpos, color, moves.message[k], str, &ko_move,\
stackp <= ko_depth && savecode == 0)) { \
int acode = do_attack(str, (attack_hint)); \
popgo(); \
\
if (!ko_move) { \
CHECK_RESULT(savecode, savemove, acode, dpos, move, \
"defense effective"); \
} \
else { \
if (acode != WIN) { \
savemove = dpos; \
savecode = KO_B; \
} \
} \
} \
\
if ((no_deep_branching) && stackp >= branch_depth) \
RETURN_RESULT(savecode, savemove, move, "branching limit"); \
} \
\
moves.num_tried = moves.num; \
} while (0)
/* Attack version of the macro above. This one is a bit more
* complicated, because when defender fails to defend, attacker has to
* prove that he can capture the string before claiming victory.
*/
#define ATTACK_TRY_MOVES(no_deep_branching, defense_hint) \
do { \
int k; \
\
for (k = moves.num_tried; k < moves.num; k++) { \
int ko_move; \
int apos = moves.pos[k]; \
\
if ((board_ko_pos != NO_MOVE || !send_two_return_one(apos, other))\
&& komaster_trymove(apos, other, moves.message[k], \
str, &ko_move, \
stackp <= ko_depth && savecode == 0)) { \
int dcode = do_find_defense(str, (defense_hint)); \
\
if (REVERSE_RESULT(dcode) > savecode \
&& do_attack(str, NULL)) { \
if (!ko_move) { \
if (dcode == 0) { \
popgo(); \
RETURN_RESULT(WIN, apos, move, "attack effective"); \
} \
\
savemove = apos; \
savecode = REVERSE_RESULT(dcode); \
} \
else { \
savemove = apos; \
savecode = KO_B; \
} \
} \
\
popgo(); \
} \
\
if ((no_deep_branching) && stackp >= branch_depth) \
RETURN_RESULT(savecode, savemove, move, "branching limit"); \
} \
\
moves.num_tried = moves.num; \
} while (0)
struct reading_moves
{
int pos[MAX_MOVES];
int score[MAX_MOVES];
const char *message[MAX_MOVES];
int num;
int num_tried;
};
/*
* The functions in reading.c are used to read whether groups
* can be captured or not. See the Texinfo documentation
* (Reading) for more information.
*
* NULL POINTERS: Many functions in this file can use pointers
* to return the locations of recommended plays. These can be
* set NULL in which case these values are not returned.
*/
static int do_find_defense(int str, int *move);
static int defend1(int str, int *move);
static int defend2(int str, int *move);
static int defend3(int str, int *move);
static int defend4(int str, int *move);
static void special_rescue_moves(int str, int lib,
struct reading_moves *moves);
static void bamboo_rescue_moves(int str, int num_libs, int libs[],
struct reading_moves *moves);
static void special_rescue2_moves(int str, int libs[2],
struct reading_moves *moves);
static void special_rescue3_moves(int str, int libs[3],
struct reading_moves *moves);
static void special_rescue4_moves(int str, int libs[2],
struct reading_moves *moves);
static void hane_rescue_moves(int str, int libs[4],
struct reading_moves *moves);
static void special_rescue5_moves(int str, int libs[3],
struct reading_moves *moves);
static void special_rescue6_moves(int str, int libs[3],
struct reading_moves *moves);
static void set_up_snapback_moves(int str, int lib,
struct reading_moves *moves);
static void edge_clamp_moves(int str, struct reading_moves *moves);
static int do_attack(int str, int *move);
static int attack1(int str, int *move);
static int attack2(int str, int *move);
static int attack3(int str, int *move);
static int attack4(int str, int *move);
static void find_cap_moves(int str, struct reading_moves *moves);
static void special_attack2_moves(int str, int libs[2],
struct reading_moves *moves);
static void special_attack3_moves(int str, int libs[2],
struct reading_moves *moves);
static void special_attack4_moves(int str, int libs[2],
struct reading_moves *moves);
static void draw_back_moves(int str, struct reading_moves *moves);
static void edge_closing_backfill_moves(int str, int apos,
struct reading_moves *moves);
static void edge_block_moves(int str, int apos,
struct reading_moves *moves);
static void propose_edge_moves(int str, int *libs, int liberties,
struct reading_moves *moves, int color);
static void break_chain_moves(int str, struct reading_moves *moves);
static int defend_secondary_chain1_moves(int str, struct reading_moves *moves,
int min_liberties);
static void defend_secondary_chain2_moves(int str, struct reading_moves *moves,
int min_liberties);
static void break_chain2_efficient_moves(int str,
struct reading_moves *moves);
static void do_find_break_chain2_efficient_moves(int str, int adj,
struct reading_moves *moves);
static void break_chain2_moves(int str, struct reading_moves *moves,
int require_safe, int be_aggressive);
static void break_chain2_defense_moves(int str, struct reading_moves *moves,
int be_aggressive);
static void break_chain3_moves(int str, struct reading_moves *moves,
int be_aggressive);
static void break_chain4_moves(int str, struct reading_moves *moves,
int be_aggressive);
static void superstring_moves(int str, struct reading_moves *moves,
int liberty_cap, int does_attack);
static void squeeze_moves(int str, struct reading_moves *moves);
static void superstring_break_chain_moves(int str, int liberty_cap,
struct reading_moves *moves);
static void double_atari_chain2_moves(int str,
struct reading_moves *moves,
int generate_more_moves);
static void order_moves(int str, struct reading_moves *moves,
int color, const char *funcname, int killer);
static int simple_ladder_defend(int str, int *move);
static int in_list(int move, int num_moves, int *moves);
/* Statistics. */
static int reading_node_counter = 0;
static int nodes_when_called = 0;
/* ================================================================ */
/* Goal functions */
/* ================================================================ */
/*
* These functions define goals for the reading process. They use
* the rest of the reading machinery to evaluate whether the goal
* is fulfillable.
*
* The simplest goals are defined by attack() and find_defense(),
* namely to see if it is possible to capture or defend a single
* string. More complex goals are defined by e.g. attack_either()
* or defend_both().
*
* The functions in this section and the next are the only ones which are
* callable from outside this file.
*/
/* attack(str, *move) determines if the string at (str) can be
* captured, and if so, (*move) returns the attacking move, unless
* (move) is a null pointer. Use a null pointer if you are interested
* in the result of the attack but not the attacking move itself.
*
* Return WIN if the attack succeeds unconditionally, 0 if it doesn't.
* Returns KO_A or KO_B if the result depends on ko:
* - Returns KO_A if the attack succeeds provided attacker is willing to
* ignore any ko threat (the attacker makes the first ko capture).
* - Returns KO_B if attack succeeds provided attacker has a ko threat
* which must be answered (the defender makes the first ko capture).
*/
int
attack(int str, int *move)
{
int result;
int nodes;
int origin;
int the_move = NO_MOVE;
int liberties = countlib(str);
nodes_when_called = reading_node_counter;
/* Don't even spend time looking in the cache if there are more than
* enough liberties. We need this before the persistent cache lookup
* to avoid results inconsistent with find_defense().
*/
if (liberties > 4
|| (liberties == 4 && stackp > fourlib_depth)
|| (liberties == 3 && stackp > depth))
return 0;
origin = find_origin(str);
if (search_persistent_reading_cache(ATTACK, origin, &result, &the_move)) {
if (move)
*move = the_move;
return result;
}
memset(shadow, 0, sizeof(shadow));
result = do_attack(str, &the_move);
nodes = reading_node_counter - nodes_when_called;
if (debug & DEBUG_READING_PERFORMANCE) {
if (reading_node_counter - nodes_when_called
>= MIN_READING_NODES_TO_REPORT) {
if (result != 0)
gprintf("%oattack %1m(%1m) = %d %1M, %d nodes ", str, origin, result,
the_move, nodes);
else
gprintf("%oattack %1m(%1m) = %d, %d nodes ", str, origin, result,
nodes);
dump_stack();
}
}
store_persistent_reading_cache(ATTACK, origin, result, the_move, nodes);
if (move)
*move = the_move;
#if DUMP_ALL_RESULTS
do_dump_stack();
gprintf("%oattack %1m (%d)=%d %1m\n", str, depth, result, the_move);
#endif
return result;
}
/* find_defense(str, *move) attempts to find a move that will save
* the string at (str). It returns WIN if such a move is found, with
* (*move) the location of the saving move, unless (move) is a
* null pointer. It is not checked that tenuki defends, so this may
* give an erroneous answer if !attack(str).
*
* Returns KO_A or KO_B if the result depends on ko. Returns KO_A if the
* string can be defended provided the defender is willing to ignore
* any ko threat. Returns KO_B if the defender wins by having a ko threat
* which must be answered.
*/
int
find_defense(int str, int *move)
{
int result;
int nodes;
int origin;
int the_move = NO_MOVE;
int liberties = countlib(str);
nodes_when_called = reading_node_counter;
/* Don't even spend time looking in the cache if there are more than
* enough liberties.
*/
if (liberties > 4
|| (liberties == 4 && stackp > fourlib_depth)) {
if (move)
*move = NO_MOVE;
return WIN;
}
origin = find_origin(str);
if (search_persistent_reading_cache(FIND_DEFENSE, origin,
&result, &the_move)) {
if (move)
*move = the_move;
return result;
}
memset(shadow, 0, sizeof(shadow));
result = do_find_defense(str, &the_move);
nodes = reading_node_counter - nodes_when_called;
if (debug & DEBUG_READING_PERFORMANCE) {
if (reading_node_counter - nodes_when_called
>= MIN_READING_NODES_TO_REPORT) {
if (result != 0)
gprintf("%odefend %1m(%1m) = %d %1M, %d nodes ", str, origin, result,
the_move, nodes);
else
gprintf("%odefend %1m(%1m) = %d, %d nodes ", str, origin, result,
nodes);
dump_stack();
}
}
store_persistent_reading_cache(FIND_DEFENSE, origin, result,
the_move, nodes);
if (move)
*move = the_move;
#if DUMP_ALL_RESULTS
do_dump_stack();
gprintf("%odefend %1m (%d)=%d %1m\n", str, depth, result, the_move);
#endif
return result;
}
/* attack_and_defend(str, &acode, &attack_point,
* &dcode, &defense_point)
* is a frontend to the attack() and find_defense() functions, which
* guarantees a consistent result. If a string cannot be attacked, 0
* is returned and acode is 0. If a string can be attacked and
* defended, WIN is returned, acode and dcode are both non-zero, and
* (attack_point), (defense_point) both point to vertices on the board.
* If a string can be attacked but not defended, 0 is again returned,
* acode is non-zero, dcode is 0, and (attack_point) points to a vertex
* on the board.
*
* This function in particular guarantees that if there is an attack,
* it will never return (defense_point) = NO_MOVE, which means the string is
* safe without defense. Separate calls to attack() and find_defense()
* may occasionally give this result, due to irregularities introduced
* by the persistent reading cache.
*/
int
attack_and_defend(int str,
int *attack_code, int *attack_point,
int *defend_code, int *defense_point)
{
int acode = 0;
int apos = NO_MOVE;
int dcode = 0;
int dpos = NO_MOVE;
acode = attack(str, &apos);
if (acode != 0)
dcode = find_defense(str, &dpos);
ASSERT1(!(acode != 0 && dcode == WIN && dpos == NO_MOVE), str);
if (attack_code)
*attack_code = acode;
if (attack_point)
*attack_point = apos;
if (defend_code)
*defend_code = dcode;
if (defense_point)
*defense_point = dpos;
return acode != 0 && dcode != 0;
}
/*
* attack_either(astr, bstr) returns true if there is a move which
* guarantees that at least one of the strings (astr) and (bstr)
* can be captured. A typical application for this is in connection
* patterns, where after a cut it suffices to capture one of the cutting
* stones.
*
* FIXME: The current implementation only looks for uncoordinated
* attacks. This is insufficient to find double ataris or
* moves such as 'a' in positions like
*
* XOOOOOOOX
* XOXXOXXOX
* XX..a..XX
* ---------
*
* where neither of the threatened X stones can be captured right
* out. Still either can be captured by a move down to a.
*/
int
attack_either(int astr, int bstr)
{
int asuccess = 0;
int bsuccess = 0;
int color = board[astr];
ASSERT1(IS_STONE(color) , astr);
ASSERT1(color == board[bstr], bstr);
/* Start by attacking the string with the fewest liberties. On
* average this seems to be slightly more efficient.
*/
if (countlib(astr) > countlib(bstr)) {
int t = astr;
astr = bstr;
bstr = t;
}
asuccess = attack(astr, NULL);
if (asuccess == WIN)
return asuccess;
bsuccess = attack(bstr, NULL);
if (asuccess || bsuccess) {
return (asuccess > bsuccess) ? asuccess : bsuccess;
}
/* Try (a little) harder */
{
int alibs[2];
int blibs[2];
int alib = findlib(astr, 2, alibs);
int defended0 = WIN;
int defended1 = WIN;
int other = OTHER_COLOR(color);
/* Let's just try the case where the group with the fewest liberties
* has only 2, and try each atari in turn.
*/
if (alib == 2) {
if (trymove(alibs[0], other, "attack_either-A", astr)) {
defended0 = defend_both(astr, bstr);
popgo();
}
if (defended0
&& trymove(alibs[1], other, "attack_either-B", astr)) {
defended1 = defend_both(astr, bstr);
popgo();
}
}
/* The second string is possibly also short in liberties.
* Let's try to improve the result.
*/
if (defended0 > 0 && defended1 > 0
&& findlib(bstr, 2, blibs) == 2) {
defended0 = gg_min(defended0, defended1);
defended1 = defended0;
/* We may get here even if alib==1, in case there is a snapback.
* To avoid referencing uninitialized memory in this case we
* explicitly set alibs[1] to NO_MOVE.
*/
if (alib == 1)
alibs[1] = NO_MOVE;
if (blibs[0] != alibs[0] && blibs[0] != alibs[1]
&& trymove(blibs[0], other, "attack_either-C", bstr)) {
int defended = defend_both(astr, bstr);
defended0 = gg_min(defended0, defended);
popgo();
}
if (defended0
&& blibs[1] != alibs[0] && blibs[1] != alibs[1]
&& trymove(blibs[1], other, "attack_either-D", bstr)) {
int defended = defend_both(astr, bstr);
defended1 = gg_min(defended1, defended);
popgo();
}
}
return REVERSE_RESULT(gg_min(defended0, defended1));
}
}
/*
* defend_both(astr, bstr) returns true if both the strings (astr)
* and (bstr) can be defended simultaneously or if there is no attack.
* A typical application for this is in connection patterns, where
* after a cut it's necessary to defend both cutting stones.
*
* FIXME: The current implementation only makes halfhearted
* attempts to find coordinated defense moves. A proper implementation
* would require some serious reading.
*/
int
defend_both(int astr, int bstr)
{
int a_threatened = 0;
int b_threatened = 0;
int a_savepos;
int b_savepos;
int acode = 0;
int dcode = 0;
int color = board[astr];
ASSERT1(IS_STONE(color) , astr);
ASSERT1(color == board[bstr], bstr);
/* This probably helps here too...
* (see attack_either)
*/
if (countlib(astr) > countlib(bstr)) {
int t = astr;
astr = bstr;
bstr = t;
}
attack_and_defend(astr, &acode, NULL, &dcode, &a_savepos);
if (acode != 0) {
a_threatened = 1;
if (dcode != WIN)
return 0; /* (astr) already lost */
}
attack_and_defend(bstr, &acode, NULL, &dcode, &b_savepos);
if (acode != 0) {
b_threatened = 1;
if (dcode != WIN)
return 0; /* (bstr) already lost */
}
/* Neither string can be attacked or only one of them, in which case
* we have time to save it.
*/
if (!a_threatened || !b_threatened)
return WIN;
/* If both strings are threatened we assume that one will become lost,
* unless find_defense() happened to return the same defense point for
* both (which e.g. may happen if they are in fact the same string).
* This is still a bit too pessimistic, as there may be one move which
* saves both strings. To do this right we should try each move which
* defends either string and see if it also defends the other string.
*/
if (a_savepos == b_savepos)
return WIN; /* Both strings can be attacked but also defended
* by one move. */
/* We also try each of the returned defense points and see whether
* the other string can still be attacked. This still gives a
* somewhat pessimistic estimation.
*/
if (trymove(a_savepos, color, "defend_both-A", astr)) {
if (board[bstr] && !attack(bstr, NULL)) {
popgo();
return WIN;
}
popgo();
}
if (trymove(b_savepos, color, "defend_both-B", bstr)) {
if (board[astr] && !attack(astr, NULL)) {
popgo();
return WIN;
}
popgo();
}
/* The next improvement is to try to attack a common adjacent string. */
{
int adjs1[MAXCHAIN];
int neighbors1;
int adjs2[MAXCHAIN];
int neighbors2;
int r;
int s;
int epos;
int fpos;
neighbors1 = chainlinks(astr, adjs1);
neighbors2 = chainlinks(bstr, adjs2);
for (r = 0; r < neighbors1; r++) {
epos = adjs1[r];
if (countlib(epos) <= 4
&& (epos != a_savepos)
&& (epos != b_savepos)) {
/* Is (epos) also adjacent to (bstr)? */
for (s = 0; s < neighbors2; s++) {
if (adjs2[s] == adjs1[r])
break;
}
if (s == neighbors2)
continue; /* No, it wasn't. */
if (attack(epos, &fpos)) {
if (trymove(fpos, color, "defend_both-C", astr)) {
if (board[astr] && board[bstr]
&& !attack(astr, NULL)
&& !attack(bstr, NULL)) {
popgo();
return WIN;
}
popgo();
}
}
}
}
}
/* Both strings can be attacked but we have only time to defend one. */
return 0;
}
/*
* break_through(apos, bpos, cpos) returns WIN if a position can
* succesfully be broken through and CUT if it can be cut. The position
* is assumed to have the shape (the colors may be reversed)
*
* .O. dbe
* OXO aFc
*
* It is X to move and try to capture at least one of a, b, and c. If
* this succeeds, X is said to have broken through the position.
* Otherwise X may try to cut through the position, which means
* keeping F safe and getting a tactically safe string at either d or
* e.
*
* Important notice: a, b, and c must be given in the correct order.
*
* FIXME: The reading involved here can most likely be improved.
*
* FIXME: We need to take ko results properly into account.
*/
static int
break_through_helper(int apos, int bpos, int cpos,
int dpos, int epos, int Fpos,
int color, int other);
int
break_through(int apos, int bpos, int cpos)
{
int color = board[apos];
int other = OTHER_COLOR(color);
int dpos;
int epos;
int Fpos;
int gpos;
int success = 0;
int success2 = 0;
/* Basic sanity checking. */
ASSERT1(IS_STONE(color) , apos);
ASSERT1(color == board[bpos], bpos);
ASSERT1(color == board[cpos], cpos);
/* Construct the rest of the points in the pattern. */
Fpos = (apos + cpos) / 2; /* F midpoint between a and c. */
dpos = apos + bpos - Fpos; /* Use diagonal relation a+b = d+F. */
epos = bpos + cpos - Fpos; /* Use diagonal relation b+c = e+F. */
/* More sanity checking. */
ASSERT1(board[dpos] == EMPTY , dpos);
ASSERT1(board[epos] == EMPTY , epos);
/* F might already have been captured. (play_break_through_n() can't
* detect this.
*/
if (board[Fpos] == EMPTY)
return 0;
ASSERT1(board[Fpos] == other, Fpos);
/* First X tries to play at d. */
success = break_through_helper(apos, bpos, cpos, dpos, epos, Fpos,
color, other);
if (success == WIN)
return WIN;
success2 = break_through_helper(cpos, bpos, apos, epos, dpos, Fpos,
color, other);
if (success2 == WIN)
return WIN;
if (success2 == CUT)
success = CUT;
/* If we haven't been lucky yet, we might need to start by
* defending F.
*
* FIXME: The function would probably be considerably faster if we
* start by checking whether F needs defense. Beware of ko potential
* though.
*/
success2 = 0;
if (attack_and_defend(Fpos, NULL, NULL, NULL, &gpos)) {
if (trymove(gpos, other, "break_through-A", Fpos)) {
/* Now we let O defend his position by playing either d or e.
* FIXME: There may be other plausible moves too.
*/
if (trymove(dpos, color, "break_through-B", Fpos)) {
/* O connects at d, so X cuts at e. */
if (safe_move(epos, other)) {
success2 = CUT;
if (!board[cpos] || attack(cpos, NULL))
success2 = WIN;
}
popgo();
}
if (success2 > 0 && trymove(epos, color, "break_through-C", Fpos)) {
/* O connects at e, so X cuts at d. */
if (safe_move(dpos, other)) {
/* success2 is already WIN or CUT. */
if (board[apos] && !attack(apos, NULL))
success2 = CUT;
}
else
success2 = 0;
popgo();
}
popgo();
}
}
if (success2 > 0)
return success2;
return success;
}
/* Helper function for break_through(). Since we can symmetrically
* start by cutting at d or e, we use the same code for both attacks,
* simply switching positions between the two calls.
*/
static int
break_through_helper(int apos, int bpos, int cpos,
int dpos, int epos, int Fpos,
int color, int other)
{
int success = 0;
int gpos;
if (trymove(dpos, other, "break_through_helper-A", Fpos)) {
/* If F can be attacked we can't start in this way. */
if (!attack(Fpos, NULL)) {
/* If d is safe too, we have at least managed to break through. */
if (!attack(dpos, &gpos))
success = CUT;
/* Too bad, d could be attacked. We let O play the attack and
* then try to make a second cut at e. But first we must test if
* O at e is sufficient to capture d.
*/
else {
if (trymove(epos, color, "break_through_helper-E", Fpos)) {
if (!board[dpos] || !find_defense(dpos, NULL)) {
popgo();
popgo();
return 0;
}
popgo();
}
if (gpos == epos) {
popgo();
return 0;
}
if (trymove(gpos, color, "break_through_helper-F", Fpos)) {
if (trymove(epos, other, "break_through_helper-G", Fpos)) {
if (!attack(epos, NULL)) {
success = CUT;
/* Make sure b and c are safe. If not, back up & let O try
* to defend in a different way. */
if (board[bpos]
&& board[cpos]
&& defend_both(bpos, cpos)) {
/* Can't do better than CUT. */
popgo();
popgo();
popgo();
return CUT;
}
}
else {
/* Lost everything. (Note we ignore ko at the moment.) */
popgo();
popgo();
popgo();
return 0;
}
popgo();
}
else {
/* Failed to cut at all. */
popgo();
popgo();
return 0;
}
popgo();
}
}
/* By now, we're sure a cut works, so now we can try
* to capture something.
*/
if (!board[apos] || !board[bpos] || !defend_both(apos, bpos))
success = WIN;
else {
/* Both a and b could be defended, or didn't need to be.
* Let's see if a move at e is sufficient for O.
*/
int attack_on_b = 0;
int attack_on_a = 0;
if (trymove(epos, color, "break_through_helper-B", Fpos)) {
if (attack(bpos, NULL))
attack_on_b = 1;
else if (attack(apos, NULL))
attack_on_a = 1;
popgo();
}
/* Let O find a defense and play it. */
if (attack_on_a || attack_on_b) {
int hpos = NO_MOVE;
if (((attack_on_a && find_defense(apos, &hpos))
|| (attack_on_b && find_defense(bpos, &hpos)))
&& hpos != NO_MOVE
&& trymove(hpos, color, "break_through_helper-C", Fpos)) {
/* Now we make a second cut at e, trying to capture
* either b or c.
*/
if (trymove(epos, other, "break_through_helper-D", Fpos)) {
if (!board[bpos]
|| !board[cpos]
|| !defend_both(bpos, cpos))
success = WIN;
popgo();
}
popgo();
}
else
success = WIN; /* This should have been covered by
* defend_both(), so probably unnecessary. */
}
}
}
popgo();
}
return success;
}
/* ---------------------------------------------------------------- */
/* Threats */
/* ---------------------------------------------------------------- */
/* Return up to max_threats threats to capture the string at str.
* If the string is directly attackable the number of threats
* is reported to be 0.
*
* NOTE: You can call attack_threats with moves[] and codes[]
* already partly filled in. So if you want to get the
* threats from scratch, you have to set them to 0
* yourself.
*
* FIXME: Shall we report upgrades, like we can capture in ko but
* have a threat to capture unconditionally?
*/
int
attack_threats(int str, int max_points, int moves[], int codes[])
{
int other;
int num_threats;
int liberties;
int libs[MAXLIBS];
int num_adj;
int adjs[MAXCHAIN];
int k;
int l;
int r;
ASSERT1(IS_STONE(board[str]), str);
other = OTHER_COLOR(board[str]);
/* Only handle strings with no way to capture immediately.
* For now, we treat ko the same as unconditionally. */
if (attack(str, NULL) != 0)
return 0;
/* This test would seem to be unnecessary since we only threaten
* strings with attack_code == 0, but it turns out that single
* stones with one liberty that can be captured, but come to
* live again in a snap-back get attack_code == 0.
*
* The test against 6 liberties is just an optimization.
*/
liberties = findlib(str, MAXLIBS, libs);
if (liberties > 1 && liberties < 6) {
for (k = 0; k < liberties; k++) {
int aa = libs[k];
/* Try to threaten on the liberty. */
if (trymove(aa, other, "attack_threats-A", str)) {
int acode = attack(str, NULL);
if (acode != 0)
movelist_change_point(aa, acode, max_points, moves, codes);
popgo();
}
/* Try to threaten on second order liberties. */
for (l = 0; l < 4; l++) {
int bb = libs[k] + delta[l];
if (!ON_BOARD(bb)
|| IS_STONE(board[bb])
|| liberty_of_string(bb, str))
continue;
if (trymove(bb, other, "attack_threats-B", str)) {
int acode = attack(str, NULL);
if (acode != 0)
movelist_change_point(bb, acode, max_points, moves, codes);
popgo();
}
}
}
}
/* Threaten to attack by saving weak neighbors. */
num_adj = chainlinks(str, adjs);
for (k = 0; k < num_adj; k++) {
int bb;
int dd; /* Defense point of weak neighbor. */
int acode;
int dcode;
attack_and_defend(adjs[k], &acode, NULL, &dcode, &dd);
if (acode == 0 || dcode == 0)
continue;
/* The strange code using r == -1 below is only avoid duplication
* of the code starting with "if (trymove..)" below.
* If r == -1 and stackp == 0 then use the defense point what we got from
* attack_and_defend above. Otherwise step through all defense points.
*/
for (r = -1; r < max_points; r++) {
if (stackp == 0) {
if (r == -1)
continue;
if (worm[adjs[k]].defense_codes[r] == 0)
break;
bb = worm[adjs[k]].defense_points[r];
}
else {
if (r == -1)
bb = dd;
else
break;
}
/* Test the move and see if it is a threat. */
if (trymove(bb, other, "attack_threats-C", str)) {
if (board[str] == EMPTY)
acode = WIN;
else
acode = attack(str, NULL);
if (acode != 0)
movelist_change_point(bb, acode, max_points, moves, codes);
popgo();
}
}
}
/* Return actual number of threats found regardless of attack code. */
if (codes[max_points - 1] > 0)
return max_points;
for (num_threats = 0; num_threats < max_points; num_threats++)
if (codes[num_threats] == 0)
break;
return num_threats;
}
/* ================================================================ */
/* Defensive functions */
/* ================================================================ */
/* Like find_defense, but takes the komaster argument. If the
* opponent is reading functions will not try
* to take ko.
*/
static int
do_find_defense(int str, int *move)
{
int xpos = NO_MOVE;
int dcode = 0;
int liberties;
int retval;
SETUP_TRACE_INFO("find_defense", str);
/* We first check if the number of liberties is larger than four. In
* that case we don't cache the result and to avoid needlessly
* storing the position in the hash table, we must do this test
* before we look for cached results.
*/
str = find_origin(str);
liberties = countlib(str);
if (liberties > 4
|| (liberties == 4 && stackp > fourlib_depth)
|| (liberties == 3 && stackp > depth)) {
/* No need to cache the result in these cases. */
SGFTRACE(0, WIN, "too many liberties or stackp > depth");
if (move)
*move = 0;
return WIN;
}
/* Set "killer move" up. This move (if set) was successful in
* another variation, so it is reasonable to try it now. However,
* we only do this if the string has at least 3 liberties -
* otherwise the situation changes too much from variation to
* variation.
*/
if (liberties > 2 && move)
xpos = *move;
if (stackp <= depth
&& tt_get(&ttable, FIND_DEFENSE, str, NO_MOVE, depth - stackp, NULL,
&retval, NULL, &xpos) == 2) {
/* Note that if return value is 1 (too small depth), the move will
* still be used for move ordering.
*/
TRACE_CACHED_RESULT(retval, xpos);
SGFTRACE(xpos, retval, "cached");
if (move)
*move = xpos;
return retval;
}
if (liberties == 1)
dcode = defend1(str, &xpos);
else if (liberties == 2)
dcode = defend2(str, &xpos);
else if (liberties == 3)
dcode = defend3(str, &xpos);
else if (liberties == 4)
dcode = defend4(str, &xpos);
if (dcode) {
READ_RETURN(FIND_DEFENSE, str, depth - stackp, move, xpos, dcode);
}
READ_RETURN0(FIND_DEFENSE, str, depth - stackp);
}
/* Determine if a `move' by `color' allows under-the-stones tesuji
* a.k.a. "big snapback". Here is an example:
*
* |XXXX...
* |XXOOXXX
* |OOOXOOX
* |..O*OOX
* +-------
*
* Even though the move at '*' allows black to capture four white
* stones, white can later recapture black stones and create a second
* eye. This is very similar to a snapback.
*
* This function returns true if a move creates a string of with two
* liberties, which can, however, be instantly recaptured by opponent.
* It is actually not required that the move captures something. If
* the caller needs captures, it should check for them itself.
*/
static int
allows_under_the_stones_tesuji(int move, int color)
{
int result = 0;
SGFTree *save_sgf_dumptree;
int save_count_variations;
if (accuratelib(move, color, 3, NULL) != 2)
return 0;
save_sgf_dumptree = sgf_dumptree;
save_count_variations = count_variations;
sgf_dumptree = NULL;
count_variations = 0;
if (trymove(move, color, "allows_under_the_stones_tesuji", NO_MOVE)) {
int libs[2];
findlib(move, 2, libs);
if ((!is_self_atari(libs[0], color)
&& accuratelib(libs[1], OTHER_COLOR(color), 3, NULL) <= 2)
|| (!is_self_atari(libs[1], color)
&& accuratelib(libs[0], OTHER_COLOR(color), 3, NULL) <= 2))
result = 1;
popgo();
}
sgf_dumptree = save_sgf_dumptree;
count_variations = save_count_variations;
return result;
}
/* Called by the defendN functions. Don't think too much if there's
* an easy way to get enough liberties.
*/
static int
fast_defense(int str, int liberties, int *libs, int *move)
{
int color = board[str];
int j, k, l;
int goal_liberties = (stackp < fourlib_depth ? 5 : 4);
int adj, adjs[MAXCHAIN];
/* We would like to initialize liberty_mark to -1, but some
* compilers warn, quite correctly, that -1 is not an unsigned
* number.
*/
static unsigned liberty_mark = ~0U;
static unsigned lm[BOARDMAX];
ASSERT1(libs != NULL, str);
ASSERT1(move != NULL, str);
for (k = 0; k < liberties; k++) {
/* accuratelib() seems to be more efficient than fastlib() here,
* probably because it catches more cases.
*/
if (accuratelib(libs[k], color, goal_liberties, NULL) >= goal_liberties) {
*move = libs[k];
return 1;
}
}
/* Check the cases where an opponent neighbor string is in
* atari.
*/
adj = chainlinks2(str, adjs, 1);
for (j = 0; j < adj; j++) {
int lib;
int missing = goal_liberties - liberties;
int total = 0;
int adj2, adjs2[MAXCHAIN];
int alib, alibs[MAXLIBS];
int num_adjacent_stones;
findlib(adjs[j], 1, &lib);
/* We aren't interested in ko (at this stage). And playing
* our own last liberty to capture is prone to snapbacks,
* so better let the 'normal' reading routines do the job.
*/
if ((liberties == 1 && lib == libs[0]
&& countstones(adjs[j]) <= 2)
|| is_ko(lib, color, NULL))
continue;
/* Would the capture already gain enough liberties ?
* No need to test the case if the move is one of our liberties,
* it has already been done in the first loop of this function.
*/
num_adjacent_stones = count_adjacent_stones(adjs[j], str, missing);
if (!liberty_of_string(lib, str)
&& num_adjacent_stones >= missing) {
*move = lib;
return 1;
}
ASSERT1(num_adjacent_stones >= 1, str);
/* What is the total number of liberties of the friendly strings around
* the lunch?
*/
if (++liberty_mark == 0) {
memset(lm, 0, sizeof(lm));
liberty_mark++;
}
/* Loop over all neighbors of the lunch. */
adj2 = chainlinks(adjs[j], adjs2);
for (k = 0; k < adj2; k++) {
/* Loop over all liberties of the neighbor. */
alib = findlib(adjs2[k], MAXLIBS, alibs);
for (l = 0; l < alib; l++) {
if (lm[alibs[l]] != liberty_mark) {
lm[alibs[l]] = liberty_mark;
total++;
}
}
}
/* The captured string is treated as common liberties, and
* some adjustements are made :
* - we're adding a stone for capturing the lunch (-1)
* - opponent might be able to remove a liberty (-1)
* - and possibly force us to connect (-1)
* - reduce us by one more liberty with a throw-in; this
* is only possible if there is only one adjacent stone in the
* lunch to the string (-1)
* Probably there are more damezumari-type cases, but as a heuristic,
* it seems good enough.
*/
total += countstones(adjs[j]) - 2;
if (lm[lib] == liberty_mark)
total--;
if (num_adjacent_stones == 1)
total--;
if (total >= goal_liberties) {
/* One case when this code can give a false defense is an
* under-the-stones tesuji or "big snapback." See reading:199
* for an example. While this position is probably very rare,
* it is nice to make GNU Go understand "neat" tesujis.
*/
if (liberties == 1 && lib == libs[0]
&& allows_under_the_stones_tesuji(lib, color)) {
/* This is a bad "fast defense". */
continue;
}
*move = lib;
return 1;
}
}
return 0;
}
/* If str points to a string with exactly one liberty, defend1
* determines whether it can be saved by extending or capturing
* a boundary chain having one liberty. The function returns WIN if the string
* can be saved, otherwise 0. It returns KO_A or KO_B if it can be saved,
* conditioned on ko. Returns KO_A if it can be saved provided (color) is
* willing to ignore any ko threat. Returns KO_B if it can be saved if (color)
* has a ko threat which must be answered.
*
* The pair defend1-attack2 call each other recursively to
* read situations such as ladders. They read all ladders to the end.
* If the reading ply (stackp) is deeper than the deep-reading cutoff
* parameter depth, whose default value DEPTH is defined in gnugo.h, then a
* string is assumed alive if it can get 3 liberties. When
* fourlib_depth < stackp < depth, a string is considered alive if it can get
* four liberties. When stackp < fourlib_depth, it is considered alive
* if it can get 5 liberties.
* */
static int
defend1(int str, int *move)
{
int color = board[str];
int other = OTHER_COLOR(color);
int xpos;
int lib;
struct reading_moves moves;
int savemove = 0;
int savecode = 0;
int liberties;
int k;
SETUP_TRACE_INFO("defend1", str);
reading_node_counter++;
ASSERT1(IS_STONE(board[str]), str);
ASSERT1(countlib(str) == 1, str);
/* lib will be the liberty of the string. */
liberties = findlib(str, 1, &lib);
ASSERT1(liberties == 1, str);
if (fast_defense(str, liberties, &lib, &xpos))
RETURN_RESULT(WIN, xpos, move, "fast defense");
/* Collect moves to try in the first batch.
* 1. First order liberty.
* 2. Chain breaking moves.
* 3. Moves to set up a snapback.
*/
moves.pos[0] = lib;
moves.score[0] = 0;
moves.message[0] = "liberty";
moves.num = 1;
moves.num_tried = 0;
break_chain_moves(str, &moves);
set_up_snapback_moves(str, lib, &moves);
order_moves(str, &moves, color, read_function_name, *move);
DEFEND_TRY_MOVES(0, NULL);
/* If the string is a single stone and a capture would give a ko,
* try to defend it with ko by backfilling.
*
* FIXME: What is an example of this? Is it correct that the
* return value is WIN and not KO_A or KO_B?
*/
if (stackp <= backfill_depth
&& countstones(str) == 1
&& is_ko(lib, other, NULL)) {
int libs2[6];
liberties = approxlib(lib, color, 6, libs2);
if (liberties <= 5) {
for (k = 0; k < liberties; k++) {
int apos = libs2[k];
if ((liberties == 1 || !is_self_atari(apos, other))
&& trymove(apos, color, "defend1-C", str)) {
int acode = do_attack(str, NULL);
popgo();
CHECK_RESULT(savecode, savemove, acode, apos, move, "backfilling");
}
}
}
}
RETURN_RESULT(savecode, savemove, move, "saved move");
}
/* If str points to a group with two liberties, defend2 determines
* whether the group can be saved by extending, or by capturing part of
* its surrounding chain. A group is considered safe if either part of
* the surrounding chain may be captured, or if it can get 3
* liberties. It is presumed that the opponent could kill if tenuki.
* If both extensions work, it prefers the one which maximizes
* liberties.
*
* *move returns the move to save the stones.
*/
static int
defend2(int str, int *move)
{
int color, other;
int xpos = NO_MOVE;
int liberties;
int libs[2];
int liberties2;
int libs2[6];
struct reading_moves moves;
int savemove = 0;
int savecode = 0;
int k;
int r;
int suggest_move = NO_MOVE;
int string_size;
int be_aggressive;
SETUP_TRACE_INFO("defend2", str);
reading_node_counter++;
color = board[str];
other = OTHER_COLOR(color);
ASSERT1(IS_STONE(board[str]), str);
ASSERT1(countlib(str) == 2, str);
liberties = findlib(str, 2, libs);
if (fast_defense(str, liberties, libs, &xpos))
RETURN_RESULT(WIN, xpos, move, "fast defense");
/* Collect moves to try in the first batch.
* 1. First order liberties.
* 2. Chain breaking moves.
* 3. Second order liberties moving up from first line to second.
* 4. Edge clamps.
*/
moves.num = 0;
moves.num_tried = 0;
/* We don't want to play self-atari liberties, unless the string is a
* single stone (in which case it might be a snapback move). Sacrifices
* might be good moves, but not in tactical reading.
*/
string_size = countstones(str);
if (string_size == 1 || !is_self_atari(libs[0], color))
ADD_CANDIDATE_MOVE(libs[0], 0, moves, "liberty");
if (string_size == 1 || !is_self_atari(libs[1], color))
ADD_CANDIDATE_MOVE(libs[1], 0, moves, "liberty");
break_chain_moves(str, &moves);
break_chain2_efficient_moves(str, &moves);
propose_edge_moves(str, libs, liberties, &moves, color);
edge_clamp_moves(str, &moves);
if (stackp <= depth) {
for (k = 0; k < liberties; k++)
special_rescue_moves(str, libs[k], &moves);
bamboo_rescue_moves(str, liberties, libs, &moves);
}
if (stackp <= backfill_depth)
special_rescue2_moves(str, libs, &moves);
order_moves(str, &moves, color, read_function_name, *move);
DEFEND_TRY_MOVES(0, &suggest_move);
/* Look for backfilling moves. */
for (k = 0; k < liberties; k++) {
if (is_self_atari(libs[k], other)) {
liberties2 = approxlib(libs[k], color, 6, libs2);
/* Note: liberties2 must be smaller than 5, otherwise libs[k] had been
* a direct defense.
*/
for (r = 0; r < liberties2; r++) {
xpos = libs2[r];
/* If the newly placed stone would be in atari, but not a single
* stone, we don't even try.
*/
if (!is_self_atari(xpos, color)
&& has_neighbor(xpos, color))
ADD_CANDIDATE_MOVE(xpos, 0, moves, "backfill-A");
}
}
liberties2 = approxlib(libs[k], other, 3, libs2);
if (liberties2 <= 2) {
for (r = 0; r < liberties2; r++) {
xpos = libs2[r];
if (!is_self_atari(xpos, color))
ADD_CANDIDATE_MOVE(xpos, 0, moves, "backfill-B");
}
}
}
special_rescue4_moves(str, libs, &moves);
/* Only order and test the new set of moves. */
order_moves(str, &moves, color, read_function_name, *move);
DEFEND_TRY_MOVES(0, &suggest_move);
/* If we haven't found any useful moves in first batches, be more
* aggressive in break_chain[23]_moves().
*/
be_aggressive = (moves.num == 0);
if (stackp <= superstring_depth)
superstring_break_chain_moves(str, 4, &moves);
/* If nothing else works, we try playing a liberty of the
* super_string.
*/
if (stackp <= superstring_depth) {
superstring_moves(str, &moves, 3, 0);
squeeze_moves(str, &moves);
}
break_chain2_defense_moves(str, &moves, be_aggressive);
if (stackp <= backfill_depth)
special_rescue5_moves(str, libs, &moves);
if (stackp <= break_chain_depth
|| (be_aggressive && stackp <= backfill_depth))
break_chain3_moves(str, &moves, be_aggressive);
if (be_aggressive && stackp <= backfill_depth)
break_chain4_moves(str, &moves, be_aggressive);
/* Only order and test the new set of moves. */
order_moves(str, &moves, color, read_function_name, *move);
DEFEND_TRY_MOVES(0, &suggest_move);
RETURN_RESULT(savecode, savemove, move, "saved move");
}
/* defend3(str, *move) attempts to find a move rescuing the
* string at (str) with 3 liberties. If such a move can be found,
* it returns true and the saving move in *move.
*/
static int
defend3(int str, int *move)
{
int color;
int xpos = NO_MOVE;
int liberties;
int libs[3];
struct reading_moves moves;
int savemove = 0;
int savecode = 0;
int k;
int suggest_move = NO_MOVE;
SETUP_TRACE_INFO("defend3", str);
reading_node_counter++;
color = board[str];
ASSERT1(IS_STONE(board[str]), str);
ASSERT1(countlib(str) == 3, str);
liberties = findlib(str, 3, libs);
if (fast_defense(str, liberties, libs, &xpos))
RETURN_RESULT(WIN, xpos, move, "fast defense");
/* Collect moves to try in the first batch.
* 1. First order liberties.
* 2. Chain breaking moves.
* 3. Second order liberties moving up from first line to second.
* 4. Edge clamps.
*/
for (k = 0; k < liberties; k++) {
moves.pos[k] = libs[k];
moves.score[k] = 0;
moves.message[k] = "liberty";
}
moves.num = liberties;
moves.num_tried = 0;
break_chain_moves(str, &moves);
break_chain2_efficient_moves(str, &moves);
propose_edge_moves(str, libs, liberties, &moves, color);
edge_clamp_moves(str, &moves);
if (stackp <= backfill2_depth)
hane_rescue_moves(str, libs, &moves);
order_moves(str, &moves, color, read_function_name, *move);
DEFEND_TRY_MOVES(1, &suggest_move);
/* This looks a little too expensive. */
#if 0
/* Look for backfilling moves. */
if (stackp <= backfill_depth) {
int other = OTHER_COLOR(color);
int liberties2;
int libs2[6];
int r;
int s;
for (k = 0; k < liberties; k++) {
if (is_self_atari(libs[k], other)) {
liberties2 = approxlib(libs[k], color, 6, libs2);
for (r = 0; r < liberties2; r++) {
xpos = libs2[r];
/* Don't reconsider previously tested moves. */
for (s = 0; s < moves.num; s++)
if (xpos == moves.pos[s])
break;
if (s < moves.num)
continue;
if (trymove(xpos, color, "defend3-D", str)) {
int acode;
/* If the newly placed stone is in atari, we give up
* without fight.
*/
if (countlib(xpos) == 1)
acode = WIN;
else
acode = do_attack(str, NULL);
popgo();
CHECK_RESULT(savecode, savemove, acode, xpos, move,
"backfill effective");
}
}
}
else {
liberties2 = approxlib(libs[k], other, 3, libs2);
if (liberties2 <= 3) {
for (r = 0; r < liberties2; r++) {
xpos = libs2[r];
/* Don't reconsider previously tested moves. */
for (s = 0; s < moves.num; s++)
if (xpos == moves.pos[s])
break;
if (s < moves.num)
continue;
if (!is_self_atari(xpos, color)
&& trymove(xpos, color, "defend2-G", str)) {
int acode = do_attack(str, NULL);
popgo();
CHECK_RESULT(savecode, savemove, acode, xpos, move
"backfill effective");
}
}
}
}
}
}
#endif
/* If nothing else works, try to defend with second order liberties. */
if (stackp <= backfill_depth)
special_rescue3_moves(str, libs, &moves);
if (stackp <= depth) {
for (k = 0; k < liberties; k++)
special_rescue_moves(str, libs[k], &moves);
bamboo_rescue_moves(str, liberties, libs, &moves);
}
if (get_level() >= 8 && stackp <= backfill2_depth)
superstring_break_chain_moves(str, 4, &moves);
if (stackp <= break_chain_depth)
break_chain2_defense_moves(str, &moves, 0);
if (stackp <= backfill_depth) {
special_rescue5_moves(str, libs, &moves);
special_rescue6_moves(str, libs, &moves);
}
/* Only order and test the new set of moves. */
order_moves(str, &moves, color, read_function_name, *move);
DEFEND_TRY_MOVES(1, &suggest_move);
/* If nothing else works, we try playing a liberty of the
* super_string.
*/
if (get_level() >= 8 && stackp <= backfill2_depth) {
superstring_moves(str, &moves, 3, 0);
squeeze_moves(str, &moves);
}
if (stackp <= break_chain_depth)
break_chain3_moves(str, &moves, 0);
/* Only order and test the new set of moves. */
order_moves(str, &moves, color, read_function_name, *move);
DEFEND_TRY_MOVES(1, &suggest_move);
RETURN_RESULT(savecode, savemove, move, "saved move");
}
/* defend4(str, *move) attempts to find a move rescuing the
* string at (str) with 4 liberties. If such a move can be found,
* it returns true, and if the pointer move is not NULL,
* then it returns the saving move in *move.
*/
static int
defend4(int str, int *move)
{
int color;
int xpos = NO_MOVE;
int liberties;
int libs[4];
struct reading_moves moves;
int savemove = 0;
int savecode = 0;
int k;
int suggest_move = NO_MOVE;
SETUP_TRACE_INFO("defend4", str);
reading_node_counter++;
color = board[str];
ASSERT1(IS_STONE(board[str]), str);
ASSERT1(countlib(str) == 4, str);
liberties = findlib(str, 4, libs);
if (fast_defense(str, liberties, libs, &xpos))
RETURN_RESULT(WIN, xpos, move, "fast defense");
/* Collect moves to try in the first batch.
* 1. First order liberties.
* 2. Chain breaking moves.
*/
for (k = 0; k < liberties; k++) {
moves.pos[k] = libs[k];
moves.score[k] = 0;
moves.message[k] = "liberty";
}
moves.num = liberties;
moves.num_tried = 0;
break_chain_moves(str, &moves);
break_chain2_efficient_moves(str, &moves);
if (stackp <= backfill_depth) {
break_chain2_defense_moves(str, &moves, 0);
break_chain3_moves(str, &moves, 0);
break_chain4_moves(str, &moves, 0);
#if 0
hane_rescue_moves(str, libs, &moves);
#endif
if (stackp <= superstring_depth)
superstring_moves(str, &moves, 4, 0);
squeeze_moves(str, &moves);
}
order_moves(str, &moves, color, read_function_name, *move);
DEFEND_TRY_MOVES(1, &suggest_move);
if (stackp <= depth) {
for (k = 0; k < liberties; k++)
special_rescue_moves(str, libs[k], &moves);
bamboo_rescue_moves(str, liberties, libs, &moves);
}
order_moves(str, &moves, color, read_function_name, *move);
DEFEND_TRY_MOVES(1, &suggest_move);
RETURN_RESULT(savecode, savemove, move, "saved move");
}
/*
* special_rescue_moves(str, lib, *move) is called with (str) a
* string having a liberty at (lib).
*
* This adds moves on a second order liberty to the list of candidate
* moves in the struct *moves; e.g. in shapes like:
*
* . O O X.XXO
* O.* or ..* or O.* or XOOXO
* O O O ...*.
* -----
*
* This will occasionally save a string where no other move will. To
* reduce the branching caused by these moves, we require that the
* opponent can be trivially captured when trying to intercept on the
* corresponding first order liberty.
*/
static void
special_rescue_moves(int str, int lib, struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int otherlib;
int k;
/* Use approxlib() to test for trivial capture. */
otherlib = approxlib(lib, other, 3, NULL);
if (otherlib > 2)
return;
/* Loop over the four neighbours of the liberty, (lib + d). */
for (k = 0; k < 4; k++) {
int d = delta[k];
if (board[lib + d] == EMPTY) {
/* Don't play into a self atari unless we have a potential snapback. */
if (is_self_atari(lib + d, color) && otherlib > 1)
continue;
/* Be more demanding when the string has four liberties. (Mostly
* because attack4() otherwise would need more move generators.)
* More precisely we require not only the first order liberty to
* become a self atari for the opponent but also one more of the
* neighbors of the proposed move. See reading:144 for a
* position where we otherwise would try to defend at D9 and
* attack4() then lacks move generators to stop black from
* continuing towards the top left corner.
*/
if (countlib(str) > 3) {
int r;
int number_protected = 0;
for (r = 0; r < 4; r++) {
if (board[lib + d + delta[r]] == EMPTY
&& approxlib(lib + d + delta[r], other, 3, NULL) < 3)
number_protected++;
if (number_protected == 2)
break;
}
if (number_protected < 2)
continue;
}
ADD_CANDIDATE_MOVE(lib + d, 0, *moves, "special_rescue");
}
}
}
/*
* In situations like
*
* XXXXXO
* XO.*.O
* XO.O.O
* XXXXXO
*
* playing at * is the correct rescue move, although the opponent cannot
* be captured at the respective first-order liberty.
*/
static void
bamboo_rescue_moves(int str, int num_libs, int libs[],
struct reading_moves *moves)
{
int color = board[str];
int l1, l2;
for (l1 = 0; l1 < num_libs; l1++)
for (l2 = 0; l2 < num_libs; l2++) {
if (l1 == l2)
continue;
if (libs[l1] == WEST(libs[l2]) || libs[l1] == EAST(libs[l2])) {
if (board[SOUTH(libs[l1])] == EMPTY
&& board[SOUTH(libs[l2])] == color
&& !is_self_atari(SOUTH(libs[l1]), color))
ADD_CANDIDATE_MOVE(SOUTH(libs[l1]), 0, *moves, "bamboo_rescue");
if (board[NORTH(libs[l1])] == EMPTY
&& board[NORTH(libs[l2])] == color
&& !is_self_atari(NORTH(libs[l1]), color))
ADD_CANDIDATE_MOVE(NORTH(libs[l1]), 0, *moves, "bamboo_rescue");
}
else if (libs[l1] == NORTH(libs[l2]) || libs[l1] == SOUTH(libs[l2])) {
if (board[WEST(libs[l1])] == EMPTY
&& board[WEST(libs[l2])] == color
&& !is_self_atari(WEST(libs[l1]), color))
ADD_CANDIDATE_MOVE(WEST(libs[l1]), 0, *moves, "bamboo_rescue");
if (board[EAST(libs[l1])] == EMPTY
&& board[EAST(libs[l2])] == color
&& !is_self_atari(EAST(libs[l1]), color))
ADD_CANDIDATE_MOVE(EAST(libs[l1]), 0, *moves, "bamboo_rescue");
}
}
}
/* In a situation like this:
*
* OOXXXX the following code can find the
* .OXOOX defensive move at 'c'.
* .cO.OX
* .X.OOX
* ------
*
* OOXXXX It also can find more general moves like 'c' here.
* .OXOOX
* cXO.OX
* ...OOX
* ------
*/
static void
special_rescue2_moves(int str, int libs[2], struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int newlibs[4];
int liberties;
int newstr;
int k, r, s;
for (r = 0; r < 2; r++) {
/* Let alib be one of the liberties and require it to be suicide
* for the opponent.
*/
int alib = libs[r];
if (!is_suicide(alib, other))
continue;
for (k = 0; k < 4; k++) {
if (board[alib + delta[k]] == color
&& !same_string(alib + delta[k], str)) {
newstr = alib + delta[k];
liberties = findlib(newstr, 4, newlibs);
for (s = 0; s < liberties && s < 4; s++) {
if (!is_self_atari(newlibs[s], color))
ADD_CANDIDATE_MOVE(newlibs[s], 0, *moves, "special_rescue2");
}
break_chain_moves(newstr, moves);
break_chain2_efficient_moves(newstr, moves);
edge_clamp_moves(newstr, moves);
}
}
}
}
/* In a situation like this:
*
* ...X.XXO
* .XXXOOXO
* XXOO.OXO the following code can find the
* .O..X.*. defensive move at '*'.
* --------
*
* OXO cde
* .*. afg
* --- b--
*/
static void
special_rescue3_moves(int str, int libs[3], struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int apos, bpos, cpos, dpos, epos, fpos, gpos;
int k, l, r;
ASSERT1(countlib(str) == 3, str);
for (r = 0; r < 3; r++) {
/* Let (apos) be one of the three liberties. */
apos = libs[r];
/* Try to find the configuration above. */
for (k = 0; k < 4; k++) {
bpos = apos + delta[k];
if (ON_BOARD(bpos))
continue;
cpos = apos - delta[k];
if (board[cpos] != color)
continue;
if (!same_string(cpos, str))
continue;
for (l = 0; l < 2; l++) {
int normal = delta[(k+1)%4];
if (l == 1)
normal = -normal;
dpos = cpos + normal;
if (board[dpos] != other)
continue;
epos = dpos + normal;
if (board[epos] != color)
continue;
fpos = apos + normal;
if (board[fpos] != EMPTY)
continue;
gpos = fpos + normal;
if (board[gpos] != EMPTY)
continue;
/* Configuration found. Now require an X move at 'a' not
* getting too many liberties.
*/
if (approxlib(apos, other, 4, NULL) > 3)
continue;
/* Try to play at (fpos). */
ADD_CANDIDATE_MOVE(fpos, 0, *moves, "special_rescue3");
}
}
}
}
/* This code can find moves to counter attack moves generated by
* special_attack3_moves(). In case such an attack move has only two
* liberties, this function finds the liberty which is not common with
* the attacked string.
*
* For a typical example, see reading:198 where black L7 is generated
* by special_attack3_moves() and the response at L8 is generated by
* this function.
*/
static void
special_rescue4_moves(int str, int libs[2], struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int xpos;
int apos;
int bpos;
int libs2[2];
int k;
int r;
ASSERT1(countlib(str) == 2, str);
for (k = 0; k < 2; k++) {
apos = libs[k];
bpos = libs[1-k];
if (apos == SOUTH(bpos) || apos == NORTH(bpos)) {
if (board[WEST(apos)] == other)
xpos = WEST(apos);
else if (board[EAST(apos)] == other)
xpos = EAST(apos);
else
continue;
}
else if (apos == WEST(bpos) || apos == EAST(bpos)) {
if (board[SOUTH(apos)] == other)
xpos = SOUTH(apos);
else if (board[NORTH(apos)] == other)
xpos = NORTH(apos);
else
continue;
}
else
return; /* Incorrect configuration, give up. */
if (findlib(xpos, 2, libs2) == 2) {
for (r = 0; r < 2; r++)
if (libs2[r] != apos && libs2[r] != bpos
&& !is_self_atari(libs2[r], color))
ADD_CANDIDATE_MOVE(libs2[r], 0, *moves, "special_rescue4");
}
}
}
/* In a situation like this:
*
* .XXXXX
* XX.*OO
* X.OX.. the following code can find the
* ...... defensive move at '*'.
* ------
*
* .* ac
* OX bd
*
* The only requirement is that d has at most as many liberties as b,
* and as the newly placed stone at c.
*/
static void
hane_rescue_moves(int str, int libs[4], struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int apos, bpos, cpos, dpos;
int num_libs = countlib(str);
int k, l, r;
ASSERT1(num_libs <= 4, str);
for (r = 0; r < num_libs; r++) {
/* Let (apos) be one of the three liberties. */
apos = libs[r];
/* Try to find the configuration above. */
for (k = 0; k < 4; k++) {
bpos = apos + delta[k];
if (board[bpos] != color)
continue;
if (!same_string(bpos, str))
continue;
for (l = 0; l < 2; l++) {
int normal = delta[(k+1)%4];
if (l == 1)
normal = -normal;
cpos = apos + normal;
if (board[cpos] != EMPTY)
continue;
dpos = bpos + normal;
if (board[dpos] != other)
continue;
/* Configuration found. Now check liberty constraint. */
{
int dlibs = countlib(dpos);
if (dlibs > num_libs
|| dlibs > accuratelib(cpos, color, dlibs, NULL))
continue;
}
if (0 && !in_list(cpos, moves->num, moves->pos)) {
gprintf("hane_rescue_move added for %1m at %1m\n", str, cpos);
dump_stack();
showboard(0);
}
ADD_CANDIDATE_MOVE(cpos, 0, *moves, "hane_rescue");
}
}
}
}
/* In situations like these
*
* |XXXX |.X... |.X...
* |OOOX |.XOO. |XXOO.
* |..OX |OOXO. |OOXO.
* |O.OX |O.X*O |O.XOO
* |.X*. |O.X.O |O.X*O
* +---- +----- +-----
*
* the smaller of the O strings can be defended by *. The property
* they have in common is that the defended string has (at least) two
* liberties in common with an X string and it's effective to play on
* an exterior liberty of this string. Similarly it may be worth
* defending a weak neighbor of the X string.
*
* This function may be called for strings with 2 or 3 liberties and
* returns moves which are potentially useful in these positions.
*/
static void
special_rescue5_moves(int str, int libs[3],
struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int apos, bpos;
int k, r, s;
int liberties = countlib(str);
int libs2[4];
int liberties2;
ASSERT1(liberties == 2 || liberties == 3, str);
for (r = 0; r < liberties; r++) {
apos = libs[r];
for (k = 0; k < 4; k++) {
bpos = apos + delta[k];
if (board[bpos] != other)
continue;
/* Don't bother if it has too many liberties. */
if (countlib(bpos) > liberties + 1)
continue;
if (count_common_libs(str, bpos) < 2)
continue;
liberties2 = findlib(bpos, 4, libs2);
for (s = 0; s < liberties2; s++)
if (!liberty_of_string(libs2[s], str)
&& !is_self_atari(libs2[s], color))
ADD_CANDIDATE_MOVE(libs2[s], 0, *moves, "special_rescue5-A");
/* Reinforce the second order chain. */
if (liberties2 <= liberties) {
int adj;
int adjs[MAXCHAIN];
int t;
adj = chainlinks2(bpos, adjs, 1);
for (t = 0; t < adj; t++) {
int cpos;
break_chain_moves(adjs[t], moves);
findlib(adjs[t], 1, &cpos);
if (!is_self_atari(cpos, color))
ADD_CANDIDATE_MOVE(cpos, 0, *moves, "special_rescue5-B");
}
/* Defend against double atari in the surrounding chain early. */
double_atari_chain2_moves(bpos, moves, 0);
}
}
}
}
/* In situations like this
*
* |.bOX
* |.Xa.
* |.OXX
* |.O..
* |.XX.
*
* the lower O string can often be defended at a or b.
*
* This function may be called for strings with 3 or 4 liberties and
* returns the * moves in the configuration below:
*
* |..O |.*O
* |.X. |.c*
* |.O? |ab?
*
* It also adds the * move in these configurations:
*
* |.X. |.c*
* |.OX |abX
*
* |.X. |.c*
* |.O. |ab.
*
* Provided that * is not a self atari and that the X strings have
* sufficiently few liberties.
*/
static void
special_rescue6_moves(int str, int libs[3], struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int apos, bpos, cpos;
int right, up;
int k, l, r;
int liberties = countlib(str);
ASSERT1(liberties == 3 || liberties == 4, str);
for (r = 0; r < liberties; r++) {
apos = libs[r];
for (k = 0; k < 4; k++) {
right = delta[k];
if (ON_BOARD(apos - right))
continue;
bpos = apos + right;
if (board[bpos] != color || !same_string(str, bpos))
continue;
for (l = 0; l < 2; l++) {
up = delta[(k+1) % 4];
if (l == 1)
up = -up;
cpos = bpos + up;
if (board[cpos] != other)
continue;
if (board[apos + up] != EMPTY)
continue;
if (board[cpos + right] != EMPTY)
continue;
if (board[apos + up + up] == EMPTY
&& board[cpos + up] == EMPTY
&& board[cpos + up + right] == color) {
ADD_CANDIDATE_MOVE(cpos + right, 0, *moves, "special_rescue6-A");
ADD_CANDIDATE_MOVE(cpos + up, 0, *moves, "special_rescue6-B");
}
else if (countlib(cpos) <= 3
&& (board[bpos + right] == EMPTY
|| (board[bpos + right] == other
&& countlib(bpos + right) <= 4))
&& !is_self_atari(cpos + right, color)) {
ADD_CANDIDATE_MOVE(cpos + right, 0, *moves, "special_rescue6-C");
}
}
}
}
}
/*
* set_up_snapback_moves() is called with (str) a string having a
* single liberty at (lib).
*
* This adds moves which may defend a string in atari by capturing a
* neighbor in a snapback. One example is this position:
*
* OOOOO
* OXXXO
* OX.OX
* OXOXX
* OX*..
* -----
*
* This code also finds the move * to defend the lone O stone with ko
* in this position:
*
* |XXXXX
* |XOOOX
* |OX.OO
* |.*...
* +-----
*
*/
static void
set_up_snapback_moves(int str, int lib, struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int libs2[2];
ASSERT1(countlib(str) == 1, str);
/* This can only work if our string is a single stone and the
* opponent is short of liberties.
*/
if (stackp <= backfill_depth
&& countstones(str) == 1
&& approxlib(lib, other, 2, libs2) == 1
&& (!is_self_atari(libs2[0], color)
|| is_ko(libs2[0], color, NULL)))
ADD_CANDIDATE_MOVE(libs2[0], 0, *moves, "set_up_snapback");
}
/* This function adds liberties of the superstring as candidate moves.
* For performance, this is restricted to strings with liberty_cap
* liberties, and to cases where at most 5 liberties would get considered.
*
* When attacking, we also try backfilling in case the direct approach
* would be self-atari.
* When defending, we also try second order liberties.
*/
static void
superstring_moves(int str, struct reading_moves *moves,
int liberty_cap, int does_attack)
{
int ss_liberties;
int ss_libs[MAX_LIBERTIES + 4];
int color = board[str];
int other = OTHER_COLOR(color);
int k;
find_superstring_liberties(str, &ss_liberties, ss_libs, liberty_cap);
if (ss_liberties <= 5) {
for (k = 0; k < ss_liberties; k++) {
int apos = ss_libs[k];
int alibs[2];
int alib = accuratelib(apos, other, 2, alibs);
if (liberty_of_string(apos, str))
continue;
if (alib >= 2)
ADD_CANDIDATE_MOVE(apos, 0, *moves, "superstring liberty");
else if (alib == 1
&& does_attack
&& board[alibs[0]] == EMPTY
&& approxlib(alibs[0], other, 3, NULL) >= 3)
ADD_CANDIDATE_MOVE(alibs[0], 0, *moves, "superstring backfill");
if (!does_attack)
special_rescue_moves(str, apos, moves);
}
}
}
/* This function is somewhat related to superstring_moves() but tries
* to find moves to squeeze out liberties from the superstring, aiming
* to capture the main string in a shortage of liberties.
*
* For a typical example, see the move E9 in reading:203,204. It is
* assumed that the same move is effective both for attack and
* defense.
*/
static void
squeeze_moves(int str, struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int libs[4];
int num_libs;
int libs2[4];
int num_libs2;
int k;
int r;
int potential_move = NO_MOVE;
int previous_liberty;
num_libs = findlib(str, 4, libs);
gg_assert(num_libs <= 4);
for (k = 0; k < num_libs; k++) {
if (!is_suicide(libs[k], other))
continue;
num_libs2 = approxlib(libs[k], color, 4, libs2);
if (num_libs2 != num_libs)
continue;
for (r = 0; r < num_libs2; r++)
if (!liberty_of_string(libs2[r], str)) {
potential_move = libs2[r];
break;
}
previous_liberty = libs[k];
while (is_suicide(potential_move, other)) {
num_libs2 = approxlib(potential_move, color, 3, libs2);
if (num_libs2 != 2) {
potential_move = NO_MOVE;
break;
}
if (libs2[0] == previous_liberty) {
previous_liberty = potential_move;
potential_move = libs2[1];
}
else {
previous_liberty = potential_move;
potential_move = libs2[0];
}
if (liberty_of_string(potential_move, str)) {
potential_move = NO_MOVE;
break;
}
}
if (potential_move == NO_MOVE
|| !is_self_atari(potential_move, other))
continue;
approxlib(potential_move, other, 1, libs2);
num_libs2 = approxlib(libs2[0], color, MAXLIBS, NULL);
if (num_libs2 < 3
&& num_libs2 < approxlib(potential_move, color, MAXLIBS, NULL))
ADD_CANDIDATE_MOVE(potential_move, 0, *moves, "squeeze move");
}
}
/* In positions like
*
* |.XXOO.
* |XXOX..
* |OOOX*.
* |......
* +------
*
* the O stones to the left are best defended by the move at *.
*
* This function tries to find an adjacent string (apos) with exactly
* three liberties. One of the liberties (bpos) must be on the edge
* (but not in the corner). Diagonal to this liberty must be one stone
* of the attacked string (cpos) and another liberty (dpos) of the
* adjacent string. The third liberty (epos) must be adjacent to
* (dpos). Furthermore must an O stone at (dpos) get at least three
* liberties and and X stone at (epos) must get at most three
* liberties.
*
* |.XXOO.
* |XXOXe.
* |OOcad.
* |...b..
* +------
*
* The defense move at (dpos) is proposed if the above conditions
* are satisfied.
*/
static void
edge_clamp_moves(int str, struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int apos;
int bpos;
int cpos;
int dpos;
int epos;
int adj, adjs[MAXCHAIN];
int libs[3];
int k, l, r;
/* Pick up neighbors with three liberties. */
adj = chainlinks2(str, adjs, 3);
for (r = 0; r < adj; r++) {
apos = adjs[r];
/* Find a liberty at the edge. */
bpos = NO_MOVE;
findlib(apos, 3, libs);
for (k = 0; k < 3; k++) {
if (is_edge_vertex(libs[k])) {
bpos = libs[k];
break;
}
}
if (bpos == NO_MOVE)
continue;
/* Edge liberty found. Establish up and right directions. */
for (k = 0; k < 4; k++) {
int up = delta[k];
if (ON_BOARD(bpos - up))
continue;
if (board[bpos + up] != other)
continue;
for (l = 0; l < 2; l++) {
int right = delta[(k+1)%4];
if (l == 1)
right = -right;
cpos = bpos + up - right;
dpos = bpos + up + right;
if (board[cpos] != color || !same_string(cpos, str))
continue;
if (board[dpos] != EMPTY || !liberty_of_string(dpos, apos))
continue;
epos = dpos + up;
if (board[epos] != EMPTY || !liberty_of_string(epos, apos))
continue;
if (approxlib(dpos, color, 3, NULL) < 3)
continue;
if (approxlib(epos, other, 4, NULL) > 3)
continue;
/* (dpos) looks like a good move. Add it to the list with a
* substantial initial score.
*/
ADD_CANDIDATE_MOVE(dpos, 10, *moves, "edge_clamp");
}
}
}
}
/*
* This function handles some special cases on the edge.
*
* 1. If (str) points to a string and 'a' an edge liberty of it,
* there is no point of trying to defend the string by crawling
* along the edge if there is no hope of ever getting more liberties.
* This is of course if the blocking enemy group has enough liberties
* of its own.
*
* XX XX
* O. Oa
* -- --
*
* This function searches the edge towards the corner and sees if there
* is a friendly stone on one of the two first lines. If not, the move
* is removed from the list of moves.
*
* 2. If (str) points to a string and 'a' an edge liberty of it,
* the drawing back/climbing up move 'b' is often correct attack or
* defense. Another good move to try is 'c' (but usually not for
* defense of a 2 liberty string).
*
* X.? Xbc
* O.. Oa.
* --- ---
*
* This function adds the points configured like 'b' and 'c' relative to
* (str) to the list of moves.
*
* color is the color to move.
*/
static void
propose_edge_moves(int str, int *libs, int liberties,
struct reading_moves *moves, int to_move)
{
int color = board[str];
int other = OTHER_COLOR(color);
int right;
int up;
int apos;
int k, l;
int r;
for (r = 0; r < liberties; r++) {
apos = libs[r];
for (k = 0; k < 4; k++) {
up = delta[k];
if (ON_BOARD(apos - up))
continue;
for (l = 0; l < 2; l++) {
right = delta[(k+1)%4];
if (l == 1)
right = -right;
if (board[apos + up] == other /* other on top of liberty */
&& countlib(apos + up) > 4 /* blocking group must be secure */
&& color == to_move) { /* only applicable as defense */
/* Case 1: other above the liberty (crawl along the edge). */
int xpos = apos;
while (ON_BOARD(xpos)) {
if (board[xpos] == color
|| board[xpos + up] == color)
break;
xpos += right;
}
/* If no friendly stone found, then it is pointless and we
* can just as well remove the move.
*/
if (!ON_BOARD(xpos)) {
REMOVE_CANDIDATE_MOVE(apos, *moves);
}
}
else if (board[apos + up] == EMPTY /* empty above the liberty */
&& board[apos - right + up] == other
&& board[apos + right] == EMPTY) { /* empty to the right */
/* Case 2: Try to escape or contain. */
/* Add b
* If adjacent X stone in atari, boost the initial score of this
* move.
*/
if (countlib(apos + up - right) == 1)
ADD_CANDIDATE_MOVE(apos + up, 10, *moves, "propose_edge-A");
else {
ADD_CANDIDATE_MOVE(apos + up, 0, *moves, "propose_edge-B");
/* Add c if empty */
if (board[apos + right + up] == EMPTY
&& (liberties != 2 || color != to_move))
ADD_CANDIDATE_MOVE(apos + right + up, 0, *moves,
"propose_edge-C");
}
}
}
}
}
}
/* ================================================================ */
/* Attacking functions */
/* ================================================================ */
/* Like attack. If the opponent is komaster reading functions will not try
* to take ko.
*/
static int
do_attack(int str, int *move)
{
int color = board[str];
int xpos = NO_MOVE;
int liberties;
int result = 0;
int retval;
SETUP_TRACE_INFO("attack", str);
ASSERT1(color != 0, str);
if (color == 0) /* if assertions are turned off, silently fails */
return 0;
str = find_origin(str);
liberties = countlib(str);
if (liberties > 4
|| (liberties == 4 && stackp > fourlib_depth)
|| (liberties == 3 && stackp > depth)) {
/* No need to cache the result in these cases. */
if (sgf_dumptree) {
char buf[100];
sprintf(buf, "got 4 liberties (stackp:%d>%d)",
stackp, fourlib_depth);
SGFTRACE(0, 0, buf);
}
return 0;
}
/* Set "killer move" up. This move (if set) was successful in
* another variation, so it is reasonable to try it now. However,
* we only do this if the string has 4 liberties - otherwise the
* situation changes too much from variation to variation.
*/
if (liberties > 3 && move)
xpos = *move;
/* Note that if return value is 1 (too small depth), the move will
* still be used for move ordering.
*/
if (stackp <= depth
&& tt_get(&ttable, ATTACK, str, NO_MOVE, depth - stackp, NULL,
&retval, NULL, &xpos) == 2) {
TRACE_CACHED_RESULT(retval, xpos);
SGFTRACE(xpos, retval, "cached");
if (move)
*move = xpos;
return retval;
}
/* Treat the attack differently depending on how many liberties the
string at (str) has. */
if (liberties == 1)
result = attack1(str, &xpos);
else if (liberties == 2) {
if (stackp > depth + 10)
result = simple_ladder(str, &xpos);
else
result = attack2(str, &xpos);
}
else if (liberties == 3)
result = attack3(str, &xpos);
else if (liberties == 4)
result = attack4(str, &xpos);
ASSERT1(result >= 0 && result <= WIN, str);
if (result) {
READ_RETURN(ATTACK, str, depth - stackp, move, xpos, result);
}
READ_RETURN0(ATTACK, str, depth - stackp);
}
/* If (str) points to a group with exactly one liberty, attack1
* determines whether it can be captured by playing at this liberty.
* If successful, (*move) is the killing move. move may be NULL if
* caller is only interested in whether it can be captured.
*
* The attack may fail for two different reasons. The first one is
* that the attack may be an illegal ko capture, in this case KO_B is
* returned (need to play a ko threat before the attack can be
* fulfilled).
*
* The second cause for failure is that the attack is caught in a
* snapback. We must require that it is a proper snapback, though. By
* proper snapback we mean a position like
*
* XXXXO
* XO.XO
* XOXOO
* -----
*
* where capture by O and recapture by X leaves the X stone intact
* with at least two liberties:
*
* XXXXO
* X..XO
* X.XOO
* -----
*
* There are a number of different kinds of improper snapbacks, which
* have in common that the attacked string ends up captured. We don't
* consider these as failures to attack. Three examples are given below.
*
* XXOOOOO (X can recapture but loses most of the string.)
* X.XXXXO
* -------
*
* XXXOOOOOOOO (Like the previous example, except O loses one more stone)
* XO*XXXXXXXO
* -----------
*
* XXXOO (After three captures, the lone X stone is gone.)
* XO.XO
* -----
*
* This function is fast and never branches. There's little point in
* caching the result.
*/
static int
attack1(int str, int *move)
{
int color = board[str];
int other = OTHER_COLOR(color);
int xpos;
int savemove = 0;
int savecode = 0;
int liberties;
int libs[6];
int k;
int r;
int adjs[MAXCHAIN];
int adj;
int apos;
SETUP_TRACE_INFO("attack1", str);
reading_node_counter++;
/* Pick up the position of the liberty. */
findlib(str, 1, &xpos);
/* If the attacked string consists of more than one stone, the
* attack never fails. (This assumes simple ko rule. With superko
* rule it could still be a ko violation.)
*/
if (countstones(str) > 1) {
RETURN_RESULT(WIN, xpos, move, "last liberty");
}
/* Try to play on the liberty. This fails if and only if it is an
* illegal ko capture.
*/
if (trymove(xpos, other, "attack1-A", str)) {
/* Is the attacker in atari? If not the attack was successful. */
if (countlib(xpos) > 1) {
popgo();
RETURN_RESULT(WIN, xpos, move, "last liberty");
}
/* If the attacking string is also a single stone, a possible
* recapture would be a ko violation, so the defender has to make
* a ko threat first.
*/
else if (countstones(xpos) == 1) {
if (get_komaster() != other) {
/* If the defender is allowed to take the ko the result is KO_A. */
CHECK_RESULT_UNREVERSED(savecode, savemove, KO_A, xpos, move,
"last liberty - ko");
}
else {
/* But if the attacker is the attack was successful. */
popgo();
RETURN_RESULT(WIN, xpos, move, "last liberty");
}
}
/* Otherwise, do recapture. Notice that the liberty must be
* at (str) since we have already established that this string
* was a single stone.
*/
else if (trymove(str, color, "attack1-B", str)) {
/* If this was a proper snapback, (str) will now have more
* than one liberty.
*/
if (countlib(str) > 1) {
/* Proper snapback, attack fails. */
popgo();
}
else {
popgo();
popgo();
RETURN_RESULT(WIN, xpos, move, "last liberty");
}
}
popgo();
}
else {/* Illegal ko capture. */
if (get_komaster() != color) {
CHECK_RESULT_UNREVERSED(savecode, savemove, KO_B, xpos, move,
"last liberty - ko");
}
}
/* If not yet successful, try backfilling and back-capturing.
* An example of back-capturing can be found in reading:234.
* Backfilling is maybe only meaningful in positions involving ko.
*/
liberties = approxlib(xpos, color, 6, libs);
if (liberties <= 5)
for (k = 0; k < liberties; k++) {
apos = libs[k];
if (!is_self_atari(apos, other)
&& trymove(apos, other, "attack1-C", str)) {
int dcode = do_find_defense(str, NULL);
if (dcode != WIN && do_attack(str, NULL)) {
if (dcode == 0) {
popgo();
RETURN_RESULT(WIN, apos, move, "backfilling");
}
UPDATE_SAVED_KO_RESULT(savecode, savemove, dcode, apos);
}
popgo();
}
}
adj = chainlinks2(str, adjs, 1);
for (r = 0; r < adj; r++) {
if (liberty_of_string(xpos, adjs[r])) {
int adjs2[MAXCHAIN];
int adj2;
adj2 = chainlinks2(adjs[r], adjs2, 1);
for (k = 0; k < adj2; k++) {
int ko_move;
if (adjs2[k] == str)
continue;
findlib(adjs2[k], 1, &apos);
if (komaster_trymove(apos, other, "attack1-D", str,
&ko_move, stackp <= ko_depth && savecode == 0)) {
if (!ko_move) {
int dcode = do_find_defense(str, NULL);
if (dcode != WIN
&& do_attack(str, NULL)) {
popgo();
CHECK_RESULT(savecode, savemove, dcode, apos, move,
"attack effective");
}
else
popgo();
}
else {
if (do_find_defense(str, NULL) != WIN
&& do_attack(str, NULL) != 0) {
savemove = apos;
savecode = KO_B;
}
popgo();
}
}
}
}
}
if (savecode == 0) {
RETURN_RESULT(0, 0, move, NULL);
}
RETURN_RESULT(savecode, savemove, move, "saved move");
}
/* If str points to a group with exactly two liberties
* attack2 determines whether it can be captured in ladder or net.
* If yes, *move is the killing move. move may be null if caller
* is only interested in whether it can be captured.
*
* Returns KO_A or KO_B if it can be killed conditioned on ko. Returns
* KO_A if it can be killed provided (other) is willing to ignore any
* ko threat. Returns KO_B if (other) wins provided he has a ko threat
* which must be answered. Can give a return code KO_B yet *move=0 if
* the winning move is an illegal ko capture. In this case, making a
* ko threat and having it answered should transform the position to
* one where the return code is KO_A.
*
* See the comment before defend1 about ladders and reading depth.
*/
static int
attack2(int str, int *move)
{
int color = board[str];
int other = OTHER_COLOR(color);
int hpos;
int xpos = NO_MOVE;
int liberties, r;
int libs[2];
int libs2[2];
int adj, adjs[MAXCHAIN];
int savemove = 0;
int savecode = 0;
int k;
int atari_possible = 0;
struct reading_moves moves;
int adjacent_liberties = 0;
int pass;
int suggest_move = NO_MOVE;
SETUP_TRACE_INFO("attack2", str);
reading_node_counter++;
moves.num = 0;
moves.num_tried = 0;
str = find_origin(str);
ASSERT1(IS_STONE(board[str]), str);
ASSERT1(countlib(str) == 2, str);
for (pass = 0; pass < 4; pass++) {
switch (pass) {
case 0:
/* The attack may fail if a boundary string is in atari and cannot
* be defended. First we must try defending such a string.
*
* We start by trying to defend the boundary string by looking for an
* adjacent string which is in atari.
*/
adj = chainlinks2(str, adjs, 1);
for (r = 0; r < adj; r++) {
/* If stackp > depth and any boundary chain is in atari, assume safe.
* However, if the captured chain is only of size 1, there can still
* be a working ladder, so continue if that is the case.
* Also if the string in atari shares its liberty with the
* attacked string, drawing it out may enable the ladder to
* continue.
*/
if (stackp > depth
&& countstones(adjs[r]) > 1
&& !have_common_lib(str, adjs[r], NULL)) {
RETURN_RESULT(0, 0, move, "boundary in atari");
}
/* Pick up moves breaking the second order chain. */
if (stackp <= depth)
break_chain_moves(adjs[r], &moves);
findlib(adjs[r], 1, &hpos);
ADD_CANDIDATE_MOVE(hpos, 0, moves, "save_boundary");
}
/* Get the two liberties of (str). */
liberties = findlib(str, 2, libs);
ASSERT1(liberties == 2, str);
if (DIRECT_NEIGHBORS(libs[0], libs[1]))
adjacent_liberties = 1;
for (k = 0; k < 2; k++) {
int apos = libs[k];
if (!is_self_atari(apos, other))
atari_possible = 1;
/* We only want to consider the move at (apos) if:
* stackp <= backfill_depth
* -or- stackp <= depth and it is an isolated stone
* -or- it is not in immediate atari
*/
if (stackp <= backfill_depth
|| ((stackp <= depth || adjacent_liberties)
&& !has_neighbor(apos, other))
|| !is_self_atari(apos, other))
ADD_CANDIDATE_MOVE(apos, 0, moves, "liberty");
/* Try backfilling if atari is impossible. */
if (stackp <= backfill_depth
&& approxlib(apos, other, 2, libs2) == 1) {
ADD_CANDIDATE_MOVE(libs2[0], 0, moves, "backfill");
/* If there is a neighbor in atari, we also try back-capturing. */
for (r = 0; r < 4; r++) {
int bpos = libs2[0] + delta[r];
if (board[bpos] == other && chainlinks2(bpos, adjs, 1) > 0) {
/* FIXME: If there is more than one neighbor in atari, we
* currently just take one randomly. This is maybe not good
* enough. We might also want to check against snapback.
*
* FIXME: What is the purpose of this? It produces some
* completely irrelevant moves (e.g. if bpos is a huge string
* with many liberties and adjs[0] is somewhere else on the
* board).
*/
findlib(adjs[0], 1, &xpos);
ADD_CANDIDATE_MOVE(xpos, 0, moves, "back-capture");
}
}
}
}
/* If we can't make a direct atari, look for edge blocking moves. */
if (!atari_possible)
for (k = 0; k < 2; k++)
edge_block_moves(str, libs[k], &moves);
/* If one of the surrounding chains have only two liberties, which
* coincide with the liberties of the attacked string, we try to
* backcapture.
*/
adj = chainlinks2(str, adjs, 2);
for (r = 0; r < adj; r++) {
int apos = adjs[r];
if (liberty_of_string(libs[0], apos)
&& liberty_of_string(libs[1], apos))
break_chain_moves(apos, &moves);
}
propose_edge_moves(str, libs, liberties, &moves, other);
break;
case 1:
if (stackp <= backfill_depth) {
special_attack2_moves(str, libs, &moves);
special_attack3_moves(str, libs, &moves);
special_attack4_moves(str, libs, &moves);
}
break;
case 2:
find_cap_moves(str, &moves);
break;
case 3:
/* If it is not possible to make a direct atari, we try filling
* a liberty of the superstring.
*/
if (get_level() >= 8
&& stackp <= backfill_depth
&& (stackp <= superstring_depth || !atari_possible)) {
int liberty_cap = 2;
if (stackp <= backfill2_depth)
liberty_cap = 3;
superstring_moves(str, &moves, liberty_cap, 1);
squeeze_moves(str, &moves);
}
break;
default:
abort();
} /* switch (pass) */
order_moves(str, &moves, other, read_function_name, *move);
ATTACK_TRY_MOVES(0, &suggest_move);
}
RETURN_RESULT(savecode, savemove, move, "saved move");
}
/* attack3(str, *move) is used when (str) points to a group with
* three liberties. It returns true if it finds a way to kill the group.
*
* Return code is KO_A if the group can be killed if the attacker is
* willing to ignore any ko threat.
*
* Return code is KO_B if the group can be killed if the attacker is
* able to find a ko threat which must be answered.
*
* If non-NULL (*move) will be set to the move which makes the
* attack succeed.
*/
static int
attack3(int str, int *move)
{
int color = board[str];
int other = OTHER_COLOR(color);
int adj, adjs[MAXCHAIN];
int liberties;
int libs[3];
int r;
int k;
struct reading_moves moves;
int savemove = 0;
int savecode = 0;
int pass;
int suggest_move = NO_MOVE;
SETUP_TRACE_INFO("attack3", str);
reading_node_counter++;
moves.num = 0;
moves.num_tried = 0;
ASSERT1(IS_STONE(board[str]), str);
ASSERT1(stackp <= depth, str);
for (pass = 0; pass < 4; pass++) {
switch (pass) {
case 0:
adj = chainlinks2(str, adjs, 1);
for (r = 0; r < adj; r++) {
int hpos;
break_chain_moves(adjs[r], &moves);
findlib(adjs[r], 1, &hpos);
ADD_CANDIDATE_MOVE(hpos, 0, moves, "save_boundary");
}
/* Defend against double atari in the surrounding chain early. */
double_atari_chain2_moves(str, &moves, stackp <= superstring_depth);
/* Get the three liberties of (str). */
liberties = findlib(str, 3, libs);
ASSERT1(liberties == 3, str);
for (k = 0; k < 3; k++) {
#if 0
int libs2[2];
#endif
int apos = libs[k];
/* We only want to consider the move at (apos) if:
* stackp <= backfill_depth
* -or- stackp <= depth and it is an isolated stone
* -or- it is not in immediate atari
*/
if (stackp <= backfill_depth
|| (stackp <= depth
&& !has_neighbor(apos, other))
|| !is_self_atari(apos, other))
ADD_CANDIDATE_MOVE(apos, 0, moves, "liberty");
edge_closing_backfill_moves(str, apos, &moves);
#if 0
/* Try backfilling if atari is impossible. */
if (stackp <= backfill_depth
&& approxlib(apos, other, 2, libs2) == 1) {
ADD_CANDIDATE_MOVE(libs2[0], 0, moves, "backfill");
}
#endif
/* Look for edge blocking moves. */
edge_block_moves(str, apos, &moves);
}
/* Pick up some edge moves. */
propose_edge_moves(str, libs, liberties, &moves, other);
break;
case 1:
/* The simple ataris didn't work. Try something more fancy. */
if (stackp <= backfill_depth)
find_cap_moves(str, &moves);
if (stackp <= fourlib_depth)
draw_back_moves(str, &moves);
break;
case 2:
/* Try to defend chain links with two liberties. */
if (stackp <= backfill2_depth) {
adj = chainlinks2(str, adjs, 2);
for (r = 0; r < adj; r++) {
int libs2[2];
findlib(adjs[r], 2, libs2);
if (approxlib(libs2[0], other, 4, NULL) > 3
&& approxlib(libs2[1], other, 4, NULL) > 3)
continue;
break_chain_moves(adjs[r], &moves);
break_chain2_moves(adjs[r], &moves, 1, 0);
for (k = 0; k < 2; k++)
ADD_CANDIDATE_MOVE(libs2[k], 0, moves, "save_boundary-2");
}
}
break;
case 3:
/* If nothing else works, we try filling a liberty of the
* super_string.
*/
if (get_level() >= 8 && stackp <= backfill2_depth) {
superstring_moves(str, &moves, 3, 1);
squeeze_moves(str, &moves);
}
break;
default:
abort();
}
order_moves(str, &moves, other, read_function_name, *move);
ATTACK_TRY_MOVES(1, &suggest_move);
} /* for (pass... */
RETURN_RESULT(savecode, savemove, move, "saved move");
}
/* attack4 tries to capture a string with 4 liberties. */
static int
attack4(int str, int *move)
{
int color = board[str];
int other = OTHER_COLOR(color);
int r;
int k;
int liberties;
int libs[4];
int adj, adjs[MAXCHAIN];
struct reading_moves moves;
int savemove = 0;
int savecode = 0;
int pass;
int suggest_move = NO_MOVE;
SETUP_TRACE_INFO("attack4", str);
ASSERT1(IS_STONE(board[str]), str);
reading_node_counter++;
moves.num = 0;
moves.num_tried = 0;
if (stackp > depth) {
SGFTRACE(0, 0, "stackp > depth");
return 0;
}
for (pass = 0; pass < 2; pass++) {
switch (pass) {
case 0:
adj = chainlinks2(str, adjs, 1);
for (r = 0; r < adj; r++) {
int hpos;
break_chain_moves(adjs[r], &moves);
findlib(adjs[r], 1, &hpos);
ADD_CANDIDATE_MOVE(hpos, 0, moves, "save_boundary");
}
/* Defend against double atari in the surrounding chain early. */
double_atari_chain2_moves(str, &moves, stackp <= superstring_depth);
/* Give a score bonus to the chain preserving moves. */
for (k = 0; k < moves.num; k++)
moves.score[k] += 5;
/* Get the four liberties of (str). */
liberties = findlib(str, 4, libs);
ASSERT1(liberties == 4, str);
for (k = 0; k < 4; k++) {
int apos = libs[k];
/* We only want to consider the move at (apos) if:
* stackp <= backfill_depth
* -or- stackp <= depth and it is an isolated stone
* -or- it is not in immediate atari
*/
if (stackp <= backfill_depth
|| (stackp <= depth
&& !has_neighbor(apos, other))
|| !is_self_atari(apos, other))
ADD_CANDIDATE_MOVE(apos, 0, moves, "liberty");
edge_closing_backfill_moves(str, apos, &moves);
/* Look for edge blocking moves. */
edge_block_moves(str, apos, &moves);
}
/* Pick up some edge moves. */
propose_edge_moves(str, libs, liberties, &moves, other);
break;
case 1:
if (stackp <= backfill_depth)
find_cap_moves(str, &moves);
break;
default:
abort();
}
order_moves(str, &moves, other, read_function_name, *move);
ATTACK_TRY_MOVES(1, &suggest_move);
} /* for (pass = ... */
RETURN_RESULT(savecode, savemove, move, "saved move");
}
/* If (str) points to a string with 2 - 4 liberties,
* find_cap_moves(str, &moves)
* looks for a configuration of the following type:
*
* Xa
* b*
*
* where X are elements of the string in question and a and b are
* two of its liberties.
*
* For larger strings, this can find moves like
*
* XXXXX
* XX.XX
* X.*.X
* XX.XX
* XXXXX
*
* even though they are not capping moves.
*/
static void
find_cap_moves(int str, struct reading_moves *moves)
{
int alib, blib;
int numlibs;
int libs[4];
int i, j;
int ai, aj;
int bi, bj;
numlibs = findlib(str, 4, libs);
if (numlibs > 4 || numlibs < 2)
return;
for (i = 0; i < numlibs - 1; i++) {
for (j = i + 1; j < numlibs; j++) {
alib = libs[i];
blib = libs[j];
/* Check if the two liberties are located like the figure above. */
if (!DIAGONAL_NEIGHBORS(alib, blib))
continue;
ai = I(alib);
aj = J(alib);
bi = I(blib);
bj = J(blib);
/* Which of the two corner points should we use? One of them is
* always occupied by the string at (str), the other one is either
* free or occupied by something else.
*/
if (BOARD(bi, aj) == EMPTY)
ADD_CANDIDATE_MOVE(POS(bi, aj), 10, *moves, "find_cap");
else if (BOARD(ai, bj) == EMPTY)
ADD_CANDIDATE_MOVE(POS(ai, bj), 10, *moves, "find_cap");
}
}
}
/* In a situation like this:
*
* ----- the code that
* cO.OX follows can find
* XXOOX the attacking move
* XO.OX at c.
* XOOOX
* XXXXX
*
* The name of the function corresponds to special_rescue2, which is
* fairly similar to this situation.
*/
static void
special_attack2_moves(int str, int libs[2], struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int newlibs[3];
int xpos;
int k;
for (k = 0; k < 2; k++) {
if (is_suicide(libs[k], other)
&& (approxlib(libs[k], color, 3, newlibs) == 2)) {
if (newlibs[0] != libs[1-k])
xpos = newlibs[0];
else
xpos = newlibs[1];
if (!is_self_atari(xpos, other)) {
ADD_CANDIDATE_MOVE(xpos, 0, *moves, "special_attack2");
}
}
}
}
/* In situations like these:
*
* ..XXX.. ...XX
* .XX.XX. .cO.X
* XXOOOXX ....X
* XO.O.OX XOOXX
* XO.c.OX XXXX.
* -------
*
* the code that follows can find the attacking move at c.
*/
static void
special_attack3_moves(int str, int libs[2], struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int xpos;
int apos;
int bpos;
int k;
ASSERT1(countlib(str) == 2, str);
for (k = 0; k < 2; k++) {
apos = libs[k];
bpos = libs[1-k];
if (apos == SOUTH(bpos) || apos == NORTH(bpos)) {
if (board[WEST(apos)] == EMPTY)
xpos = WEST(apos);
else if (board[EAST(apos)] == EMPTY)
xpos = EAST(apos);
else
continue;
}
else if (apos == WEST(bpos) || apos == EAST(bpos)) {
if (board[SOUTH(apos)] == EMPTY)
xpos = SOUTH(apos);
else if (board[NORTH(apos)] == EMPTY)
xpos = NORTH(apos);
else
continue;
}
else
return; /* Incorrect configuration, give up. */
if (!is_self_atari(xpos, other))
ADD_CANDIDATE_MOVE(xpos, 0, *moves, "special_attack3");
}
}
/* In situations like these:
*
* ...O.O... ...O.O...
* XXXXOOXXX XXXXOOXXX
* XOOOXXO*. Xsssbbcd.
* .X.O..... .X.sa.e..
* --------- ---------
*
* the code that follows can find the attacking move at *.
*
* Also for situations in which c has three liberties, one of which in common
* with b, the respective attacking move is found (see reading:52 for an
* example).
*/
static void
special_attack4_moves(int str, int libs[2], struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int adj, adjs[MAXCHAIN];
int adj2, adjs2[MAXCHAIN];
int libs2[3];
int apos;
int bpos = 0;
int cpos;
int dpos;
int epos;
int clibs;
int dlibs;
int elibs;
int bc_common_lib;
int k, s, t, u;
ASSERT1(countlib(str) == 2, str);
/* To avoid making this too general, we require that both
* liberties are self ataris for X.
*/
if (!is_self_atari(libs[0], other)
|| !is_self_atari(libs[1], other))
return;
/* Pick up chain links with 2 liberties. */
adj = chainlinks2(str, adjs, 2);
for (k = 0; k < 2; k++) {
apos = libs[k];
/* Check that (apos) also is a liberty of one of the two liberty
* chain links.
*/
for (s = 0; s < adj; s++)
if (liberty_of_string(apos, adjs[s])) {
bpos = adjs[s];
break;
}
/* Nothing found. */
if (s == adj)
continue;
/* Now require that (bpos) has a chain link, different from (str),
* also with two liberties, or with three liberties, but one in common
* with (bpos).
*/
adj2 = chainlinks3(bpos, adjs2, 3);
for (s = 0; s < adj2; s++) {
cpos = adjs2[s];
if (same_string(cpos, str))
continue;
/* Pick up the liberties of (cpos). */
clibs = findlib(cpos, 3, libs2);
/* No need to do something fancy if it is in atari already. */
if (clibs < 2)
continue;
/* (cpos) has three liberties, none of which in commmon with (bpos)
* attacking it seems too difficult. */
bc_common_lib = have_common_lib(bpos, cpos, NULL);
if (clibs > 2 && !bc_common_lib)
continue;
/* Try playing at a liberty. Before doing this, verify that
* (cpos) cannot get more than three liberties by answering on
* another liberty and that we are not putting ourselves in atari.
* We also should only allow ourselves to get fewer liberties than
* the defender in case (bpos) and (cpos) have a common liberty.
*/
for (t = 0; t < clibs; t++) {
dpos = libs2[t];
if (is_self_atari(dpos, other))
continue;
for (u = 0; u < clibs; u++) {
if (t == u)
continue;
epos = libs2[u];
elibs = approxlib(epos, color, 4, NULL);
if (elibs > 3)
break;
dlibs = approxlib(dpos, other, 3, NULL);
if (elibs > dlibs && !bc_common_lib)
break;
}
if (u >= clibs) /* No break occurred. */
ADD_CANDIDATE_MOVE(dpos, 0, *moves, "special_attack4");
}
}
}
}
/*
* If (str) points to a string, draw_back(str, &moves)
* looks for a move in the following configuration which attacks
* the string:
*
* X* X=attacker, O=defender
* O.
*
* In the initial implementation we consider cases
* where X has exactly 2 liberties.
*
*/
static void
draw_back_moves(int str, struct reading_moves *moves)
{
int r, k;
int adj, adjs[MAXCHAIN];
int libs[2];
adj = chainlinks2(str, adjs, 2);
for (r = 0; r < adj; r++) {
findlib(adjs[r], 2, libs);
for (k = 0; k < 2; k++) {
if (!liberty_of_string(libs[k], str)
&& ((ON_BOARD1(SOUTH(libs[k]))
&& liberty_of_string(SOUTH(libs[k]), str))
|| (ON_BOARD1(WEST(libs[k]))
&& liberty_of_string(WEST(libs[k]), str))
|| (ON_BOARD1(NORTH(libs[k]))
&& liberty_of_string(NORTH(libs[k]), str))
|| (ON_BOARD1(EAST(libs[k]))
&& liberty_of_string(EAST(libs[k]), str)))) {
ADD_CANDIDATE_MOVE(libs[k], 0, *moves, "draw_back");
}
}
}
}
/* In the following position the reading is much simplifed if we start
* with the edge closing backfilling move at *.
*
* |OO...
* |.OOO.
* |.X.O.
* |XXXO.
* |.X.*.
* +-----
*
* This function identifies the situation
*
* ?XOb
* Xatc
* ----
*
* where a is a liberty of the attacked string, t is the proposed move,
* and b and c do not contain more O stones than X stones.
*/
static void
edge_closing_backfill_moves(int str, int apos, struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int k;
int bpos;
int cpos;
int number_x, number_o;
for (k = 0; k < 4; k++) {
int up = delta[k];
int right = delta[(k+1)%4];
if (ON_BOARD(apos - up))
continue;
if (board[apos + up] != color)
return;
if (board[apos + right] == EMPTY
&& (!ON_BOARD(apos - right)
|| board[apos - right] == color))
; /* Everything ok so far. */
else if (board[apos - right] == EMPTY
&& (!ON_BOARD(apos + right)
|| board[apos + right] == color)) {
/* Negate right direction. */
right = -right;
}
else
return;
if (board[apos + up + right] != other)
return;
bpos = apos + up + 2 * right;
if (!ON_BOARD(bpos))
return;
cpos = apos + 2 * right;
number_x = 0;
number_o = 0;
if (board[bpos] == color)
number_x++;
else if (board[bpos] == other)
number_o++;
if (board[cpos] == color)
number_x++;
else if (board[cpos] == other)
number_o++;
if (number_o > number_x)
return;
ADD_CANDIDATE_MOVE(apos + right, 0, *moves, "edge_closing_backfill");
return;
}
}
/* The first version of this function seemed to induce too many
* variations and has therefore been replaced by a much more limited
* version.
*/
#if 0
/* In positions like
*
* OO...
* XXO*.
* x.X*.
* -----
*
* where the X stones to the left are being attacked, it is often a
* good idea to first consider either or both of the moves marked by *
* in the diagram. Notice that propose_edge_moves() doesn't help with
* this, since the rightmost X stone is not part of the attacked
* string, only the corresponding superstring.
*
* This function identifies the situation
*
* ?XO.? ?bdf?
* ?.X.o haceg
* ----- -----
*
* where a is a liberty of the attacked string, b is a stone of the
* attacked string, and e and f are the considered moves. Also
* considered is the situation where the conditions to the right are
* not correct but c has only two liberties anyway. If safe, the move
* to make atari on c is proposed.
*
* Notice, this code is disabled, as commented above.
*/
static void
edge_block_moves(int str, int apos, struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int cpos;
int dpos;
int epos;
int fpos;
int gpos;
int hpos;
int score;
int k, l;
/* Search for the right orientation. */
for (k = 0; k < 4; k++) {
int up = delta[k];
if (ON_BOARD(apos - up))
continue;
if (board[apos + up] != color || !same_string(apos + up, str))
return;
for (l = 0; l < 2; l++) {
int right = delta[(k+1)%4];
if (l == 1)
right = -right;
cpos = apos + right;
dpos = apos + right + up;
if (board[cpos] != color || board[dpos] != other)
continue;
epos = cpos + right;
fpos = dpos + right;
gpos = epos + right;
hpos = apos - right;
if (!ON_BOARD(epos))
continue;
if (board[epos] == EMPTY && board[fpos] == EMPTY
&& (board[gpos] != color)) {
/* Everything is set up, suggest moves at e and f. */
if (!ON_BOARD(hpos) || board[hpos] == color)
score = 0;
else
score = -5;
if (countlib(str) == 2)
score -= 10;
ADD_CANDIDATE_MOVE(epos, score, *moves, "edge_block-A");
if (countlib(dpos) == 1)
score = 25;
else
score = 0;
if (countlib(str) == 2)
score -= 10;
ADD_CANDIDATE_MOVE(fpos, score, *moves, "edge_block-B");
}
else if (countlib(cpos) == 2 && countlib(dpos) > 1) {
int libs[2];
int move;
findlib(cpos, 2, libs);
if (libs[0] == apos)
move = libs[1];
else
move = libs[0];
if (!is_self_atari(move, other))
ADD_CANDIDATE_MOVE(move, 0, *moves, "edge_block-C");
}
}
}
}
#else
/* In positions like
*
* OOX..
* XXO*.
* x.X..
* -----
*
* where the X stones to the left are being attacked, it is usually
* important to start by considering the move at *. Thus we propose
* the move at * with a high initial score.
*
* Also, it is often needed to prevent "crawling" along first line
* which can eventually give defender more liberties, like here:
*
* O.OO..X
* OXXO..X
* ...X*..
* -------
*
* This function identifies the situation
*
* XO.? bdf?
* .X.o aceg
* ---- ----
*
* where a is a liberty of the attacked string, b is a stone of the
* attacked string, and e and f are the considered moves.
*/
static void
edge_block_moves(int str, int apos, struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int k;
/* Search for the right orientation. */
for (k = 0; k < 4; k++) {
int l;
int up = delta[k];
if (ON_BOARD(apos - up))
continue;
if (board[apos + up] != color || !same_string(apos + up, str))
return;
for (l = 0; l < 2; l++) {
int right = delta[(k+1)%4];
int cpos;
int dpos;
int epos;
int fpos;
if (l == 1)
right = -right;
cpos = apos + right;
dpos = apos + right + up;
epos = cpos + right;
fpos = dpos + right;
if (board[cpos] == color && board[dpos] == other
&& board[epos] == EMPTY && board[fpos] == EMPTY) {
if (countlib(dpos) == 1) {
int gpos = epos + right;
/* Check if we have the first situation. */
if (board[gpos] != color)
ADD_CANDIDATE_MOVE(fpos, 30, *moves, "edge_block-A");
}
else {
int edge_scan;
/* Look along board edge to see if the defender's string can
* run away to a friend.
*/
for (edge_scan = epos; ; edge_scan += right) {
if (board[edge_scan] == color || board[edge_scan + up] == color) {
ADD_CANDIDATE_MOVE(epos, 10, *moves, "edge_block-B");
break;
}
if (board[edge_scan] != EMPTY || board[edge_scan + up] != EMPTY)
break;
}
}
}
}
}
}
#endif
/* ================================================================ */
/* Defending by attacking surrounding strings */
/* ================================================================ */
/* Add the chainbreaking moves relative to the string (str) to the
* (moves) struct.
*/
static void
break_chain_moves(int str, struct reading_moves *moves)
{
int r;
int xpos;
int adj, adjs[MAXCHAIN];
/* Find links in atari. */
adj = chainlinks2(str, adjs, 1);
for (r = 0; r < adj; r++) {
findlib(adjs[r], 1, &xpos);
ADD_CANDIDATE_MOVE(xpos, 1, *moves, "break_chain");
}
}
/* defend_secondary_chain1_moves() tries to break a chain by defending
* "secondary chain", that is, own strings surrounding a given
* opponent string (which is in turn a chainlink for another own
* string, phew... :). It only defends own strings in atari.
*
* When defending is done by stretching, it is required that the defending
* stone played gets at least `min_liberties', or one less if it is
* adjacent to the opponent chainlink.
*
* Returns true if there where any secondary strings that needed defence
* (which does not imply they actually where defended).
*/
static int
defend_secondary_chain1_moves(int str, struct reading_moves *moves,
int min_liberties)
{
int r, s;
int color = OTHER_COLOR(board[str]);
int xpos;
int adj;
int adj2;
int adjs[MAXCHAIN];
int adjs2[MAXCHAIN];
/* Find links in atari. */
adj = chainlinks2(str, adjs, 1);
for (r = 0; r < adj; r++) {
/* Stretch out. */
findlib(adjs[r], 1, &xpos);
if (approxlib(xpos, color, min_liberties, NULL)
+ neighbor_of_string(xpos, str) >= min_liberties)
ADD_CANDIDATE_MOVE(xpos, 0, *moves, "defend_secondary_chain1-A");
/* Capture adjacent stones in atari, if any. */
adj2 = chainlinks2(adjs[r], adjs2, 1);
for (s = 0; s < adj2; s++) {
findlib(adjs2[s], 1, &xpos);
if (!is_self_atari(xpos, color))
ADD_CANDIDATE_MOVE(xpos, 0, *moves, "defend_secondary_chain1-B");
}
}
return adj;
}
/* defend_secondary_chain2_moves() tries to break a chain by defending
* "secondary chain", that is, own strings surrounding a given
* opponent string (which is in turn a chainlink for another own
* string, phew... :). It only defends own strings in
* with two liberties.
*
* When defending is done by stretching, it is required that the defending
* stone played gets at least `min_liberties', or one less if it is
* adjacent to the opponent chainlink. Defence can also be done by capturing
* opponent stones or trying to capture them with an atari.
*/
static void
defend_secondary_chain2_moves(int str, struct reading_moves *moves,
int min_liberties)
{
int r, s, t;
int color = OTHER_COLOR(board[str]);
int xpos;
int adj;
int adj2;
int adjs[MAXCHAIN];
int adjs2[MAXCHAIN];
int libs[2];
/* Find links with two liberties. */
adj = chainlinks2(str, adjs, 2);
for (r = 0; r < adj; r++) {
if (!have_common_lib(str, adjs[r], NULL))
continue;
/* Stretch out. */
findlib(adjs[r], 2, libs);
for (t = 0; t < 2; t++) {
xpos = libs[t];
if (approxlib(xpos, color, min_liberties, NULL)
+ neighbor_of_string(xpos, str) >= min_liberties)
ADD_CANDIDATE_MOVE(xpos, 0, *moves, "defend_secondary_chain2-A");
}
/* Capture adjacent stones in atari, if any. */
adj2 = chainlinks2(adjs[r], adjs2, 1);
for (s = 0; s < adj2; s++) {
findlib(adjs2[s], 1, &xpos);
if (!is_self_atari(xpos, color))
ADD_CANDIDATE_MOVE(xpos, 0, *moves, "defend_secondary_chain2-B");
}
/* Look for neighbours we can atari. */
adj2 = chainlinks2(adjs[r], adjs2, 2);
for (s = 0; s < adj2; s++) {
findlib(adjs2[s], 2, libs);
for (t = 0; t < 2; t++) {
/* Only atari if target has no easy escape with his other liberty. */
if (approxlib(libs[1-t], OTHER_COLOR(color), 3, NULL) < 3
&& !is_self_atari(libs[t], color)) {
ADD_CANDIDATE_MOVE(libs[t], 0, *moves, "defend_secondary_chain2-C");
}
}
}
}
}
/*
* Find moves which immediately capture chain links with 2
* liberties, in the sense that the links cannot escape atari.
*
* The used heuristics are slightly sloppy, so useless moves may
* appear occasionally. This should, however, only lead to slightly
* worse performance but not to incorrect results.
*/
static void
break_chain2_efficient_moves(int str, struct reading_moves *moves)
{
int r;
int adj, adjs[MAXCHAIN];
/* Find links with 2 liberties. */
adj = chainlinks2(str, adjs, 2);
for (r = 0; r < adj; r++)
do_find_break_chain2_efficient_moves(str, adjs[r], moves);
}
/* Helper function for break_chain2_efficient_moves(). */
static void
do_find_break_chain2_efficient_moves(int str, int adj,
struct reading_moves *moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int k;
int adj2, adjs2[MAXCHAIN];
int libs[2];
int pos1;
int pos2;
ASSERT1(countlib(adj) == 2, adj);
adj2 = chainlinks2(adj, adjs2, 1);
if (adj2 == 1 && countlib(str) > 2) {
int apos;
break_chain_moves(adjs2[0], moves);
findlib(adjs2[0], 1, &apos);
if (!is_self_atari(apos, color))
ADD_CANDIDATE_MOVE(apos, 0, *moves, "break_chain2_efficient-A");
return;
}
if (adj2 > 1)
return;
findlib(adj, 2, libs);
for (k = 0; k < 2; k++)
if (approxlib(libs[k], other, 3, NULL) <= 2
&& !is_self_atari(libs[1 - k], color))
ADD_CANDIDATE_MOVE(libs[1 - k], 0, *moves, "break_chain2_efficient-B");
/* A common special case is this kind of edge position
*
* ..XXX.
* X.XOO.
* XOOX*.
* ......
* ------
*
* where a move at * is most effective for saving the two stones
* to the left.
*
* The code below tries to identify this case. We use the crude
* heuristic that the two liberties of the X stone we want to
* capture should be placed diagonally and that one liberty should
* be on the edge. Then we propose to play the other liberty.
* Notice that both moves may be proposed when attacking a stone
* on 2-2.
*
* Update: This was too crude. Also require that the X stone is on
* the second line and that the proposed move is not a self-atari.
*/
if (!DIAGONAL_NEIGHBORS(libs[0], libs[1]))
return;
/* Since we know that the two liberties are diagonal, the following
* construction gives the two vertices "between" the liberties.
*/
pos1 = NORTH(gg_max(libs[0], libs[1]));
pos2 = SOUTH(gg_min(libs[0], libs[1]));
if ((board[pos1] != other
|| !is_edge_vertex(pos2)
|| !same_string(pos1, adj))
&& (board[pos2] != other
|| !is_edge_vertex(pos1)
|| !same_string(pos2, adj)))
return;
if (is_edge_vertex(libs[0]) && !is_self_atari(libs[1], color))
ADD_CANDIDATE_MOVE(libs[1], 1, *moves, "break_chain2_efficient-C");
if (is_edge_vertex(libs[1]) && !is_self_atari(libs[0], color))
ADD_CANDIDATE_MOVE(libs[0], 1, *moves, "break_chain2_efficient-C");
}
/* (str) points to a string with two or more liberties. break_chain2_moves()
* tries to defend this string by attacking a neighbouring string with
* two liberties.
* This is done by playing on either of its liberties
* (if (require_safe) is true these are only used if they are not
* self-ataris), taking a neighbour out of atari or by backfilling if
* both liberties are self-ataris.
*/
static void
break_chain2_moves(int str, struct reading_moves *moves, int require_safe,
int be_aggressive)
{
int color = board[str];
int other = OTHER_COLOR(color);
int r;
int adj;
int adjs[MAXCHAIN];
adj = chainlinks2(str, adjs, 2);
for (r = 0; r < adj; r++) {
int k;
int apos = adjs[r];
int libs[2];
int unsafe[2];
int dummy_adjs[MAXCHAIN];
findlib(apos, 2, libs);
/* If stackp > backfill_depth, don't bother playing liberties of
* 2-liberty strings if those also have at least one neighbor in
* atari. This is intended to solve reading:171 and generally reduce
* the number of nodes.
*/
if (stackp > backfill_depth
&& chainlinks2(apos, dummy_adjs, 1) > 0)
continue;
for (k = 0; k < 2; k++) {
unsafe[k] = is_self_atari(libs[k], color);
if (!unsafe[k]
|| is_ko(libs[k], color, NULL)
|| (!require_safe
&& approxlib(libs[k], other, 5, NULL) < 5))
ADD_CANDIDATE_MOVE(libs[k], 0, *moves, "break_chain2-A");
}
if (stackp <= break_chain_depth
|| (be_aggressive && stackp <= backfill_depth)) {
/* If the chain link cannot escape easily, try to defend all adjacent
* friendly stones in atari (if any). If there are none, defend
* adjacent friendly stones with only two liberties.
*/
if (approxlib(libs[0], other, 4, NULL) < 4
&& approxlib(libs[1], other, 4, NULL) < 4) {
if (!defend_secondary_chain1_moves(adjs[r], moves, 2))
defend_secondary_chain2_moves(adjs[r], moves, 2);
}
}
if (unsafe[0] && unsafe[1]
&& (stackp <= backfill2_depth || have_common_lib(str, apos, NULL))) {
int lib;
/* Find backfilling moves. */
for (k = 0; k < 2; k++) {
int libs2[3];
if (approxlib(libs[k], other, 3, libs2) == 2) {
if (!is_self_atari(libs2[0], color))
ADD_CANDIDATE_MOVE(libs2[0], 0, *moves, "break_chain2-B");
if (!is_self_atari(libs2[1], color))
ADD_CANDIDATE_MOVE(libs2[1], 0, *moves, "break_chain2-B");
}
}
/* Consider this case (reading:188):
*
* |.OOOXXX
* |OXXXOOO
* |.X.O...
* +-------
*
* We cannot atari the corner X string immediatly, so we need to
* backfill. However, to avoid generating too many variations,
* we require that the opponent string is well restrained.
* Otherwise it could just run away while we backfill.
*/
if (approxlib(libs[0], other, 3, NULL) <= 2
&& approxlib(libs[1], other, 3, NULL) <= 2) {
if (approxlib(libs[0], color, 1, &lib) == 1
&& approxlib(lib, color, 3, NULL) >= 3)
ADD_CANDIDATE_MOVE(lib, 0, *moves, "break_chain2-C");
if (approxlib(libs[1], color, 1, &lib) == 1
&& approxlib(lib, color, 3, NULL) >= 3)
ADD_CANDIDATE_MOVE(lib, 0, *moves, "break_chain2-C");
}
}
}
}
/*
* (str) points to a group to be defended.
* break_chain2_defense_moves is a wrapper around break_chain2_moves.
* It devalues all entries by 2.
*
* Rationale: Otherwise, these moves get overvalued by order_moves. In
* particular, if there is both a direct and a break_chain2 defense,
* then the latter one might be just an irrelevant intermediate forcing
* move. Hence, we should rather return the direct defense.
*/
static void
break_chain2_defense_moves(int str, struct reading_moves *moves,
int be_aggressive)
{
int saved_num_moves = moves->num;
int k;
break_chain2_moves(str, moves, !(stackp <= backfill_depth), be_aggressive);
for (k = saved_num_moves; k < moves->num; k++)
moves->score[k] = -2;
}
/* Helper function for break_chain3_moves() and
* superstring_break_chain_moves().
*/
static void
do_find_break_chain3_moves(int *chain_links, int num_chain_links,
struct reading_moves *moves, int be_aggressive,
const char *caller_function_name)
{
int other = board[chain_links[0]];
int color = OTHER_COLOR(other);
signed char move_added[BOARDMAX];
int possible_moves[MAX_MOVES];
int num_possible_moves = 0;
int r;
int k;
gg_assert(num_chain_links > 0);
memset(move_added, 0, sizeof move_added);
for (r = 0; r < num_chain_links; r++) {
int lib1;
int lib2;
int lib3;
int libs[3];
/* We make a list in the (adjs) array of the liberties
* of boundary strings having exactly three liberties. We mark
* each liberty in the mw array so that we do not list any
* more than once.
*/
findlib(chain_links[r], 3, libs);
/* If the 3 liberty chain easily can run away through one of the
* liberties, we don't play on any of the other liberties.
*/
lib1 = approxlib(libs[0], other, 4, NULL);
lib2 = approxlib(libs[1], other, 4, NULL);
if (lib1 >= 4 && lib2 >= 4)
continue;
lib3 = approxlib(libs[2], other, 4, NULL);
if ((lib1 >= 4 || lib2 >= 4) && lib3 >= 4)
continue;
if (lib1 >= 4) {
if (!move_added[libs[0]]) {
possible_moves[num_possible_moves++] = libs[0];
move_added[libs[0]] = 1;
}
continue;
}
if (lib2 >= 4) {
if (!move_added[libs[1]]) {
possible_moves[num_possible_moves++] = libs[1];
move_added[libs[1]] = 1;
}
continue;
}
if (lib3 >= 4) {
if (!move_added[libs[2]]) {
possible_moves[num_possible_moves++] = libs[2];
move_added[libs[2]] = 1;
}
continue;
}
/* No easy escape, try all liberties. */
for (k = 0; k < 3; k++) {
if (!move_added[libs[k]]) {
possible_moves[num_possible_moves++] = libs[k];
move_added[libs[k]] = 1;
}
}
if (stackp <= backfill2_depth
|| (be_aggressive && stackp <= backfill_depth))
defend_secondary_chain1_moves(chain_links[r], moves, 3);
}
for (k = 0; k < num_possible_moves; k++) {
/* We do not wish to consider the move if it can be immediately
* recaptured, unless stackp < backfill2_depth. Beyond
* backfill2_depth, the necessary capturing move might not get
* generated in follow-up for the attacker. (This currently only
* makes a difference at stackp == backfill2_depth.)
*/
int move = possible_moves[k];
if (stackp <= break_chain_depth
|| (be_aggressive && stackp <= backfill_depth)
|| approxlib(move, color, 2, NULL) > 1)
/* We use a negative initial score here as we prefer to find
* direct defense moves.
*/
ADD_CANDIDATE_MOVE(move, -2, *moves, caller_function_name);
}
}
/*
* (str) points to a group.
* If there is a string in the surrounding chain having
* exactly three liberties whose attack leads to the rescue of
* (str), break_chain3_moves(str, *moves) adds attack moves against
* the surrounding string as candidate moves.
*/
static void
break_chain3_moves(int str, struct reading_moves *moves, int be_aggressive)
{
int chain_links[MAXCHAIN];
int num_chain_links = chainlinks2(str, chain_links, 3);
if (num_chain_links > 0) {
do_find_break_chain3_moves(chain_links, num_chain_links,
moves, be_aggressive, "break_chain3");
}
}
/*
* (str) points to a group.
* If there is a string in the surrounding chain having
* exactly four liberties whose attack leads to the rescue of
* (str), break_chain4_moves(str, *moves) adds attack moves against
* the surrounding string as candidate moves.
*/
static void
break_chain4_moves(int str, struct reading_moves *moves, int be_aggressive)
{
int color = board[str];
int other = OTHER_COLOR(color);
int r;
int k;
int u = 0, v;
int apos;
int adj;
int adjs[MAXCHAIN];
int libs[4];
int possible_moves[MAX_MOVES];
int mw[BOARDMAX];
memset(mw, 0, sizeof(mw));
adj = chainlinks2(str, adjs, 4);
for (r = 0; r < adj; r++) {
int lib1 = 0, lib2 = 0, lib3 = 0, lib4 = 0;
apos = adjs[r];
/* We make a list in the (adjs) array of the liberties
* of boundary strings having exactly four liberties. We mark
* each liberty in the mw array so that we do not list any
* more than once.
*/
findlib(apos, 4, libs);
/* If the 4 liberty chain easily can run away through one of the
* liberties, we don't play on any of the other liberties.
*/
lib1 = approxlib(libs[0], other, 5, NULL);
lib2 = approxlib(libs[1], other, 5, NULL);
if (lib1 >= 5 && lib2 >= 5)
continue;
lib3 = approxlib(libs[2], other, 5, NULL);
if ((lib1 >= 5 || lib2 >= 5) && lib3 >= 5)
continue;
lib4 = approxlib(libs[3], other, 5, NULL);
if ((lib1 >= 5 || lib2 >= 5 || lib3 >= 5) && lib4 >= 5)
continue;
if (lib1 >= 5 && !mw[libs[0]]) {
mw[libs[0]] = 1;
possible_moves[u++] = libs[0];
continue;
}
if (lib2 >= 5 && !mw[libs[1]]) {
mw[libs[1]] = 1;
possible_moves[u++] = libs[1];
continue;
}
if (lib3 >= 5 && !mw[libs[2]]) {
mw[libs[2]] = 1;
possible_moves[u++] = libs[2];
continue;
}
if (lib4 >= 5 && !mw[libs[3]]) {
mw[libs[3]] = 1;
possible_moves[u++] = libs[3];
continue;
}
/* No easy escape, try all liberties. */
for (k = 0; k < 4; k++) {
if (!mw[libs[k]]) {
mw[libs[k]] = 1;
possible_moves[u++] = libs[k];
}
}
if (stackp <= backfill2_depth
|| (be_aggressive && stackp <= backfill_depth))
defend_secondary_chain1_moves(adjs[r], moves, 4);
}
for (v = 0; v < u; v++) {
/* We do not wish to consider the move if it can be
* immediately recaptured, unless stackp < backfill2_depth.
* Beyond backfill2_depth, the necessary capturing move might not
* get generated in follow-up for the attacker.
* (This currently only makes a difference at stackp == backfill2_depth.)
*/
int xpos = possible_moves[v];
if (stackp <= break_chain_depth
|| (be_aggressive && stackp <= backfill_depth)
|| approxlib(xpos, color, 2, NULL) > 1)
/* We use a negative initial score here as we prefer to find
* direct defense moves.
*/
ADD_CANDIDATE_MOVE(xpos, -2, *moves, "break_chain4");
}
}
/* This function looks for moves attacking those components
* of the surrounding chain of the superstring (see find_superstring
* for the definition) which have fewer than liberty_cap liberties,
* and which are not adjacent to the string itself, since those
* are tested by break_chain_moves.
*/
static void
superstring_break_chain_moves(int str, int liberty_cap,
struct reading_moves *moves)
{
int adj;
int adjs[MAXCHAIN];
int chain_links3[MAXCHAIN];
int num_chain_links3 = 0;
int k;
int apos;
proper_superstring_chainlinks(str, &adj, adjs, liberty_cap);
for (k = 0; k < adj; k++) {
int liberties = countlib(adjs[k]);
if (liberties == 1) {
findlib(adjs[k], 1, &apos);
ADD_CANDIDATE_MOVE(apos, 0, *moves, "superstring_break_chain");
}
else if (liberties == 2)
do_find_break_chain2_efficient_moves(str, adjs[k], moves);
else if (liberties == 3)
chain_links3[num_chain_links3++] = adjs[k];
}
if (num_chain_links3 > 0) {
do_find_break_chain3_moves(chain_links3, num_chain_links3,
moves, 0, "superstring_break_chain-3");
}
}
/*
* If `str' points to a group, double_atari_chain2_moves() adds all
* moves which make a double atari on some strings in the surrounding
* chain to the moves[] array. In addition, if `generate_more_moves'
* is set, it adds moves that make atari on a string in the
* surrounding chain and are adjacent to another string with 3
* liberties.
*/
static void
double_atari_chain2_moves(int str, struct reading_moves *moves,
int generate_more_moves)
{
int r, k;
int adj;
int adjs[MAXCHAIN];
int libs[3];
int mw[BOARDMAX];
memset(mw, 0, sizeof(mw));
adj = chainlinks2(str, adjs, 2);
for (r = 0; r < adj; r++) {
findlib(adjs[r], 2, libs);
for (k = 0; k < 2; k++) {
mw[libs[k]]++;
if (mw[libs[k]] == 2) {
/* Found a double atari, but don't play there unless the move
* is safe for the defender.
*/
if (!is_self_atari(libs[k], board[str]))
ADD_CANDIDATE_MOVE(libs[k], 1, *moves, "double_atari_chain2-A");
}
}
}
if (generate_more_moves) {
int adj3;
int adjs3[MAXCHAIN];
adj3 = chainlinks2(str, adjs3, 3);
for (r = 0; r < adj3; r++) {
findlib(adjs3[r], 3, libs);
for (k = 0; k < 3; k++) {
if (mw[libs[k]] == 1) {
mw[libs[k]] = 2;
if (!is_self_atari(libs[k], board[str]))
ADD_CANDIDATE_MOVE(libs[k], -3, *moves, "double_atari_chain2-B");
}
}
}
}
}
/* ================================================================ */
/* Restricted Attack and Defense */
/* ================================================================ */
/* These functions try to attack and defend a string, avoiding moves
* from a certain set. It is assumed that as soon as the string gets
* three liberties, it is alive.
*
* These functions can be used to generate backfilling moves as
* follows: Suppose that we would like to make atari on a
* string, but the atari is not safe until we make a backfilling
* move. To find the backfilling move, we make a list of the
* liberties of the string under attack, declaring these moves
* forbidden. Neither player will play them while the restricted
* functions are in effect. We fill the liberty, creating a
* string which is under attack, and look for a defensive move
* which avoids the forbidden moves. This is the backfilling
* move.
*
* These are minimalist functions capable of reading a ladder
* and not much more.
*/
/* Given a list of moves, restricted_defend1 tries to find a
* move that defends the string (str) with one liberty,
* not considering moves from the list.
*/
int
restricted_defend1(int str, int *move,
int num_forbidden_moves, int *forbidden_moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int xpos;
int lib;
struct reading_moves moves;
int savemove = 0;
int savecode = 0;
int liberties;
int k;
SETUP_TRACE_INFO("restricted_defend1", str);
reading_node_counter++;
moves.num = 0;
ASSERT1(IS_STONE(board[str]), str);
ASSERT1(countlib(str) == 1, str);
/* (lib) will be the liberty of the string. */
liberties = findlib(str, 1, &lib);
ASSERT1(liberties == 1, str);
/* Collect moves to try in the first batch.
* 1. First order liberty.
* 2. Chain breaking moves.
* 3. Moves to set up a snapback.
*/
moves.pos[0] = lib;
moves.score[0] = 0;
moves.message[0] = "liberty";
moves.num = 1;
moves.num_tried = 0;
break_chain_moves(str, &moves);
set_up_snapback_moves(str, lib, &moves);
order_moves(str, &moves, color, read_function_name, NO_MOVE);
for (k = 0; k < moves.num; k++) {
int ko_capture;
xpos = moves.pos[k];
if (in_list(xpos, num_forbidden_moves, forbidden_moves))
continue;
/* To avoid loops with double ko, we do not allow any ko captures,
* even legal ones, if the opponent is komaster.
*/
if (is_ko(xpos, color, NULL))
ko_capture = 1;
else
ko_capture = 0;
if ((get_komaster() != other || !ko_capture)
&& trymove(xpos, color, moves.message[k], str)) {
int libs = countlib(str);
if (libs > 2) {
popgo();
SGFTRACE(xpos, WIN, "defense effective");
if (move)
*move = xpos;
return WIN;
}
if (libs == 2) {
int acode;
if (!ko_capture)
acode = restricted_attack2(str, NULL,
num_forbidden_moves, forbidden_moves);
else
acode = restricted_attack2(str, NULL,
num_forbidden_moves, forbidden_moves);
popgo();
if (acode == 0) {
SGFTRACE(xpos, WIN, "defense effective");
if (move)
*move = xpos;
return WIN;
}
/* if the move works with ko we save it, then look for something
* better.
*/
UPDATE_SAVED_KO_RESULT(savecode, savemove, acode, xpos);
}
else
popgo();
}
else {
int ko_pos;
if (stackp <= ko_depth
&& savecode == 0
&& (get_komaster() == EMPTY
|| (get_komaster() == color
&& get_kom_pos() == xpos))
&& is_ko(xpos, color, &ko_pos)
&& tryko(xpos, color, "restricted_defend1-B")) {
int libs = countlib(str);
if (libs > 2) {
popgo();
UPDATE_SAVED_KO_RESULT(savecode, savemove, 2, xpos);
}
else if (libs == 2) {
int acode;
acode = restricted_attack2(str, NULL,
num_forbidden_moves, forbidden_moves);
popgo();
UPDATE_SAVED_KO_RESULT(savecode, savemove, acode, xpos);
}
else
popgo();
}
}
}
if (savecode != 0) {
if (move)
*move = savemove;
SGFTRACE(savemove, savecode, "saved move");
return savecode;
}
SGFTRACE(0, 0, NULL);
return 0;
}
/* Given a list of moves, restricted_attack2 tries to find a
* move that attacks the string (str) with two liberties,
* not considering moves from the list.
*/
int
restricted_attack2(int str, int *move,
int num_forbidden_moves, int *forbidden_moves)
{
int color = board[str];
int other = OTHER_COLOR(color);
int apos;
int liberties;
int libs[2];
int savemove = 0;
int savecode = 0;
int k;
SETUP_TRACE_INFO("restricted_attack2", str);
reading_node_counter++;
str = find_origin(str);
ASSERT1(IS_STONE(board[str]), str);
ASSERT1(countlib(str) == 2, str);
/* The attack may fail if a boundary string is in atari and cannot
* be defended. First we must try defending such a string.
*/
/* Get the two liberties of (str). */
liberties = findlib(str, 2, libs);
ASSERT1(liberties == 2, str);
for (k = 0; k < 2; k++) {
int ko_pos;
int ko_capture;
apos = libs[k];
if (in_list(apos, num_forbidden_moves, forbidden_moves))
continue;
/* To avoid loops with double ko, we do not allow any ko captures,
* even legal ones, if the opponent is komaster.
*/
if (is_ko(apos, other, &ko_pos))
ko_capture = 1;
else
ko_capture = 0;
if ((get_komaster() != color || !ko_capture)
&& trymove(apos, other, "restricted_attack2", str)) {
if ((!ko_capture
&& !restricted_defend1(str, NULL,
num_forbidden_moves, forbidden_moves))
|| (ko_capture
&& !restricted_defend1(str, NULL,
num_forbidden_moves, forbidden_moves))) {
popgo();
SGFTRACE(apos, WIN, "attack effective");
if (move)
*move = apos;
return WIN;
}
popgo();
}
else if (savecode == 0
&& (get_komaster() == EMPTY
|| (get_komaster() == other
&& get_kom_pos() == apos))
&& tryko(apos, other, "restricted_attack2")) {
if (!restricted_defend1(str, NULL,
num_forbidden_moves, forbidden_moves)) {
popgo();
savecode = KO_B;
savemove = apos;
}
else
popgo();
}
}
if (savecode != 0) {
if (move)
*move = savemove;
SGFTRACE(savemove, savecode, "saved move");
return savecode;
}
SGFTRACE(0, 0, NULL);
return 0;
}
/*
* Returns true if the move is in a given list of moves.
*/
static int
in_list(int move, int num_moves, int *moves)
{
int k;
for (k = 0; k < num_moves; k++)
if (moves[k] == move)
return 1;
return 0;
}
/* ================================================================ */
/* Move ordering */
/* ================================================================ */
/* Parameters used in the ordering of moves to try in the tactical
* reading.
*/
/* 0 1 2 3 4 >4 */
static int defend_lib_score[6] = {-5, -4, 0, 3, 5, 50};
static int defend_not_adjacent_lib_score[5] = { 0, 0, 2, 3, 5};
static int defend_capture_score[6] = { 0, 6, 9, 13, 18, 24};
static int defend_atari_score[6] = { 0, 2, 4, 6, 7, 8};
static int defend_save_score[6] = { 0, 3, 6, 8, 10, 12};
static int defend_open_score[5] = { 0, 1, 2, 3, 4};
static int attack_own_lib_score[5] = {10, -4, 2, 3, 4};
static int attack_string_lib_score[6] = {-5, 2, 3, 7, 10, 19};
static int attack_capture_score[6] = {-4, 4, 10, 15, 20, 25};
static int attack_save_score[6] = { 0, 10, 13, 18, 21, 24};
static int attack_open_score[5] = { 0, 0, 2, 4, 4};
static int defend_not_edge_score = 5;
static int attack_not_edge_score = 1;
static int attack_ko_score = -15;
static int cannot_defend_penalty = -20;
static int safe_atari_score = 8;
static void
sgf_dumpmoves(struct reading_moves *moves, const char *funcname)
{
char buf[500];
char *pos;
int i, chars;
sprintf(buf, "Move order for %s: %n", funcname, &chars);
pos = buf + chars;
for (i = moves->num_tried; i < moves->num; i++) {
sprintf(pos, "%c%d (%d) %n",
J(moves->pos[i]) + 'A' + (J(moves->pos[i]) >= 8),
board_size - I(moves->pos[i]), moves->score[i], &chars);
pos += chars;
}
sgftreeAddComment(sgf_dumptree, buf);
}
/* The string at (str) is under attack. The moves.num moves in
* (moves) for color have been deemed interesting in
* the attack or defense of the group. Most of these moves will be
* immediate liberties of the group.
*
* This function orders the moves in the order where the move most
* likely to succeed to attack or defend the string will be first and
* so on.
*
* Currently, this is defined as:
* 1) Moves which let the defending string get more liberties are more
* interesting.
* 2) Moves adjacent to the most open liberties are more
* interesting than those with fewer open liberties.
* 3) Moves on the edge are less interesting.
*
* Moves below first_move are ignored and assumed to be sorted already.
*/
static void
order_moves(int str, struct reading_moves *moves, int color,
const char *funcname, int killer)
{
int string_color = board[str];
int string_libs = countlib(str);
int r;
int i, j;
/* Don't bother sorting if only one move, or none at all. */
if (moves->num - moves->num_tried < 2) {
/* But let's still have a single candidate in the sgf output */
if (sgf_dumptree && moves->num > moves->num_tried)
sgf_dumpmoves(moves, funcname);
return;
}
/* Assign a score to each move. */
for (r = moves->num_tried; r < moves->num; r++) {
int move = moves->pos[r];
/* Look at the neighbors of this move and count the things we
* find. Friendly and opponent stones are related to color, i.e.
* the player to move, not to the color of the string.
*/
int number_edges = 0; /* outside board */
int number_same_string = 0; /* the string being attacked */
int number_own = 0; /* friendly stone */
int number_opponent = 0; /* opponent stone */
int captured_stones = 0; /* number of stones captured by this move */
int threatened_stones = 0; /* number of stones threatened by this move */
int saved_stones = 0; /* number of stones in atari saved */
int number_open = 0; /* empty intersection */
/* We let the incremental_board code do the heavy work. */
incremental_order_moves(move, color, str, &number_edges,
&number_same_string, &number_own,
&number_opponent, &captured_stones,
&threatened_stones, &saved_stones, &number_open);
if (0)
gprintf("%o %1m values: %d %d %d %d %d %d %d %d\n", move, number_edges,
number_same_string, number_own, number_opponent, captured_stones,
threatened_stones, saved_stones, number_open);
/* Different score strategies depending on whether the move is
* attacking or defending the string.
*/
if (color == string_color) {
/* Defense move.
*
* 1) Add twice the number of liberties the group receives by
* extending to the intersection of the move, if more than one.
* Only applicable if the move is adjacent to the group.
*/
int libs = approxlib(move, color, 10, NULL);
if (number_same_string > 0) {
if (libs > 5 || (libs == 4 && stackp > fourlib_depth))
moves->score[r] += defend_lib_score[5] + (libs - 4);
else
moves->score[r] += defend_lib_score[libs];
}
else {
/* Add points for the number of liberties the played stone
* obtains when not adjacent to the attacked string.
*/
if (libs > 4)
moves->score[r] += defend_not_adjacent_lib_score[4];
else
moves->score[r] += defend_not_adjacent_lib_score[libs];
}
/* 2) Add the number of open liberties near the move to its score. */
gg_assert(number_open <= 4);
moves->score[r] += defend_open_score[number_open];
/* 3) Add a bonus if the move is not on the edge.
*/
if (number_edges == 0 || captured_stones > 0)
moves->score[r] += defend_not_edge_score;
/* 4) Add thrice the number of captured stones. */
if (captured_stones <= 5)
moves->score[r] += defend_capture_score[captured_stones];
else
moves->score[r] += defend_capture_score[5] + captured_stones;
/* 5) Add points for stones put into atari, unless this is a
* self atari.
*/
if (libs + captured_stones > 1) {
if (threatened_stones <= 5)
moves->score[r] += defend_atari_score[threatened_stones];
else
moves->score[r] += defend_atari_score[5] + threatened_stones;
}
/* 6) Add a bonus for saved stones. */
if (saved_stones <= 5)
moves->score[r] += defend_save_score[saved_stones];
else
moves->score[r] += defend_save_score[5];
}
else {
/* Attack move.
*
* 1) Add the number of liberties the attacker gets when playing
* there, but never more than four.
*/
int libs = approxlib(move, color, 4, NULL);
if (libs > 4)
libs = 4;
moves->score[r] += attack_own_lib_score[libs];
if (libs == 0 && captured_stones == 1)
moves->score[r] += attack_ko_score;
/* 2) If the move is not a self atari and adjacent to the
* string, add the number of liberties the opponent would
* gain by playing there. If the string has two liberties,
* self-ataris are also ok since they may be snapbacks, but
* only if a single stone is sacrificed.
*/
if ((libs + captured_stones > 1 || (string_libs <= 2 && number_own == 0))
&& number_same_string > 0) {
int safe_atari;
int liberties = approxlib(move, string_color, 5, NULL);
if (liberties > 5 || (liberties == 4 && stackp > fourlib_depth))
liberties = 5;
moves->score[r] += attack_string_lib_score[liberties];
safe_atari = (string_libs <= 2 && libs + captured_stones > 1);
/* The defender can't play here without getting into atari, so
* we probably souldn't either.
*/
if (liberties == 1 && saved_stones == 0 && !safe_atari)
moves->score[r] += cannot_defend_penalty;
/* Bonus if we put the attacked string into atari without
* ourselves getting into atari.
*/
if (safe_atari)
moves->score[r] += safe_atari_score;
}
/* 3) Add the number of open liberties near the move to its score. */
gg_assert(number_open <= 4);
moves->score[r] += attack_open_score[number_open];
/* 4) Add a bonus if the move is not on the edge. */
if (number_edges == 0)
moves->score[r] += attack_not_edge_score;
/* 5) Add twice the number of captured stones. */
if (captured_stones <= 5)
moves->score[r] += attack_capture_score[captured_stones];
else
moves->score[r] += attack_capture_score[5];
/* 6) Add a bonus for saved stones. */
if (saved_stones <= 5)
moves->score[r] += attack_save_score[saved_stones];
else
moves->score[r] += attack_save_score[5];
}
if (moves->pos[r] == killer)
moves->score[r] += 50;
}
/* Now sort the moves. We use selection sort since this array will
* probably never be more than 10 moves long. In this case, the
* overhead imposed by quicksort will probably overshadow the gains
* given by the O(n*log(n)) behaviour over the O(n^2) behaviour of
* selection sort.
*/
for (i = moves->num_tried; i < moves->num-1; i++) {
int maxscore = moves->score[i];
int max_at = 0; /* This is slightly faster than max_at = i. */
/* Find the move with the biggest score. */
for (j = i + 1; j < moves->num; j++) {
if (moves->score[j] > maxscore) {
maxscore = moves->score[j];
max_at = j;
}
}
/* Now exchange the move at i with the move at max_at.
* Don't forget to exchange the scores as well.
*/
if (max_at != 0) {
int temp = moves->pos[max_at];
const char *temp_message = moves->message[max_at];
moves->pos[max_at] = moves->pos[i];
moves->score[max_at] = moves->score[i];
moves->message[max_at] = moves->message[i];
moves->pos[i] = temp;
moves->score[i] = maxscore;
moves->message[i] = temp_message;
}
}
if (0) {
gprintf("%oVariation %d:\n", count_variations);
for (i = moves->num_tried; i < moves->num; i++)
gprintf("%o %1M %d\n", moves->pos[i], moves->score[i]);
}
if (sgf_dumptree)
sgf_dumpmoves(moves, funcname);
}
/* Set new values for the move ordering parameters. */
void
tune_move_ordering(int params[MOVE_ORDERING_PARAMETERS])
{
int k;
for (k = 0; k < 6; k++) {
defend_lib_score[k] = params[k];
if (k < 5)
defend_not_adjacent_lib_score[k] = params[k + 6];
defend_capture_score[k] = params[k + 11];
defend_atari_score[k] = params[k + 17];
defend_save_score[k] = params[k + 23];
if (k < 5) {
defend_open_score[k] = params[k + 29];
attack_own_lib_score[k] = params[k + 34];
}
attack_string_lib_score[k] = params[k + 39];
attack_capture_score[k] = params[k + 45];
attack_save_score[k] = params[k + 51];
if (k < 5)
attack_open_score[k] = params[k + 57];
}
defend_not_edge_score = params[62];
attack_not_edge_score = params[63];
attack_ko_score = params[64];
cannot_defend_penalty = params[65];
safe_atari_score = params[66];
if (verbose) {
gprintf("static int defend_lib_score[6] = {%d, %d, %d, %d, %d, %d};\n",
defend_lib_score[0], defend_lib_score[1],
defend_lib_score[2], defend_lib_score[3],
defend_lib_score[4], defend_lib_score[5]);
gprintf("static int defend_not_adjacent_lib_score[5] = {%d, %d, %d, %d, %d};\n",
defend_not_adjacent_lib_score[0], defend_not_adjacent_lib_score[1],
defend_not_adjacent_lib_score[2], defend_not_adjacent_lib_score[3],
defend_not_adjacent_lib_score[4]);
gprintf("static int defend_capture_score[6] = {%d, %d, %d, %d, %d, %d};\n",
defend_capture_score[0], defend_capture_score[1],
defend_capture_score[2], defend_capture_score[3],
defend_capture_score[4], defend_capture_score[5]);
gprintf("static int defend_atari_score[6] = {%d, %d, %d, %d, %d, %d};\n",
defend_atari_score[0], defend_atari_score[1],
defend_atari_score[2], defend_atari_score[3],
defend_atari_score[4], defend_atari_score[5]);
gprintf("static int defend_save_score[6] = {%d, %d, %d, %d, %d, %d};\n",
defend_save_score[0], defend_save_score[1],
defend_save_score[2], defend_save_score[3],
defend_save_score[4], defend_save_score[5]);
gprintf("static int defend_open_score[5] = {%d, %d, %d, %d, %d};\n",
defend_open_score[0], defend_open_score[1],
defend_open_score[2], defend_open_score[3],
defend_open_score[4]);
gprintf("static int attack_own_lib_score[5] = {%d, %d, %d, %d, %d};\n",
attack_own_lib_score[0], attack_own_lib_score[1],
attack_own_lib_score[2], attack_own_lib_score[3],
attack_own_lib_score[4]);
gprintf("static int attack_string_lib_score[6] = {%d, %d, %d, %d, %d, %d};\n",
attack_string_lib_score[0], attack_string_lib_score[1],
attack_string_lib_score[2], attack_string_lib_score[3],
attack_string_lib_score[4], attack_string_lib_score[5]);
gprintf("static int attack_capture_score[6] = {%d, %d, %d, %d, %d, %d};\n",
attack_capture_score[0], attack_capture_score[1],
attack_capture_score[2], attack_capture_score[3],
attack_capture_score[4], attack_capture_score[5]);
gprintf("static int attack_save_score[6] = {%d, %d, %d, %d, %d, %d};\n",
attack_save_score[0], attack_save_score[1],
attack_save_score[2], attack_save_score[3],
attack_save_score[4], attack_save_score[5]);
gprintf("static int attack_open_score[5] = {%d, %d, %d, %d, %d};\n",
attack_open_score[0], attack_open_score[1],
attack_open_score[2], attack_open_score[3],
attack_open_score[4]);
gprintf("static int defend_not_edge_score = %d;\n", defend_not_edge_score);
gprintf("static int attack_not_edge_score = %d;\n", attack_not_edge_score);
gprintf("static int attack_ko_score = %d;\n", attack_ko_score);
gprintf("static int cannot_defend_penalty = %d;\n", cannot_defend_penalty);
gprintf("static int safe_atari_score = %d;\n", safe_atari_score);
}
}
/* ================================================================ */
/* Reading utilities */
/* ================================================================ */
static int safe_move_cache[BOARDMAX][2];
static int safe_move_cache_when[BOARDMAX][2];
static void clear_safe_move_cache(void);
static void
clear_safe_move_cache(void)
{
int k;
for (k = BOARDMIN; k < BOARDMAX; k++) {
safe_move_cache_when[k][0] = -1;
safe_move_cache_when[k][1] = -1;
}
}
/* safe_move(move, color) checks whether a move at (move) is illegal
* or can immediately be captured. If stackp==0 the result is cached.
* If the move only can be captured by a ko, it's considered safe.
* This may or may not be a good convention.
*
* For performance reasons, the result of this function is cached.
*/
int
safe_move(int move, int color)
{
int safe = 0;
static int initialized = 0;
int ko_move;
if (!initialized) {
clear_safe_move_cache();
initialized = 1;
}
/* If we have this position cached, use the previous value.
* Only use cached values when stackp is 0 and reading is not being done
* at a modified depth.
*/
if (stackp == 0
&& depth_offset == 0
&& safe_move_cache_when[move][color == BLACK] == position_number)
return safe_move_cache[move][color == BLACK];
/* Otherwise calculate the value... */
if (komaster_trymove(move, color, "safe_move", 0, &ko_move, 1)) {
safe = REVERSE_RESULT(attack(move, NULL));
if (ko_move && safe != 0)
safe = KO_B;
popgo();
}
/* ...and store it in the cache.
* FIXME: Only store result in cache when we're working at
* full depth.
*
* Comment: This is currently not a problem since no reduced depth
* reading is performed.
*/
if (stackp == 0 && depth_offset == 0) {
if (0)
gprintf("Safe move at %1m for %s cached when depth=%d, position number=%d\n",
move, color_to_string(color), depth, position_number);
safe_move_cache_when[move][color == BLACK] = position_number;
safe_move_cache[move][color == BLACK] = safe;
}
return safe;
}
/* Checks if a move by color makes an opponent move at pos a self atari.
*/
int
does_secure(int color, int move, int pos)
{
int result = 0;
if (trymove(move, color, NULL, NO_MOVE)) {
if (is_self_atari(pos, OTHER_COLOR(color)))
result = 1;
popgo();
}
return result;
}
/* ===================== Statistics ============================= */
/* Clear statistics. */
void
reset_reading_node_counter()
{
reading_node_counter = 0;
}
/* Retrieve statistics. */
int
get_reading_node_counter()
{
return reading_node_counter;
}
/* ============ Reading shadow =============== */
/* Draw the reading shadow, for debugging purposes */
void
draw_reading_shadow()
{
int i, j;
int c = ' ';
int pos;
start_draw_board();
for (i = 0; i < board_size; i++) {
fprintf(stderr, "\n%2d", board_size - i);
for (j = 0; j < board_size; j++) {
pos = POS(i, j);
if (!shadow[pos] && board[pos] == EMPTY)
c = '.';
else if (!shadow[pos] && board[pos] == WHITE)
c = 'O';
else if (!shadow[pos] && board[pos] == BLACK)
c = 'X';
if (shadow[pos] && board[pos] == EMPTY)
c = ',';
else if (shadow[pos] && board[pos] == WHITE)
c = 'o';
else if (shadow[pos] && board[pos] == BLACK)
c = 'x';
fprintf(stderr, " %c", c);
}
fprintf(stderr, " %d", board_size - i);
}
end_draw_board();
}
/* ================================================================ */
/* Code for special purposes. */
/* ================================================================ */
/* simple_ladder(str, &move) tries to capture a string (str)
* with exactly two liberties under simplified assumptions, which are
* adequate in a ladder. The rules are as follows:
*
* 1. The attacker is allowed to play at each of the two liberties,
* but no other move. If the move was legal, the string now has
* exactly one liberty.
* 2. The defender must move out of atari. This can only be done by
* either extending at the liberty or capturing a neighboring
* string which was in atari. All such moves may be tested.
* 3. Depending on the resulting number of liberties of the string
* after the defender's move, we value each node as follows:
*
* 3 or more liberties: the attack has failed
* 2 liberties: recurse
* 1 liberty: the attack has succeeded
*
* illegal move for the defender: successful attack
* illegal move for the attacker: failed attack
*
* Return codes are as usual 0 for failure, WIN for success, KO_A for
* a ko where the defender must make the first ko threat and KO_B for
* a ko where the attacked has to make the first threat. If the attack
* was successful, (*move) contains the attacking move, unless it is a
* null pointer.
*
* The differences compared to the attack2()/defend1() combination for
* reading ladders is that this one is a strict ladder reader which
* never allows the defender to have more than one liberty when it's
* in turn to move. This has a number of consequences.
*
* 1. This function will miss tactical captures involving other
* techniques than the ladder.
*
* 2. This function is faster because it gives up faster when the
* ladder doesn't work. In particular it can't branch out in a huge
* tree of exotic variations.
*
* 3. This function always reads ladders to the very end. There are no
* depth limits or other assumptions to stop reading prematurely.
*
* 4. If this function returns WIN, it is guaranteed that the defender
* has no way whatsoever to escape, all possibilities are tried.
* The converse is definitely not true.
*/
int
simple_ladder(int str, int *move)
{
int color = board[str];
int other = OTHER_COLOR(color);
int apos;
int libs[2];
int savemove = 0;
int savecode = 0;
int dcode;
int k;
struct reading_moves moves;
SETUP_TRACE_INFO("simple_ladder", str);
reading_node_counter++;
moves.num = 0;
moves.num_tried = 0;
str = find_origin(str);
ASSERT1(IS_STONE(board[str]), str);
ASSERT1(countlib(str) == 2, str);
/* Give up if we attacked depending on ko for too long. */
if (stackp > depth + 20 && get_komaster() == OTHER_COLOR(board[str])) {
SGFTRACE(0, 0, NULL);
if (move)
*move = PASS_MOVE;
return 0;
}
/* Get the two liberties of (str). */
findlib(str, 2, libs);
/* If the defender can get enough liberties by playing one of these
* two, then we have no choice but to block there and consequently,
* it is unnecesary to try the other liberty.
*/
if (approxlib(libs[0], color, 4, NULL) <= 3)
ADD_CANDIDATE_MOVE(libs[1], 0, moves, "simple_ladder");
if (approxlib(libs[1], color, 4, NULL) <= 3)
ADD_CANDIDATE_MOVE(libs[0], 0, moves, "simple_ladder");
order_moves(str, &moves, other, read_function_name, NO_MOVE);
for (k = 0; k < moves.num; k++) {
int ko_move;
apos = moves.pos[k];
if (komaster_trymove(apos, other, moves.message[k], str,
&ko_move, savecode == 0)) {
if (!ko_move) {
dcode = simple_ladder_defend(str, NULL);
if (dcode != WIN) {
if (dcode == 0) {
popgo();
SGFTRACE(apos, WIN, "attack effective");
if (move)
*move = apos;
return WIN;
}
UPDATE_SAVED_KO_RESULT(savecode, savemove, dcode, apos);
}
}
else {
if (simple_ladder_defend(str, NULL) != WIN) {
savemove = apos;
savecode = KO_B;
}
}
popgo();
}
}
RETURN_RESULT(savecode, savemove, move, "saved move");
}
static int
simple_ladder_defend(int str, int *move)
{
int color = board[str];
int xpos;
int lib;
struct reading_moves moves;
int savemove = 0;
int savecode = 0;
int k;
SETUP_TRACE_INFO("simple_ladder_defend", str);
reading_node_counter++;
ASSERT1(IS_STONE(board[str]), str);
ASSERT1(countlib(str) == 1, str);
/* lib will be the liberty of the string. */
findlib(str, 1, &lib);
moves.pos[0] = lib;
moves.score[0] = 0;
moves.message[0] = "liberty";
moves.num = 1;
moves.num_tried = 0;
break_chain_moves(str, &moves);
order_moves(str, &moves, color, read_function_name, NO_MOVE);
for (k = 0; k < moves.num; k++) {
int ko_move;
xpos = moves.pos[k];
if (komaster_trymove(xpos, color, moves.message[k], str,
&ko_move, savecode == 0)) {
int acode;
int new_libs = countlib(str);
if (new_libs > 2)
acode = 0;
else if (new_libs < 2)
acode = WIN;
else
acode = simple_ladder(str, NULL);
popgo();
if (!ko_move)
CHECK_RESULT(savecode, savemove, acode, xpos, move,
"defense effective");
else {
if (acode != WIN) {
savemove = xpos;
savecode = KO_B;
}
}
}
}
RETURN_RESULT(savecode, savemove, move, "saved move");
}
/*
* Local Variables:
* tab-width: 8
* c-basic-offset: 2
* End:
*/