Next: Combinations, Up: Pattern Based Reading
The life and death code in optics.c, described elsewhere (see Eyes), works reasonably well as long as the position is in a terminal position, which we define to be one where there are no moves left which can expand the eye space, or limit it. In situations where the dragon is surrounded, yet has room to thrash around a bit making eyes, a simple application of the graph-based analysis will not work. Instead, a bit of reading is needed to reach a terminal position.
The defender tries to expand his eyespace, the attacker to limit it, and when neither finds an effective move, the position is evaluated. We call this type of life and death reading Optics With Limit-negotiation (OWL). The module which implements it is in engine/owl.c.
There are two reasonably small databases patterns/owl_defendpats.db and patterns/owl_attackpats.db of expanding and limiting moves. The code in owl.c generates a small move tree, allowing the attacker only moves from owl_attackpats.db, and the defender only moves from owl_defendpats.db. In addition to the moves suggested by patterns, vital moves from the eye space analysis are also tested.
A third database, owl_vital_apats.db includes patterns which override the eyespace analysis done by the optics code. Since the eyeshape graphs ignore the complications of shortage of liberties and cutting points in the surrounding chains, the static analysis of eyespace is sometimes wrong. The problem is when the optics code says that a dragon definitely has 2 eyes, but it isn't true due to shortage of liberties, so the ordinary owl patterns never get into play. In such situations owl_vital_apats.db is the only available measure to correct mistakes by the optics. Currently the patterns in owl_vital_apats.db are only matched when the level is 9 or greater.
The owl code is tuned by editing these three pattern databases, principally the first two.
A node of the move tree is considered terminal
if no further moves
are found from owl_attackpats.db or owl_defendpats.db, or if
the function compute_eyes_pessimistic()
reports that the group is
definitely alive. At this point, the status of the group is evaluated.
The functions owl_attack()
and owl_defend()
, with
usage similar to attack()
and find_defense()
, make
use of the owl pattern databases to generate the move tree and decide
the status of the group.
The function compute_eyes_pessimistic()
used by the owl
code is very conservative and only feels certain about eyes if the
eyespace is completely closed (i.e. no marginal vertices).
The maximum number of moves tried at each node is limited by
the parameter MAX_MOVES
defined at the beginning of
engine/owl.c. The most most valuable moves are
tried first, with the following restrictions:
stackp > owl_branch_depth
then only one move is tried per
variation.
stackp > owl_reading_depth
then the reading terminates,
and the situation is declared a win for the defender (since
deep reading may be a sign of escape).
owl_node_limit
, the reading also
terminates with a win for the defender.
owl_attack
or owl_defend
, the
function returns true. This feature must be used most carefully.
The functions owl_attack()
and owl_defend()
may, like
attack()
and find_defense()
, return an attacking or
defending move through their pointer arguments. If the position is
already won, owl_attack()
may or may not return an attacking
move. If it finds no move of interest, it will return PASS
, that
is, 0
. The same goes for owl_defend()
.
When owl_attack()
or owl_defend()
is called,
the dragon under attack is marked in the array goal
.
The stones of the dragon originally on the board are marked
with goal=1; those added by owl_defend()
are marked
with goal=2. If all the original strings of the original dragon
are captured, owl_attack()
considers the dragon to be defeated,
even if some stones added later can make a live group.
Only dragons with small escape route are studied when the
functions are called from make_dragons()
.
The owl code can be conveniently tested using the --decide-owl location option. This should be used with -t to produce a useful trace, -o to produce an SGF file of variations produced when the life and death of the dragon at location is checked, or both. --decide-position performs the same analysis for all dragons with small escape route.