/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* This is GNU Go, a Go program. Contact gnugo@gnu.org, or see *
* http://www.gnu.org/software/gnugo/ for more information. *
* Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, *
* 2008 and 2009 by the Free Software Foundation. *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation - version 3 or *
* (at your option) any later version. *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License in file COPYING for more details. *
* You should have received a copy of the GNU General Public *
* License along with this program; if not, write to the Free *
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
* Boston, MA 02111, USA. *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Maximum number of dragons considered by a, B, C, and d class patterns. */
#define MAX_DRAGONS_PER_PATTERN 5
#define MAX_STRINGS_PER_PATTERN 5
/* Values of joseki patterns. */
/* Take care of joseki patterns. */
handle_joseki_patterns(struct pattern_attribute
*attributes
,
unsigned int class, int move
,
int my_dragons
[MAX_DRAGONS_PER_PATTERN
],
int your_dragons
[MAX_DRAGONS_PER_PATTERN
],
struct pattern_attribute
*attribute
;
/* Pattern class J, joseki standard move. Add expand territory and
* moyo, and require the value at least J_value.
TRACE("...joseki standard move\n");
add_expand_territory_move(move
);
TRACE("...expands territory\n");
add_expand_moyo_move(move
);
TRACE("...expands moyo\n");
set_minimum_move_value(move
, J_VALUE
);
TRACE("... minimum move value %f\n", J_VALUE
);
/* Class `j' and `t' patterns are treated similarly. */
if (class & (CLASS_j
| CLASS_t
)) {
TRACE("...less urgent joseki move\n");
add_expand_territory_move(move
);
TRACE("...expands territory\n");
add_expand_moyo_move(move
);
TRACE("...expands moyo\n");
TRACE("...minor joseki move\n");
/* Board size modification. */
min_value
*= board_size
/ 19.0;
for (attribute
= attributes
; attribute
->type
!= LAST_ATTRIBUTE
;
if (attribute
->type
== SHAPE
) {
shape_value
= attribute
->value
;
min_value
*= (1 + 0.01 * shape_value
);
if ((board_size
>= 17) && (class & CLASS_F
)) {
/* Otherwise, `j' and `t' patterns not of CLASS_F would get
* preferred in value_move_reasons().
set_maximum_move_value(move
, min_value
);
scale_randomness(move
, 5.0);
TRACE("...move value %f (shape %f)\n", min_value
, shape_value
);
TRACE("...minimum move value %f (shape %f)\n", min_value
, shape_value
);
set_minimum_move_value(move
, min_value
);
/* Pattern class U, very urgent joseki move. Add strategical defense
* and attack, plus a shape bonus of 15 and a minimum value of 40.
TRACE("...joseki urgent move\n");
for (k
= 0; k
< my_ndragons
; k
++) {
add_strategical_defense_move(move
, my_dragons
[k
]);
TRACE("...strategical defense of %1m\n", my_dragons
[k
]);
for (k
= 0; k
< your_ndragons
; k
++) {
add_strategical_attack_move(move
, your_dragons
[k
]);
TRACE("...strategical attack on %1m\n", your_dragons
[k
]);
add_shape_value(move
, 15);
TRACE("...shape value 15\n");
set_minimum_move_value(move
, U_VALUE
);
TRACE("...(min) move value %f\n", U_VALUE
);
/* Pattern class T, joseki trick move. For the moment we never play
TRACE("...joseki trick move\n");
for (attribute
= attributes
; attribute
->type
!= LAST_ATTRIBUTE
;
switch (attribute
->type
) {
set_minimum_move_value(move
, attribute
->value
);
TRACE("...(min) move value %f\n", attribute
->value
);
set_maximum_move_value(move
, attribute
->value
);
TRACE("...max move value %f\n", attribute
->value
);
set_minimum_territorial_value(move
, attribute
->value
);
TRACE("...(min) territorial value %f\n", attribute
->value
);
set_maximum_territorial_value(move
, attribute
->value
);
TRACE("...max territorial value %f\n", attribute
->value
);
/* For class `j' and `t' patterns shape value has been counted
if (!(class & (CLASS_j
| CLASS_t
))) {
add_shape_value(move
, attribute
->value
);
TRACE("...shape value %f\n", attribute
->value
);
add_followup_value(move
, attribute
->value
);
TRACE("...followup value %f\n", attribute
->value
);
add_reverse_followup_value(move
, attribute
->value
);
TRACE("...reverse followup value %f\n", attribute
->value
);
* This callback is invoked for each matched pattern.
shapes_callback(int anchor
, int color
, struct pattern
*pattern
, int ll
,
int other
= OTHER_COLOR(color
);
/* For restricted search, the pattern must intersect the search area */
/* Dragons of our color. */
int my_dragons
[MAX_DRAGONS_PER_PATTERN
];
/* Dragons of other color. */
int your_dragons
[MAX_DRAGONS_PER_PATTERN
];
/* Strings of our color. */
int my_strings
[MAX_STRINGS_PER_PATTERN
];
/* Strings of other color. */
int your_strings
[MAX_STRINGS_PER_PATTERN
];
/* Make a local copy of the classification that we may modify. */
unsigned int class = pattern
->class;
/* Don't accept fuseki marked patterns while scoring. */
if (doing_scoring
&& (class & CLASS_F
))
/* Don't need auxiliary data in this callback. */
/* Pick up the location of the move */
move
= AFFINE_TRANSFORM(pattern
->move_offset
, ll
, anchor
);
/* For some classes of patterns we need to find all dragons present
if ((class & (CLASS_B
| CLASS_C
| CLASS_c
| CLASS_a
| CLASS_d
| CLASS_O
| CLASS_J
| CLASS_j
| CLASS_U
| CLASS_T
| CLASS_t
)) != 0) {
for (k
= 0; k
< pattern
->patlen
; ++k
) {
int pos
; /* absolute (board) co-ord of (transformed) pattern element */
int origin
; /* dragon origin */
/* all the following stuff (currently) applies only at occupied cells */
if (pattern
->patn
[k
].att
== ATT_dot
)
/* transform pattern real coordinate */
pos
= AFFINE_TRANSFORM(pattern
->patn
[k
].offset
, ll
, anchor
);
/* Already, matchpat rejects O patterns containing a friendly stone with
* DEAD or CRITICAL matcher_status. If the stone is tactically
* CRITICAL it still could have matcher_status ALIVE since it might
* be amalgamated into a live dragon. In this case we want to reject the
* pattern if (move) does not rescue it. This is most easily tested
* here within shapes_callback(), since the value of (move) is not
&& worm
[pos
].attack_points
[0] != 0
&& !does_defend(move
, pos
))
origin
= dragon
[pos
].origin
;
if (board
[pos
] == color
&& my_ndragons
< MAX_DRAGONS_PER_PATTERN
) {
for (l
= 0; l
< my_ndragons
; l
++) {
if (my_dragons
[l
] == origin
)
/* We found another dragon of our color. Check that it (or
* rather the underlying worm) cannot be tactically
* captured before adding it to the list of my_dragons.
if (worm
[pos
].attack_codes
[0] == 0
|| does_defend(move
, pos
)) {
/* Ok, add the dragon to the list. */
if (board
[pos
] == other
&& your_ndragons
< MAX_DRAGONS_PER_PATTERN
) {
for (l
= 0; l
< your_ndragons
; l
++) {
if (your_dragons
[l
] == origin
)
if (l
== your_ndragons
) {
/* We found another opponent dragon, add it to the list. */
your_dragons
[l
] = origin
;
if (pattern
->patn
[k
].att
== ATT_O
|| pattern
->patn
[k
].att
== ATT_X
) {
origin
= find_origin(pos
);
if (board
[pos
] == color
&& my_nstrings
< MAX_STRINGS_PER_PATTERN
) {
for (l
= 0; l
< my_nstrings
; l
++) {
if (my_strings
[l
] == origin
)
/* We found another string of our color. Check that it
* cannot be tactically captured before adding it to the
if (worm
[pos
].attack_codes
[0] == 0
|| does_defend(move
, pos
)) {
/* Ok, add the string to the list. */
if (board
[pos
] == other
&& your_nstrings
< MAX_STRINGS_PER_PATTERN
) {
for (l
= 0; l
< your_nstrings
; l
++) {
if (your_strings
[l
] == origin
)
if (l
== your_nstrings
) {
/* We found another opponent string, add it to the list. */
your_strings
[l
] = origin
;
} /* loop over elements */
} /* if we need to loop over the elements */
/* Nothing to connect. Remove C class bit. */
/* Nothing to cut. Remove B class bit. */
* If this pattern can't produce any effect (e.g. if it was a B or C
* pattern with only one dragon of the appropriate color), don't
* do any expensive checking but return immediately.
* If it only has some move_values, these will be ignored.
&& !(pattern
->autohelper_flag
& HAVE_ACTION
)
&& !(class & CLASS_MOVE_REASONS
)
&& pattern
->attributes
->type
== LAST_ATTRIBUTE
)
/* For sacrifice patterns, the survival of the stone to be played is
* not checked (but it still needs to be legal). Otherwise we
* discard moves which can be captured.
if (!(class & CLASS_s
)) {
/* Don't allow ko unsafety. */
if (safe_move(move
, color
) != WIN
) {
TRACE(" move at %1m wasn't safe, discarded\n", move
);
/* Allow illegal ko captures at this stage. */
if (!is_ko(move
, color
, NULL
) && !is_legal(move
, color
)) {
TRACE(" move at %1m wasn't legal, discarded\n", move
);
/* For class n patterns, the pattern is contingent on an opponent
* move at * not being captured.
if (safe_move(move
, other
) == 0) {
TRACE(" opponent can't play safely at %1m, move discarded\n", move
);
/* If the pattern has a constraint, call the autohelper to see
* if the pattern must be rejected.
if (pattern
->autohelper_flag
& HAVE_CONSTRAINT
) {
if (!pattern
->autohelper(ll
, move
, color
, 0))
/* Ask helper for acceptance of pattern. */
/* ask helper function to consider the move */
DEBUG(DEBUG_HELPER
, " asking helper to consider '%s'+%d at %1m\n",
pattern
->name
, ll
, move
);
accepted
= pattern
->helper(pattern
, ll
, move
, color
);
DEBUG(DEBUG_HELPER
, "helper likes pattern '%s' at %1m\n",
DEBUG(DEBUG_HELPER
, " helper does not like pattern '%s' at %1m\n",
return; /* pattern matcher does not like it */
/* If using -a, want to see all matches even if not -v */
if (allpats
|| verbose
) {
TRACE("pattern '%s'+%d matched at %1m\n", pattern
->name
, ll
, move
);
/* does the pattern have an action? */
if (pattern
->autohelper_flag
& HAVE_ACTION
)
pattern
->autohelper(ll
, move
, color
, 1);
/* Pattern class B, try to cut all combinations of opponent strings. */
for (k
= 0; k
< your_nstrings
; k
++)
for (l
= k
+1; l
< your_nstrings
; l
++) {
if (string_connect(your_strings
[k
], your_strings
[l
], NULL
)
&& !play_connect_n(color
, 1, 1, move
,
your_strings
[k
], your_strings
[l
])) {
add_cut_move(move
, your_strings
[k
], your_strings
[l
]);
TRACE("...cuts strings %1m, %1m\n",
your_strings
[k
], your_strings
[l
]);
/* Pattern class C, try to connect all combinations of our strings. */
for (k
= 0; k
< my_nstrings
; k
++)
for (l
= k
+1; l
< my_nstrings
; l
++) {
if (disconnect(my_strings
[k
], my_strings
[l
], NULL
)
&& !play_connect_n(color
, 0, 1, move
,
my_strings
[k
], my_strings
[l
])) {
add_connection_move(move
, my_strings
[k
], my_strings
[l
]);
TRACE("...connects strings %1m, %1m\n",
my_strings
[k
], my_strings
[l
]);
/* Pattern class c, add strategical defense move reason for all our
* dragons and a small shape bonus.
* This is a preliminary effect of "weak connection" and may need to
for (k
= 0; k
< my_ndragons
; k
++) {
add_strategical_defense_move(move
, my_dragons
[k
]);
TRACE("...strategical defense (weak connection) of %1m\n",
add_shape_value(move
, 1);
TRACE("...shape value 1\n");
/* Pattern class b is obsolete in the pattern databases handled here. */
gg_assert(!(class & CLASS_b
));
/* Pattern class e, expand to make territory. */
add_expand_territory_move(move
);
TRACE("...expands territory\n");
/* Pattern class E, expand to make moyo. */
add_expand_moyo_move(move
);
TRACE("...expands moyo\n");
/* Pattern class i, an invasion. */
TRACE("...is an invasion\n");
/* Pattern class a, strategical level attack on all opponent dragons. */
for (k
= 0; k
< your_ndragons
; k
++) {
add_strategical_attack_move(move
, your_dragons
[k
]);
TRACE("...strategical attack on %1m\n", your_dragons
[k
]);
/* Pattern class d, strategical level defense of all own dragons. */
for (k
= 0; k
< my_ndragons
; k
++) {
add_strategical_defense_move(move
, my_dragons
[k
]);
TRACE("...strategical defense of %1m\n", my_dragons
[k
]);
/* Pattern class W, worthwhile threat move. */
TRACE("...worthwhile threat move\n");
add_worthwhile_threat_move(move
);
handle_joseki_patterns(pattern
->attributes
, class, move
,
my_dragons
, my_ndragons
, your_dragons
, your_ndragons
);
/* This callback is invoked for each matched pattern from joseki
* database. This function is just a copy of relevant parts of
* shapes_callback(). However, most of the common code resides in
* handle_joseki_patterns().
joseki_callback(int move
, int color
, struct corner_pattern
*pattern
,
int trans
, int *stones
, int num_stones
)
int class = pattern
->class;
/* Dragons of our color. */
int my_dragons
[MAX_DRAGONS_PER_PATTERN
];
/* Dragons of other color. */
int your_dragons
[MAX_DRAGONS_PER_PATTERN
];
/* For urgent joseki patterns we need to find all dragons present in the
* pattern since such patterns are assumed to have strategical effect on
/* Loop over all stones in the pattern. */
for (k
= 0; k
< num_stones
; k
++) {
int origin
= dragon
[pos
].origin
;
if (board
[pos
] == color
&& my_ndragons
< MAX_DRAGONS_PER_PATTERN
) {
for (l
= 0; l
< my_ndragons
; l
++) {
if (my_dragons
[l
] == origin
)
/* We found another dragon of our color. Check that it (or
* rather the underlying worm) cannot be tactically
* captured before adding it to the list of my_dragons.
if (worm
[pos
].attack_codes
[0] == 0 || does_defend(move
, pos
)) {
/* Ok, add the dragon to the list. */
if (board
[pos
] != color
&& your_ndragons
< MAX_DRAGONS_PER_PATTERN
) {
for (l
= 0; l
< your_ndragons
; l
++) {
if (your_dragons
[l
] == origin
)
if (l
== your_ndragons
) {
/* We found another opponent dragon, add it to the list. */
your_dragons
[l
] = origin
;
/* For joseki patterns we don't check if the proposed move is safe or legal.
/* If the pattern has a constraint, call the autohelper to see
* if the pattern must be rejected.
if (pattern
->autohelper_flag
& HAVE_CONSTRAINT
) {
if (!pattern
->autohelper(trans
, move
, color
, 0))
/* If using -a, want to see all matches even if not -v. */
TRACE("pattern '%s'+%d matched at %1m\n", pattern
->name
, trans
, move
);
/* Does the pattern have an action? */
if (pattern
->autohelper_flag
& HAVE_ACTION
)
pattern
->autohelper(trans
, move
, color
, 1);
/* Pattern class N, antisuji move. */
TRACE("...antisuji move\n");
handle_joseki_patterns(pattern
->attributes
, class, move
,
my_dragons
, my_ndragons
, your_dragons
, your_ndragons
);
* Match all patterns in patterns.db and patterns2.db on all positions.
* This function is one of the basic generators of move reasons, called
TRACE("\nPattern matcher is looking for move reasons for %s!\n",
matchpat(shapes_callback
, color
, &pat_db
, NULL
, NULL
);
/* Don't match joseki patterns while scoring. */
if (josekidb
&& !doing_scoring
)
corner_matchpat(joseki_callback
, color
, &joseki_db
);
matchpat(shapes_callback
, color
, &joseki_db
, NULL
, NULL
);
if (!disable_fuseki
&& !doing_scoring
)
matchpat(shapes_callback
, color
, &fusekipat_db
, NULL
, NULL
);
* Match all patterns in endgame.db on all positions.
endgame_shapes(int color
)
TRACE("\nEndgame pattern matcher is looking for move reasons for %s!\n",
matchpat(shapes_callback
, color
, &endpat_db
, NULL
, NULL
);