/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* 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. *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* 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
/* 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 */
openregion(int i1
, int i2
, int j1
, int j2
)
return openregion(i2
, i1
, 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.
for (x
= i1
; x
<= i2
; x
++)
for (y
= j1
; y
<= j2
; y
++)
if (BOARD(x
, y
) != EMPTY
)
/* This function sets the global variables indicating symmetries of the
* position. (Important for etiquette.)
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;
!= board
[POS(board_size
- 1 - j
, board_size
- 1 - i
)])
diagonally_symmetric
= 0;
static int corners
[][2] =
/* Relative weights for different corner moves at different board
static int small_board
[] =
static int medium_board
[] =
static int large_board
[] =
choose_corner_move(int corner
, int *m
, int *n
)
else if (board_size
<= 15)
sum_of_weights
+= table
[i
];
q
= gg_rand() % sum_of_weights
;
for (i
= 0; i
< 8; i
++) {
/* Announce move, but check for politeness first. */
announce_move(int move
, int value
, int color
)
/* This shouldn't happen. */
if (board
[move
] != EMPTY
)
/* 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
if (horizontally_symmetric
) {
if ((2 * j
< board_size
- 1) ^ (color
== WHITE
))
move
= POS(i
, board_size
- 1 - j
);
if (vertically_symmetric
) {
if ((2 * i
> board_size
- 1) ^ (color
== WHITE
))
move
= POS(board_size
- 1 - i
, j
);
if (diagonally_symmetric
) {
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. */
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
;
/* Full board matching in database for fuseki moves. Return 1 if any
search_fuseki_database(int color
)
struct fullboard_pattern
*database
;
/* Disable matching after a certain number of stones are placed on
if (stones_on_board(BLACK
| WHITE
) > MAX_FUSEKI_DATABASE_STONES
)
/* We only have databases for 9x9, 13x13 and 19x19. */
else if (board_size
== 13)
else if (board_size
== 19)
fullboard_matchpat(fuseki_callback
, color
, database
);
if (num_fuseki_moves
== 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
++) {
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);
/* Generate move in empty corner or in middle of small board.*/
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. */
/* Search in fuseki database unless disabled by --nofusekidb option. */
if (fusekidb
&& search_fuseki_database(color
))
/* On 9x9, only play open corners after the first move if nothing
if (board_size
== 9 && stones_on_board(color
) > 0)
/* 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
);
else if (board_size
== 9)
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
);