Next: , Up: Tactical Reading


11.1 Reading Basics

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.

11.1.1 Organization of the reading code

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.

11.1.2 Return Codes

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.

11.1.3 Reading cutoff and depth parameters

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.