2005 lines
53 KiB
C
2005 lines
53 KiB
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:
|
|
*/
|