Initial commit of GNU Go v3.8.
[sgk-go] / engine / utils.c
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* 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. *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "gnugo.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
#include "liberty.h"
#include "sgftree.h"
#include "random.h"
#include "gg_utils.h"
#include "patterns.h"
/*
* Change the status of all the stones in the dragon at (dr).
*/
void
change_dragon_status(int dr, enum dragon_status status)
{
int pos;
int origin = dragon[dr].origin;
for (pos = BOARDMIN; pos < BOARDMAX; pos++)
if (ON_BOARD(pos)) {
if (dragon[pos].origin == origin)
dragon[pos].status = status;
}
}
/*
* Check whether a move at (move) stops the enemy from playing at (apos).
*/
int
defend_against(int move, int color, int apos)
{
if (trymove(move, color, "defend_against", NO_MOVE)) {
if (safe_move(apos, OTHER_COLOR(color)) == 0) {
popgo();
return 1;
}
popgo();
}
return 0;
}
/*
* Returns true if color can cut at (pos), or if connection through (pos)
* is inhibited. This information is collected by find_cuts(), using the B
* patterns in the connections database.
*/
int
cut_possible(int pos, int color)
{
return (cutting_points[pos] & OTHER_COLOR(color)) != 0;
}
/*
* does_attack(move, str) returns the result code for an attack on the
* string 'str' by the move 'move'. However, if the move does not
* improve the attack result compared to tenuki, 0 is returned. In
* particular if the string is already captured, does_attack() always
* returns 0.
*/
int
does_attack(int move, int str)
{
int color = board[str];
int other = OTHER_COLOR(color);
int result = 0;
int acode = 0;
int dcode = 0;
int spos = NO_MOVE;
attack_and_defend(str, &acode, NULL, &dcode, &spos);
if (acode != 0 && dcode == 0)
return 0;
if (trymove(move, other, "does_attack-A", str)) {
if (!board[str])
result = WIN;
else
result = REVERSE_RESULT(find_defense(str, NULL));
if (result != 0) {
increase_depth_values();
if (spos != NO_MOVE && trymove(spos, color, "does_attack-B", str)) {
if (board[str]) {
int new_result = attack(str, NULL);
if (new_result < result)
result = new_result;
}
popgo();
}
decrease_depth_values();
}
popgo();
}
if (result < acode)
result = 0;
return result;
}
/*
* does_defend(move, str) returns true if the move at (move)
* defends (str). This means that it defends the string, and that
* (str) can be captured if no defense is made.
*
* FIXME: Make does_defend() ko aware like does_attack().
*/
int
does_defend(int move, int str)
{
int color = board[str];
int other = OTHER_COLOR(color);
int result = 0;
int spos = NO_MOVE;
if (!attack(str, &spos))
return 0;
gg_assert(spos != NO_MOVE);
if (trymove(move, color, "does_defend-A", str)) {
if (!attack(str, NULL)) {
result = 1;
increase_depth_values();
if (trymove(spos, other, "does_defend-B", str)) {
if (!board[str] || !find_defense(str, NULL))
result = 0;
popgo();
}
decrease_depth_values();
}
popgo();
}
return result;
}
/*
* Example: somewhere(WHITE, 2, apos, bpos, cpos).
*
* Returns true if one of the vertices listed
* satisfies board[pos]==color. Here num_moves is the
* number of moves. If check_alive is true, the dragon is not allowed
* to be dead. This check is only valid if stackp==0.
*/
int
somewhere(int color, int check_alive, int num_moves, ...)
{
va_list ap;
int pos;
int k;
gg_assert(stackp == 0 || !check_alive);
va_start(ap, num_moves);
for (k = 0; k < num_moves; k++) {
pos = va_arg(ap, int);
if (board[pos] == color
&& (!check_alive || dragon[pos].status != DEAD)) {
va_end(ap);
return 1;
}
}
va_end(ap);
return 0;
}
/* Search along the edge for the first visible stone. Start at apos
* and move in the direction of bpos. Return 1 if the first visible
* stone is of the given color. It is required that apos and bpos are
* at the same distance from the edge.
*
* FIXME: The detection of the first visible stone is quite crude and
* probably needs to be improved.
*/
int
visible_along_edge(int color, int apos, int bpos)
{
int ai = I(apos);
int aj = J(apos);
int bi = I(bpos);
int bj = J(bpos);
int pos;
int forward;
int up;
ASSERT1((ai == bi) ^ (aj == bj), apos);
if (ai == bi) {
if (aj > bj)
forward = WEST(0);
else
forward = EAST(0);
if (ai < board_size/2) {
pos = POS(0, bj);
up = SOUTH(0);
}
else {
pos = POS(board_size - 1, bj);
up = NORTH(0);
}
}
else {
if (ai > bi)
forward = NORTH(0);
else
forward = SOUTH(0);
if (aj < board_size/2) {
pos = POS(bi, 0);
up = EAST(0);
}
else {
pos = POS(bi, board_size - 1);
up = WEST(0);
}
}
for (; ON_BOARD(pos); pos += forward) {
int k;
for (k = 4; k >= 0; k--) {
ASSERT_ON_BOARD1(pos + k * up);
if (board[pos + k * up] == color)
return 1;
else if (board[pos + k * up] == OTHER_COLOR(color))
return 0;
}
}
return 0;
}
/* Is the board symmetric (or rather antisymmetric) with respect to
* mirroring in tengen after a specific move has been played? If the
* move is PASS_MOVE, check the current board.
*
* If strict is set we require that each stone is matched by a stone
* of the opposite color at the mirrored vertex. Otherwise we only
* require that each stone is matched by a stone of either color.
*/
int
test_symmetry_after_move(int move, int color, int strict)
{
int pos;
int result = 1;
if (move != PASS_MOVE) {
if (board[move] != EMPTY)
return 0;
if (!trymove(move, color, "find_mirror_move", NO_MOVE))
return 0;
}
for (pos = BOARDMIN; pos < MIRROR_MOVE(pos); pos++) {
int sum;
if (!ON_BOARD(pos))
continue;
sum = board[pos] + board[MIRROR_MOVE(pos)];
if (sum != EMPTY + EMPTY && sum != BLACK + WHITE) {
if (strict || sum == EMPTY + WHITE || sum == EMPTY + BLACK) {
result = 0;
break;
}
}
}
if (move != PASS_MOVE)
popgo();
return result;
}
/* The function play_break_through_n() plays a sequence of moves,
* alternating between the players and starting with color. After
* having played through the sequence, the three last coordinate pairs
* gives a position to be analyzed by break_through(), to see whether
* either color has managed to enclose some stones and/or connected
* his own stones. If any of the three last positions is empty, it's
* assumed that the enclosure has failed, as well as the attempt to
* connect.
*
* If one or more of the moves to play turns out to be illegal for
* some reason, the rest of the sequence is played anyway, and
* break_through() is called as if nothing special happened.
*
* Like break_through(), this function returns 1 if the attempt to
* break through was succesful and 2 if it only managed to cut
* through.
*/
int
play_break_through_n(int color, int num_moves, ...)
{
va_list ap;
int mcolor = color;
int success = 0;
int i;
int played_moves = 0;
int apos;
int xpos;
int ypos;
int zpos;
va_start(ap, num_moves);
/* Do all the moves with alternating colors. */
for (i = 0; i < num_moves; i++) {
apos = va_arg(ap, int);
if (apos != NO_MOVE
&& (trymove(apos, mcolor, "play_break_through_n", NO_MOVE)
|| tryko(apos, mcolor, "play_break_through_n")))
played_moves++;
mcolor = OTHER_COLOR(mcolor);
}
/* Now do the real work. */
xpos = va_arg(ap, int);
ypos = va_arg(ap, int);
zpos = va_arg(ap, int);
/* Temporarily increase the depth values with the number of explicitly
* placed stones.
*/
#if 0
modify_depth_values(played_moves);
#endif
if (board[xpos] == EMPTY
|| board[ypos] == EMPTY
|| board[zpos] == EMPTY)
success = 1;
else
success = break_through(xpos, ypos, zpos);
#if 0
modify_depth_values(-played_moves);
#endif
/* Pop all the moves we could successfully play. */
for (i = 0; i < played_moves; i++)
popgo();
va_end(ap);
return success;
}
/* The function play_attack_defend_n() plays a sequence of moves,
* alternating between the players and starting with color. After
* having played through the sequence, the last coordinate pair gives
* a target to attack or defend, depending on the value of do_attack.
* If there is no stone present to attack or defend, it is assumed
* that it has already been captured. If one or more of the moves to
* play turns out to be illegal for some reason, the rest of the
* sequence is played anyway, and attack/defense is tested as if
* nothing special happened.
*
* A typical use for these functions is to set up a ladder in an
* autohelper and see whether it works or not.
*/
int
play_attack_defend_n(int color, int do_attack, int num_moves, ...)
{
va_list ap;
int mcolor = color;
int success = 0;
int i;
int played_moves = 0;
int apos;
int zpos;
va_start(ap, num_moves);
/* Do all the moves with alternating colors. */
for (i = 0; i < num_moves; i++) {
apos = va_arg(ap, int);
if (apos != NO_MOVE
&& (trymove(apos, mcolor, "play_attack_defend_n", NO_MOVE)
|| tryko(apos, mcolor, "play_attack_defend_n")))
played_moves++;
mcolor = OTHER_COLOR(mcolor);
}
/* Now do the real work. */
zpos = va_arg(ap, int);
/* Temporarily increase the depth values with the number of explicitly
* placed stones.
*
* This improves the reading of pattern constraints but
* unfortunately tends to be too expensive. For the time being it is
* disabled.
*/
#if 0
modify_depth_values(played_moves);
#endif
if (do_attack) {
if (board[zpos] == EMPTY)
success = WIN;
else
success = attack(zpos, NULL);
}
else {
if (board[zpos] == EMPTY)
success = 0;
else {
int dcode = find_defense(zpos, NULL);
if (dcode == 0 && !attack(zpos, NULL))
success = WIN;
else
success = dcode;
}
}
#if 0
modify_depth_values(-played_moves);
#endif
/* Pop all the moves we could successfully play. */
for (i = 0; i < played_moves; i++)
popgo();
va_end(ap);
return success;
}
/* The function play_attack_defend2_n() plays a sequence of moves,
* alternating between the players and starting with color. After
* having played through the sequence, the two last coordinate pairs
* give two targets to simultaneously attack or defend, depending on
* the value of do_attack. If there is no stone present to attack or
* defend, it is assumed that it has already been captured. If one or
* more of the moves to play turns out to be illegal for some reason,
* the rest of the sequence is played anyway, and attack/defense is
* tested as if nothing special happened.
*
* A typical use for these functions is to set up a crosscut in an
* autohelper and see whether at least one cutting stone can be
* captured.
*/
int
play_attack_defend2_n(int color, int do_attack, int num_moves, ...)
{
va_list ap;
int mcolor = color;
int success = 0;
int i;
int played_moves = 0;
int apos;
int ypos;
int zpos;
va_start(ap, num_moves);
/* Do all the moves with alternating colors. */
for (i = 0; i < num_moves; i++) {
apos = va_arg(ap, int);
if (apos != NO_MOVE
&& (trymove(apos, mcolor, "play_attack_defend_n", NO_MOVE)
|| tryko(apos, mcolor, "play_attack_defend_n")))
played_moves++;
mcolor = OTHER_COLOR(mcolor);
}
/* Now do the real work. */
ypos = va_arg(ap, int);
zpos = va_arg(ap, int);
/* Temporarily increase the depth values with the number of explicitly
* placed stones.
*/
#if 0
modify_depth_values(played_moves);
#endif
/* FIXED: tm - returns ko results correctly (3.1.22) */
if (do_attack) {
if (board[ypos] == EMPTY || board[zpos] == EMPTY)
success = WIN;
else
success = attack_either(ypos, zpos);
}
else {
if (board[ypos] == EMPTY || board[zpos] == EMPTY)
success = 0;
else
success = defend_both(ypos, zpos);
}
#if 0
modify_depth_values(-played_moves);
#endif
/* Pop all the moves we could successfully play. */
for (i = 0; i < played_moves; i++)
popgo();
va_end(ap);
return success;
}
/* The function play_connect_n() plays a sequence of moves,
* alternating between the players and starting with color. After
* having played through the sequence, the two last coordinates
* give two targets that should be connected or disconnected, depending on
* the value of do_connect. If there is no stone present to connect or
* disconnect, it is assumed that the connection has failed. If one or
* more of the moves to play turns out to be illegal for some reason,
* the rest of the sequence is played anyway, and connection/disconnection
* is tested as if nothing special happened.
*/
int
play_connect_n(int color, int do_connect, int num_moves, ...)
{
va_list ap;
int mcolor = color;
int success = 0;
int i;
int played_moves = 0;
int apos;
int ypos;
int zpos;
va_start(ap, num_moves);
/* Do all the moves with alternating colors. */
for (i = 0; i < num_moves; i++) {
apos = va_arg(ap, int);
if (apos != NO_MOVE
&& (trymove(apos, mcolor, "play_connect_n", NO_MOVE)
|| tryko(apos, mcolor, "play_connect_n")))
played_moves++;
mcolor = OTHER_COLOR(mcolor);
}
/* Now do the real work. */
ypos = va_arg(ap, int);
zpos = va_arg(ap, int);
/* Temporarily increase the depth values with the number of explicitly
* placed stones.
*
* This improves the reading of pattern constraints but
* unfortunately tends to be too expensive. For the time being it is
* disabled.
*/
#if 0
modify_depth_values(played_moves);
#endif
/* See if ypos and zpos can be connected (or disconnected). */
if (do_connect) {
if (board[ypos] == EMPTY || board[zpos] == EMPTY)
success = 0;
else
success = string_connect(ypos, zpos, NULL);
}
else {
if (board[ypos] == EMPTY || board[zpos] == EMPTY)
success = WIN;
else
success = disconnect(ypos, zpos, NULL);
}
#if 0
modify_depth_values(-played_moves);
#endif
/* Pop all the moves we could successfully play. */
for (i = 0; i < played_moves; i++)
popgo();
va_end(ap);
return success;
}
/* The function play_lib_n() plays a sequence of moves, alternating
* between the players and starting with color. After having played
* through the sequence, the last coordinate gives a target for liberty
* counting. The number of liberties is returned.
*
* If only one move is to be played and that stone is the target,
* accuratelib (or approxlib if appropriate) is more efficient.
*/
int
play_lib_n(int color, int num_moves, ...)
{
va_list ap;
int mcolor = color;
int libs = 0;
int i;
int played_moves = 0;
int apos;
int ypos;
va_start(ap, num_moves);
/* Do all the moves with alternating colors. */
for (i = 0; i < num_moves; i++) {
apos = va_arg(ap, int);
if (apos != NO_MOVE
&& (trymove(apos, mcolor, "play_connect_n", NO_MOVE)
|| tryko(apos, mcolor, "play_connect_n")))
played_moves++;
mcolor = OTHER_COLOR(mcolor);
}
/* Now do the real work. */
ypos = va_arg(ap, int);
if (board[ypos] == EMPTY)
libs = 0;
else
libs = countlib(ypos);
/* Pop all the moves we could successfully play. */
for (i = 0; i < played_moves; i++)
popgo();
va_end(ap);
return libs;
}
/*
* It is assumed in reading a ladder if stackp >= depth that
* as soon as a bounding stone is in atari, the string is safe.
* It is used similarly at other places in reading.c to implement simplifying
* assumptions when stackp is large. DEPTH is the default value of depth.
*
* Unfortunately any such scheme invites the ``horizon effect.'' Increasing
* DEPTH will make the program stronger and slower.
*
*/
/* Tactical reading using C functions */
#define DEPTH 16
#define BRANCH_DEPTH 13
#define BACKFILL_DEPTH 12
#define BACKFILL2_DEPTH 5
#define BREAK_CHAIN_DEPTH 7
#define SUPERSTRING_DEPTH 7
#define FOURLIB_DEPTH 7
#define KO_DEPTH 8
#if 0
#undef FOURLIB_DEPTH
#define FOURLIB_DEPTH 9
#endif
#define AA_DEPTH 6
/* Pattern based reading */
#define OWL_DISTRUST_DEPTH 6
#define OWL_BRANCH_DEPTH 8
#define OWL_READING_DEPTH 20
#define SEMEAI_BRANCH_DEPTH 12
#define SEMEAI_BRANCH_DEPTH2 6
/* Connecton reading */
#define CONNECT_NODE_LIMIT 2000
#define CONNECT_DEPTH 64
#define CONNECT_DEPTH2 20
#define BREAKIN_NODE_LIMIT 400
#define BREAKIN_DEPTH 14
/* Set the various reading depth parameters. If mandated_depth_value
* is not -1 that value is used; otherwise the depth values are
* set as a function of level. The parameter mandated_depth_value
* can be set at the command line to force a particular value of
* depth; normally it is -1.
*/
void
set_depth_values(int level, int report_levels)
{
static int node_limits[] = {500, 500, 450, 400, 400, 325, 275,
200, 150, 100, 75, 50};
int depth_level;
/*
* Other policies depending on level:
* owl.c: >= 9: use vital attack pattern database
* >= 8: increase depth values in owl_substantial
* >= 8: don't turn off owl_phase in semeai reading
* reading.c: >= 8: Use superstrings and do more backfilling.
* value_moves.c: >= 6: try to find more owl attacks/defenses
* breakin.c: >= 10: try to find break-ins. (*)
* worm.c: >= 10: detect unconditionally meaningless moves
*
* The break-in code (*) is particularly expensive.
*
* Speedups between levels 9 and 10 and between levels 7 and 8
* are obtained by turning off services, and between these
* levels no changes are made in the depths. The parameter
* depth_level is the correction compared to the default settings at level
* 10 for most reading depths.
*/
if (level >= 10)
depth_level = level - 10;
else if (level == 9)
depth_level = 0;
else if (level == 8)
depth_level = -1;
else
depth_level = level - 8;
depth = gg_max(6, DEPTH + depth_level);
branch_depth = gg_max(3, BRANCH_DEPTH + depth_level);
backfill_depth = gg_max(2, BACKFILL_DEPTH + depth_level);
backfill2_depth = gg_max(1, BACKFILL2_DEPTH + depth_level);
break_chain_depth = gg_max(2, BREAK_CHAIN_DEPTH + depth_level);
if (level >= 8)
owl_distrust_depth = gg_max(1, (2 * OWL_DISTRUST_DEPTH + depth_level) / 2);
else
owl_distrust_depth = gg_max(1, (2 * OWL_DISTRUST_DEPTH - 1
+ depth_level) / 2);
owl_branch_depth = gg_max(2, (2 * OWL_BRANCH_DEPTH + depth_level) / 2);
owl_reading_depth = gg_max(5, (2 * OWL_READING_DEPTH + depth_level) / 2);
/* Atari-atari depth levels are unchanged only between levels 7/8, 9/10: */
if (level >= 10)
aa_depth = gg_max(0, AA_DEPTH + (level - 10));
else if (level == 9)
aa_depth = gg_max(0, AA_DEPTH);
else if (level >= 7)
aa_depth = gg_max(0, AA_DEPTH - 1);
else
aa_depth = gg_max(0, AA_DEPTH - (8 - level));
/* Exceptions:
* fourlib_depth: This is constant from levels 7 to 10.
* superstring_depth: set to 0 below level 8.
*/
if (level >= 10)
ko_depth = gg_max(1, KO_DEPTH + (level - 10));
else if (level == 9)
ko_depth = gg_max(1, KO_DEPTH);
else if (level >= 7)
ko_depth = gg_max(1, KO_DEPTH - 1);
else
ko_depth = gg_max(1, KO_DEPTH + (level - 8));
if (level >= 10)
fourlib_depth = gg_max(1, FOURLIB_DEPTH + (level - 10));
else if (level >= 7)
fourlib_depth = gg_max(1, FOURLIB_DEPTH);
else
fourlib_depth = gg_max(1, FOURLIB_DEPTH + (level - 7));
if (level >= 8)
superstring_depth = gg_max(1, SUPERSTRING_DEPTH);
else
superstring_depth = 0;
if (level >= 10)
owl_node_limit = OWL_NODE_LIMIT * pow(1.5, depth_level);
else {
owl_node_limit = (OWL_NODE_LIMIT * node_limits[10 - level] /
node_limits[0]);
owl_node_limit = gg_max(20, owl_node_limit);
}
semeai_branch_depth = gg_max(2, (2*SEMEAI_BRANCH_DEPTH + depth_level) / 2);
semeai_branch_depth2 = gg_max(2, (2*SEMEAI_BRANCH_DEPTH2 + depth_level) / 2);
semeai_node_limit = SEMEAI_NODE_LIMIT * pow(1.5, depth_level);
connect_depth = gg_max(2, CONNECT_DEPTH + 2 * depth_level);
connect_depth2 = gg_max(2, CONNECT_DEPTH2 + 2 * depth_level);
connection_node_limit = CONNECT_NODE_LIMIT * pow(1.45, depth_level);
breakin_depth = gg_max(2, BREAKIN_DEPTH + 2 * depth_level);
breakin_node_limit = BREAKIN_NODE_LIMIT * pow(1.5, depth_level);
if (mandated_depth != -1)
depth = mandated_depth;
if (mandated_backfill_depth != -1)
backfill_depth = mandated_backfill_depth;
if (mandated_backfill2_depth != -1)
backfill2_depth = mandated_backfill2_depth;
if (mandated_break_chain_depth != -1)
break_chain_depth = mandated_break_chain_depth;
if (mandated_superstring_depth != -1)
superstring_depth = mandated_superstring_depth;
if (mandated_branch_depth != -1)
branch_depth = mandated_branch_depth;
if (mandated_fourlib_depth != -1)
fourlib_depth = mandated_fourlib_depth;
if (mandated_ko_depth != -1)
ko_depth = mandated_ko_depth;
if (mandated_aa_depth != -1)
aa_depth = mandated_aa_depth;
if (mandated_owl_distrust_depth != -1)
owl_distrust_depth = mandated_owl_distrust_depth;
if (mandated_owl_branch_depth != -1)
owl_branch_depth = mandated_owl_branch_depth;
if (mandated_owl_reading_depth != -1)
owl_reading_depth = mandated_owl_reading_depth;
if (mandated_owl_node_limit != -1)
owl_node_limit = mandated_owl_node_limit;
if (mandated_semeai_node_limit != -1)
semeai_node_limit = mandated_semeai_node_limit;
depth_offset = 0;
if (report_levels) {
fprintf(stderr, "at level %d:\n\n\
depth: %d\n\
branch_depth: %d\n\
backfill_depth: %d\n\
backfill2_depth: %d\n\
break_chain_depth: %d\n\
owl_distrust_depth: %d\n\
owl_branch_depth: %d\n\
owl_reading_depth: %d\n\
aa_depth: %d\n\
ko_depth: %d\n\
fourlib_depth: %d\n\
superstring_depth: %d\n\
owl_node_limit: %d\n\
semeai_branch_depth: %d\n\
semeai_branch_depth2: %d\n\
semeai_node_limit: %d\n\
connect_depth: %d\n\
connect_depth2: %d\n\
connection_node_limit: %d\n\
breakin_depth: %d\n\
breakin_node_limit: %d\n\n",
level, depth, branch_depth, backfill_depth, backfill2_depth,
break_chain_depth, owl_distrust_depth, owl_branch_depth,
owl_reading_depth, aa_depth, ko_depth, fourlib_depth,
superstring_depth, owl_node_limit, semeai_branch_depth,
semeai_branch_depth2, semeai_node_limit, connect_depth,
connect_depth2, connection_node_limit, breakin_depth,
breakin_node_limit);
}
}
static int depth_modification = 0;
/*
* Modify the various tactical reading depth parameters. This is
* typically used to avoid horizon effects. By temporarily increasing
* the depth values when trying some move, one can avoid that an
* irrelevant move seems effective just because the reading hits a
* depth limit earlier than it did when reading only on relevant
* moves.
*/
void
modify_depth_values(int n)
{
depth += n;
backfill_depth += n;
backfill2_depth += n;
break_chain_depth += n;
superstring_depth += n;
branch_depth += n;
fourlib_depth += n;
ko_depth += n;
breakin_depth += n;
depth_offset += n;
depth_modification += n;
}
void
increase_depth_values(void)
{
modify_depth_values(1);
}
void
decrease_depth_values(void)
{
modify_depth_values(-1);
}
int
get_depth_modification(void)
{
return depth_modification;
}
/*******************
* Detect blunders *
*******************/
static int detect_owl_blunder(int move, int color, int *defense_point,
signed char safe_stones[BOARDMAX], int liberties,
float *return_value, int save_verbose);
static void detect_tactical_blunder(int move, int color, int *defense_point,
signed char safe_stones[BOARDMAX],
int liberties, int *libs,
float *return_value, int save_verbose);
/* Check that the move at color doesn't involve any kind of blunder,
* regardless of size.
*/
int
confirm_safety(int move, int color, int *defense_point,
signed char safe_stones[BOARDMAX])
{
return (blunder_size(move, color, defense_point, safe_stones) == 0.0);
}
/* This function will detect some blunders. If the move reduces the
* number of liberties of an adjacent friendly string, there is a
* danger that the move could backfire, so the function checks that no
* friendly worm which was formerly not attackable becomes attackable,
* and it checks that no opposing worm which was not defendable
* becomes defendable.
*
* It returns the estimated size of the blunder, or 0.0 if nothing
* bad has happened.
*
* The array safe_stones[] contains the stones that are supposedly
* safe after (move). It may be NULL.
*
* For use when called from fill_liberty, this function may optionally
* return a point of defense, which, if taken, will presumably make
* the move at (move) safe on a subsequent turn.
*/
float
blunder_size(int move, int color, int *defense_point,
signed char safe_stones[BOARDMAX])
{
int libs[5];
int liberties = accuratelib(move, color, 5, libs);
int trouble = 0;
int save_verbose = verbose;
float return_value = 0.0;
int atari;
signed char defense_moves[BOARDMAX];
if (defense_point)
*defense_point = NO_MOVE;
TRACE("Checking safety of a %s move at %1m\n", color_to_string(color), move);
if (verbose > 0)
verbose--;
/* We start by checking whether we have accidentally killed an own
* dragon.
*/
trouble = detect_owl_blunder(move, color, defense_point,
safe_stones, liberties,
&return_value, save_verbose);
/* Next we see whether the move has caused tactical complications.
* The trouble variable is set if a string next to the move with few
* liberties has not gained liberties by the move.
*/
if (trouble)
detect_tactical_blunder(move, color, defense_point, safe_stones,
liberties, libs, &return_value, save_verbose);
/* FIXME: We would also need a detect_semeai_blunder() to check
* against moves which make the outcome of a semeai worse, e.g. by
* letting the opponent live in seki.
*/
/* Finally we call the atari-atari code to see whether the move has
* set up some combination attack that didn't exist before. We do
* this last to avoid duplicate blunder reports.
*/
atari = atari_atari_blunder_size(color, move, defense_moves, safe_stones);
if (atari) {
if (defense_point) {
/* FIXME: Choose defense point more systematically. */
int pos;
*defense_point = NO_MOVE;
for (pos = BOARDMIN; pos < BOARDMAX; pos++)
if (ON_BOARD(pos) && defense_moves[pos]) {
*defense_point = pos;
break;
}
}
verbose = save_verbose;
TRACE("Combination attack appears.\n");
return_value += (float) atari;
}
verbose = save_verbose;
return return_value;
}
/* Check whether we have accidentally killed an own dragon adjacent to
* move. If this happens, we mark its stones as no longer safe, and
* remember the dragon's size.
*/
static int
detect_owl_blunder(int move, int color, int *defense_point,
signed char safe_stones[BOARDMAX], int liberties,
float *return_value, int save_verbose)
{
int k;
int ii;
int trouble = 0;
int dragon_analyzed[4] = {0, 0, 0, 0};
int current_verbose = verbose;
for (k = 0; k < 4; k++) {
int bpos = move + delta[k];
int j;
/* We get worried if there is a liberty problem (and in this case
* there might also be tactical trouble), or if we play inside
* our eye space and the dragon is only just alive.
*/
if (board[bpos] != color)
continue;
if (liberties <= worm[bpos].liberties
&& liberties <= 4)
trouble = 1;
else
if (min_eyes(&(DRAGON2(bpos).genus)) > 2
|| !is_proper_eye_space(move))
continue;
/* Don't test the same dragon twice. */
for (j = 0; j < k; j++)
if (dragon_analyzed[j] == dragon[bpos].origin)
break;
if (j < k)
continue;
dragon_analyzed[k] = dragon[bpos].origin;
/* Don't reanalyze if (move) is an owl defense for (bpos). */
if (safe_stones && safe_stones[bpos] == OWL_SAVED_STONE)
continue;
if ((dragon[bpos].status == ALIVE
|| (safe_stones
&& safe_stones[bpos]))
&& DRAGON2(bpos).safety != INVINCIBLE
&& DRAGON2(bpos).safety != STRONGLY_ALIVE) {
int kworm = NO_MOVE;
int acode = owl_confirm_safety(move, bpos, defense_point, &kworm);
/* If owl couldn't confirm safety, maybe semeai can. */
if (acode != WIN) {
int r;
for (r = 0; r < DRAGON2(bpos).neighbors; r++) {
int neighbor = dragon2[DRAGON2(bpos).adjacent[r]].origin;
int resultb;
if (board[neighbor] == color)
continue;
owl_analyze_semeai_after_move(move, color, neighbor, bpos,
NULL, &resultb, NULL, 1, NULL, 0);
if (resultb == 0)
acode = WIN;
}
}
if (acode == 0) {
verbose = save_verbose;
TRACE("Dragon at %1m becomes attackable.\n", bpos);
verbose = current_verbose;
*return_value += 2.0 * dragon[bpos].effective_size;
if (safe_stones)
mark_dragon(bpos, safe_stones, 0);
}
else if (acode == LOSS) {
verbose = save_verbose;
TRACE("Dragon at %1m becomes attackable.\n", bpos);
verbose = current_verbose;
if (kworm == move) {
int l;
/* the worm origin was messed by our own move */
for (l = 0; l < 4; l++) {
int kworm = move + delta[l];
if (board[kworm] == color) {
*return_value += 2.0 * worm[kworm].effective_size;
if (safe_stones)
for (ii = BOARDMIN; ii < BOARDMAX; ii++)
if (ON_BOARD(ii) && worm[ii].origin == worm[kworm].origin)
safe_stones[ii] = 0;
}
}
}
else {
*return_value += 2.0 * worm[kworm].effective_size;
if (safe_stones)
for (ii = BOARDMIN; ii < BOARDMAX; ii++)
if (ON_BOARD(ii) && worm[ii].origin == worm[kworm].origin)
safe_stones[ii] = 0;
}
}
}
}
return trouble;
}
/* Check whether a move causes any unexpected and unwelcome changes in
* the tactical status of worms all over the board.
*/
static void
detect_tactical_blunder(int move, int color, int *defense_point,
signed char safe_stones[BOARDMAX],
int liberties, int *libs,
float *return_value, int save_verbose)
{
int other = OTHER_COLOR(color);
int pos;
int ii;
int current_verbose = verbose;
if (!trymove(move, color, NULL, NO_MOVE))
return;
/* Need to increase the depth values during this reading to avoid
* horizon effects.
*/
increase_depth_values();
for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
if (!IS_STONE(board[pos])
|| worm[pos].origin != pos
|| pos == move)
continue;
/* First, we look for a new tactical attack.
* FIXME: Verify that the tactically attacked stone matters. See
* e.g. the D6 move in filllib:51 which invites a harmless
* tactical attack of A4.
*/
if (board[pos] == color
&& ((safe_stones && safe_stones[pos])
|| (!safe_stones && worm[pos].attack_codes[0] == 0))
&& attack(pos, NULL)) {
/* A safe worm of ours has become attackable. */
if (defense_point) {
find_defense(pos, defense_point);
/* Check that this move is legal and effective also on the
* original board, otherwise find a tactical defense there
* instead.
*/
popgo();
if (!is_legal(*defense_point, color)
|| play_attack_defend_n(color, 1, 1, *defense_point, pos))
find_defense(pos, defense_point);
/* Redo the move, we know that it won't fail. */
trymove(move, color, NULL, NO_MOVE);
}
verbose = save_verbose;
TRACE("After %1m Worm at %1m becomes attackable.\n", move, pos);
verbose = current_verbose;
*return_value += worm[pos].effective_size;
if (safe_stones) /* Can't use mark_string. */
for (ii = BOARDMIN; ii < BOARDMAX; ii++)
if (worm[ii].origin == worm[pos].origin)
safe_stones[ii] = 0;
}
else if (board[pos] == other
&& worm[pos].origin == pos
&& worm[pos].attack_codes[0] != 0
&& worm[pos].defense_codes[0] == 0
&& find_defense(pos, NULL)) {
/* A dead opponent's worm has become defendable.
* Also ask the owl code whether the string can live
* strategically. To do this we need to temporarily undo
* the trymove().
*/
int owl_attacks;
int defense_effective = 0;
popgo();
decrease_depth_values();
owl_attacks = owl_does_attack(move, pos, NULL);
if (owl_attacks != WIN) {
*return_value += 2 * worm[pos].effective_size;
defense_effective = 1;
verbose = save_verbose;
TRACE("After %1m worm at %1m becomes defendable - A.\n", move, pos);
verbose = current_verbose;
}
else if (dragon[pos].status != ALIVE) {
/* Before redoing the trymove we also check whether the worm now
* has a semeai defense. See blunder:26 for an example.
*
* If the worm already was alive in seki, it is generally okay
* that it also becomes tactically safe when the outer
* liberties are filled, see e.g. blunder:32,34. Thus the
* check above.
*/
int k;
int adj[MAXCHAIN];
int num_adj;
num_adj = extended_chainlinks(pos, adj, 0);
for (k = 0; k < num_adj; k++) {
int neighbor = adj[k];
int resulta;
owl_analyze_semeai_after_move(move, color, pos, neighbor,
&resulta, NULL, NULL, 1, NULL, 1);
if (resulta != 0) {
*return_value += 2 * worm[pos].effective_size;
defense_effective = 1;
verbose = save_verbose;
TRACE("After %1m worm at %1m becomes defendable - B.\n",
move, pos);
verbose = current_verbose;
break;
}
}
}
trymove(move, color, NULL, NO_MOVE);
increase_depth_values();
if (defense_effective && defense_point) {
int dpos;
if (attack(pos, &dpos)) {
*defense_point = dpos;
/* Check that this move is legal and effective also on the
* original board, otherwise find a tactical attack there
* instead.
*/
popgo();
if (!is_legal(dpos, color)
|| play_attack_defend_n(color, 0, 1, dpos, pos))
attack(pos, defense_point);
/* Redo the move, we know that it won't fail. */
trymove(move, color, NULL, NO_MOVE);
}
else {
verbose = save_verbose;
TRACE("No attack found (unexpectedly) on %1m after move at %1m.\n",
pos, move);
verbose = current_verbose;
}
}
}
}
/* Look for double atari style complications of the move.
*
* FIXME: Since we have an atari_atari check in blunder_size(), do
* we still need to do this step?
*/
if (liberties == 2) {
float d_a_blunder_size;
if (double_atari(libs[0], other, &d_a_blunder_size, safe_stones)) {
if (defense_point && safe_move(libs[0], color) == WIN)
*defense_point = libs[0];
*return_value += d_a_blunder_size;
verbose = save_verbose;
TRACE("Double threat appears at %1m.\n", libs[0]);
verbose = current_verbose;
}
else if (double_atari(libs[1], other, &d_a_blunder_size, safe_stones)) {
if (defense_point && safe_move(libs[1], color) == WIN)
*defense_point = libs[1];
*return_value += d_a_blunder_size;
verbose = save_verbose;
TRACE("Double threat appears at %1m.\n", libs[1]);
verbose = current_verbose;
}
}
/* Reset the depth values. */
decrease_depth_values();
popgo();
}
/* Returns true if a move by (color) fits the following shape:
*
*
* X* (O=color)
* OX
*
* capturing one of the two X strings. The name is a slight
* misnomer since this includes attacks which are not necessarily
* double ataris, though the common double atari is the most
* important special case.
*
* If safe_stones != NULL, then only attacks on stones marked as safe are
* tried.
*
* The value of the double atari attack is returned in *value (unless
* value is NULL), and the attacked stones are marked unsafe.
*/
int
double_atari(int move, int color, float *value,
signed char safe_stones[BOARDMAX])
{
int other = OTHER_COLOR(color);
int k;
int m = I(move);
int n = J(move);
if (!ON_BOARD(move))
return 0;
/* Loop over the diagonal directions. */
for (k = 4; k < 8; k++) {
int dm = deltai[k];
int dn = deltaj[k];
/* because (m, n) and (m+dm, n+dn) are opposite
* corners of a square, ON_BOARD2(m, n) && ON_BOARD2(m+dm, n+dn)
* implies ON_BOARD2(m+dm, n) and ON_BOARD2(n, n+dn)
*
* Only try to attack supposedly safe stones.
*/
if (BOARD(m+dm, n+dn) == color
&& BOARD(m, n+dn) == other
&& BOARD(m+dm, n) == other
&& (!safe_stones
|| (safe_stones[POS(m, n+dn)] && safe_stones[POS(m+dm, n)]))
&& trymove(move, color, "double_atari", NO_MOVE)) {
if (countlib(move) > 1
&& (BOARD(m, n+dn) == EMPTY || BOARD(m+dm, n) == EMPTY
|| !defend_both(POS(m, n+dn), POS(m+dm, n)))) {
popgo();
if (value) {
if (worm[POS(m, n+dn)].effective_size
> worm[POS(m+dm, n)].effective_size) {
*value = 2.0 * worm[POS(m, n+dn)].effective_size;
if (safe_stones)
mark_string(POS(m, n+dn), safe_stones, 0);
}
else {
*value = 2.0 * worm[POS(m+dm, n)].effective_size;
if (safe_stones)
mark_string(POS(m+dm, n), safe_stones, 0);
}
}
return 1;
}
popgo();
}
}
return 0;
}
/* Returns true if a move by (color) plays into a snapback. */
int
playing_into_snapback(int move, int color)
{
int libs[2];
int k;
if (approxlib(move, color, 1, NULL) != 0
|| accuratelib(move, color, 2, libs) != 1)
return 0;
for (k = 0; k < 4; k++)
if (board[move + delta[k]] == color
&& adjacent_strings(libs[0], move + delta[k]))
return 1;
return 0;
}
/* Score the game and determine the winner */
void
who_wins(int color, FILE *outfile)
{
float result;
silent_examine_position(EXAMINE_DRAGONS);
#if 0
float white_score;
float black_score;
int winner;
#endif
if (color != BLACK && color != WHITE)
color = BLACK;
#if 0
/* Use the aftermath code to compute the final score. (Slower but
* more reliable.)
*/
result = aftermath_compute_score(color, NULL);
if (result > 0.0)
winner = WHITE;
else {
winner = BLACK;
result = -result;
}
#endif
result = (white_score + black_score)/2.0;
if (result == 0.0)
fprintf(outfile, "Result: jigo ");
else
fprintf(outfile, "Result: %c+%.1f ",
(result > 0.0) ? 'W' : 'B', gg_abs(result));
}
/* Find the stones of an extended string, where the extensions are
* through the following kinds of connections:
*
* 1. Solid connections (just like ordinary string).
*
* OO
*
* 2. Diagonal connection or one space jump through an intersection
* where an opponent move would be suicide or self-atari.
*
* ...
* O.O
* XOX
* X.X
*
* 3. Bamboo joint.
*
* OO
* ..
* OO
*
* 4. Diagonal connection where both adjacent intersections are empty.
*
* .O
* O.
*
* 5. Connection through adjacent or diagonal tactically captured stones.
* Connections of this type are omitted when the superstring code is
* called from reading.c, but included when the superstring code is
* called from owl.c
*/
static void
do_find_superstring(int str, int *num_stones, int *stones,
int *num_lib, int *libs, int maxlibs,
int *num_adj, int *adjs, int liberty_cap,
int proper, int type);
static void
superstring_add_string(int str,
int *num_my_stones, int *my_stones,
int *num_stones, int *stones,
int *num_libs, int *libs, int maxlibs,
int *num_adj, int *adjs, int liberty_cap,
signed char mx[BOARDMAX],
signed char ml[BOARDMAX],
signed char ma[BOARDMAX],
int do_add);
void
find_superstring(int str, int *num_stones, int *stones)
{
do_find_superstring(str, num_stones, stones,
NULL, NULL, 0,
NULL, NULL, 0,
0, 1);
}
/* This is the same as find_superstring, except that connections of
* type 5 are omitted. This is used in semeai analysis.
*/
void
find_superstring_conservative(int str, int *num_stones, int *stones)
{
do_find_superstring(str, num_stones, stones,
NULL, NULL, 0,
NULL, NULL, 0,
0, 0);
}
/* This function computes the superstring at (str) as described above,
* but omitting connections of type 5. Then it constructs a list of
* liberties of the superstring which are not already liberties of
* (str).
*
* If liberty_cap is nonzero, only liberties of substrings of the
* superstring which have fewer than liberty_cap liberties are
* generated.
*/
void
find_superstring_liberties(int str,
int *num_libs, int *libs, int liberty_cap)
{
do_find_superstring(str, NULL, NULL,
num_libs, libs, MAX_LIBERTIES,
NULL, NULL, liberty_cap,
0, 0);
}
/* This function is the same as find_superstring_liberties, but it
* omits those liberties of the string (str), presumably since
* those have already been treated elsewhere.
*
* If liberty_cap is nonzero, only liberties of substrings of the
* superstring which have at most liberty_cap liberties are
* generated.
*/
void
find_proper_superstring_liberties(int str,
int *num_libs, int *libs,
int liberty_cap)
{
do_find_superstring(str, NULL, NULL,
num_libs, libs, MAX_LIBERTIES,
NULL, NULL, liberty_cap,
1, 0);
}
/* This function computes the superstring at (str) as described above,
* but omitting connections of type 5. Then it constructs a list of
* liberties of the superstring which are not already liberties of
* (str).
*
* If liberty_cap is nonzero, only liberties of substrings of the
* superstring which have fewer than liberty_cap liberties are
* generated.
*/
void
find_superstring_stones_and_liberties(int str,
int *num_stones, int *stones,
int *num_libs, int *libs,
int liberty_cap)
{
do_find_superstring(str, num_stones, stones,
num_libs, libs, MAX_LIBERTIES,
NULL, NULL, liberty_cap,
0, 0);
}
/* analogous to chainlinks, this function finds boundary chains of the
* superstring at (str), including those which are boundary chains of
* (str) itself. If liberty_cap != 0, only those boundary chains with
* <= liberty_cap liberties are reported.
*/
void
superstring_chainlinks(int str,
int *num_adj, int adjs[MAXCHAIN],
int liberty_cap)
{
do_find_superstring(str, NULL, NULL,
NULL, NULL, 0,
num_adj, adjs, liberty_cap,
0, 2);
}
/* analogous to chainlinks, this function finds boundary chains of the
* superstring at (str), omitting those which are boundary chains of
* (str) itself. If liberty_cap != 0, only those boundary chains with
* <= liberty_cap liberties are reported.
*/
void
proper_superstring_chainlinks(int str,
int *num_adj, int adjs[MAXCHAIN],
int liberty_cap)
{
do_find_superstring(str, NULL, NULL,
NULL, NULL, 0,
num_adj, adjs, liberty_cap,
1, 2);
}
/* Do the real work finding the superstring and recording stones,
* liberties, and/or adjacent strings.
*/
static void
do_find_superstring(int str, int *num_stones, int *stones,
int *num_libs, int *libs, int maxlibs,
int *num_adj, int *adjs, int liberty_cap,
int proper, int type)
{
int num_my_stones;
int my_stones[MAX_BOARD * MAX_BOARD];
signed char mx[BOARDMAX]; /* stones */
signed char ml[BOARDMAX]; /* liberties */
signed char ma[BOARDMAX]; /* adjacent strings */
int k, l, r;
int color = board[str];
int other = OTHER_COLOR(color);
memset(mx, 0, sizeof(mx));
memset(ml, 0, sizeof(ml));
memset(ma, 0, sizeof(ma));
if (num_stones)
*num_stones = 0;
if (num_libs)
*num_libs = 0;
if (num_adj)
*num_adj = 0;
/* Include the string itself in the superstring. Only record stones,
* liberties, and/or adjacent strings if proper==0.
*/
num_my_stones = 0;
superstring_add_string(str, &num_my_stones, my_stones,
num_stones, stones,
num_libs, libs, maxlibs,
num_adj, adjs, liberty_cap,
mx, ml, ma,
!proper);
/* Loop over all found stones, looking for more strings to include
* in the superstring. The loop is automatically extended over later
* found stones as well.
*/
for (r = 0; r < num_my_stones; r++) {
int pos = my_stones[r];
for (k = 0; k < 4; k++) {
/* List of relative coordinates. (pos) is marked by *.
*
* ef.
* gb.
* *ac
* .d.
*
*/
int right = delta[k];
int up = delta[(k+1)%4];
int apos = pos + right;
int bpos = pos + right + up;
int cpos = pos + 2*right;
int dpos = pos + right - up;
int epos = pos + 2*up;
int fpos = pos + right + 2*up;
int gpos = pos + up;
int unsafe_move;
if (!ON_BOARD(apos))
continue;
/* Case 1. Nothing to do since stones are added string by string. */
/* Case 2. */
if (board[apos] == EMPTY) {
if (type == 2)
unsafe_move = (approxlib(apos, other, 2, NULL) < 2);
else
unsafe_move = is_self_atari(apos, other);
if (unsafe_move && type == 1 && is_ko(apos, other, NULL))
unsafe_move = 0;
if (unsafe_move) {
if (board[bpos] == color && !mx[bpos])
superstring_add_string(bpos, &num_my_stones, my_stones,
num_stones, stones,
num_libs, libs, maxlibs,
num_adj, adjs, liberty_cap,
mx, ml, ma, 1);
if (board[cpos] == color && !mx[cpos])
superstring_add_string(cpos, &num_my_stones, my_stones,
num_stones, stones,
num_libs, libs, maxlibs,
num_adj, adjs, liberty_cap,
mx, ml, ma, 1);
if (board[dpos] == color && !mx[dpos])
superstring_add_string(dpos, &num_my_stones, my_stones,
num_stones, stones,
num_libs, libs, maxlibs,
num_adj, adjs, liberty_cap,
mx, ml, ma, 1);
}
}
/* Case 3. */
/* Notice that the order of these tests is significant. We must
* check bpos before fpos and epos to avoid accessing memory
* outside the board array. (Notice that fpos is two steps away
* from pos, which we know is on the board.)
*/
if (board[apos] == color && board[bpos] == EMPTY
&& board[fpos] == color && board[epos] == color && !mx[epos]
&& board[gpos] == EMPTY)
superstring_add_string(epos, &num_my_stones, my_stones,
num_stones, stones,
num_libs, libs, maxlibs,
num_adj, adjs, liberty_cap,
mx, ml, ma, 1);
/* Don't bother with f, it is part of the string just added. */
/* Case 4. */
if (board[bpos] == color && !mx[bpos]
&& board[apos] == EMPTY && board[gpos] == EMPTY)
superstring_add_string(bpos, &num_my_stones, my_stones,
num_stones, stones,
num_libs, libs, maxlibs,
num_adj, adjs, liberty_cap,
mx, ml, ma, 1);
/* Case 5. */
if (type == 1)
for (l = 0; l < 2; l++) {
int upos;
if (l == 0) {
/* adjacent lunch */
upos = apos;
}
else {
/* diagonal lunch */
upos = bpos;
}
if (board[upos] != other)
continue;
upos = find_origin(upos);
/* Only do the reading once. */
if (mx[upos] == 1)
continue;
mx[upos] = 1;
if (attack(upos, NULL)
&& !find_defense(upos, NULL)) {
int lunch_stones[MAX_BOARD*MAX_BOARD];
int num_lunch_stones = findstones(upos, MAX_BOARD*MAX_BOARD,
lunch_stones);
int m, n;
for (m = 0; m < num_lunch_stones; m++)
for (n = 0; n < 8; n++) {
int vpos = lunch_stones[m] + delta[n];
if (board[vpos] == color && !mx[vpos])
superstring_add_string(vpos,
&num_my_stones, my_stones,
num_stones, stones,
num_libs, libs, maxlibs,
num_adj, adjs, liberty_cap,
mx, ml, ma, 1);
}
}
}
if (num_libs && maxlibs > 0 && *num_libs >= maxlibs)
return;
}
}
}
/* Add a new string to a superstring. Record stones, liberties, and
* adjacent strings as asked for.
*/
static void
superstring_add_string(int str,
int *num_my_stones, int *my_stones,
int *num_stones, int *stones,
int *num_libs, int *libs, int maxlibs,
int *num_adj, int *adjs, int liberty_cap,
signed char mx[BOARDMAX],
signed char ml[BOARDMAX],
signed char ma[BOARDMAX],
int do_add)
{
int num_my_libs;
int my_libs[MAXLIBS];
int num_my_adj;
int my_adjs[MAXCHAIN];
int new_stones;
int k;
ASSERT1(mx[str] == 0, str);
/* Pick up the stones of the new string. */
new_stones = findstones(str, board_size * board_size,
&(my_stones[*num_my_stones]));
mark_string(str, mx, 1);
if (stones) {
gg_assert(num_stones);
for (k = 0; k < new_stones; k++) {
if (do_add) {
stones[*num_stones] = my_stones[*num_my_stones + k];
(*num_stones)++;
}
}
}
(*num_my_stones) += new_stones;
/* Pick up the liberties of the new string. */
if (libs) {
gg_assert(num_libs);
/* Get the liberties of the string. */
num_my_libs = findlib(str, MAXLIBS, my_libs);
/* Remove this string from the superstring if it has too many
* liberties.
*/
if (liberty_cap > 0 && num_my_libs > liberty_cap)
(*num_my_stones) -= new_stones;
for (k = 0; k < num_my_libs; k++) {
if (ml[my_libs[k]])
continue;
ml[my_libs[k]] = 1;
if (do_add && (liberty_cap == 0 || num_my_libs <= liberty_cap)) {
libs[*num_libs] = my_libs[k];
(*num_libs)++;
if (*num_libs == maxlibs)
break;
}
}
}
/* Pick up adjacent strings to the new string. */
if (adjs) {
gg_assert(num_adj);
num_my_adj = chainlinks(str, my_adjs);
for (k = 0; k < num_my_adj; k++) {
if (liberty_cap > 0 && countlib(my_adjs[k]) > liberty_cap)
continue;
if (ma[my_adjs[k]])
continue;
ma[my_adjs[k]] = 1;
if (do_add) {
adjs[*num_adj] = my_adjs[k];
(*num_adj)++;
}
}
}
}
/* Internal timers for assessing time spent on various tasks. */
#define NUMBER_OF_TIMERS 4
static double timers[NUMBER_OF_TIMERS];
/* Start a timer. */
void
start_timer(int n)
{
gg_assert(n >= 0 && n < NUMBER_OF_TIMERS);
if (!showtime)
return;
timers[n] = gg_cputime();
}
/* Report time spent and restart the timer. Make no report if elapsed
* time is less than mintime.
*/
double
time_report(int n, const char *occupation, int move, double mintime)
{
double t;
double dt;
gg_assert(n >= 0 && n < NUMBER_OF_TIMERS);
if (!showtime)
return 0.0;
t = gg_cputime();
dt = t - timers[n];
if (dt > mintime) {
gprintf("%s", occupation);
if (move != NO_MOVE)
gprintf("%1m", move);
fprintf(stderr, ": %.2f sec\n", dt);
}
timers[n] = t;
return dt;
}
void
clearstats()
{
stats.nodes = 0;
stats.read_result_entered = 0;
stats.read_result_hits = 0;
stats.trusted_read_result_hits = 0;
}
void
showstats()
{
gprintf("Nodes: %d\n", stats.nodes);
gprintf("Read results entered: %d\n", stats.read_result_entered);
gprintf("Read result hits: %d\n", stats.read_result_hits);
gprintf("Trusted read result hits: %d\n", stats.trusted_read_result_hits);
}
/* Set up a compiled in pattern database for use by the Monte Carlo
* code. If name is NULL, the first pattern database is used.
*
* The reason why this function and the next are placed here rather
* than in montecarlo.c is to keep that file free from dependency on
* patterns.h.
*/
int
choose_mc_patterns(char *name)
{
int k;
for (k = 0; mc_pattern_databases[k].name; k++) {
if (!name || strcmp(name, mc_pattern_databases[k].name) == 0) {
mc_init_patterns(mc_pattern_databases[k].values);
return 1;
}
}
return 0;
}
/* List compiled in Monte Carlo pattern databases. */
void
list_mc_patterns(void)
{
int k;
printf("Available builtin Monte Carlo local patterns:\n\n");
for (k = 0; mc_pattern_databases[k].name; k++) {
if (k == 0)
printf("* %s (default)\n", mc_pattern_databases[k].name);
else
printf("* %s\n", mc_pattern_databases[k].name);
}
printf("\nUse \"--mc-patterns name\" to choose one of these.\n");
printf("Use \"--mc-load-patterns filename\" to directly load a pattern database.\n");
}
/*
* Local Variables:
* tab-width: 8
* c-basic-offset: 2
* End:
*/