[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
7.1 Worms | ||
7.2 Amalgamation | How two Worms are amalgamated. | |
7.3 Connection | Connections. | |
7.4 Half Eyes and False Eyes | ||
7.5 Dragons | Union of WORMS. | |
7.6 Colored Dragon Display | Colored display of DRAGONS. |
Before considering its move, GNU Go collects some data in several
arrays. Two of these arrays, called worm
and dragon
, are
discussed in this document. Others are discussed in See section Eyes and Half Eyes.
This information is intended to help evaluate the connectedness, eye shape, escape potential and life status of each group.
Later routines called by genmove()
will then have access to this
information. This document attempts to explain the philosophy and
algorithms of this preliminary analysis, which is carried out by the
two routines make_worm()
and make_dragon()
in
‘dragon.c’.
A worm is a maximal set of stones on the board which are connected along the horizontal and vertical lines, and are of the same color. We often say string instead of worm.
A dragon is a union of strings of the same color which will be treated as a unit. The dragons are generated anew at each move. If two strings are in the dragon, it is the computer's working hypothesis that they will live or die together and are effectively connected.
The purpose of the dragon code is to allow the computer to formulate meaningful statements about life and death. To give one example, consider the following situation:
OOOOO OOXXXOO OX...XO OXXXXXO OOOOO |
The X's here should be considered a single group with one three-space eye, but they consist of two separate strings. Thus we must amalgamate these two strings into a single dragon. Then the assertion makes sense, that playing at the center will kill or save the dragon, and is a vital point for both players. It would be difficult to formulate this statement if the X's are not perceived as a unit.
The present implementation of the dragon code involves simplifying assumptions which can be refined in later implementations.
The array struct worm_data worm[MAX_BOARD]
collects information about
the worms. We will give definitions of the various fields. Each field has
constant value at each vertex of the worm. We will define each field.
struct worm_data { int color; int size; float effective_size; int origin; int liberties; int liberties2; int liberties3; int liberties4; int lunch; int cutstone; int cutstone2; int genus; int inessential; int invincible; int unconditional_status; int attack_points[MAX_TACTICAL_POINTS]; int attack_codes[MAX_TACTICAL_POINTS]; int defense_points[MAX_TACTICAL_POINTS]; int defend_codes[MAX_TACTICAL_POINTS]; int attack_threat_points[MAX_TACTICAL_POINTS]; int attack_threat_codes[MAX_TACTICAL_POINTS]; int defense_threat_points[MAX_TACTICAL_POINTS]; int defense_threat_codes[MAX_TACTICAL_POINTS]; }; |
color
The color of the worm.
size
This field contains the cardinality of the worm.
effective_size
This is the number of stones in a worm plus the number of empty intersections that are at least as close to this worm as to any other worm. Intersections that are shared are counted with equal fractional values for each worm. This measures the direct territorial value of capturing a worm. effective_size is a floating point number. Only intersections at a distance of 4 or less are counted.
origin
Each worm has a distinguished member, called its origin. The purpose of this field is to make it easy to determine when two vertices lie in the same worm: we compare their origin. Also if we wish to perform some test once for each worm, we simply perform it at the origin and ignore the other vertices. The origin is characterized by the test:
worm[pos].origin == pos.
liberties
liberties2
liberties3
liberties4
For a nonempty worm the field liberties is the number of liberties of the string. This is supplemented by
LIBERTIES2
,LIBERTIES3
andLIBERTIES4
, which are the number of second order, third order, and fourth order liberties, respectively. The definition of liberties of order >1 is adapted to the problem of detecting the shape of the surrounding empty space. In particular we want to be able to see if a group is loosely surrounded. A liberty of order n is an empty vertex which may be connected to the string by placing n stones of the same color on the board, but no fewer. The path of connection may pass through an intervening group of the same color. The stones placed at distance >1 may not touch a group of the opposite color. Connections through ko are not permitted. Thus in the following configuration:
.XX... We label the .XX.4. XO.... liberties of XO1234 XO.... order < 5 of XO1234 ...... the O group: .12.4. .X.X.. .X.X..The convention that liberties of order >1 may not touch a group of the opposite color means that knight's moves and one space jumps are perceived as impenetrable barriers. This is useful in determining when the string is becoming surrounded.
The path may also not pass through a liberty at distance 1 if that liberty is flanked by two stones of the opposing color. This reflects the fact that the O stone is blocked from expansion to the left by the two X stones in the following situation:
X. .O X.We say that n is the distance of the liberty of order n from the dragon.
lunch
If nonzero,
lunch
points to a boundary worm which can be easily captured. (It does not matter whether or not the string can be defended.)
We have two distinct notions of cutting stone, which we keep track
of in the separate fields worm.cutstone
and worm.cutstone2
.
We use currently use both concepts in parallel.
cutstone
This field is equal to 2 for cutting stones, 1 for potential cutting stones. Otherwise it is zero. Definitions for this field: a cutting stone is one adjacent to two enemy strings, which do not have a liberty in common. The most common type of cutting string is in this situation:
XO OXA potential cutting stone is adjacent to two enemy strings which do share a liberty. For example, X in:
XO O.For cutting strings we set
worm[].cutstone=2
. For potential cutting strings we setworm[].cutstone=1
.
cutstone2
Cutting points are identified by the patterns in the connections database. Proper cuts are handled by the fact that attacking and defending moves also count as moves cutting or connecting the surrounding dragons. The
cutstone2
field is set duringfind_cuts()
, called frommake_domains()
.
genus
There are two separate notions of genus for worms and dragons. The dragon notion is more important, so
dragon[pos].genus
is a far more useful field thanworm[pos].genus
. Both fields are intended as approximations to the number of eyes. The genus of a string is the number of connected components of its complement, minus one. It is an approximation to the number of eyes of the string.
inessential
An inessential string is one which meets a criterion designed to guarantee that it has no life potential unless a particular surrounding string of the opposite color can be killed. More precisely an inessential string is a string S of genus zero, not adjacent to any opponent string which can be easily captured, and which has no edge liberties or second order liberties, and which satisfies the following further property: If the string is removed from the board, then the remaining cavity only borders worms of the opposite color.
invincible
An invincible worm is one which GNU Go thinks cannot be captured. Invincible worms are computed by the function
unconditional_life()
which tries to find those worms of the given color that can never be captured, even if the opponent is allowed an arbitrary number of consecutive moves.
Unconditional status is also set by the function
unconditional_life
. This is setALIVE
for stones which are invincible. Stones which can not be turned invincible even if the defender is allowed an arbitrary number of consecutive moves are given an unconditional status ofDEAD
. Empty points where the opponent cannot form an invincible worm are called unconditional territory. The unconditional status is set toWHITE_TERRITORY
orBLACK_TERRITORY
depending on who owns the territory. Finally, if a stone can be captured but is adjacent to unconditional territory of its own color, it is also given the unconditional statusALIVE
. In all other cases the unconditional status isUNKNOWN
.To make sense of these definitions it is important to notice that any stone which is alive in the ordinary sense (even if only in seki) can be transformed into an invincible group by some number of consecutive moves. Well, this is not entirely true because there is a rare class of seki groups not satisfying this condition. Exactly which these are is left as an exercise for the reader. Currently
unconditional_life
, which strictly follows the definitions above, calls such seki groups unconditionally dead, which of course is a misfeature. It is possible to avoid this problem by making the algorithm slightly more complex, but this is left for a later revision.
int attack_points[MAX_TACTICAL_POINTS]
attack_codes[MAX_TACTICAL_POINTS]
int defense_points[MAX_TACTICAL_POINTS];
int defend_codes[MAX_TACTICAL_POINTS];
If the tactical reading code (see section Tactical reading) finds that the worm can be attacked,
attack_points[0]
is a point of attack, andattack_codes[0]
is the attack code,WIN
,KO_A
orKO_B
. If multiple attacks are known,attack_points[k]
andattack_codes[k]
are used. Similarly with the defense codes and defense points.
int attack_threat_points[MAX_TACTICAL_POINTS];
int attack_threat_codes[MAX_TACTICAL_POINTS];
int defense_threat_points[MAX_TACTICAL_POINTS];
int defense_threat_codes[MAX_TACTICAL_POINTS];
These are points that threaten to attack or defend a worm.
The function makeworms()
will generate data for all worms.
A dragon, we have said, is a group of stones which are treated as a unit. It is a working hypothesis that these stones will live or die together. Thus the program will not expect to disconnect an opponent's strings if they have been amalgamated into a single dragon.
The function make_dragons()
will amalgamate worms into dragons by
maintaining separate arrays worm[]
and dragon[]
containing
similar data. Each dragon is a union of worms. Just as the data maintained in
worm[]
is constant on each worm, the data in
dragon[]
is constant on each dragon.
Amalgamation of worms in GNU Go proceeds as follows. First we amalgamate all boundary components of an eyeshape. Thus in the following example:
.OOOO. The four X strings are amalgamated into a OOXXO. single dragon because they are the boundary OX..XO components of a blackbordered cave. The OX..XO cave could contain an inessential string OOXXO. with no effect on this amalgamation. XXX... |
The code for this type of amalgamation is in the routine
dragon_eye()
, discussed further in EYES.
Next, we amalgamate strings which seem uncuttable. We amalgamate dragons which either share two or more common liberties, or share one liberty into the which the opponent cannot play without being captured. (ignores ko rule).
X. X.X XXXX.XXX X.O .X X.X X......X X.X XXXXXX.X OXX |
A database of connection patterns may be found in ‘patterns/conn.db’.
The fields black_eye.cut
and white_eye.cut
are set where the
opponent can cut, and this is done by the B (break) class patterns in
‘conn.db’. There are two important uses for this field, which can be
accessed by the autohelper functions xcut()
and ocut()
. The
first use is to stop amalgamation in positions like
..X.. OO*OO X.O.X ..O.. |
where X can play at * to cut off either branch. What happens here is that first connection pattern CB1 finds the double cut and marks * as a cutting point. Later the C (connection) class patterns in conn.db are searched to find secure connections over which to amalgamate dragons. Normally a diagonal connection would be deemed secure and amalgamated by connection pattern CC101, but there is a constraint requiring that neither of the empty intersections is a cutting point.
A weakness with this scheme is that X can only cut one connection, not
both, so we should be allowed to amalgamate over one of the connections.
This is performed by connection pattern CC401, which with the help of
amalgamate_most_valuable_helper()
decides which connection to
prefer.
The other use is to simplify making alternative connection patterns to
the solid connection. Positions where the diag_miai helper thinks a
connection is necessary are marked as cutting points by connection
pattern 12. Thus we can write a connection pattern like CC6
:
?xxx? straight extension to connect XOO*? O...? :8,C,NULL ?xxx? XOOb? Oa..? ;xcut(a) && odefend_against(b,a) |
where we verify that a move at *
would stop the enemy from safely
playing at the cutting point, thus defending against the cut.
A half eye is a place where, if the defender plays first, an eye will materialize, but where if the attacker plays first, no eye will materialize. A false eye is a vertex which is surrounded by a dragon yet is not an eye. Here is a half eye:
XXXXX OO..X O.O.X OOXXX |
Here is a false eye:
XXXXX XOO.X O.O.X OOXXX |
The "topological" algorithm for determining half and false eyes is described elsewhere (see section Topology of Half Eyes and False Eyes).
The half eye data is collected in the dragon array. Before this is done,
however, an auxiliary array called half_eye_data is filled with
information. The field type
is 0, or else HALF_EYE
or
FALSE_EYE
depending on which type is found; the fields
attack_point[]
point to up to 4 points to attack
the half eye, and similarly defense_point[]
gives points
to defend the half eye.
struct half_eye_data half_eye[MAX_BOARD]; struct half_eye_data { float value; /* Topological eye value */ int type; /* HALF_EYE or FALSE_EYE */ int num_attacks; /* Number of attacking points */ int attack_point[4]; /* The moves to attack a topological halfeye */ int num_defends; /* Number of defending points */ int defense_point[4]; /* The moves to defend a topological halfeye */ }; |
The array struct half_eye_data half_eye[MAX_BOARD]
contains information about half and false eyes. If the type is
HALF_EYE
then up to four moves are recorded which can
either attack or defend the eye. In rare cases the attack points
could be different from the defense points.
The array struct dragon_data dragon[MAX_BOARD]
collects information about the dragons. We will give definitions of the
various fields. Each field has constant value at each vertex of the
dragon. (Fields will be discussed below.)
struct dragon_data { int color; /* its color */ int id; /* the index into the dragon2 array */ int origin; /* the origin of the dragon. Two vertices */ /* are in the same dragon iff they have */ /* same origin. */ int size; /* size of the dragon */ float effective_size; /* stones and surrounding spaces */ int crude_status; /* (ALIVE, DEAD, UNKNOWN, CRITICAL)*/ int status; /* best trusted status */ }; extern struct dragon_data dragon[BOARDMAX]; |
Other fields attached to the dragon are contained in the dragon_data2
struct array. (Fields will be discussed below.)
struct dragon_data2 { int origin; int adjacent[MAX_NEIGHBOR_DRAGONS]; int neighbors; int hostile_neighbors; int moyo_size; float moyo_territorial_value; int safety; float weakness; float weakness_pre_owl; int escape_route; struct eyevalue genus; int heye; int lunch; int surround_status; int surround_size; int semeais; int semeai_margin_of_safety; int semeai_defense_point; int semeai_defense_certain; int semeai_attack_point; int semeai_attack_certain; int owl_threat_status; int owl_status; int owl_attack_point; int owl_attack_code; int owl_attack_certain; int owl_second_attack_point; int owl_defense_point; int owl_defense_code; int owl_defense_certain; int owl_second_defense_point; int owl_attack_kworm; int owl_defense_kworm; }; extern struct dragon_data2 *dragon2; |
The difference between the two arrays is that the dragon
array
is indexed by the board, and there is a copy of the dragon data
at every stone in the dragon, while there is only one copy of
the dragon2 data. The dragons are numbered, and the id
field
of the dragon is a key into the dragon2 array. Two macros DRAGON
and DRAGON2 are provided for gaining access to the two arrays.
#define DRAGON2(pos) dragon2[dragon[pos].id] #define DRAGON(d) dragon[dragon2[d].origin] |
Thus if you know the position pos
of a stone in the dragon
you can access the dragon array directly, for example accessing the
origin with dragon[pos].origin
. However if you need a field
from the dragon2 array, you can access it using the DRAGON2 macro,
for example you can access its neighor dragons by
for (k = 0; k < DRAGON2(pos).neighbors; k++) { int d = DRAGON2(pos).adjacent[k]; int apos = dragon2[d].origin; do_something(apos); } |
Similarly if you know the dragon number (which is dragon[pos].id
)
then you can access the dragon2
array directly, or you can
access the dragon
array using the DRAGON macro.
Here are the definitions of each field in the dragon
arrray.
color
The color of the dragon.
id
The dragon number, used as a key into the
dragon2
array.
The origin of the dragon is a unique particular vertex of the dragon, useful for determining when two vertices belong to the same dragon. Before amalgamation the worm origins are copied to the dragon origins. Amalgamation of two dragons amounts to changing the origin of one.
The number of stones in the dragon.
The sum of the effective sizes of the constituent worms. Remembering that vertices equidistant between two or more worms are counted fractionally in
worm.effective_size
, this equals the cardinality of the dragon plus the number of empty vertices which are nearer this dragon than any other.
(ALIVE, DEAD, UNKNOWN, CRITICAL). An early measure of the life potential of the dragon. It is computed before the owl code is run and is superceded by the status as soon as that becomes available.
The dragon status is the best measure of the dragon's health. It is computed after the owl code is run, then revised again when the semeai code is run.
Here are definitions of the fields in the dragon2
array.
The origin field is duplicated here.
adjacent[MAX_NEIGHBOR_DRAGONS]
Dragons of either color near the given one are called neighbors. They are computed by the function
find_neighbor_dragons()
. Thedragon2.adjacent
array gives the dragon numbers of these dragons.
neighbors
Dragons of either color near the given one are called neighbors. They are computed by the function
find_neighbor_dragons()
. Thedragon2.adjacent
array gives the dragon numbers of these dragons.
The number of neighbor dragons.
The number of neighbor dragons of the opposite color.
The function
compute_surrounding_moyo_sizes()
assigns a size and a territorial value to the moyo around each dragon (see section Territory, Moyo and Area). This is the moyo size. They are recorded in these fields.
The dragon safety can take on one of the values
- - TACTICALLY_DEAD - a dragon consisting of a single worm found dead by the reading code (very reliable)
- - ALIVE - found alive by the owl or semeai code
- - STRONGLY_ALIVE - alive without much question
- - INVINCIBLE - definitively alive even after many tenukis
- - ALIVE_IN_SEKI - determined to be seki by the semeai code
- - CRITICAL - lives or dies depending on who moves first
- - DEAD - found to be dead by the owl code
- - INESSENTIAL - the dragon is unimportant (e.g. nakade stones) and dead
A floating point measure of the safety of a dragon. The dragon weakness is a number between 0. and 1., higher numbers for dragons in greater need of safety. The field
weakness_pre_owl
is a preliminary computation before the owl code is run.
A measure of the dragon's potential to escape towards safety, in case it cannot make two eyes locally. Documentation may be found in Escape.
The approximate number of eyes the dragon can be expected to get. Not guaranteed to be accurate. The eyevalue struct, which is used throughout the engine, is declared thus:
struct eyevalue { unsigned char a; /* # of eyes if attacker plays twice */ unsigned char b; /* # of eyes if attacker plays first */ unsigned char c; /* # of eyes if defender plays first */ unsigned char d; /* # of eyes if defender plays twice */ };
Location of a half eye attached to the dragon.
If nonzero, this is the location of a boundary string which can be captured. In contrast with worm lunches, a dragon lunch must be able to defend itself.
In estimating the safety of a dragon it is useful to know if it is surrounded. See Surrounded Dragons and the comments in ‘surround.c’ for more information about the algorithm. Used in computing the escape_route, and also callable from patterns (currently used by CB258).
If two dragons of opposite color both have the status CRITICAL or DEAD they are in a semeai (capturing race), and their status must be adjudicated by the function
owl_analyze_semeai()
in ‘owl.c’, which attempts to determine which is alive, which dead, or if the result is seki, and whether it is important who moves first. The function ‘new_semeai()’ in ‘semeai.c’ attempts to revise the statuses and to generate move reasons based on these results. The fielddragon2.semeais
is nonzero if the dragon is an element of a semeai, and equals the number of semeais (seldom more than one). The semeai defense and attack points are locations the defender or attacker must move to win the semeai. The fieldsemeai_margin_of_safety
is intended to indicate whether the semeai is close or not but currently this field is not maintained. The fieldssemeai_defense_certain
andsemeai_attack_certain
indicate that the semeai code was able to finish analysis without running out of nodes.
This is a classification similar to
dragon.crude_status
, but based on the life and death reading in ‘owl.c’. The owl code (see section The Owl Code) is skipped for dragons which appear safe by certain heuristics. If the owl code is not run, the owl status isUNCHECKED
. Ifowl_attack()
determines that the dragon cannot be attacked, it is classified asALIVE
. Otherwise,owl_defend()
is run, and if it can be defended it is classified asCRITICAL
, and if not, asDEAD
.
If the dragon can be attacked this is the point to attack the dragon.
The owl attack code, It can be WIN, KO_A, KO_B or 0 (see Return Codes).
The owl reading is able to finish analyzing the attack without running out of nodes.
A second attack point.
If the dragon can be defended, this is the place to play.
The owl defense code, It can be WIN, KO_A, KO_B or 0 (see Return Codes).
The owl code is able to finish analyzing the defense without running out of nodes.
A second owl defense point.
You can get a colored ASCII display of the board in which each dragon
is assigned a different letter; and the different values of
dragon.status
values (ALIVE
, DEAD
, UNKNOWN
,
CRITICAL
) have different colors. This is very handy for debugging.
A second diagram shows the values of owl.status
. If this
is UNCHECKED
the dragon is displayed in White.
Save a game in sgf format using CGoban, or using the ‘-o’ option with GNU Go itself.
Open an xterm
or rxvt
window. You may also use the Linux
console. Using the console, you may need to use “SHIFT-PAGE UP” to see the
first diagram. Xterm will only work if it is compiled with color support—if
you do not see the colors try rxvt
. Make the background color black
and the foreground color white.
Execute:
gnugo -l [filename] -L [movenum] -T
to get the colored display.
The color scheme: Green = ALIVE
; Yellow = UNKNOWN
;
Cyan = DEAD
and Red = CRITICAL
. Worms which have been
amalgamated into the same dragon are labelled with the same letter.
Other useful colored displays may be obtained by using instead:
The colored displays are documented elsewhere (see section Colored Display).
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This document was generated by Daniel Bump on February, 19 2009 using texi2html 1.78.