/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * 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 #include #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: */