[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [gnugo-devel] readconnect
From: |
Tristan Cazenave |
Subject: |
Re: [gnugo-devel] readconnect |
Date: |
Fri, 02 Nov 2001 22:32:28 +0100 |
Daniel Bump a écrit :
>
> > For ko values, it is doable.
> > For integer values, i am working on it...
>
> The ko values *are* integer values:
>
> #define WIN 3
> #define KO_A 2
> #define KO_B 1
> #define LOSE 0
>
> Dan
>
I think ko can be easily integrated in APS, but I did not
integrated it yet.
For a great number of integer values, say [-361,+361],
I think APS can also be used just by calling it with
a null window. This has to be tested.
Here is a commented readconnect, with the prevention
of simple depth 5 connections, it should help a lot.
next things are move ordering, optimization of
captures, writing the function commented as
not yet written, and integrating the ko values.
Teun patch is included.
Tristan.
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* This is GNU GO, a Go program. Contact address@hidden, or see *
* http://www.gnu.org/software/gnugo/ for more information. *
* *
* Copyright 1999, 2000, 2001 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 2. *
* *
* 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., 59 Temple Place - Suite 330, *
* Boston, MA 02111, USA. *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "liberty.h"
#include "cache.h"
/* Size of array where candidate moves are stored. */
#define MAX_MOVES 362
static int add_array(int *array, int elt);
static int element_array (int *array,int elt);
static void intersection_array(int *array1, int *array2);
static int snapback(int str);
static int connection_one_move(int str1, int str2);
static int prevent_connection_one_move(int *moves, int str1, int str2);
static int connected_one_move(int str1, int str2);
static int moves_to_connect_in_two_moves(int *moves, int str1, int str2);
static int connection_two_moves(int str1, int str2);
static int prevent_connection_two_moves(int *moves, int str1, int str2);
#if 0
static int connected_two_moves(int str1, int str2);
#endif
static int moves_to_connect_in_three_moves(int *moves, int str1, int str2);
#if 0
static int simple_connection_three_moves(int str1, int str2);
static int prevent_simple_connection_three_moves(int *moves,
int str1, int str2);
#endif
static int recursive_connect(int str1, int str2, int *move);
static int recursive_disconnect(int str1, int str2, int *move);
static int quiescence_connect(int str1, int str2, int *move);
static int quiescence_capture(int str, int *move);
/* static int capture_one_move(int str); */
static int prevent_capture_one_move(int *moves, int str1);
int nodes_connect=0,max_nodes_connect=2000,max_connect_depth=64;
/* adds an integer to an array of integers if it is not already there
* the number of elements of the array is in array[0]
*/
static int add_array (int *array, int elt) {
int r;
for (r = 1; r < array[0] + 1; r++)
if (array[r] == elt)
return 0;
array[0]++;
array[array[0]] = elt;
return 1;
}
/* test if an element is part of an array */
static int element_array (int *array,int elt) {
int r;
for (r = 1; r < array[0] + 1; r++)
if (array[r] == elt)
return 1;
return 0;
}
/* only keep the elements of array1 which are also in array2 */
static void intersection_array(int *array1, int *array2) {
int r, s;
for (r = 1; r < array1[0] + 1; r++)
if (!element_array(array2, array1[r])) {
for (s = r; s< array1[0]; s++)
array1[s]=array1[s+1];
array1[0]--;
r--;
}
}
/* verifies that capturing the stone at str is not a snapback */
static int snapback (int str) {
int stones, liberties, lib;
/* if more than one stone captured, not a snapback */
stones = countstones(str);
if (stones > 1)
return 0;
/* if more than one liberty, not a snapback */
liberties = findlib(str, 1, &lib);
if (liberties > 1)
return 0;
/* if only one liberty after capture */
if (trymove(lib, OTHER_COLOR(board[str]),
"snapback", str, EMPTY, 0)) {
liberties=0;
if (IS_STONE(board[lib]))
liberties = countlib(lib);
popgo();
if (liberties > 1)
return 0;
return WIN;
}
return 0;
}
/* verifies that the strings str1 and str2 can be connected
* directly by playing one move, either by playing a common liberty
* of the two strings, either by capturing a common adjacent string
*
* this is the gi1 game function
*/
static int connection_one_move(int str1, int str2) {
int r;
int adj, adjs[MAXCHAIN];
/* Common liberties. */
if (have_common_lib(str1, str2, NULL))
return WIN;
/* Common adjacent string in atari, more than one stone, no snapback. */
adj = chainlinks2(str1, adjs, 1);
for (r = 0; r < adj; r++)
if (adjacent_strings(adjs[r], str2)
&& !snapback(adjs[r]))
return WIN;
return 0;
}
/* if the two strings str1 and str2 can be connected sends back WIN
* fill the array moves with the only move that can prevent a connection
* in one move (common liberties, liberties of common adjacent strings in atari)
*
* this is the ip1 game function
*/
static int prevent_connection_one_move (int *moves, int str1, int str2) {
int r, s;
int liberties, libs[MAXLIBS];
int adj, adjs[MAXCHAIN];
int adjadj, adjadjs[MAXCHAIN];
/* Common liberties. */
if (have_common_lib(str1, str2, libs)) {
add_array(moves, libs[0]);
return WIN;
}
/* Save a common adjacent string in atari, more than one stone, no snapback.
*/
adj = chainlinks2(str1, adjs, 1);
for (r = 0; r < adj; r++)
if (adjacent_strings(adjs[r], str2)
&& !snapback(adjs[r])) {
liberties = findlib(adjs[r], MAXLIBS, libs);
add_array(moves, libs[0]);
adjadj = chainlinks2(adjs[r], adjadjs, 1);
for (s = 0; s < adjadj; s++) {
findlib(adjadjs[s], MAXLIBS, libs);
add_array(moves, libs[0]);
}
return WIN;
}
return 0;
}
/* returns WIN if str1 and str2 are connected in at most
* one move even if the opponent plays first.
* Verify that the strings are connectable in one move
* and find the only possible moves that can prevent
* using prevent_connection_one_move. If none of these
* moves works, the two strings are connected.
*
* this is the g1 game function
*/
static int connected_one_move (int str1, int str2) {
int r, res=0;
int moves[MAX_MOVES];
moves[0] = 0;
if (prevent_connection_one_move(moves, str1, str2)) {
res = WIN;
for (r = 1; ((r < moves[0] + 1) && res); r++) {
if (trymove(moves[r], OTHER_COLOR(board[str1]),
"connected_one_move", str1, EMPTY, 0)) {
if (!connection_one_move(str1, str2))
res = 0;
popgo();
}
}
}
return res;
}
/* find the moves that might be able to connect in less than three plies
* that is moves that can connect the strings if another move of the same
* color is played jut after :
* - common liberties of the two strings
* - moves on the liberties of an opponent string with less than two liberties
* adjacent to both strings, or adjacent to one string and that has a common
* liberty with the second string
* - liberties of one string that are second order liberties of the other string
* returns WIN if a direct connection has been found
* returns 0 elsewhere
*/
static int moves_to_connect_in_two_moves (int *moves, int str1, int str2) {
int r, s, common_adj_liberty;
int liberties, libs[MAXLIBS];
int adj, adjs[MAXCHAIN];
int adjadj, adjadjs[MAXCHAIN];
int k;
int color = board[str1];
moves[0] = 0;
/* Common liberties. */
if (have_common_lib(str1, str2, libs)) {
add_array(moves, libs[0]);
return 1;
}
/* Capture a common adjacent string or an adjacent liberty of str1
* that has a common liberty with str2...
*/
adj = chainlinks2(str1, adjs, 2);
for (r = 0; r < adj; r++) {
liberties = findlib(adjs[r], MAXLIBS, libs);
common_adj_liberty=0;
for (s = 0; s < liberties; s++)
if (liberty_of_string(libs[s], str2))
common_adj_liberty=1;
if (common_adj_liberty || adjacent_strings(adjs[r], str2)) {
for (s = 0; s < liberties; s++)
add_array(moves, libs[s]);
adjadj = chainlinks2(adjs[r], adjadjs, 1);
for (s = 0; s < adjadj; s++) {
findlib(adjadjs[s], MAXLIBS, libs);
add_array(moves, libs[0]);
}
}
}
/* ...and vice versa. */
adj = chainlinks2(str2, adjs, 2);
for (r = 0; r < adj; r++) {
liberties = findlib(adjs[r], MAXLIBS, libs);
common_adj_liberty=0;
for (s = 0; s < liberties; s++)
if (liberty_of_string(libs[s], str1))
common_adj_liberty=1;
if (common_adj_liberty || adjacent_strings(adjs[r], str1)) {
for (s = 0; s < liberties; s++)
add_array(moves, libs[s]);
adjadj = chainlinks2(adjs[r], adjadjs, 1);
for (s = 0; s < adjadj; s++) {
findlib(adjadjs[s], MAXLIBS, libs);
add_array(moves, libs[0]);
}
}
}
/* Liberties of str1 that are second order liberties of str2 and
* vice versa.
*/
liberties = findlib(str1, MAXLIBS, libs);
for (r = 0; r < liberties; r++) {
if (board[WEST(libs[r])] == EMPTY) {
if (liberty_of_string(WEST(libs[r]), str2)) {
add_array(moves, libs[r]);
add_array(moves, WEST(libs[r]));
}
}
if (board[EAST(libs[r])] == EMPTY) {
if (liberty_of_string(EAST(libs[r]), str2)) {
add_array(moves, libs[r]);
add_array(moves, EAST(libs[r]));
}
}
if (board[SOUTH(libs[r])] == EMPTY) {
if (liberty_of_string(SOUTH(libs[r]), str2)) {
add_array(moves, libs[r]);
add_array(moves, SOUTH(libs[r]));
}
}
if (board[NORTH(libs[r])] == EMPTY) {
if (liberty_of_string(NORTH(libs[r]), str2)) {
add_array(moves, libs[r]);
add_array(moves, NORTH(libs[r]));
}
}
}
/* Liberties of str1 which are adjacent to a friendly string with
* common liberty with str2.
*/
liberties = findlib(str1, MAXLIBS, libs);
for (r = 0; r < liberties; r++) {
for (k = 0; k < 4; k++) {
int pos = libs[r] + delta[k];
if (board[pos] == color && have_common_lib(pos, str2, NULL))
add_array(moves, libs[r]);
}
}
/* And vice versa. */
liberties = findlib(str2, MAXLIBS, libs);
for (r = 0; r < liberties; r++) {
for (k = 0; k < 4; k++) {
int pos = libs[r] + delta[k];
if (board[pos] == color && have_common_lib(pos, str1, NULL))
add_array(moves, libs[r]);
}
}
return 0;
}
/* tests if the strings can be connected in three plies
* starts with finding the possible moves that can connect
* if two moves in a row are played, then try them
* and stops at the first working move
* the strings are connected in two moves if the
* function connected_one_move is verified after a move
*
* this is the gi2 game function
*/
static int connection_two_moves (int str1, int str2) {
int r, res = 0, moves[MAX_MOVES];
if (moves_to_connect_in_two_moves(moves, str1, str2))
return WIN;
for (r = 1; ((r < moves[0] + 1) && !res); r++) {
if (trymove(moves[r], board[str1],
"connection_two_moves", str1, EMPTY, 0)) {
if (connected_one_move(str1, str2))
res = WIN;
popgo();
}
}
return res;
}
/* find the complete set of possible moves that can prevent
* a connection in three plies
* the function is not yet written, but moves_to_connect_in_two_moves does
* a similar job, so it is called temporarly
*/
static int moves_to_prevent_connection_in_two_moves (int *moves,
int str1, int str2) {
if (moves_to_connect_in_two_moves(moves, str1, str2))
return 1;
return 0;
}
/* find all the move that prevent to connect in a three plies deep search
* and put them in the moves array
* returns 0 if there is no three plies connection
* else it tries all the possible preventing moves
* if after a possible preventing moves, there no connection in
* one move and no connection in two moves, then the moves
* prevents a three plies deep connection, and it is added to the moves array
* this is the ip2 game function
*/
static int prevent_connection_two_moves (int *moves, int str1, int str2) {
int r, res=0;
int possible_moves[MAX_MOVES];
if (connection_two_moves(str1, str2)) {
res = WIN;
moves_to_prevent_connection_in_two_moves(possible_moves, str1, str2);
for (r = 1; r < possible_moves[0] + 1; r++) {
if (trymove(possible_moves[r], OTHER_COLOR(board[str1]),
"prevent_connection_two_moves", str1, EMPTY, 0)) {
if (!connection_one_move(str1, str2))
if (!connection_two_moves(str1, str2))
add_array(moves, possible_moves[r]);
popgo();
}
}
}
return res;
}
/* not yet written
* find all the moves than can connect if two subsequent
* moves of the same color are played after
* - common liberties
* - liberties of common adjacent strings with 3 liberties or less
* - liberties of adjacent strings with 2 liberties or less that have
* liberties that are second order liberties of the other string
* - liberties of one string that are second order liberties of the other string
* - second order liberties of the first string that are second order
* liberties of the other string
*/
static int moves_to_connect_in_three_moves (int *moves, int str1, int str2) {
if (moves_to_connect_in_two_moves(moves, str1, str2))
return 1;
return 0;
}
/* not yet written
* find the complete set of possible moves that can prevent
* a connection in 5 plies
*/
static int moves_to_prevent_connection_in_three_moves (int *moves,
int str1, int str2) {
if (moves_to_prevent_connection_in_two_moves(moves, str1, str2))
return 1;
return 0;
}
/*
* the most simple depth 4 connection
* if there are forced moves to prevent connection in
* one move, try them, and verify that they all
* lead to a depth 1 or depth 3 connection
*
* this is the g211 game function
*/
static int simply_connected_two_moves (int str1, int str2) {
int r, res=0;
int moves[MAX_MOVES];
moves[0] = 0;
if (prevent_connection_one_move(moves, str1, str2)) {
res = WIN;
for (r = 1; ((r < moves[0] + 1) && res); r++) {
if (trymove(moves[r], OTHER_COLOR(board[str1]),
"connected_one_move", str1, EMPTY, 0)) {
if (!connection_one_move(str1, str2))
if (!connection_two_moves(str1, str2))
res = 0;
popgo();
}
}
}
return res;
}
/* test if a move is a simple depth 5 connection
*
* this is the gi311 game function
*/
static int simple_connection_three_moves (int str1, int str2) {
int r, res = 0, moves[MAX_MOVES];
if (moves_to_connect_in_two_moves(moves, str1, str2))
return WIN;
for (r = 1; ((r < moves[0] + 1) && !res); r++) {
if (trymove(moves[r], board[str1],
"connection_two_moves", str1, EMPTY, 0)) {
if (simply_connected_two_moves(str1, str2))
res = WIN;
popgo();
}
}
return res;
}
/* find the forced moves that prevent a simple depth 5 connection
* fills the array moves with the forced moves
*
* this is the ip311 game function
* it finds moves in very important situations such as :
* + + + O + +
* + @ @ O + +
* + @ O @ @ +
* + @ O + + +
* + + + + + +
* - - - - - -
* and enables recursive_disconnect to prove the two black
* strings are connected in these situations
*/
static int prevent_simple_connection_three_moves (int *moves, int str1, int
str2) {
int r, res=0;
int possible_moves[MAX_MOVES];
if (simple_connection_three_moves(str1, str2)) {
res = WIN;
moves_to_prevent_connection_in_three_moves(possible_moves, str1, str2);
for (r = 1; r < possible_moves[0] + 1; r++) {
if (trymove(possible_moves[r], OTHER_COLOR(board[str1]),
"prevent_connection_two_moves", str1, EMPTY, 0)) {
if (!connection_one_move(str1, str2))
if (!connection_two_moves(str1, str2))
if (!simple_connection_three_moves(str1, str2))
add_array(moves, possible_moves[r]);
popgo();
}
}
}
return res;
}
/* find simple connections by looking at common liberties
* or directly capturing a common adjacent string without a snapback
* or looking at a ladder for a common adjacent string
*/
static int quiescence_connect(int str1, int str2, int *move) {
int r;
int lib;
int adj, adjs[MAXCHAIN];
/* Common liberties. */
if (have_common_lib(str1, str2, &lib)) {
*move = lib;
return WIN;
}
/* Common adjacent string in atari, more than one stone, no snapback. */
adj = chainlinks2(str1, adjs, 1);
for (r = 0; r < adj; r++)
if (adjacent_strings(adjs[r], str2)
&& !snapback(adjs[r])) {
findlib(adjs[r], 1, move);
return WIN;
}
/* Common adjacent string two liberties, read ladder. */
adj = chainlinks2(str1, adjs, 2);
for (r = 0; r < adj; r++)
if (adjacent_strings(adjs[r], str2))
if (quiescence_capture(adjs[r], move))
return WIN;
return 0;
}
/* Externally callable frontend to recursive_connect(). */
int
string_connect(int str1, int str2, int *move)
{
nodes_connect = 0;
*move = PASS_MOVE;
return recursive_connect(str1, str2, move);
}
/* returns WIN if str1 and str2 can be connected */
static int recursive_connect (int str1, int str2, int *move) {
int i, res = 0, Moves[MAX_MOVES], ForcedMoves[MAX_MOVES];
SETUP_TRACE_INFO2("recursive_connect", str1, str2);
if ( (board[str1] == EMPTY) || (board[str2] == EMPTY) ) {
SGFTRACE2(PASS_MOVE, 0, "one string already captured");
return 0;
}
if (same_string(str1, str2)) {
SGFTRACE2(PASS_MOVE, WIN, "already connected");
return WIN;
}
if (nodes_connect > max_nodes_connect) {
SGFTRACE2(PASS_MOVE, 0, "connection node limit reached");
return 0;
}
if (stackp == max_connect_depth) {
SGFTRACE2(PASS_MOVE, 0, "connection depth limit reached");
return 0;
}
nodes_connect++;
if (quiescence_connect (str1, str2, move)) {
SGFTRACE2(*move, WIN, "quiescence_connect");
return WIN;
}
ForcedMoves[0] = 0;
Moves[0] = 0;
/* if one of the strings to connect can be captured
* and there are forced moves to prevent the capture
* then the only moves to try are the moves that
* defend the string: all the other moves will
* lead to the capture of the string
*/
if (!prevent_capture_one_move(ForcedMoves, str1))
prevent_capture_one_move(ForcedMoves, str2);
/* else if (prevent_capture_two_moves(ForcedMoves, str1)) */
/* ; */
/* else if (prevent_capture_two_moves(ForcedMoves, str2)) */
/* ; */
/* we are at a max node, so any move we can find
* is ok. Try moves that can connect in three moves
* because the function that prevent connection in one
* and two moves are called at and nodes
*/
moves_to_connect_in_three_moves (Moves, str1, str2);
/* if there are some forced moves to prevent the capture
* of one of the two strings, then we only look at
* the moves that prevent capture and that might also
* connect
*/
if ( (ForcedMoves[0] != 0) && (Moves[0] != 0) )
intersection_array(Moves, ForcedMoves);
for (i = 1; ((i < Moves[0] + 1) && (res == 0)); i++) {
if (trymove(Moves[i], board[str1], "recursive_connect", str1, EMPTY, 0)) {
if (!recursive_disconnect(str1, str2, move)) {
*move = Moves[i];
res = WIN;
}
popgo();
}
}
if (res == WIN) {
SGFTRACE2(*move, WIN, "success");
}
else {
SGFTRACE2(PASS_MOVE, 0, "failure");
}
return res;
}
/* Externally callable frontend to recursive_disconnect(). */
int
disconnect(int str1, int str2, int *move)
{
nodes_connect = 0;
*move = PASS_MOVE;
return recursive_disconnect(str1, str2, move);
}
/* returns WIN if str1 and str2 can be disconnected */
static int recursive_disconnect (int str1, int str2, int *move) {
int i, res = WIN, Moves[MAX_MOVES];
SETUP_TRACE_INFO2("recursive_disconnect", str1, str2);
if ((board[str1] == EMPTY) || (board[str2] == EMPTY)) {
SGFTRACE2(PASS_MOVE, WIN, "one string already captured");
return WIN;
}
if (quiescence_capture(str1, move)) {
SGFTRACE2(*move, WIN, "first string capturable");
return WIN;
}
if (quiescence_capture(str2, move)) {
SGFTRACE2(*move, WIN, "second string capturable");
return WIN;
}
if (same_string(str1, str2)) {
SGFTRACE2(PASS_MOVE, 0, "already connected");
return 0;
}
if (nodes_connect > max_nodes_connect) {
SGFTRACE2(PASS_MOVE, WIN, "connection node limit reached");
return WIN;
}
if (stackp == max_connect_depth) {
SGFTRACE2(PASS_MOVE, WIN, "connection depth limit reached");
return WIN;
}
nodes_connect++;
/* we are at an and node
* only look at forced moves here,
* it ensures that the result of recursive_disconnect
* is proved if it returns 0 (that is connections are proved)
*/
Moves[0] = 0;
if (prevent_connection_one_move(Moves, str1, str2))
res = 0;
else if (prevent_connection_two_moves(Moves, str1, str2))
res = 0;
else if (prevent_simple_connection_three_moves(Moves, str1, str2))
res = 0;
if (res == 0)
for (i = 1; ((i < Moves[0] + 1) && (res == 0)); i++)
if (trymove(Moves[i], OTHER_COLOR(board[str1]),
"recursive_disconnect", str1, EMPTY, 0)) {
if (!recursive_connect(str1, str2, move)) {
*move = Moves[i];
res = WIN;
}
popgo();
}
if (res == WIN) {
SGFTRACE2(*move, WIN, "success");
}
else {
SGFTRACE2(PASS_MOVE, 0, "failure");
}
return res;
}
/* reads simple ladders
*/
static int quiescence_capture (int str, int *move) {
SGFTree *save_sgf_dumptree = sgf_dumptree;
int save_count_variations = count_variations;
int result = 0;
/* We turn off the sgf traces here to avoid cluttering them up with
* naive_ladder moves.
*/
sgf_dumptree = NULL;
count_variations = 0;
if (countlib(str) == 1) {
findlib(str, 1, move);
result = WIN;
}
else if (countlib(str) == 2)
result = naive_ladder(str, move);
/* Turn the sgf traces back on. */
sgf_dumptree = save_sgf_dumptree;
count_variations = save_count_variations;
return result;
}
/* static int capture_one_move (int str) { */
/* if (countlib(str) == 1) */
/* return 1; */
/* return 0; */
/* } */
/* find all the possible moves that can prevent the capture
* of a string in atari
*
* the ip1 game function
*/
static int prevent_capture_one_move(int *moves, int str1) {
int r, res=0;
int liberties, libs[MAXLIBS];
int adj, adjs[MAXCHAIN];
liberties = findlib(str1, MAXLIBS, libs);
if (liberties==1) {
res = WIN;
adj = chainlinks2(str1, adjs, 1);
for (r = 0; r < adj; r++)
add_array(moves, adjs[r]);
add_array(moves, libs[0]);
}
return res;
}
/*
* Local Variables:
* tab-width: 8
* c-basic-offset: 2
* End:
*/