Initial commit of GNU Go v3.8.
[sgk-go] / engine / handicap.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 "liberty.h"
#include "patterns.h"
#include "random.h"
/* ================================================================ */
/* Set up fixed placement handicap stones for black side */
/* ================================================================ */
/* Handicap stones are set up according to the following diagrams:
*
* 2 stones: 3 stones:
*
* A B C D E F G H J A B C D E F G H J
* 9 . . . . . . . . . 9 9 . . . . . . . . . 9
* 8 . . . . . . . . . 8 8 . . . . . . . . . 8
* 7 . . + . . . X . . 7 7 . . X . . . X . . 7
* 6 . . . . . . . . . 6 6 . . . . . . . . . 6
* 5 . . . . + . . . . 5 5 . . . . + . . . . 5
* 4 . . . . . . . . . 4 4 . . . . . . . . . 4
* 3 . . X . . . + . . 3 3 . . X . . . + . . 3
* 2 . . . . . . . . . 2 2 . . . . . . . . . 2
* 1 . . . . . . . . . 1 1 . . . . . . . . . 1
* A B C D E F G H J A B C D E F G H J
*
* 4 stones: 5 stones:
*
* A B C D E F G H J A B C D E F G H J
* 9 . . . . . . . . . 9 9 . . . . . . . . . 9
* 8 . . . . . . . . . 8 8 . . . . . . . . . 8
* 7 . . X . . . X . . 7 7 . . X . . . X . . 7
* 6 . . . . . . . . . 6 6 . . . . . . . . . 6
* 5 . . . . + . . . . 5 5 . . . . X . . . . 5
* 4 . . . . . . . . . 4 4 . . . . . . . . . 4
* 3 . . X . . . X . . 3 3 . . X . . . X . . 3
* 2 . . . . . . . . . 2 2 . . . . . . . . . 2
* 1 . . . . . . . . . 1 1 . . . . . . . . . 1
* A B C D E F G H J A B C D E F G H J
*
* 6 stones: 7 stones:
*
* A B C D E F G H J A B C D E F G H J
* 9 . . . . . . . . . 9 9 . . . . . . . . . 9
* 8 . . . . . . . . . 8 8 . . . . . . . . . 8
* 7 . . X . . . X . . 7 7 . . X . . . X . . 7
* 6 . . . . . . . . . 6 6 . . . . . . . . . 6
* 5 . . X . + . X . . 5 5 . . X . X . X . . 5
* 4 . . . . . . . . . 4 4 . . . . . . . . . 4
* 3 . . X . . . X . . 3 3 . . X . . . X . . 3
* 2 . . . . . . . . . 2 2 . . . . . . . . . 2
* 1 . . . . . . . . . 1 1 . . . . . . . . . 1
* A B C D E F G H J A B C D E F G H J
*
* 8 stones: 9 stones:
*
* A B C D E F G H J A B C D E F G H J
* 9 . . . . . . . . . 9 9 . . . . . . . . . 9
* 8 . . . . . . . . . 8 8 . . . . . . . . . 8
* 7 . . X . X . X . . 7 7 . . X . X . X . . 7
* 6 . . . . . . . . . 6 6 . . . . . . . . . 6
* 5 . . X . + . X . . 5 5 . . X . X . X . . 5
* 4 . . . . . . . . . 4 4 . . . . . . . . . 4
* 3 . . X . X . X . . 3 3 . . X . X . X . . 3
* 2 . . . . . . . . . 2 2 . . . . . . . . . 2
* 1 . . . . . . . . . 1 1 . . . . . . . . . 1
* A B C D E F G H J A B C D E F G H J
*
* For odd-sized boards larger than 9x9, the same pattern is followed,
* except that the edge stones are moved to the fourth line for 13x13
* boards and larger.
*
* For even-sized boards at least 8x8, only the four first diagrams
* are used, because there is no way to place the center stones
* symmetrically. As for odd-sized boards, the edge stones are moved
* to the fourth line for boards larger than 11x11.
*
* At most four stones are placed on 7x7 boards too (this size may or
* may not be supported by the rest of the engine). No handicap stones
* are ever placed on smaller boards.
*
* Notice that this function only deals with fixed handicap placement.
* Larger handicaps can be added by free placement if the used
* interface supports it.
*/
/* This table contains the (coded) positions of the stones.
* 2 maps to 2 or 3, depending on board size
* 0 maps to center
* -ve numbers map to board_size - number
*
* The stones are placed in this order, *except* if there are
* 5 or 7 stones, in which case center ({0, 0}) is placed, and
* then as for 4 or 6.
*/
static const int places[][2] = {
{2, -2}, {-2, 2}, {2, 2}, {-2, -2}, /* first 4 are easy */
/* for 5, {0,0} is explicitly placed */
{0, 2}, {0, -2}, /* for 6 these two are placed */
/* for 7, {0,0} is explicitly placed */
{2, 0}, {-2, 0}, /* for 8, these two are placed */
{0, 0}, /* finally tengen for 9 */
};
/*
* Sets up fixed handicap placement stones, returning the number of
* placed handicap stones and also setting the global variable
* handicap to the same value.
*/
int
place_fixed_handicap(int desired_handicap)
{
int r;
int max_handicap;
int remaining_stones;
int three = board_size > 11 ? 3 : 2;
int mid = board_size/2;
/* A handicap of 1 just means that B plays first, no komi.
* Black is not told where to play the first stone so no handicap
* is set.
*/
if (desired_handicap < 2) {
handicap = 0;
return 0;
}
if ((board_size % 2 == 1) && (board_size >= 9))
max_handicap = 9;
else if (board_size >= 7)
max_handicap = 4;
else
max_handicap = 0;
/* It's up to the caller of this function to notice if the handicap
* was too large for fixed placement and act upon that.
*/
if (desired_handicap > max_handicap)
handicap = max_handicap;
else
handicap = desired_handicap;
remaining_stones = handicap;
/* special cases: 5 and 7 */
if (desired_handicap == 5 || desired_handicap == 7) {
add_stone(POS(mid, mid), BLACK);
remaining_stones--;
}
for (r = 0; r < remaining_stones; r++) {
int i = places[r][0];
int j = places[r][1];
/* Translate the encoded values to board co-ordinates. */
if (i == 2)
i = three; /* 2 or 3 */
else if (i == 0)
i = mid;
else if (i == -2)
i = board_size - 1 - three;
if (j == 2)
j = three;
else if (j == 0)
j = mid;
else if (j == -2)
j = board_size - 1 - three;
add_stone(POS(i, j), BLACK);
}
return handicap;
}
/* ================================================================ */
/* Set up free placement handicap stones for black side */
/* ================================================================ */
/*
* Sets up free handicap placement stones, returning the number of
* placed handicap stones.
*/
static int remaining_handicap_stones = -1;
static int total_handicap_stones = -1;
static int find_free_handicap_pattern(void);
static void free_handicap_callback(int anchor, int color,
struct pattern *pattern,
int ll, void *data);
/*
* Sets up free placement handicap stones, returning the number of
* placed handicap stones and also setting the global variable
* handicap to the same value.
*/
int
place_free_handicap(int desired_handicap)
{
gg_assert(desired_handicap == 0 || desired_handicap >= 2);
if (desired_handicap == 0) {
handicap = 0;
return 0;
}
total_handicap_stones = desired_handicap;
remaining_handicap_stones = desired_handicap;
/* First place black stones in the four corners to enable the
* pattern matching scheme.
*/
add_stone(POS(0, 0), BLACK);
add_stone(POS(0, board_size - 1), BLACK);
add_stone(POS(board_size - 1, 0), BLACK);
add_stone(POS(board_size - 1, board_size - 1), BLACK);
/* Find and place free handicap stones by pattern matching. */
while (remaining_handicap_stones > 0) {
if (!find_free_handicap_pattern())
break;
}
/* Remove the artificial corner stones. */
remove_stone(POS(0, 0));
remove_stone(POS(0, board_size - 1));
remove_stone(POS(board_size - 1, 0));
remove_stone(POS(board_size - 1, board_size - 1));
/* Find and place additional free handicap stones by the aftermath
* algorithm.
*/
while (remaining_handicap_stones > 0) {
int move;
/* Call genmove_conservative() in order to prepare the engine for
* an aftermath_genmove() call. We discard the genmove result.
*/
genmove_conservative(BLACK, NULL);
move = aftermath_genmove(BLACK, 0, NULL);
if (move != PASS_MOVE) {
add_stone(move, BLACK);
remaining_handicap_stones--;
}
else
break;
}
/* Set handicap to the number of actually placed stones. */
handicap = desired_handicap - remaining_handicap_stones;
/* Reset these to invalid values, so that improper use of handicap
* helper functions can be detected.
*/
total_handicap_stones = -1;
remaining_handicap_stones = -1;
return handicap;
}
struct handicap_match {
int value;
int anchor;
struct pattern *pattern;
int ll;
};
#define MAX_HANDICAP_MATCHES 40
static struct handicap_match handicap_matches[MAX_HANDICAP_MATCHES];
static int number_of_matches;
static int
find_free_handicap_pattern()
{
int k;
int highest_value = -1;
int sum_values = 0;
int r;
int anchor;
struct pattern *pattern;
int ll;
int move;
number_of_matches = 0;
matchpat(free_handicap_callback, BLACK, &handipat_db, NULL, NULL);
if (number_of_matches == 0)
return 0;
/* Find the highest value among the matched patterns. */
for (k = 0; k < number_of_matches; k++)
if (highest_value < handicap_matches[k].value)
highest_value = handicap_matches[k].value;
/* Replace the values by 2^(value - highest_value + 10) and compute
* the sum of these values. Fractional values are discarded.
*/
for (k = 0; k < number_of_matches; k++) {
if (handicap_matches[k].value < highest_value - 10)
handicap_matches[k].value = 0;
else
handicap_matches[k].value = 1 << (handicap_matches[k].value
- highest_value + 10);
sum_values += handicap_matches[k].value;
}
/* Pick a random number between 0 and sum_values. Don't bother with
* the fact that lower numbers will tend to be very slightly
* overrepresented.
*/
r = gg_rand() % sum_values;
/* Find the chosen pattern. */
for (k = 0; k < number_of_matches; k++) {
r -= handicap_matches[k].value;
if (r < 0)
break;
}
/* Place handicap stones according to pattern k. */
anchor = handicap_matches[k].anchor;
pattern = handicap_matches[k].pattern;
ll = handicap_matches[k].ll;
/* Pick up the location of the move */
move = AFFINE_TRANSFORM(pattern->move_offset, ll, anchor);
add_stone(move, BLACK);
remaining_handicap_stones--;
/* Add stones at all '!' in the pattern. */
for (k = 0; k < pattern->patlen; k++) {
if (pattern->patn[k].att == ATT_not) {
int pos = AFFINE_TRANSFORM(pattern->patn[k].offset, ll, anchor);
add_stone(pos, BLACK);
remaining_handicap_stones--;
}
}
return 1;
}
static void
free_handicap_callback(int anchor, int color, struct pattern *pattern,
int ll, void *data)
{
int r = -1;
int k;
int number_of_stones = 1;
/* Pick up the location of the move */
int move = AFFINE_TRANSFORM(pattern->move_offset, ll, anchor);
UNUSED(data);
/* Check how many stones are placed by the pattern. This must not be
* larger than the number of remaining handicap stones.
*/
for (k = 0; k < pattern->patlen; k++) {
if (pattern->patn[k].att == ATT_not)
number_of_stones++;
}
if (number_of_stones > remaining_handicap_stones)
return;
/* If the pattern has a constraint, call the autohelper to see
* if the pattern must be rejected.
*/
if (pattern->autohelper_flag & HAVE_CONSTRAINT) {
if (!pattern->autohelper(ll, move, color, 0))
return;
}
if (number_of_matches < MAX_HANDICAP_MATCHES) {
r = number_of_matches;
number_of_matches++;
}
else {
int least_value = handicap_matches[0].value + 1;
for (k = 0; k < number_of_matches; k++) {
if (handicap_matches[k].value < least_value) {
r = k;
least_value = handicap_matches[k].value;
}
}
}
gg_assert(r >= 0 && r < MAX_HANDICAP_MATCHES);
handicap_matches[r].value = pattern->value;
handicap_matches[r].anchor = anchor;
handicap_matches[r].pattern = pattern;
handicap_matches[r].ll = ll;
}
int
free_handicap_remaining_stones()
{
gg_assert(remaining_handicap_stones >= 0);
return remaining_handicap_stones;
}
int
free_handicap_total_stones()
{
gg_assert(total_handicap_stones >= 0);
return total_handicap_stones;
}
/*
* Local Variables:
* tab-width: 8
* c-basic-offset: 2
* End:
*/