[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

# 7. Worms and 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.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 7.1 Worms

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` and `LIBERTIES4`, 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
  >               OX

  > A 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 set `worm[].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 during `find_cuts()`, called from `make_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 than `worm[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

  > Unconditional status is also set by the function `unconditional_life`. This is set `ALIVE` 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 of `DEAD`. Empty points where the opponent cannot form an invincible worm are called unconditional territory. The unconditional status is set to `WHITE_TERRITORY` or `BLACK_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 status `ALIVE`. In all other cases the unconditional status is `UNKNOWN`.

  > 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, and `attack_codes[0]` is the attack code, `WIN`, `KO_A` or `KO_B`. If multiple attacks are known, `attack_points[k]` and `attack_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.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 7.2 Amalgamation

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`’.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 7.3 Connection

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.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 7.4 Half Eyes and False Eyes

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.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 7.5 Dragons

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.

- origin

  > 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.

- size

  > The number of stones in the dragon.

- effective size

  > 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.

- crude_status

  > (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.

- status

  > 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.

- origin

  > The origin field is duplicated here.

- adjacent
- `adjacent[MAX_NEIGHBOR_DRAGONS]`

  > Dragons of either color near the given one are called neighbors. They are computed by the function `find_neighbor_dragons()`. The `dragon2.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()`. The `dragon2.adjacent` array gives the dragon numbers of these dragons.

- neighbors

  > The number of neighbor dragons.

- hostile_neighbors

  > The number of neighbor dragons of the opposite color.

- moyo_size
- float moyo_territorial_value

  > 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.

- safety

  > The dragon safety can take on one of the values

- weakness
- weakness_pre_owl

  > 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.

- escape_route

  > 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.

- struct eyevalue genus

  > 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 */
  >     };

- heye

  > Location of a half eye attached to the dragon.

- lunch

  > 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.

- surround_status
- surround_size

  > 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).

- semeais
- semeai_defense_point
- semeai_defense_certain
- semeai_attack_point
- semeai_attack_certain

  > 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 field `dragon2.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 field `semeai_margin_of_safety` is intended to indicate whether the semeai is close or not but currently this field is not maintained. The fields `semeai_defense_certain` and `semeai_attack_certain` indicate that the semeai code was able to finish analysis without running out of nodes.

- owl_status

  > 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 is `UNCHECKED`. If `owl_attack()` determines that the dragon cannot be attacked, it is classified as `ALIVE`. Otherwise, `owl_defend()` is run, and if it can be defended it is classified as `CRITICAL`, and if not, as `DEAD`.

- owl_attack_point

  > If the dragon can be attacked this is the point to attack the dragon.

- owl_attack_code

  > The owl attack code, It can be WIN, KO_A, KO_B or 0 (see Return Codes).

- owl_attack_certain

  > The owl reading is able to finish analyzing the attack without running out of nodes.

- owl_second_attack_point

  > A second attack point.

- owl_defense_point

  > If the dragon can be defended, this is the place to play.

- owl_defense_code

  > The owl defense code, It can be WIN, KO_A, KO_B or 0 (see Return Codes).

- owl_defense_certain

  > The owl code is able to finish analyzing the defense without running out of nodes.

- owl_second_defense_point

  > A second owl defense point.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 7.6 Colored Dragon Display

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 option -E to display eye spaces (see section Eyes and Half Eyes).
- the option -m 0x0180 to display territory, moyo and area (see section Territory, Moyo and Area).

The colored displays are documented elsewhere (see section Colored Display).

[ << ] | [ >> ] |  |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

This document was generated on March 25, 2024 using texi2html 1.82.