Next: Hashing, Up: Tactical Reading
What we call Tactical Reading is the analysis whether there is a direct capture of a single string, or whether there is a move to prevent such a direct capture.
If the reading module finds out that the string can get captured, this answer should (usually) be trusted. However, if it says it can be defended, this does not say as much. It is often the case that such a string has no chance to make a life, but that it cannot be captured within the horizon (and the cutoff heuristics) of the tactical reading.
The tactical reading is done by the functions in engine/reading.c. It is a minimax search that declares win for the attacker once he can physically take the string off board, whereas the defense is considered successful when the string has sufficiently many liberties. A string with five liberties is always considered alive. At higher depth within the search tree even fewer liberties cause GNU Go to give up the attack, See depthparams.
The reading code makes use of a stack onto which board positions can
be pushed. The parameter stackp
is zero if GNU Go is
examining the true board position; if it is higher than zero, then
GNU Go is examining a hypothetical position obtained by playing
several moves.
The most important public reading functions are attack
and
find_defense
. These are wrappers for functions do_attack
and
do_find_defense
which are declared statically in reading.c. The
functions do_attack
and do_find_defense
call each other
recursively.
The function do_attack
and do_find_defense
are wrappers
themselves and call attack1
, attack2
, attack3
or
attack4
resp. defend1
, defend1
, defend1
or defend1
depending on the number of liberties.
These are fine-tuned to generate and try out the moves in an efficient
order. They generate a few moves themselves (mostly direct liberties
of the string), and then call helper functions called ..._moves
which suggest less obvious moves. Which of these functions get called
depends on the number of liberties and of the current search depth.
The return codes of the reading (and owl) functions and owl can
be 0
, KO_B
, KO_A
or WIN
. Each reading
function determines whether a particular player (assumed to have the
move) can solve a specific problem, typically attacking or defending
a string.
A return code of WIN
means success, 0 failure, while KO_A
and
KO_B
are success conditioned on ko. A function returns KO_A
if the position results in ko and that the player to move
will get the first ko capture (so the opponent has to make the
first ko threat). A return code of KO_B
means that the player
to move will have to make the first ko threat.
If GNU Go is compiled with the configure option
--enable-experimental-owl-ext then the owl functions also have
possible return codes of GAIN
and LOSS
. A code of GAIN
means that the attack (or defense) does not succeed, but that in the process
of trying to attack or defend, an opponent's worm is captured. A code
of LOSS
means that the attack or defense succeeds, but that another
friendly worm dies during the attack or defense.
Depth of reading is controlled by the parameters depth
and branch_depth
. The depth
has a default value
DEPTH
(in liberty.h), which is set to 16 in the
distribution, but it may also be set at the command line using
the -D or --depth option. If depth
is
increased, GNU Go will be stronger and slower. GNU Go will read
moves past depth, but in doing so it makes simplifying
assumptions that can cause it to miss moves.
Specifically, when stackp > depth
, GNU Go assumes that as
soon as the string can get 3 liberties it is alive. This
assumption is sufficient for reading ladders.
The branch_depth
is typically set a little below depth
.
Between branch_depth
and depth
, attacks on strings with
3 liberties are considered, but branching is inhibited, so fewer
variations are considered.
%%Currently the reading code does not try to defend a string by
%attacking a boundary string with more than two liberties. Because
%of this restriction, it can make oversights. A symptom of this is
%two adjacent strings, each having three or four liberties, each
%classified as DEAD
. To resolve such situations, a function
%small_semeai()
(in engine/semeai.c) looks for such
%pairs of strings and corrects their classification.
The backfill_depth
is a similar variable with a default 12. Below
this depth, GNU Go will try "backfilling" to capture stones.
For example in this situation:
.OOOOOO. on the edge of the board, O can capture X but OOXXXXXO in order to do so he has to first play at a in .aObX.XO preparation for making the atari at b. This is -------- called backfilling.
Backfilling is only tried with stackp <= backfill_depth
. The
parameter backfill_depth
may be set using the -B
option.
The fourlib_depth
is a parameter with a default of only 7.
Below this depth, GNU Go will try to attack strings with
four liberties. The fourlib_depth
may be set using the
-F option.
The parameter ko_depth
is a similar cutoff. If
stackp<ko_depth
, the reading code will make experiments
involving taking a ko even if it is not legal to do so (i.e., it
is hypothesized that a remote ko threat is made and answered
before continuation). This parameter may be set using the
-K option.
int attack(int str, int *move)
Determines if the string atstr
can be attacked, and if so,*move
returns the attacking move, unless*movei
is a null pointer. (Use null pointers if you are interested in the result of the attack but not the attacking move itself.) ReturnsWIN
, if the attack succeeds, 0 if it fails, andKO_A
orKO_B
if the result depends on ko Return Codes.
find_defense(int str, int *move)
Attempts to find a move that will save the string atstr
. It returns true 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)
. ReturnsKO_A
orKO_B
if the result depends on ko See Return Codes.
safe_move(int str, int color)
:
The functionsafe_move(str, color)
checks whether a move atstr
is illegal or can immediately be captured. Ifstackp==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.