461 lines
19 KiB
Plaintext
461 lines
19 KiB
Plaintext
|
|
||
|
@menu
|
||
|
* Move generation Intro:: Introduction.
|
||
|
* Move Reasons:: Generation of move reasons.
|
||
|
* Move Reason Details:: Detailed Descriptions of Move Reasons
|
||
|
* Valuation:: Valuating the moves
|
||
|
* End Game:: Endgame move generation
|
||
|
@end menu
|
||
|
|
||
|
@node Move generation Intro
|
||
|
@section Introduction
|
||
|
|
||
|
GNU Go 3.0 introduced a move generation scheme substantially different
|
||
|
from earlier versions. In particular, it was different from the method of move
|
||
|
generation in GNU Go 2.6.
|
||
|
|
||
|
In the old scheme, various move generators suggested different moves with
|
||
|
attached values. The highest such value then decided the move. There were two
|
||
|
important drawbacks with this scheme:
|
||
|
|
||
|
@itemize @bullet
|
||
|
@item
|
||
|
Efficient multipurpose moves could only be found by patterns which
|
||
|
explicitly looked for certain combinations, such as a simultaneous
|
||
|
connection and cut. There was also no good way to e.g. choose among
|
||
|
several attacking moves.
|
||
|
|
||
|
@item
|
||
|
The absolute move values were increasingly becoming harder to tune with
|
||
|
the increasing number of patterns. They were also fairly subjective and
|
||
|
the tuning could easily break in unexpected ways when something changed,
|
||
|
e.g. the worm valuation.
|
||
|
@end itemize
|
||
|
|
||
|
The basic idea of the new move generation scheme is that the various
|
||
|
move generators suggest reasons for moves, e.g. that a move captures
|
||
|
something or connects two strings, and so on. When all reasons for the
|
||
|
different moves have been found, the valuation starts. The primary
|
||
|
advantages are
|
||
|
|
||
|
@itemize @bullet
|
||
|
@item
|
||
|
The move reasons are objective, in contrast to the move values in
|
||
|
the old scheme. Anyone can verify whether a suggested move reason is
|
||
|
correct.
|
||
|
|
||
|
@item
|
||
|
The centralized move valuation makes tuning easier. It also allows
|
||
|
for style dependent tuning, e.g. how much to value influence
|
||
|
compared to territory. Another possibility is to increase the value
|
||
|
of safe moves in a winning position.
|
||
|
@end itemize
|
||
|
|
||
|
|
||
|
@node Move Reasons
|
||
|
@section Generation of move reasons
|
||
|
@cindex move reasons
|
||
|
|
||
|
Each move generator suggests a number of moves. It justifies each move
|
||
|
suggestion with one or move @dfn{move reasons}. These move reasons
|
||
|
are collected at each intersection where the moves are suggested for
|
||
|
later valuation. Here is a partial list of of move reasons considered by GNU
|
||
|
Go. (The complete list may be found in @file{move_reasons.h}.)
|
||
|
|
||
|
@table @code
|
||
|
@item ATTACK_MOVE
|
||
|
@itemx DEFEND_MOVE
|
||
|
Attack or defend a worm.
|
||
|
@item ATTACK_THREAT_MOVE
|
||
|
@itemx DEFEND_THREAT_MOVE
|
||
|
Threaten to attack or defend a worm.
|
||
|
@item EITHER_MOVE
|
||
|
A move that either achieves one goal or another (at the moment this only
|
||
|
used for attacks on worms).
|
||
|
@item ALL_MOVE
|
||
|
At the moment this is used for a move that defends two worms threatened
|
||
|
by a double attack.
|
||
|
@item CONNECT_MOVE
|
||
|
@itemx CUT_MOVE
|
||
|
Connect or cut two worms.
|
||
|
@item ANTISUJI_MOVE
|
||
|
Declare an antisuji or forbidden move.
|
||
|
@item SEMEAI_MOVE
|
||
|
@itemx SEMEAI_THREAT
|
||
|
Win or threaten to win a semeai.
|
||
|
@item EXPAND_TERRITORY_MOVE
|
||
|
@item EXPAND_MOYO_MOVE
|
||
|
Move expanding our territory/moyo. These reasons are at the moment
|
||
|
treated identically.
|
||
|
@item VITAL_EYE_MOVE
|
||
|
A vital point for life and death.
|
||
|
@item STRATEGIC_ATTACK_MOVE
|
||
|
@itemx STRATEGIC_DEFEND_MOVE
|
||
|
Moves added by 'a' and 'd' class patterns (@pxref{Pattern Classification})
|
||
|
which (perhaps intangibly) attack or defend a dragon.
|
||
|
@item OWL_ATTACK_MOVE
|
||
|
@itemx OWL_DEFEND_MOVE
|
||
|
An owl attack or defense move.
|
||
|
@item OWL_ATTACK_THREAT
|
||
|
@itemx OWL_DEFEND_THREAT
|
||
|
A threat to owl attack or defend a group.
|
||
|
@item OWL_PREVENT_THREAT
|
||
|
A move to remove an owl threat.
|
||
|
@item UNCERTAIN_OWL_ATTACK
|
||
|
@itemx UNCERTAIN_OWL_DEFENSE
|
||
|
An uncertain owl attack or defense. This means that the owl code could
|
||
|
not decide the outcome, because the owl node limit was reached.
|
||
|
@item MY_ATARI_ATARI_MOVE
|
||
|
A move that starts a chain of ataris, eventually leading to a
|
||
|
capture.
|
||
|
@item YOUR_ATARI_ATARI_MOVE
|
||
|
A move that if played by the opponent starts a chain of ataris for the
|
||
|
opponent, leading to capture, which is also a safe move for us. Preemptively
|
||
|
playing such a move almost always defends the threat.
|
||
|
@end table
|
||
|
|
||
|
The attack and defend move types can have a suffix to denote moves whose
|
||
|
result depends on a ko, e.g. @code{OWL_ATTACK_MOVE_GOOD_KO}. Here
|
||
|
@code{..._GOOD_KO} and @code{..._BAD_KO} correspond to @code{KO_A} and
|
||
|
@code{KO_B} as explained in @ref{Ko}.
|
||
|
See @file{engine/move_reasons.h} for the full of move reasons.
|
||
|
|
||
|
@strong{NOTICE:} Some of these are reasons for @strong{not} playing a move.
|
||
|
|
||
|
More detailed discussion of these move reasons will be found in the
|
||
|
next section.
|
||
|
|
||
|
@node Move Reason Details
|
||
|
@section Detailed Descriptions of various Move Reasons
|
||
|
|
||
|
@menu
|
||
|
* Attack and Defense:: Worm Attack and Defense
|
||
|
* Threats to Attack or Defend:: Worm Threats
|
||
|
* Multi Attack or Defense:: Combined Attacks and Defenses
|
||
|
* Cutting and Connecting:: Cutting and Connecting moves
|
||
|
* Semeai:: Semeai winning moves
|
||
|
* Making eyes:: Vital eye moves
|
||
|
* Antisuji moves:: Never play these!
|
||
|
* Territorial moves:: Block or expand territory
|
||
|
* Owl attack and defense:: Owl Attack and Defense
|
||
|
* Combination Attacks:: Coordinated threats such as double ataris
|
||
|
@end menu
|
||
|
|
||
|
@node Attack and Defense
|
||
|
@subsection Attacking and defending moves
|
||
|
|
||
|
A move which tactically captures a worm is called an @dfn{attack move} and a
|
||
|
move which saves a worm from being tactically captured is called a
|
||
|
@dfn{defense move}. It is understood that a defense move can only exist if
|
||
|
the worm can be captured, and that a worm without defense only is
|
||
|
attacked by moves that decrease the liberty count or perform necessary
|
||
|
backfilling.
|
||
|
|
||
|
It is important that all moves which attack or defend a certain string
|
||
|
are found, so that the move generation can make an informed choice
|
||
|
about how to perform a capture, or find moves which capture and/or
|
||
|
defend several worms.
|
||
|
|
||
|
Attacking and defending moves are first found in @code{make_worms} while it
|
||
|
evaluates the tactical status of all worms, although this step only
|
||
|
gives one attack and defense (if any) move per worm. Immediately
|
||
|
after, still in @code{make_worms}, all liberties of the attacked worms are
|
||
|
tested for additional attack and defense moves. More indirect moves
|
||
|
are found by @code{find_attack_patterns} and @code{find_defense_patterns},
|
||
|
which match the A (attack) and D (defense) class patterns in
|
||
|
@file{patterns/attack.db} and @file{patterns/defense.db} As a final step, all
|
||
|
moves which fill some purpose at all are tested whether they additionally
|
||
|
attacks or defends some worm. (Only unstable worms are analyzed.)
|
||
|
|
||
|
@node Threats to Attack or Defend
|
||
|
@subsection Threats to Attack or Defend
|
||
|
|
||
|
A threat to attack a worm, but where the worm can be defended is used as
|
||
|
a secondary move reason. This move reason can enhance the value of a
|
||
|
move so that it becomes sente. A threatening move without any other
|
||
|
justification can also be used as a ko threat. The same is true for a
|
||
|
move that threatens defense of a worm, but where the worm can still be
|
||
|
captured if the attacker doesn't tenuki.
|
||
|
|
||
|
Threats found by the owl code are called @strong{owl threats} and they
|
||
|
have their own owl reasons.
|
||
|
|
||
|
@node Multi Attack or Defense
|
||
|
@subsection Multiple attack or defense moves
|
||
|
|
||
|
Sometimes a move attacks at least one of a number of worms or
|
||
|
simultaneously defends all of several worms. These moves are noted
|
||
|
by their own move reasons.
|
||
|
|
||
|
@node Cutting and Connecting
|
||
|
@subsection Cutting and connecting moves
|
||
|
|
||
|
Moves which connect two distinct dragons are called @code{connecting moves}.
|
||
|
Moves which prevent such connections are called @dfn{cutting moves}. Cutting
|
||
|
and connecting moves are primarily found by pattern matching, the @code{C}
|
||
|
and @code{B} class patterns.
|
||
|
|
||
|
A second source of cutting and connecting moves comes from the attack
|
||
|
and defense of cutting stones. A move which attacks a worm
|
||
|
automatically counts as a connecting move if there are multiple
|
||
|
dragons adjacent to the attacked worm. Similarly a defending move
|
||
|
counts as a cutting move. The action taken when a pattern of
|
||
|
this type is found is to induce a connect or cut move reason.
|
||
|
|
||
|
When a cut or connect move reason is registered, the involved dragons
|
||
|
are of course stored. Thus the same move may cut and/or connect
|
||
|
several pairs of dragons.
|
||
|
|
||
|
@node Semeai
|
||
|
@subsection Semeai winning moves
|
||
|
|
||
|
A move which is necessary to win a capturing race is called a @dfn{semeai
|
||
|
move}. These are similar to attacking moves, except that they involve
|
||
|
the simultaneous attack of one worm and the defense of another. As for
|
||
|
attack and defense moves, it's important that all moves which win a
|
||
|
semeai are found, so an informed choice can be made between them.
|
||
|
|
||
|
Semeai move reasons should be set by the semeai module. However this
|
||
|
has not been implemented yet. One might also wish to list moves
|
||
|
which increase the lead in a semeai race (removes ko threats) for use
|
||
|
as secondary move reasons. Analogously if we are behind in the race.
|
||
|
|
||
|
@node Making eyes
|
||
|
@subsection Making or destroying eyes
|
||
|
|
||
|
A move which makes a difference in the number of eyes produced from an
|
||
|
eye space is called an @dfn{eye move}. It's not necessary that the eye is
|
||
|
critical for the life and death of the dragon in question, although it
|
||
|
will be valued substantially higher if this is the case. As usual it's
|
||
|
important to find all moves that change the eye count.
|
||
|
|
||
|
(This is part of what eye_finder was doing. Currently it only finds
|
||
|
one vital point for each unstable eye space.)
|
||
|
|
||
|
@node Antisuji moves
|
||
|
@subsection Antisuji moves
|
||
|
|
||
|
Moves which are locally inferior or for some other reason must not be
|
||
|
played are called @dfn{antisuji moves}. These moves are generated by pattern
|
||
|
matching. Care must be taken with this move reason as the move under
|
||
|
no circumstances will be played.
|
||
|
|
||
|
@node Territorial moves
|
||
|
@subsection Territorial moves
|
||
|
|
||
|
Any move that increases territory gets a move reason. This is the expand
|
||
|
territory move reason. That move reason is added by the @samp{e}
|
||
|
patterns in @file{patterns/patterns.db}. Similarly the @samp{E} patterns
|
||
|
attempt to generate or mitigate a moyo, which is a region of influence
|
||
|
not yet secure territory, yet valuable. Such a pattern sets the ``expand
|
||
|
moyo'' move reason.
|
||
|
|
||
|
@node Owl attack and defense
|
||
|
@subsection Attacking and Defending Dragons
|
||
|
@findex owl_reasons
|
||
|
|
||
|
Just as the tactical reading code tries to determine when a worm
|
||
|
can be attacked or defended, the owl code tries to determine
|
||
|
when a dragon can get two eyes and live. The function @code{owl_reasons()}
|
||
|
generates the corresponding move reasons.
|
||
|
|
||
|
The owl attack and owl defense move reasons are self explanatory.
|
||
|
|
||
|
The owl attack threat reason is generated if owl attack on an
|
||
|
opponent's dragon fails but the owl code determines that the
|
||
|
dragon can be killed with two consecutive moves. The killing
|
||
|
moves are stored in @code{dragon[pos].owl_attack_point}
|
||
|
and @code{dragon[pos].owl_second_attack_point}.
|
||
|
|
||
|
Similarly if a friendly dragon is dead but two moves can revive it,
|
||
|
an owl defense threat move reason is generated.
|
||
|
|
||
|
The prevent threat reasons are similar but with the colors
|
||
|
reversed: if the opponent has an attack threat move then a
|
||
|
move which removes the threat gets a prevent threat move
|
||
|
reason.
|
||
|
|
||
|
The owl uncertain move reasons are generated when the owl
|
||
|
code runs out of nodes. In order to prevent the owl code from
|
||
|
running too long, a cap is put on the number of nodes one owl
|
||
|
read can generate. If this is exceeded, the reading is cut
|
||
|
short and the result is cached as usual, but marked uncertain.
|
||
|
In this case an owl uncertain move reason may be generated.
|
||
|
For example, if the owl code finds the dragon alive but is
|
||
|
unsure, a move to defend may still be generated.
|
||
|
|
||
|
@node Combination Attacks
|
||
|
@subsection Combination Attacks
|
||
|
@findex atari_atari
|
||
|
|
||
|
The function @code{atari_atari} tries to find a sequence of ataris
|
||
|
culminating in an unexpected change of status of any opponent string,
|
||
|
from @code{ALIVE} to @code{CRITICAL}. Once such a sequence of ataris
|
||
|
is found, it tries to shorten it by rejecting irrelevant moves.
|
||
|
|
||
|
@node Valuation
|
||
|
@section Valuation of suggested moves
|
||
|
@findex value_move_reasons()
|
||
|
|
||
|
At the end of the move generation process, the function
|
||
|
@code{value_move_reasons()} tries to assign values to the
|
||
|
moves for the purpose of selecting the best move. The
|
||
|
single purpose of the move valuation is to try to rank
|
||
|
the moves so that the best move gets the highest
|
||
|
score. In principle these values could be arbitrary,
|
||
|
but in order to make it easier to evaluate how well the
|
||
|
valuation performs, not to mention simplify the tuning,
|
||
|
we try to assign values which are consistent with the
|
||
|
usual methods of counting used by human Go players,
|
||
|
as explained for example in @emph{The Endgame} by Ogawa
|
||
|
and Davies.
|
||
|
|
||
|
Moves are valued with respect to four different criteria. These are
|
||
|
|
||
|
@itemize @bullet
|
||
|
@item territorial value
|
||
|
@item strategical value
|
||
|
@item shape value,
|
||
|
@item secondary value.
|
||
|
@end itemize
|
||
|
|
||
|
All of these are floats and should be measured in terms of actual
|
||
|
points.
|
||
|
|
||
|
The territorial value is the total change of expected territory caused
|
||
|
by this move. This includes changes in the status of groups if the move
|
||
|
is an attack or a defense move.
|
||
|
|
||
|
Beginning with GNU Go 3.0, the influence function plays an important role
|
||
|
in estimating territory (@pxref{Influence and Territory}). It is used
|
||
|
to make a guess at each intersection how likely it is that it will become
|
||
|
black or white territory. The territorial value sums up the changes
|
||
|
in these valuations.
|
||
|
|
||
|
Strategical value is a measure of the effect the move has on the
|
||
|
safety of all groups on the board. Typically cutting and connecting
|
||
|
moves have their main value here. Also edge extensions, enclosing
|
||
|
moves and moves towards the center have high strategical value. The
|
||
|
strategical value should be the sum of a fraction of the territorial
|
||
|
value of the involved dragons. The fraction is determined by the
|
||
|
change in safety of the dragon.
|
||
|
|
||
|
Shape value is a purely local shape analysis. An
|
||
|
important role of this measure is to offset mistakes made by the
|
||
|
estimation of territorial values. In open positions it's
|
||
|
often worth sacrificing a few points of (apparent) immediate profit to
|
||
|
make good shape. Shape value is implemented by pattern matching, the
|
||
|
Shape patterns.
|
||
|
|
||
|
Secondary value is given for move reasons which by themselves are not
|
||
|
sufficient to play the move. One example is to reduce the number of
|
||
|
eyes for a dragon that has several or to attack a defenseless worm.
|
||
|
|
||
|
When all these values have been computed, they are summed, possibly
|
||
|
weighted (secondary value should definitely have a small weight), into
|
||
|
a final move value. This value is used to decide the move.
|
||
|
|
||
|
@menu
|
||
|
* Territorial value:: How much territory does a move gain
|
||
|
* Strategical value:: Strategical gains from a move
|
||
|
* Shape factor:: Local shape
|
||
|
* Minimum Value:: Minimum value
|
||
|
* Secondary Value:: Other, more indirect, gains from a move
|
||
|
* Threats and Followup Value:: Valuation of attack and defense threats
|
||
|
@end menu
|
||
|
|
||
|
@node Territorial value
|
||
|
@subsection Territorial Value
|
||
|
@findex estimate_territorial_value
|
||
|
|
||
|
The algorithm for computing territorial value is in the function
|
||
|
@code{estimate_territorial_value}. As the name suggests, it seeks
|
||
|
to estimate the change in territory.
|
||
|
|
||
|
It considers all groups that are changed from alive to death or vice-versa
|
||
|
due to this move. Also, it makes an assumption whether the move should be
|
||
|
considered safe. If so, the influence module is called: The function
|
||
|
@code{influence_delta_territory} estimates the territorial effect of
|
||
|
both the stone played and of the changes of group status'.
|
||
|
|
||
|
The result returned by the influence module is subject to a number of
|
||
|
corrections. This is because some move reasons cannot be evaluated by a
|
||
|
single call to the influence function, such as moves depending on a ko.
|
||
|
|
||
|
@node Strategical value
|
||
|
@subsection Strategical Value
|
||
|
|
||
|
Strategical defense or attack reasons are assigned to any move
|
||
|
which matches a pattern of type @samp{a} or @samp{d}. These are
|
||
|
moves which in some (often intangible) way tend to help
|
||
|
strengthen or weaken a dragon. Of course strengthening a
|
||
|
dragon which is already alive should not be given much value,
|
||
|
but when the move reason is generated it is not necessary
|
||
|
to check its status or safety. This is done later, during
|
||
|
the valuation phase.
|
||
|
|
||
|
@node Shape factor
|
||
|
@subsection Shape Factor
|
||
|
|
||
|
In the value field of a pattern (@pxref{Pattern Values}) one may
|
||
|
specify a shape value.
|
||
|
|
||
|
This is used to compute the shape factor, which multiplies the
|
||
|
score of a move. We take the largest positive contribution to
|
||
|
shape and add 1 for each additional positive contribution
|
||
|
found. Then we take the largest negative contribution to
|
||
|
shape, and add 1 for each additional negative contribution. The
|
||
|
resulting number is raised to the power 1.05 to obtain the
|
||
|
shape factor.
|
||
|
|
||
|
The rationale behind this complicated scheme is that every
|
||
|
shape point is very significant. If two shape contributions
|
||
|
with values (say) 5 and 3 are found, the second contribution
|
||
|
should be devalued to 1. Otherwise the engine is too difficult
|
||
|
to tune since finding multiple contributions to shape can cause
|
||
|
significant overvaluing of a move.
|
||
|
|
||
|
@node Minimum Value
|
||
|
@subsection Minimum Value
|
||
|
|
||
|
A pattern may assign a minimum (and sometimes also a maximum)
|
||
|
value. For example the Joseki patterns have values which are
|
||
|
prescribed in this way, or ones with a @code{value} field.
|
||
|
One prefers not to use this approach but in practice it is
|
||
|
sometimes needed.
|
||
|
|
||
|
In the fuseki, there are often several moves with identical minimum
|
||
|
value. GNU Go chooses randomly between such moves, which ensures
|
||
|
some indeterminacy of GNU Go's play. Later in the game, GNU Go's
|
||
|
genuine valuation of such a move is used as a secondary criterion.
|
||
|
|
||
|
@node Secondary Value
|
||
|
@subsection Secondary Value
|
||
|
|
||
|
Secondary move reasons are weighed very slightly. Such a move
|
||
|
can tip the scales if all other factors are equal.
|
||
|
|
||
|
@node Threats and Followup Value
|
||
|
@subsection Threats and Followup Value
|
||
|
|
||
|
Followup value refers to value which may acrue if we get two
|
||
|
moves in a row in a local area. It is assigned for moves that threaten
|
||
|
to attack or defend a worm or dragon. Also, since GNU Go 3.2 the influence
|
||
|
module makes an assessment of the possible purely territorial followup
|
||
|
moves. In cases where these two heuristics are not sufficient we
|
||
|
add patterns with a @code{followup_value} autohelper macro.
|
||
|
|
||
|
Usually, the followup value gives only a small contribution; e.g. if
|
||
|
it the followup value is very large, then GNU Go treats the move as sente by
|
||
|
doubling its value. However, if the largest move on the board is a ko
|
||
|
which we cannot legally take, then such a move becomes attractive as a ko
|
||
|
threat and the full followup value is taken into account.
|
||
|
|
||
|
@node End Game
|
||
|
@section End Game
|
||
|
|
||
|
Endgame moves are generated just like any other move by GNU Go. In fact,
|
||
|
the concept of endgame does not exist explicitly, but if the largest
|
||
|
move initially found is worth 6 points or less, an extra set of patterns
|
||
|
in @file{endgame.db} is matched and the move valuation is redone.
|