411 lines
11 KiB
C
411 lines
11 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 <stdlib.h>
|
||
|
|
||
|
#include "liberty.h"
|
||
|
#include "patterns.h"
|
||
|
#include "random.h"
|
||
|
|
||
|
#include "sgftree.h"
|
||
|
|
||
|
|
||
|
/* Pointless to do fuseki database pattern matching after this number
|
||
|
* of stones have been placed on the board.
|
||
|
*
|
||
|
* Notice that we are not talking of the move number here but the
|
||
|
* number of stones actually residing on the board. This does in
|
||
|
* particular include handicap stones.
|
||
|
*/
|
||
|
#define MAX_FUSEKI_DATABASE_STONES 30
|
||
|
|
||
|
|
||
|
#define UPPER_LEFT 0
|
||
|
#define UPPER_RIGHT 1
|
||
|
#define LOWER_LEFT 2
|
||
|
#define LOWER_RIGHT 3
|
||
|
|
||
|
/* Global variables remembering which symmetries the position has. */
|
||
|
static int horizontally_symmetric; /* symmetry with respect to K column */
|
||
|
static int vertically_symmetric; /* symmetry with respect to 10 row */
|
||
|
static int diagonally_symmetric; /* with respect to diagonal from UR to LL */
|
||
|
|
||
|
/* This value must be lower than the value for an ongoing joseki.
|
||
|
* (Gets multiplied with board_size / 19.)
|
||
|
*/
|
||
|
#define EMPTY_CORNER_VALUE 25
|
||
|
|
||
|
/* check if region from i1, j1 to i2, j2 is open */
|
||
|
|
||
|
static int
|
||
|
openregion(int i1, int i2, int j1, int j2)
|
||
|
{
|
||
|
int x, y;
|
||
|
|
||
|
if (i1 > i2)
|
||
|
return openregion(i2, i1, j1, j2);
|
||
|
if (j1 > j2)
|
||
|
return openregion(i1, i2, j2, j1);
|
||
|
|
||
|
/* Disregard parts of the region off the board. This is convenient
|
||
|
* in order not to have to special-case tiny boards. It also secures
|
||
|
* against potential reading outside the board[] array boundaries.
|
||
|
*/
|
||
|
if (i1 < 0)
|
||
|
i1 = 0;
|
||
|
if (j1 < 0)
|
||
|
j1 = 0;
|
||
|
if (i2 >= board_size)
|
||
|
i2 = board_size - 1;
|
||
|
if (j2 >= board_size)
|
||
|
j2 = board_size - 1;
|
||
|
|
||
|
for (x = i1; x <= i2; x++)
|
||
|
for (y = j1; y <= j2; y++)
|
||
|
if (BOARD(x, y) != EMPTY)
|
||
|
return 0;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* This function sets the global variables indicating symmetries of the
|
||
|
* position. (Important for etiquette.)
|
||
|
*/
|
||
|
static void
|
||
|
set_symmetries(void)
|
||
|
{
|
||
|
int i, j;
|
||
|
horizontally_symmetric = 1;
|
||
|
vertically_symmetric = 1;
|
||
|
diagonally_symmetric = 1;
|
||
|
for (i = 0; i < board_size
|
||
|
&& (vertically_symmetric || horizontally_symmetric
|
||
|
|| diagonally_symmetric); i++)
|
||
|
for (j = 0; j < board_size; j++) {
|
||
|
if (board[POS(i, j)] != board[POS(i, board_size - 1 - j)])
|
||
|
horizontally_symmetric = 0;
|
||
|
if (board[POS(i, j)] != board[POS(board_size - 1 - i, j)])
|
||
|
vertically_symmetric = 0;
|
||
|
if (board[POS(i, j)]
|
||
|
!= board[POS(board_size - 1 - j, board_size - 1 - i)])
|
||
|
diagonally_symmetric = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* The corner moves. */
|
||
|
|
||
|
static int corners[][2] =
|
||
|
{
|
||
|
{3, 3},
|
||
|
{3, 4},
|
||
|
{4, 3},
|
||
|
{4, 4},
|
||
|
{5, 3},
|
||
|
{3, 5},
|
||
|
{5, 4},
|
||
|
{4, 5},
|
||
|
};
|
||
|
|
||
|
/* Relative weights for different corner moves at different board
|
||
|
sizes. */
|
||
|
|
||
|
/* up to 11x11 */
|
||
|
static int small_board[] =
|
||
|
{
|
||
|
50, /* 3-3 */
|
||
|
18, /* 3-4 */
|
||
|
17, /* 4-3 */
|
||
|
15, /* 4-4 */
|
||
|
0, /* 5-3 */
|
||
|
0, /* 3-5 */
|
||
|
0, /* 5-4 */
|
||
|
0, /* 4-5 */
|
||
|
};
|
||
|
|
||
|
/* 12x12 to 15x15 */
|
||
|
static int medium_board[] =
|
||
|
{
|
||
|
30, /* 3-3 */
|
||
|
20, /* 3-4 */
|
||
|
20, /* 4-3 */
|
||
|
22, /* 4-4 */
|
||
|
2, /* 5-3 */
|
||
|
2, /* 3-5 */
|
||
|
2, /* 5-4 */
|
||
|
2, /* 4-5 */
|
||
|
};
|
||
|
|
||
|
/* 16x16 and larger */
|
||
|
static int large_board[] =
|
||
|
{
|
||
|
15, /* 3-3 */
|
||
|
15, /* 3-4 */
|
||
|
15, /* 4-3 */
|
||
|
35, /* 4-4 */
|
||
|
5, /* 5-3 */
|
||
|
5, /* 3-5 */
|
||
|
5, /* 5-4 */
|
||
|
5, /* 4-5 */
|
||
|
};
|
||
|
|
||
|
static void
|
||
|
choose_corner_move(int corner, int *m, int *n)
|
||
|
{
|
||
|
int *table = 0;
|
||
|
int sum_of_weights = 0;
|
||
|
int i;
|
||
|
int q;
|
||
|
|
||
|
if (board_size <= 11)
|
||
|
table = small_board;
|
||
|
else if (board_size <= 15)
|
||
|
table = medium_board;
|
||
|
else
|
||
|
table = large_board;
|
||
|
|
||
|
for (i = 0; i < 8; i++)
|
||
|
sum_of_weights += table[i];
|
||
|
|
||
|
q = gg_rand() % sum_of_weights;
|
||
|
for (i = 0; i < 8; i++) {
|
||
|
q -= table[i];
|
||
|
if (q < 0)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
*m = corners[i][0];
|
||
|
*n = corners[i][1];
|
||
|
|
||
|
switch (corner) {
|
||
|
case UPPER_LEFT:
|
||
|
*m = *m - 1;
|
||
|
*n = *n - 1;
|
||
|
break;
|
||
|
case UPPER_RIGHT:
|
||
|
*m = *m - 1;
|
||
|
*n = board_size - *n;
|
||
|
break;
|
||
|
case LOWER_LEFT:
|
||
|
*m = board_size - *m;
|
||
|
*n = *n - 1;
|
||
|
break;
|
||
|
case LOWER_RIGHT:
|
||
|
*m = board_size - *m;
|
||
|
*n = board_size - *n;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Announce move, but check for politeness first. */
|
||
|
static void
|
||
|
announce_move(int move, int value, int color)
|
||
|
{
|
||
|
int i, j;
|
||
|
/* This shouldn't happen. */
|
||
|
if (board[move] != EMPTY)
|
||
|
return;
|
||
|
|
||
|
/* Politeness: Black plays in lower right half of upper right corner first.
|
||
|
* White plays in upper left half of lower left corner first.
|
||
|
* (Not sure whether this is correct for handicap games. Is this an
|
||
|
* urgent FIXME? :-) )
|
||
|
*/
|
||
|
if (horizontally_symmetric) {
|
||
|
i = I(move);
|
||
|
j = J(move);
|
||
|
if ((2 * j < board_size - 1) ^ (color == WHITE))
|
||
|
move = POS(i, board_size - 1 - j);
|
||
|
}
|
||
|
if (vertically_symmetric) {
|
||
|
i = I(move);
|
||
|
j = J(move);
|
||
|
if ((2 * i > board_size - 1) ^ (color == WHITE))
|
||
|
move = POS(board_size - 1 - i, j);
|
||
|
}
|
||
|
if (diagonally_symmetric) {
|
||
|
i = I(move);
|
||
|
j = J(move);
|
||
|
if ((board_size - 1 - j > i) ^ (color == WHITE))
|
||
|
move = POS(board_size - 1 - j, board_size - 1 - i);
|
||
|
}
|
||
|
|
||
|
if (set_minimum_move_value(move, value))
|
||
|
TRACE("Fuseki Player suggests %1m with value %d\n", move, value);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Storage for values collected during pattern matching. */
|
||
|
static int fuseki_moves[MAX_BOARD * MAX_BOARD];
|
||
|
static int fuseki_value[MAX_BOARD * MAX_BOARD];
|
||
|
static int num_fuseki_moves;
|
||
|
static int fuseki_total_value;
|
||
|
|
||
|
/* Callback for fuseki database pattern matching. */
|
||
|
static void
|
||
|
fuseki_callback(int move, struct fullboard_pattern *pattern, int ll)
|
||
|
{
|
||
|
TRACE("Fuseki database move at %1m with relative weight %d, pattern %s+%d\n",
|
||
|
move, pattern->value, pattern->name, ll);
|
||
|
|
||
|
/* Store coordinates and relative weight for the found move. */
|
||
|
fuseki_moves[num_fuseki_moves] = move;
|
||
|
fuseki_value[num_fuseki_moves] = pattern->value;
|
||
|
fuseki_total_value += pattern->value;
|
||
|
num_fuseki_moves++;
|
||
|
}
|
||
|
|
||
|
/* Full board matching in database for fuseki moves. Return 1 if any
|
||
|
* pattern found.
|
||
|
*/
|
||
|
static int
|
||
|
search_fuseki_database(int color)
|
||
|
{
|
||
|
struct fullboard_pattern *database;
|
||
|
int q;
|
||
|
int k;
|
||
|
|
||
|
/* Disable matching after a certain number of stones are placed on
|
||
|
* the board.
|
||
|
*/
|
||
|
if (stones_on_board(BLACK | WHITE) > MAX_FUSEKI_DATABASE_STONES)
|
||
|
return 0;
|
||
|
|
||
|
/* We only have databases for 9x9, 13x13 and 19x19. */
|
||
|
if (board_size == 9)
|
||
|
database = fuseki9;
|
||
|
else if (board_size == 13)
|
||
|
database = fuseki13;
|
||
|
else if (board_size == 19)
|
||
|
database = fuseki19;
|
||
|
else
|
||
|
return 0;
|
||
|
|
||
|
/* Do the matching. */
|
||
|
num_fuseki_moves = 0;
|
||
|
fuseki_total_value = 0;
|
||
|
fullboard_matchpat(fuseki_callback, color, database);
|
||
|
|
||
|
/* No match. */
|
||
|
if (num_fuseki_moves == 0)
|
||
|
return 0;
|
||
|
|
||
|
/* Choose randomly with respect to relative weights for matched moves.
|
||
|
*/
|
||
|
q = gg_rand() % fuseki_total_value;
|
||
|
for (k = 0; k < num_fuseki_moves; k++) {
|
||
|
q -= fuseki_value[k];
|
||
|
if (q < 0)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
gg_assert(k < num_fuseki_moves);
|
||
|
/* Give this move an arbitrary value of 75. The actual value doesn't
|
||
|
* matter much since the intention is that we should play this move
|
||
|
* whatever the rest of the analysis thinks.
|
||
|
*/
|
||
|
announce_move(fuseki_moves[k], 75, color);
|
||
|
|
||
|
/* Also make sure the other considered moves can be seen in the
|
||
|
* traces and in the output file.
|
||
|
*/
|
||
|
for (k = 0; k < num_fuseki_moves; k++)
|
||
|
set_minimum_move_value(fuseki_moves[k], 74);
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* Generate move in empty corner or in middle of small board.*/
|
||
|
void
|
||
|
fuseki(int color)
|
||
|
{
|
||
|
int i = -1;
|
||
|
int j = -1;
|
||
|
int width; /* Side of the open region required in the corner. */
|
||
|
int empty_corner_value = EMPTY_CORNER_VALUE * board_size/19;
|
||
|
|
||
|
/* Return immediately if --disable_fuseki option used. */
|
||
|
if (disable_fuseki)
|
||
|
return;
|
||
|
|
||
|
set_symmetries();
|
||
|
|
||
|
/* Search in fuseki database unless disabled by --nofusekidb option. */
|
||
|
if (fusekidb && search_fuseki_database(color))
|
||
|
return;
|
||
|
|
||
|
/* On 9x9, only play open corners after the first move if nothing
|
||
|
* else useful is found.
|
||
|
*/
|
||
|
if (board_size == 9 && stones_on_board(color) > 0)
|
||
|
empty_corner_value = 5;
|
||
|
|
||
|
if (board_size <= 11) {
|
||
|
/* For boards of size 11x11 or smaller we first go for the center point. */
|
||
|
int middle = board_size/2;
|
||
|
if (openregion(middle-2, middle+2, middle-2, middle+2)) {
|
||
|
announce_move(POS(middle, middle), 45, color);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (board_size < 9)
|
||
|
return;
|
||
|
|
||
|
if (board_size >= 18)
|
||
|
width = 8;
|
||
|
else if (board_size == 9)
|
||
|
width = 5;
|
||
|
else
|
||
|
width = board_size/2;
|
||
|
|
||
|
if (openregion(0, width-1, board_size-width, board_size-1)) {
|
||
|
choose_corner_move(UPPER_RIGHT, &i, &j);
|
||
|
announce_move(POS(i, j), empty_corner_value, color);
|
||
|
}
|
||
|
|
||
|
if (openregion(board_size-width, board_size-1, 0, width-1)) {
|
||
|
choose_corner_move(LOWER_LEFT, &i, &j);
|
||
|
announce_move(POS(i, j), empty_corner_value, color);
|
||
|
}
|
||
|
if (openregion(board_size-width, board_size-1,
|
||
|
board_size-width, board_size-1)) {
|
||
|
choose_corner_move(LOWER_RIGHT, &i, &j);
|
||
|
announce_move(POS(i, j), empty_corner_value, color);
|
||
|
}
|
||
|
|
||
|
if (openregion(0, width-1, 0, width-1)) {
|
||
|
choose_corner_move(UPPER_LEFT, &i, &j);
|
||
|
announce_move(POS(i, j), empty_corner_value, color);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Local Variables:
|
||
|
* tab-width: 8
|
||
|
* c-basic-offset: 2
|
||
|
* End:
|
||
|
*/
|
||
|
|