Esempio n. 1
0
/* Generate a move. */
static void
generate_move(int *i, int *j, int color)
{
  int moves[MAX_BOARD * MAX_BOARD];
  int num_moves = 0;
  int move;
  int ai, aj;
  int k;

  memset(moves, 0, sizeof(moves));
  for (ai = 0; ai < board_size; ai++)
    for (aj = 0; aj < board_size; aj++) {
      /* Consider moving at (ai, aj) if it is legal and not suicide. */
      if (legal_move(ai, aj, color)
	  && !suicide(ai, aj, color)) {
	/* Further require the move not to be suicide for the opponent... */
	if (!suicide(ai, aj, OTHER_COLOR(color)))
	  moves[num_moves++] = POS(ai, aj);
	else {
	  /* ...however, if the move captures at least one stone,
           * consider it anyway.
	   */
	  for (k = 0; k < 4; k++) {
	    int bi = ai + deltai[k];
	    int bj = aj + deltaj[k];
	    if (on_board(bi, bj) && get_board(bi, bj) == OTHER_COLOR(color)) {
	      moves[num_moves++] = POS(ai, aj);
	      break;
	    }
	  }
	}
      }
    }

  /* Choose one of the considered moves randomly with uniform
   * distribution. (Strictly speaking the moves with smaller 1D
   * coordinates tend to have a very slightly higher probability to be
   * chosen, but for all practical purposes we get a uniform
   * distribution.)
   */
  if (num_moves > 0) {
    move = moves[xor_randn(num_moves)];
    *i = I(move);
    *j = J(move);
  }
  else {
    /* But pass if no move was considered. */
    *i = -1;
    *j = -1;
  }
}
Esempio n. 2
0
bool stone::can_live(chain* itor)
{
	int visited[BOARD_SIZE*BOARD_SIZE];
	memset(visited, 0, sizeof(visited));
	int a = 0;
	list <int> q1;
	q1.push_back(POS(this->row, this->col));
	while (q1.size() != EMPTY)
	{
		int top = q1.front();
		q1.pop_front();
		if (visited[top] == 1){ continue; }
		visited[top] = 1;
		for (int k = 0; k < 4; k++)
		{
			if (stone_board->on_board(I(top) + deltai[k], J(top) + deltaj[k]))
			{
				int index = POS(I(top) + deltai[k], J(top) + deltaj[k]);
				if (stone_board->main_board[index]->color == EMPTY)
				{
					return true;
				}
				else if (stone_board->main_board[index]->color == (itor)->chain_block->color)
				{
					continue;
				}
				else if (stone_board->main_board[index]->color == OTHER_COLOR((itor)->chain_block->color))
				{
					q1.push_back(index);
				}
			}
		}
	}
	return false;
}
Esempio n. 3
0
//PART 1
void stone::evaluating_empty(int *arr)
{	//arr[4] -> [my, enemy, empty, outside]
	int countforshared = 0;
	for (int k = 0; k < 4; k++)
	{
		int index = POS(row + deltai[k], col + deltaj[k]);
		if (!stone_board->on_board(row + deltai[k], col + deltaj[k]))
		{
			arr[3]++;
		}
		else if (stone_board->main_board[index]->color == stone_block->color)
		{
			arr[0]++;
			this->shared[countforshared] = stone_board->main_board[index]->stone_chain;
			countforshared++;
			//record the chain if we find the same color
		}
		else if (stone_board->main_board[index]->color == OTHER_COLOR(stone_block->color))
		{
			arr[1]++;
		}
		else if (stone_board->main_board[index]->color == EMPTY)
		{
			arr[2]++;
		}
	}
}
Esempio n. 4
0
int GoBoard::is_anti_dian33_available(int color, int rival_move)
{
    int star[4] = { 3 * board_size + 3, 3 * board_size + 9, 9 * board_size + 3, 9 * board_size + 9 }; //stars position
    int other = OTHER_COLOR(color);
    int dian, anti;

    dian = POS(I(star[0]) - 1, J(star[0]) - 1);
    anti = POS(I(star[0]) - 0, J(star[0]) - 1);
    if (board[star[0]] == color && board[dian] == other && board[anti] == EMPTY && heavy_policy(anti, color))
        return anti;

    dian = POS(I(star[1]) - 1, J(star[1]) + 1);
    anti = POS(I(star[1]) + 0, J(star[1]) + 1);
    if (board[star[1]] == color && board[dian] == other && board[anti] == EMPTY && heavy_policy(anti, color))
        return anti;

    dian = POS(I(star[2]) + 1, J(star[2]) - 1);
    anti = POS(I(star[2]) - 0, J(star[2]) - 1);
    if (board[star[2]] == color && board[dian] == other && board[anti] == EMPTY && heavy_policy(anti, color))
        return anti;

    dian = POS(I(star[3]) + 1, J(star[3]) + 1);
    anti = POS(I(star[3]) + 0, J(star[3]) + 1);
    if (board[star[3]] == color && board[dian] == other && board[anti] == EMPTY && heavy_policy(anti, color))
        return anti;
    return -1;
}
Esempio n. 5
0
void updateboard(int i, int j, int color)
{
  int other = OTHER_COLOR(color);

  assert(i >= 0 && i < board_size && j >= 0 && j < board_size);
  if (p[i][j] != EMPTY) {
    gprintf("Stone overlay problem at %m!\n", i, j);
    gprintf("Try reducing the move range with -s and -e.\n");
    abort();
  }

  p[i][j] = color;

  if (1)
    DEBUG("Update board : %m = %d\n", i,j, color);

  if (i > 0 && p[i-1][j] == other)
    check_for_capture(i-1, j, other);
  if (i < board_size-1 && p[i+1][j] == other)
    check_for_capture(i+1, j, other);
  if (j > 0 && p[i][j-1] == other)
    check_for_capture(i, j-1, other);
  if (j < board_size-1 && p[i][j+1] == other)
    check_for_capture(i, j+1, other);

}
Esempio n. 6
0
static int
do_pass(Gameinfo *gameinfo, int *passes, int force)
{
  (*passes)++;
  init_sgf(gameinfo);
  gnugo_play_move(PASS_MOVE, gameinfo->to_move);
  sgffile_add_debuginfo(sgftree.lastnode, 0.0);
  sgftreeAddPlay(&sgftree, gameinfo->to_move, -1, -1);
  sgffile_output(&sgftree);

  gameinfo->to_move = OTHER_COLOR(gameinfo->to_move);
  if (force) {
    gameinfo->computer_player = OTHER_COLOR(gameinfo->computer_player);
    sgftreeAddComment(&sgftree, "forced");
    return 0;
  }

  return computer_move(gameinfo, passes);
}
Esempio n. 7
0
int GoBoard::is_xiaomu_available(int color, int rival_move)
{
    int star[4] = { 3 * board_size + 3, 3 * board_size + 9, 9 * board_size + 3, 9 * board_size + 9 }; //stars position
    int anti, xiaomu;
    int other = OTHER_COLOR(color);

    if (board[star[0]] != color)
    {
        anti = POS(I(star[0]) + 1, J(star[0]) - 1);
        xiaomu = POS(I(star[0]) - 1, J(star[0]) - 0);
        if (board[xiaomu] == other && board[anti] == EMPTY && heavy_policy(anti, color))
            return anti;
        anti = POS(I(star[0]) - 1, J(star[0]) + 1);
        xiaomu = POS(I(star[0]) - 0, J(star[0]) - 1);
        if (board[xiaomu] == other && board[anti] == EMPTY && heavy_policy(anti, color))
            return anti;
    }

    if (board[star[1]] != color)
    {
        anti = POS(I(star[1]) + 1, J(star[1]) + 1);
        xiaomu = POS(I(star[1]) - 1, J(star[1]) - 0);
        if (board[xiaomu] == other && board[anti] == EMPTY && heavy_policy(anti, color))
            return anti;
        anti = POS(I(star[1]) - 1, J(star[1]) - 1);
        xiaomu = POS(I(star[1]) + 0, J(star[1]) + 1);
        if (board[xiaomu] == other && board[anti] == EMPTY && heavy_policy(anti, color))
            return anti;
    }

    if (board[star[2]] != color)
    {
        anti = POS(I(star[2]) - 1, J(star[2]) - 1);
        xiaomu = POS(I(star[2]) + 1, J(star[2]) + 0);
        if (board[xiaomu] == other && board[anti] == EMPTY && heavy_policy(anti, color))
            return anti;
        anti = POS(I(star[2]) + 1, J(star[2]) + 1);
        xiaomu = POS(I(star[2]) - 0, J(star[2]) - 1);
        if (board[xiaomu] == other && board[anti] == EMPTY && heavy_policy(anti, color))
            return anti;
    }

    if (board[star[3]] != color)
    {
        anti = POS(I(star[3]) + 1, J(star[3]) - 1);
        xiaomu = POS(I(star[3]) + 0, J(star[3]) + 1);
        if (board[xiaomu] == other && board[anti] == EMPTY && heavy_policy(anti, color))
            return anti;
        anti = POS(I(star[3]) - 1, J(star[3]) + 1);
        xiaomu = POS(I(star[3]) + 1, J(star[3]) + 0);
        if (board[xiaomu] == other && board[anti] == EMPTY && heavy_policy(anti, color))
            return anti;
    }
    return -1;
}
Esempio n. 8
0
static int
autohelperread_attack5(int trans, int move, int color, int action)
{
  int a, b;
  UNUSED(color);
  UNUSED(action);

  a = AFFINE_TRANSFORM(647, trans, move);
  b = AFFINE_TRANSFORM(685, trans, move);

  return  0 && rgoal[a] == 1 && accuratelib(b, OTHER_COLOR(color), MAX_LIBERTIES, NULL) <= 2 && accuratelib(move, color, MAX_LIBERTIES, NULL) > 1;
}
Esempio n. 9
0
static int
do_move(Gameinfo *gameinfo, char *command, int *passes, int force)
{
  int move = string_to_location(board_size, command);

  if (move == NO_MOVE) {
    printf("\nInvalid move: %s\n", command);
    return 0;
  }
  
  if (!is_allowed_move(move, gameinfo->to_move)) {
    printf("\nIllegal move: %s", command);
    return 0;
  }

  *passes = 0;
  TRACE("\nyour move: %1m\n\n", move);
  init_sgf(gameinfo);
  gnugo_play_move(move, gameinfo->to_move);
  sgffile_add_debuginfo(sgftree.lastnode, 0.0);
  sgftreeAddPlay(&sgftree, gameinfo->to_move, I(move), J(move));
  sgffile_output(&sgftree);
  
  if (opt_showboard) {
    ascii_showboard();
    printf("GNU Go is thinking...\n");
  }

  if (force) {
    gameinfo->computer_player = OTHER_COLOR(gameinfo->computer_player);
    gameinfo->to_move = OTHER_COLOR(gameinfo->to_move);
    sgftreeAddComment(&sgftree, "forced");
    return 0;
  }

  gameinfo->to_move = OTHER_COLOR(gameinfo->to_move);
  return computer_move(gameinfo, passes);
}
Esempio n. 10
0
static void
benchmark(int num_games_per_point)
{
  int i, j;
  unsigned int random_seed = 1U;
  board_size = 9;
  komi = 0.5;

  init_brown(random_seed);

  for (i = 0; i < board_size; i++) {
    for (j = 0; j < board_size; j++) {
      int white_wins = 0;
      int black_wins = 0;
      int k;
      for (k = 0; k < num_games_per_point; k++) {
	int passes = 0;
	int num_moves = 1;
	int color = WHITE;
        clear_board();
	play_move(i, j, BLACK);
        while (passes < 3 && num_moves < 600) {
	  int m, n;
          generate_move(&m, &n, color);
          play_move(m, n, color);
          if (pass_move(m, n)) {
            passes++;
          }
          else {
            passes = 0;
          }
          num_moves++;
          color = OTHER_COLOR(color);
        }
	if (passes == 3) {
	  if (compute_score() > 0) {
	    white_wins++;
	  }
	  else {
	    black_wins++;
	  }
	}
      }
      /*
      printf("%d %d %f\n", i, j,
	     (float) black_wins / (float) (black_wins + white_wins));
      */
    }
  }
}
Esempio n. 11
0
int GoBoard::is_anti_yijianjia_available(int color, int rival_move)
{
    {
        int star[4] = { 3 * board_size + 3, 3 * board_size + 9, 9 * board_size + 3, 9 * board_size + 9 }; //stars position
        int kakari1, kakari2;
        int other = OTHER_COLOR(color);

        kakari1 = POS(I(star[0]) + 0, J(star[0]) + 2);
        kakari2 = POS(I(star[0]) + 2, J(star[0]) + 0);
        if (board[star[0]] == color)
        {
            if (board[kakari1] == other && board[kakari2] == EMPTY && heavy_policy(kakari2, color))
                return kakari2;
            if (board[kakari2] == other && board[kakari1] == EMPTY && heavy_policy(kakari1, color))
                return kakari1;
        }

        kakari1 = POS(I(star[1]) + 0, J(star[1]) - 2);
        kakari2 = POS(I(star[1]) + 2, J(star[1]) - 0);
        if (board[star[1]] == color)
        {
            if (board[kakari1] == other && board[kakari2] == EMPTY && heavy_policy(kakari2, color))
                return kakari2;
            if (board[kakari2] == other && board[kakari1] == EMPTY && heavy_policy(kakari1, color))
                return kakari1;
        }

        kakari1 = POS(I(star[2]) + 0, J(star[2]) + 2);
        kakari2 = POS(I(star[2]) - 2, J(star[2]) - 0);
        if (board[star[2]] == color)
        {
            if (board[kakari1] == other && board[kakari2] == EMPTY && heavy_policy(kakari2, color))
                return kakari2;
            if (board[kakari2] == other && board[kakari1] == EMPTY && heavy_policy(kakari1, color))
                return kakari1;
        }

        kakari1 = POS(I(star[3]) - 0, J(star[3]) - 2);
        kakari2 = POS(I(star[3]) - 2, J(star[3]) - 0);
        if (board[star[3]] == color)
        {
            if (board[kakari1] == other && board[kakari2] == EMPTY && heavy_policy(kakari2, color))
                return kakari2;
            if (board[kakari2] == other && board[kakari1] == EMPTY && heavy_policy(kakari1, color))
                return kakari1;
        }
        return -1;
    }
}
Esempio n. 12
0
static int
autohelperread_attack4(int trans, int move, int color, int action)
{
  int a, b;
  UNUSED(color);

  a = AFFINE_TRANSFORM(647, trans, move);
  b = AFFINE_TRANSFORM(685, trans, move);

  if (!action)
    return  rgoal[a] == 1 && accuratelib(b, OTHER_COLOR(color), MAX_LIBERTIES, NULL)==1;
   if (goallib == 2)   ((read_attack + 4)->value) = 71; else   ((read_attack + 4)->value) = 55;;

  return 0;
}
Esempio n. 13
0
bool stone::eat_this_chain(chain* itor)
{
	for (int k = 0; k < 4; k++) 
	{
		int ai = row + deltai[k];
		int aj = col + deltaj[k];
		if (stone_board->on_board(ai, aj) 
			&& stone_board->get_board(ai, aj)->color == OTHER_COLOR(color)
			&& !stone_board->has_additional_liberty(ai, aj, row, col))
		{
			if (stone_board->main_board[POS(ai,aj)]->stone_chain == itor )
			return 1;
		}
	}
	return 0;
}
Esempio n. 14
0
/* Preliminary function for playing through the aftermath. */
static void
do_play_aftermath(int color, struct aftermath_data *a)
{
  int move;
  int pass = 0;
  int moves = 0;
  int color_to_play = color;
  DEBUG(DEBUG_AFTERMATH, "The aftermath starts.\n");

  /* Disable computing worm and owl threats. */
  disable_threat_computation = 1;
  /* Disable matching of endgame patterns. */
  disable_endgame_patterns = 1;

  while (pass < 2 && moves < board_size * board_size) {
    int reading_nodes = get_reading_node_counter();
    int owl_nodes = get_owl_node_counter();
    int move_val = reduced_genmove(&move, color_to_play);
    if (move_val < 0) {
      int save_verbose = verbose;
      if (verbose > 0)
	verbose--;
      move_val = aftermath_genmove(&move, color_to_play,
				   (color_to_play == WHITE ?
				    a->white_control : a->black_control),
				   0);
      verbose = save_verbose;
    }
    play_move(move, color_to_play);
    if (aftermath_sgftree)
      sgftreeAddPlay(aftermath_sgftree, color_to_play, I(move), J(move));
    moves++;
    DEBUG(DEBUG_AFTERMATH, "%d %C move %1m (nodes %d, %d  total %d, %d)\n",
	  movenum, color_to_play, move, get_owl_node_counter() - owl_nodes,
	  get_reading_node_counter() - reading_nodes,
	  get_owl_node_counter(), get_reading_node_counter());
    if (move != PASS_MOVE)
      pass = 0;
    else
      pass++;
    color_to_play = OTHER_COLOR(color_to_play);
  }
  
  /* Reenable worm and dragon threats and endgame patterns. */
  disable_threat_computation = 0;
  disable_endgame_patterns   = 0;
}
Esempio n. 15
0
static int
computer_move(Gameinfo *gameinfo, int *passes)
{
  int move;
  float move_value;
  int resign;
  int resignation_declined = 0;
  float upper_bound, lower_bound;

  init_sgf(gameinfo);

  /* Generate computer move. */
  if (autolevel_on)
    adjust_level_offset(gameinfo->to_move);
  move = genmove(gameinfo->to_move, &move_value, &resign);
  if (resignation_allowed && resign) {
    int state = ascii_endgame(gameinfo, 2);
    if (state != -1)
      return state;

    /* The opponent declined resignation. Remember not to resign again. */
    resignation_allowed = 0;
    resignation_declined = 1;
  }

  if (showscore) {
    gnugo_estimate_score(&upper_bound, &lower_bound);
    current_score_estimate = (int) ((lower_bound + upper_bound) / 2.0);
  }
  
  mprintf("%s(%d): %1m\n", color_to_string(gameinfo->to_move),
	  movenum + 1, move);
  if (is_pass(move))
    (*passes)++;
  else
    *passes = 0;

  gnugo_play_move(move, gameinfo->to_move);
  sgffile_add_debuginfo(sgftree.lastnode, move_value);
  sgftreeAddPlay(&sgftree, gameinfo->to_move, I(move), J(move));
  if (resignation_declined)
    sgftreeAddComment(&sgftree, "GNU Go resignation was declined");
  sgffile_output(&sgftree);

  gameinfo->to_move = OTHER_COLOR(gameinfo->to_move);
  return 0;
}
Esempio n. 16
0
void
decide_semeai(int apos, int bpos)
{
  SGFTree tree;
  int resulta, resultb, move, result_certain;
  int color = board[apos];

  if (color == EMPTY || board[bpos] != OTHER_COLOR(color)) {
    gprintf("gnugo: --decide-semeai called on invalid data\n");
    return;
  }

  /* Prepare pattern matcher and reading code. */
  reset_engine();

  silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL);
  gprintf("finished examine_position\n");
  count_variations = 1;

  if (*outfilename)
    sgffile_begindump(&tree);

  gprintf("Analyzing semeai between %1m and %1m, %C moves first\n",
	  apos, bpos, board[apos]);
  owl_analyze_semeai(apos, bpos, &resulta, &resultb, &move, &result_certain);
  gprintf("Semeai defense of %1m: result %s %1m\n",
	  apos, result_to_string(resulta), move);
  gprintf("Semeai attack of %1m: result %s %1m\n",
	  bpos, result_to_string(resultb), move);
  gprintf("%d nodes%s\n\n", count_variations,
	  result_certain ? "" : ", uncertain result");
  
  gprintf("Analyzing semeai between %1m and %1m, %C moves first\n",
	  bpos, apos, board[bpos]);
  owl_analyze_semeai(bpos, apos, &resultb, &resulta, &move, &result_certain);
  gprintf("Semeai defense of %1m: result %s %1m\n",
	  bpos, result_to_string(resultb), move);
  gprintf("Semeai attack of %1m: result %s %1m\n",
	  apos, result_to_string(resulta), move);
  gprintf("%d nodes%s\n", count_variations,
	  result_certain ? "" : ", uncertain result");

  sgffile_enddump(outfilename);
  count_variations = 0;
}
void
decide_tactical_semeai(int apos, int bpos)
{
  SGFTree tree;
  int resulta, resultb, move;
  int color = board[apos];

  if (color == EMPTY || board[bpos] != OTHER_COLOR(color)) {
    gprintf("gnugo: --decide-semeai called on invalid data\n");
    return;
  }

  /* Prepare pattern matcher and reading code. */
  reset_engine();

  silent_examine_position(board[apos], EXAMINE_DRAGONS_WITHOUT_OWL);
  gprintf("finished examine_position\n");
  count_variations = 1;

  /* We want to see the reading performed, not just a result picked
   * from the cache. Thus we clear the cache here. */
  reading_cache_clear();
  
  if (*outfilename)
    sgffile_begindump(&tree);

  owl_analyze_semeai(apos, bpos, &resulta, &resultb, &move, 0);
  gprintf("After %s at %1m, %1m is %s, %1m is %s (%d nodes)\n",
	  color == BLACK ? "black" : "white",
	  move,
	  apos, safety_to_string(resulta),
  	  bpos, safety_to_string(resultb),
	  count_variations);
  owl_analyze_semeai(bpos, apos, &resultb, &resulta, &move, 0);
  gprintf("After %s at %1m, %1m is %s, %1m is %s (%d nodes)\n",
	  color == BLACK ? "white" : "black",
	  move,
	  apos, safety_to_string(resulta),
  	  bpos, safety_to_string(resultb),
	  count_variations);

  sgffile_enddump(outfilename);
  count_variations = 0;
}
Esempio n. 18
0
static int
legal_move(int i, int j, int color)
{
  int other = OTHER_COLOR(color);
  
  /* Pass is always legal. */
  if (pass_move(i, j))
    return 1;

  /* Already occupied. */
  if (get_board(i, j) != EMPTY)
    return 0;

  /* Illegal ko recapture. It is not illegal to fill the ko so we must
   * check the color of at least one neighbor.
   */
  if (i == ko_i && j == ko_j
      && ((on_board(i - 1, j) && get_board(i - 1, j) == other)
	  || (on_board(i + 1, j) && get_board(i + 1, j) == other)))
    return 0;

  return 1;
}
Esempio n. 19
0
bool GoBoard::is_virtual_eye(int point, int color)
{
    if (!is_surrounded(point, color)) return false;
    int nopponent = 0;
    int ai = I(point);
    int aj = J(point);
    bool at_edge = false;
    for (int i = 0; i < 4; i++) {
        int bi = ai + diag_i[i];
        int bj = aj + diag_j[i];
        if (!on_board(bi,bj))
        {
            at_edge = true;
            continue;
        }
        if( get_board(bi,bj) == OTHER_COLOR(color) ) {
            nopponent++;
        }
    }
    if(at_edge)
        ++nopponent;
    return nopponent < 2;
}
Esempio n. 20
0
/* This function adds the semeai related move reasons, using the information
 * stored in the dragon2 array.
 *
 * If the semeai had an uncertain result, and there is a owl move with
 * certain result doing the same, we don't trust the semeai move.
 */
void
semeai_move_reasons(int color)
{
  int other = OTHER_COLOR(color);
  int d;
  int liberties;
  int libs[MAXLIBS];
  int r;
  int resulta, resultb, semeai_move, s_result_certain;

  for (d = 0; d < number_of_dragons; d++)
    if (dragon2[d].semeais && DRAGON(d).status == CRITICAL) {
      if (DRAGON(d).color == color
          && dragon2[d].semeai_defense_point
	  && (dragon2[d].owl_defense_point == NO_MOVE
	      || dragon2[d].semeai_defense_certain >= 
	         dragon2[d].owl_defense_certain)) {
	/* My dragon can be defended. */
	add_semeai_move(dragon2[d].semeai_defense_point, dragon2[d].origin);
	DEBUG(DEBUG_SEMEAI, "Adding semeai defense move for %1m at %1m\n",
	      DRAGON(d).origin, dragon2[d].semeai_defense_point);
	if (neighbor_of_dragon(dragon2[d].semeai_defense_point,
			       dragon2[d].semeai_defense_target)
	    && !neighbor_of_dragon(dragon2[d].semeai_defense_point,
				   dragon2[d].origin)
	    && !is_self_atari(dragon2[d].semeai_defense_point, color)) {
	  
	  /* If this is a move to fill the non-common liberties of the
	   * target, and is not a ko or snap-back, then we try all
	   * non-common liberties of the target and add all winning
	   * moves to the move list.
	   */

          liberties = findlib(dragon2[d].semeai_defense_target, MAXLIBS, libs);

          for (r = 0; r < liberties; r++) {
            if (!neighbor_of_dragon(libs[r], dragon2[d].origin)
		&& !is_self_atari(libs[r], color)
		&& libs[r] != dragon2[d].semeai_defense_point) {
              owl_analyze_semeai_after_move(libs[r], color,
					    dragon2[d].semeai_defense_target,
					    dragon2[d].origin,
					    &resulta, &resultb, &semeai_move,
					    1, &s_result_certain, 0);
              if (resulta == 0 && resultb == 0) {
	        add_semeai_move(libs[r], dragon2[d].origin);
	        DEBUG(DEBUG_SEMEAI,
		      "Adding semeai defense move for %1m at %1m\n",
		      DRAGON(d).origin, libs[r]);
	      }
            }
	  }
	}
      }
      else if (DRAGON(d).color == other
	       && dragon2[d].semeai_attack_point
	       && (dragon2[d].owl_attack_point == NO_MOVE
		   || dragon2[d].owl_defense_point == NO_MOVE
		   || dragon2[d].semeai_attack_certain >= 
		      dragon2[d].owl_attack_certain)) {
	/* Your dragon can be attacked. */
	add_semeai_move(dragon2[d].semeai_attack_point, dragon2[d].origin);
	DEBUG(DEBUG_SEMEAI, "Adding semeai attack move for %1m at %1m\n",
	      DRAGON(d).origin, dragon2[d].semeai_attack_point);
	if (neighbor_of_dragon(dragon2[d].semeai_attack_point,
			       dragon2[d].origin)
	    && !neighbor_of_dragon(dragon2[d].semeai_attack_point,
				  dragon2[d].semeai_attack_target)
	    && !is_self_atari(dragon2[d].semeai_attack_point, color)) {

          liberties = findlib(dragon2[d].origin, MAXLIBS, libs);

          for (r = 0; r < liberties; r++) {
            if (!neighbor_of_dragon(libs[r], dragon2[d].semeai_attack_target)
		&& !is_self_atari(libs[r], color)
		&& libs[r] != dragon2[d].semeai_attack_point) {
              owl_analyze_semeai_after_move(libs[r], color, dragon2[d].origin,
					    dragon2[d].semeai_attack_target,
					    &resulta, &resultb, &semeai_move,
					    1, &s_result_certain, 0);
              if (resulta == 0 && resultb == 0) {
	        add_semeai_move(libs[r], dragon2[d].origin);
	        DEBUG(DEBUG_SEMEAI,
		      "Adding semeai attack move for %1m at %1m\n",
		      DRAGON(d).origin, libs[r]);
	      }
            }
	  }
	}
      }
    }
}
Esempio n. 21
0
/* Find moves turning supposed territory into seki. This is not
 * detected above since it either involves an ALIVE dragon adjacent to
 * a CRITICAL dragon, or an ALIVE dragon whose eyespace can be invaded
 * and turned into a seki.
 *
 * Currently we only search for tactically critical strings with
 * dragon status dead, which are neighbors of only one opponent
 * dragon, which is alive. Through semeai analysis we then determine
 * whether such a string can in fact live in seki. Relevant testcases
 * include gunnar:42 and gifu03:2.
 */
static void
find_moves_to_make_seki()
{
  int str;
  int defend_move;
  int resulta, resultb;
  
  for (str = BOARDMIN; str < BOARDMAX; str++) {
    if (IS_STONE(board[str]) && is_worm_origin(str, str)
	&& attack_and_defend(str, NULL, NULL, NULL, &defend_move)
	&& dragon[str].status == DEAD
	&& DRAGON2(str).hostile_neighbors == 1) {
      int k;
      int color = board[str];
      int opponent = NO_MOVE;
      int certain;
      struct eyevalue reduced_genus;

      for (k = 0; k < DRAGON2(str).neighbors; k++) {
	opponent = dragon2[DRAGON2(str).adjacent[k]].origin;
	if (board[opponent] != color)
	  break;
      }

      ASSERT1(opponent != NO_MOVE, opponent);

      if (dragon[opponent].status != ALIVE)
	continue;

      /* FIXME: These heuristics are used for optimization.  We don't
       *        want to call expensive semeai code if the opponent
       *        dragon has more than one eye elsewhere.  However, the
       *        heuristics might still need improvement.
       */
      compute_dragon_genus(opponent, &reduced_genus, str);
      if (DRAGON2(opponent).moyo_size > 10 || min_eyes(&reduced_genus) > 1)
	continue;

      owl_analyze_semeai_after_move(defend_move, color, opponent, str,
				    &resulta, &resultb, NULL, 1, &certain, 0);

      /* Do not trust uncertain results. In fact it should only take a
       * few nodes to determine the semeai result, if it is a proper
       * potential seki position.
       */
      if (resultb != WIN && certain) {
	int d = dragon[str].id;
	DEBUG(DEBUG_SEMEAI, "Move to make seki at %1m (%1m vs %1m)\n",
	      defend_move, str, opponent);
	dragon2[d].semeais++;
	update_status(str, CRITICAL, CRITICAL);
	dragon2[d].semeai_defense_point = defend_move;
	dragon2[d].semeai_defense_certain = certain;
	dragon2[d].semeai_defense_target = opponent;

	/* We need to determine a proper attack move (the one that
	 * prevents seki).  Currently we try the defense move first,
	 * and if it doesn't work -- all liberties of the string.
	 */
	owl_analyze_semeai_after_move(defend_move, OTHER_COLOR(color),
				      str, opponent, &resulta, NULL,
				      NULL, 1, NULL, 0);
	if (resulta != WIN)
	  dragon2[d].semeai_attack_point = defend_move;
	else {
	  int k;
	  int libs[MAXLIBS];
	  int liberties = findlib(str, MAXLIBS, libs);

	  for (k = 0; k < liberties; k++) {
	    owl_analyze_semeai_after_move(libs[k], OTHER_COLOR(color),
					  str, opponent, &resulta, NULL,
					  NULL, 1, NULL, 0);
	    if (resulta != WIN) {
	      dragon2[d].semeai_attack_point = libs[k];
	      break;
	    }
	  }

	  /* FIXME: What should we do if none of the tried attacks worked? */
	  if (k == liberties)
	    dragon2[d].semeai_attack_point = defend_move;
	}

	DEBUG(DEBUG_SEMEAI, "Move to prevent seki at %1m (%1m vs %1m)\n",
	      dragon2[d].semeai_attack_point, opponent, str);

	dragon2[d].semeai_attack_certain = certain;
	dragon2[d].semeai_attack_target = opponent;
      }
    }
  }
}
void
play_solo(Gameinfo *gameinfo, int moves)
{
  SGFTree sgftree;
  int passes = 0; /* num. consecutive passes */
  int move_val;
  double t1, t2;
  int save_moves = moves;
  int boardsize = gnugo_get_boardsize();

  struct stats_data totalstats;
  int total_owl_count = 0;

  /* It tends not to be very imaginative in the opening,
   * so we scatter a few stones randomly to start with.
   * We add two random numbers to reduce the probability
   * of playing stones near the edge.
   */
  
  int n = 6 + 2*gg_rand()%5;
  int i, j;

  gnugo_set_komi(5.5);

  sgftree_clear(&sgftree);
  sgftreeCreateHeaderNode(&sgftree, gnugo_get_boardsize(), gnugo_get_komi());
  sgf_write_header(sgftree.root, 1, random_seed, 5.5, level, chinese_rules);
 
  /* Generate some random moves. */
  if (boardsize > 6) {
    do {
      do {
	i = (gg_rand() % 4) + (gg_rand() % (boardsize - 4));
	j = (gg_rand() % 4) + (gg_rand() % (boardsize - 4));
      } while (!gnugo_is_legal(i, j, gameinfo->to_move));

      gnugo_play_move(i, j, gameinfo->to_move);
      sgftreeAddPlay(&sgftree, gameinfo->to_move, i, j);
      sgftreeAddComment(&sgftree, "random move");
      gameinfo->to_move = OTHER_COLOR(gameinfo->to_move);
    } while (--n > 0);
  }
  
  t1 = gg_cputime();
  memset(&totalstats, '\0', sizeof(totalstats));
  while (passes < 2 && --moves >= 0) {
    reset_owl_node_counter();
    move_val = gnugo_genmove(&i, &j, gameinfo->to_move);

    gnugo_play_move(i, j, gameinfo->to_move);
    sgffile_add_debuginfo(sgftree.lastnode, move_val);
    sgftreeAddPlay(&sgftree, gameinfo->to_move, i, j);
    sgffile_output(&sgftree);
    gameinfo->to_move = OTHER_COLOR(gameinfo->to_move);

    if (move_val < 0) {
      ++passes;
      printf("%s(%d): Pass\n", gameinfo->to_move == BLACK ? "Black" : "White",
	     movenum);
    }
    else {
      passes = 0;
      gprintf("%s(%d): %m\n", gameinfo->to_move == BLACK ? "Black" : "White",
	      movenum, i, j);
    }

    totalstats.nodes               += stats.nodes;
    totalstats.position_entered    += stats.position_entered;
    totalstats.position_hits       += stats.position_hits;
    totalstats.read_result_entered += stats.read_result_entered;
    totalstats.hash_collisions     += stats.hash_collisions;
    total_owl_count                += get_owl_node_counter();
  }
  t2 = gg_cputime();
  
  /* Two passes and it's over. (EMPTY == BOTH) */
  gnugo_who_wins(EMPTY, stdout);

  score = gnugo_estimate_score(&lower_bound, &upper_bound);
  sgfWriteResult(sgftree.root, score, 1);
  sgffile_output(&sgftree);

#if 0
  if (t2 == t1)
    printf("%.3f moves played\n", (double) (save_moves-moves));
  else
    printf("%.3f moves/sec\n", (save_moves-moves)/(t2-t1));
#else
  printf("%10d moves played in %0.3f seconds\n", save_moves-moves, t2-t1);
  if (save_moves != moves)
    printf("%10.3f seconds/move\n", (t2-t1)/(save_moves-moves));
  printf("%10d nodes\n", totalstats.nodes);
  printf("%10d positions entered\n", totalstats.position_entered);
  printf("%10d position hits\n", totalstats.position_hits);
  printf("%10d read results entered\n", totalstats.read_result_entered);
  printf("%10d hash collisions\n", totalstats.hash_collisions);
  printf("%10d owl nodes\n", total_owl_count);
#endif
}
void 
load_and_score_sgf_file(SGFTree *tree, Gameinfo *gameinfo, 
			const char *scoringmode)
{
  int i, j, move_val;
  float result;
  char *tempc = NULL;
  char dummy;
  char text[250];
  char winner;
  int next;
  int pass = 0;
  SGFTree score_tree;
  
  sgftree_clear(&score_tree);
  sgftreeCreateHeaderNode(&score_tree, board_size, komi);
  sgffile_printboard(&score_tree);
  
  next = gameinfo->to_move;
  doing_scoring = 1;
  reset_engine();
  
  if (!strcmp(scoringmode, "finish") || !strcmp(scoringmode, "aftermath")) {
    do {
      move_val = genmove_conservative(&i, &j, next);
      if (move_val >= 0) {
	pass = 0;
	gprintf("%d %s move %m\n", movenum,
		next == WHITE ? "white (O)" : "black (X)", i, j);
      }
      else {
	++pass;
	gprintf("%d %s move : PASS!\n", movenum, 
		next == WHITE ? "white (O)" : "black (X)");
      }
      play_move(POS(i, j), next);
      sgffile_add_debuginfo(score_tree.lastnode, move_val);
      sgftreeAddPlay(&score_tree, next, i, j);
      sgffile_output(&score_tree);
      next = OTHER_COLOR(next);
    } while (movenum <= 10000 && pass < 2);

    if (pass >= 2) {
      /* Calculate the score */
      if (!strcmp(scoringmode, "aftermath"))
	score = aftermath_compute_score(next, komi, &score_tree);
      else
	score = gnugo_estimate_score(&lower_bound, &upper_bound);

      if (score < 0.0) {
	sprintf(text, "Black wins by %1.1f points\n", -score);
	winner = 'B';
      }
      else if (score > 0.0) {
	sprintf(text, "White wins by %1.1f points\n", score);
	winner = 'W';
      }
      else {
	sprintf(text, "Jigo\n");
	winner = '0';
      }
      fputs(text, stdout);
      sgftreeAddComment(&score_tree, text);
      if (sgfGetCharProperty(tree->root, "RE", &tempc)) {
	if (sscanf(tempc, "%1c%f", &dummy, &result) == 2) {
	  fprintf(stdout, "Result from file: %1.1f\n", result);
	  fputs("GNU Go result and result from file are ", stdout);
	  if (result == fabs(score) && winner == dummy)
	    fputs("identical\n", stdout);
	  else
	    fputs("different\n", stdout);
	      
	}
	else {
	  if (tempc[2] == 'R') {
	    fprintf(stdout, "Result from file: Resign\n");
	    fputs("GNU Go result and result from file are ", stdout);
	    if (tempc[0] == winner)
	      fputs("identical\n", stdout);
	    else
	      fputs("different\n", stdout);
	  }
	}
      }
      sgfWriteResult(score_tree.root, score, 1);
      sgffile_output(&score_tree);
    }
  }
  doing_scoring = 0;

  if (strcmp(scoringmode, "aftermath")) {
    /* Before we call estimate_score() we must make sure that the dragon
     * status is computed. Therefore the call to examine_position().
     */
    examine_position(next, EXAMINE_ALL);
    score = estimate_score(NULL, NULL);

    fprintf(stdout, "\n%s seems to win by %1.1f points\n",
      score < 0 ? "B" : "W",
      score < 0 ? -score : score);
  }
}
Esempio n. 24
0
/* --------------------------------------------------------------*/
void
play_gmp(Gameinfo *gameinfo, int simplified)
{
  SGFTree sgftree;

  Gmp *ge;
  GmpResult message;
  const char *error;
  
  int i, j;
  int passes = 0; /* two passes and its over */
  int to_move;  /* who's turn is next ? */

  int mycolor = -1;  /* who has which color */
  int yourcolor;

  if (gameinfo->computer_player == WHITE)
    mycolor = 1;
  else if (gameinfo->computer_player == BLACK)
    mycolor = 0;

  sgftree_clear(&sgftree);
  sgftreeCreateHeaderNode(&sgftree, board_size, komi, gameinfo->handicap);

  ge = gmp_create(0, 1);
  TRACE("board size=%d\n", board_size);

  /* 
   * The specification of the go modem protocol doesn't even discuss
   * komi. So we have to guess the komi. If the komi is set on the
   * command line, keep it. Otherwise, its value will be 0.0 and we
   * use 5.5 in an even game, 0.5 otherwise.
   */
  if (komi == 0.0) {
    if (gameinfo->handicap == 0)
      komi = 5.5;
    else
      komi = 0.5;
  }

  if (!simplified) {
    /* Leave all the -1's so the client can negotiate the game parameters. */
    if (chinese_rules)
      gmp_startGame(ge, -1, -1, 5.5, -1, mycolor, 0);
    else
      gmp_startGame(ge, -1, -1, 5.5, 0, mycolor, 0);
  }
  else {
    gmp_startGame(ge, board_size, gameinfo->handicap,
		  komi, chinese_rules, mycolor, 1);
  }

  do {
    message = gmp_check(ge, 1, NULL, NULL, &error);
  } while (message == gmp_nothing || message == gmp_reset);
  
  if (message == gmp_err) {
    fprintf(stderr, "gnugo-gmp: Error \"%s\" occurred.\n", error);
    exit(EXIT_FAILURE);
  }
  else if (message != gmp_newGame) {
    fprintf(stderr, "gnugo-gmp: Expecting a newGame, got %s\n",
	    gmp_resultString(message));
    exit(EXIT_FAILURE);
  }

  gameinfo->handicap = gmp_handicap(ge);
  if (!check_boardsize(gmp_size(ge), stderr))
    exit(EXIT_FAILURE);
  
  gnugo_clear_board(gmp_size(ge));

  /* Let's pretend GMP knows about komi in case something will ever change. */
  komi = gmp_komi(ge);

#if ORACLE
  if (metamachine && oracle_exists)
    oracle_clear_board(board_size);
#endif

  sgfOverwritePropertyInt(sgftree.root, "SZ", board_size);

  TRACE("size=%d, handicap=%d, komi=%f\n", board_size,
	gameinfo->handicap, komi);

  if (gameinfo->handicap)
    to_move = WHITE;
  else
    to_move = BLACK;

  if (gmp_iAmWhite(ge)) {
    mycolor = WHITE;     /* computer white */
    yourcolor = BLACK;   /* human black */
  }
  else {
    mycolor = BLACK;
    yourcolor = WHITE;
  }

  gameinfo->computer_player = mycolor;
  sgf_write_header(sgftree.root, 1, get_random_seed(), komi,
		   gameinfo->handicap, get_level(), chinese_rules);
  gameinfo->handicap = gnugo_sethand(gameinfo->handicap, sgftree.root);
  sgfOverwritePropertyInt(sgftree.root, "HA", gameinfo->handicap);

  /* main GMP loop */
  while (passes < 2) {

    if (to_move == yourcolor) {
      int move;
      /* Get opponent's move from gmp client. */
      message = gmp_check(ge, 1, &j, &i, &error);

      if (message == gmp_err) {
	fprintf(stderr, "GNU Go: Sorry, error from gmp client\n");
        sgftreeAddComment(&sgftree, "got error from gmp client");
        sgffile_output(&sgftree);
	return;
      }

      if (message == gmp_undo) {
	int k;
	assert(j > 0);
	
	for (k = 0; k < j; k++) {
	  if (!undo_move(1)) {
	    fprintf(stderr, "GNU Go: play_gmp UNDO: can't undo %d moves\n",
		    j - k);
	    break;
	  }
	  sgftreeAddComment(&sgftree, "undone");
	  sgftreeBack(&sgftree);
	  to_move = OTHER_COLOR(to_move);
	}
	continue;
      }

      if (message == gmp_pass) {
	passes++;
	move = PASS_MOVE;
      }
      else {
	passes = 0;
	move = POS(i, j);
      }

      TRACE("\nyour move: %1m\n\n", move);
      sgftreeAddPlay(&sgftree, to_move, I(move), J(move));
      gnugo_play_move(move, yourcolor);
      sgffile_output(&sgftree);

    }
    else {
      /* Generate my next move. */
      float move_value;
      int move;
      if (autolevel_on)
	adjust_level_offset(mycolor);
      move = genmove(mycolor, &move_value, NULL);
      gnugo_play_move(move, mycolor);
      sgffile_add_debuginfo(sgftree.lastnode, move_value);
      
      if (is_pass(move)) {
	/* pass */
        sgftreeAddPlay(&sgftree, to_move, -1, -1);
	gmp_sendPass(ge);
	++passes;
      }
      else {
	/* not pass */
        sgftreeAddPlay(&sgftree, to_move, I(move), J(move));
	gmp_sendMove(ge, J(move), I(move));
	passes = 0;
	TRACE("\nmy move: %1m\n\n", move);
      }
      sgffile_add_debuginfo(sgftree.lastnode, 0.0);
      sgffile_output(&sgftree);
    }
    
    to_move = OTHER_COLOR(to_move);
  }
  
  /* two passes: game over */
  gmp_sendPass(ge);   
  
  if (!quiet)
    fprintf(stderr, "Game over - waiting for client to shut us down\n");
  who_wins(mycolor, stderr);

  if (showtime) {
    gprintf("\nSLOWEST MOVE: %d at %1m ", slowest_movenum, slowest_move);
    fprintf(stderr, "(%.2f seconds)\n", slowest_time);
    fprintf(stderr, "\nAVERAGE TIME: %.2f seconds per move\n",
	    total_time / movenum);
    fprintf(stderr, "\nTOTAL TIME: %.2f seconds\n",
	    total_time);
  }
  
  
  /* play_gmp() does not return to main(), therefore the score
   * writing code is here.
   */
  { 
    float score = gnugo_estimate_score(NULL, NULL);
    sgfWriteResult(sgftree.root, score, 1);
  }
  sgffile_output(&sgftree);

  if (!simplified) {
    /* We hang around here until cgoban asks us to go, since
     * sometimes cgoban crashes if we exit first.
     *
     * FIXME: Check if this is still needed.  I made it dependand on
     *	      `simplifed' just to avoid changes in GMP mode.
     */
    while (1) {
      message = gmp_check(ge, 1, &j, &i, &error);
      if (!quiet)
	fprintf(stderr, "Message %d from gmp\n", message);
      if (message == gmp_err)
	break;
    }
  }

#if ORACLE
  if (metamachine && oracle_exists)
    dismiss_oracle();
#endif

  if (!quiet)
    fprintf(stderr, "gnugo going down\n");
}
Esempio n. 25
0
/* Play at (i, j) for color. No legality check is done here. We need
 * to properly update the board array, the next_stone array, and the
 * ko point.
 */
static void
play_move(int i, int j, int color)
{
  int pos = POS(i, j);
  int captured_stones = 0;
  int k;

  /* Reset the ko point. */
  ko_i = -1;
  ko_j = -1;

  /* Nothing more happens if the move was a pass. */
  if (pass_move(i, j))
    return;

  /* If the move is a suicide we only need to remove the adjacent
   * friendly stones.
   */
  if (suicide(i, j, color)) {
    for (k = 0; k < 4; k++) {
      int ai = i + deltai[k];
      int aj = j + deltaj[k];
      if (on_board(ai, aj)
	  && get_board(ai, aj) == color)
	remove_string(ai, aj);
    }
    return;
  }

  /* Not suicide. Remove captured opponent strings. */
  for (k = 0; k < 4; k++) {
    int ai = i + deltai[k];
    int aj = j + deltaj[k];
    if (on_board(ai, aj)
	&& get_board(ai, aj) == OTHER_COLOR(color)
	&& !has_additional_liberty(ai, aj, i, j))
      captured_stones += remove_string(ai, aj);
  }

  /* Put down the new stone. Initially build a single stone string by
   * setting next_stone[pos] pointing to itself.
   */
  board[pos] = color;
  next_stone[pos] = pos;

  /* If we have friendly neighbor strings we need to link the strings
   * together.
   */
  for (k = 0; k < 4; k++) {
    int ai = i + deltai[k];
    int aj = j + deltaj[k];
    int pos2 = POS(ai, aj);
    /* Make sure that the stones are not already linked together. This
     * may happen if the same string neighbors the new stone in more
     * than one direction.
     */
    if (on_board(ai, aj) && board[pos2] == color && !same_string(pos, pos2)) {
      /* The strings are linked together simply by swapping the the
       * next_stone pointers.
       */
      int tmp = next_stone[pos2];
      next_stone[pos2] = next_stone[pos];
      next_stone[pos] = tmp;
    }
  }

  /* If we have captured exactly one stone and the new string is a
   * single stone it may have been a ko capture.
   */
  if (captured_stones == 1 && next_stone[pos] == pos) {
    int ai, aj;
    /* Check whether the new string has exactly one liberty. If so it
     * would be an illegal ko capture to play there immediately. We
     * know that there must be a liberty immediately adjacent to the
     * new stone since we captured one stone.
     */
    for (k = 0; k < 4; k++) {
      ai = i + deltai[k];
      aj = j + deltaj[k];
      if (on_board(ai, aj) && get_board(ai, aj) == EMPTY)
	break;
    }
    
    if (!has_additional_liberty(i, j, ai, aj)) {
      ko_i = ai;
      ko_j = aj;
    }
  }
}
Esempio n. 26
0
/* This function adds the semeai related move reasons, using the information
 * stored in the dragon2 array.
 *
 * If the semeai had an uncertain result, and there is a owl move with
 * certain result doing the same, we don't trust the semeai move.
 */
void
semeai_move_reasons(int color)
{
  int other = OTHER_COLOR(color);
  int d;
  int liberties;
  int libs[MAXLIBS];
  int r;

  for (d = 0; d < number_of_dragons; d++)
    if (dragon2[d].semeais && DRAGON(d).status == CRITICAL) {
      if (DRAGON(d).color == color
          && dragon2[d].semeai_defense_point
	  && (dragon2[d].owl_defense_point == NO_MOVE
	      || dragon2[d].semeai_defense_certain >= 
	         dragon2[d].owl_defense_certain)) {
	/* My dragon can be defended. */
	add_semeai_move(dragon2[d].semeai_defense_point, dragon2[d].origin);
	DEBUG(DEBUG_SEMEAI, "Adding semeai defense move for %1m at %1m\n",
	      DRAGON(d).origin, dragon2[d].semeai_defense_point);
	if (neighbor_of_dragon(dragon2[d].semeai_defense_point,
			       dragon2[d].semeai_defense_target)
	    && !neighbor_of_dragon(dragon2[d].semeai_defense_point,
				   dragon2[d].origin)
	    && !is_self_atari(dragon2[d].semeai_defense_point, color)) {
	  
	  /* If this is a move to fill the non-common liberties of the
	   * target, and is not a ko or snap-back, then we mark all
	   * non-common liberties of the target as potential semeai moves.
	   */

          liberties = findlib(dragon2[d].semeai_defense_target, MAXLIBS, libs);

          for (r = 0; r < liberties; r++) {
            if (!neighbor_of_dragon(libs[r], dragon2[d].origin)
		&& !is_self_atari(libs[r], color)
		&& libs[r] != dragon2[d].semeai_defense_point)
	      add_potential_semeai_defense(libs[r], dragon2[d].origin,
					   dragon2[d].semeai_defense_target);
	  }
	}
      }
      else if (DRAGON(d).color == other
	       && dragon2[d].semeai_attack_point
	       && (dragon2[d].owl_attack_point == NO_MOVE
		   || dragon2[d].owl_defense_point == NO_MOVE
		   || dragon2[d].semeai_attack_certain >= 
		      dragon2[d].owl_attack_certain)) {
	/* Your dragon can be attacked. */
	add_semeai_move(dragon2[d].semeai_attack_point, dragon2[d].origin);
	DEBUG(DEBUG_SEMEAI, "Adding semeai attack move for %1m at %1m\n",
	      DRAGON(d).origin, dragon2[d].semeai_attack_point);
	if (neighbor_of_dragon(dragon2[d].semeai_attack_point,
			       dragon2[d].origin)
	    && !neighbor_of_dragon(dragon2[d].semeai_attack_point,
				  dragon2[d].semeai_attack_target)
	    && !is_self_atari(dragon2[d].semeai_attack_point, color)) {

          liberties = findlib(dragon2[d].origin, MAXLIBS, libs);

          for (r = 0; r < liberties; r++) {
            if (!neighbor_of_dragon(libs[r], dragon2[d].semeai_attack_target)
		&& !is_self_atari(libs[r], color)
		&& libs[r] != dragon2[d].semeai_attack_point)
	      add_potential_semeai_attack(libs[r], dragon2[d].origin,
					  dragon2[d].semeai_attack_target);
	  }
	}
      }
    }
}
Esempio n. 27
0
void
semeai()
{
  int semeai_results_first[MAX_DRAGONS][MAX_DRAGONS];
  int semeai_results_second[MAX_DRAGONS][MAX_DRAGONS];
  int semeai_move[MAX_DRAGONS][MAX_DRAGONS];
  signed char semeai_certain[MAX_DRAGONS][MAX_DRAGONS];
  int d1, d2;
  int k;
  int num_dragons = number_of_dragons;

  if (num_dragons > MAX_DRAGONS) {
    TRACE("Too many dragons!!! Semeai analysis disabled.");
    return;
  }

  for (d1 = 0; d1 < num_dragons; d1++)
    for (d2 = 0; d2 < num_dragons; d2++) {
      semeai_results_first[d1][d2] = -1;
      semeai_results_second[d1][d2] = -1;
    }

  for (d1 = 0; d1 < num_dragons; d1++)
    for (k = 0; k < dragon2[d1].neighbors; k++) {
      int apos = DRAGON(d1).origin;
      int bpos = DRAGON(dragon2[d1].adjacent[k]).origin;
      int result_certain;
      
      d2 = dragon[bpos].id;

      /* Look for semeais */
      
      if (dragon[apos].color == dragon[bpos].color
	  || (dragon[apos].status != DEAD
	      && dragon[apos].status != CRITICAL)
	  || (dragon[bpos].status != DEAD
	      && dragon[bpos].status != CRITICAL))
	continue;

      
      /* Ignore inessential worms or dragons */
      
      if (worm[apos].inessential 
	  || DRAGON2(apos).safety == INESSENTIAL
	  || worm[bpos].inessential 
	  || DRAGON2(bpos).safety == INESSENTIAL)
	continue;

      /* Sometimes the dragons are considered neighbors but are too
       * distant to constitute a proper semeai, e.g. in nngs4:650, P2
       * vs. R3. Then the result of semeai reading may be meaningless
       * and can confuse the analysis. In order to avoid this we check
       * that the dragons either are directly adjacent or at least
       * have one common liberty.
       */
      if (!close_enough_for_proper_semeai(apos, bpos))
	continue;

      /* The array semeai_results_first[d1][d2] will contain the status
       * of d1 after the d1 d2 semeai, giving d1 the first move.
       * The array semeai_results_second[d1][d2] will contain the status
       * of d1 after the d1 d2 semeai, giving d2 the first move.
       */
      
      DEBUG(DEBUG_SEMEAI, "Considering semeai between %1m and %1m\n",
	    apos, bpos);
      owl_analyze_semeai(apos, bpos,
			 &(semeai_results_first[d1][d2]), 
			 &(semeai_results_second[d1][d2]),
			 &(semeai_move[d1][d2]), 1, &result_certain);
      DEBUG(DEBUG_SEMEAI, "results if %s moves first: %s %s, %1m%s\n",
	    board[apos] == BLACK ? "black" : "white",
	    result_to_string(semeai_results_first[d1][d2]),
	    result_to_string(semeai_results_second[d1][d2]),
	    semeai_move[d1][d2], result_certain ? "" : " (uncertain)");
      semeai_certain[d1][d2] = result_certain;
    }
  
  /* Look for dragons which lose all their semeais outright. The
   * winners in those semeais are considered safe and further semeais
   * they are involved in are disregarded. See semeai:81-86 and
   * nicklas5:1211 for examples of where this is useful.
   *
   * Note: To handle multiple simultaneous semeais properly we would
   * have to make simultaneous semeai reading. Lacking that we can
   * only get rough guesses of the correct status of the involved
   * dragons. This code is not guaranteed to be correct in all
   * situations but should usually be an improvement.
   */
  for (d1 = 0; d1 < num_dragons; d1++) {
    int involved_in_semeai = 0;
    int all_lost = 1;
    for (d2 = 0; d2 < num_dragons; d2++) {
      if (semeai_results_first[d1][d2] != -1) {
	involved_in_semeai = 1;
	if (semeai_results_first[d1][d2] != 0) {
	  all_lost = 0;
	  break;
	}
      }
    }
    
    if (involved_in_semeai && all_lost) {
      /* Leave the status changes to the main loop below. Here we just
       * remove the presumably irrelevant semeai results.
       */
      for (d2 = 0; d2 < num_dragons; d2++) {
	if (semeai_results_first[d1][d2] == 0) {
	  int d3;
	  for (d3 = 0; d3 < num_dragons; d3++) {
	    if (semeai_results_second[d3][d2] > 0) {
	      semeai_results_first[d3][d2] = -1;
	      semeai_results_second[d3][d2] = -1;
	      semeai_results_first[d2][d3] = -1;
	      semeai_results_second[d2][d3] = -1;
	    }
	  }
	}
      }
    }
  }

  for (d1 = 0; d1 < num_dragons; d1++) {
    int semeais_found = 0;
    int best_defense = 0;
    int best_attack = 0;
    int defense_move = PASS_MOVE;
    int attack_move = PASS_MOVE;
    int defense_certain = -1;
    int attack_certain = -1;
    int semeai_attack_target = NO_MOVE;
    int semeai_defense_target = NO_MOVE;
    
    for (d2 = 0; d2 < num_dragons; d2++) {
      if (semeai_results_first[d1][d2] == -1)
	continue;
      gg_assert(semeai_results_second[d1][d2] != -1);
      semeais_found++;

      if (best_defense < semeai_results_first[d1][d2]
	  || (best_defense == semeai_results_first[d1][d2]
	      && defense_certain < semeai_certain[d1][d2])) {
	best_defense = semeai_results_first[d1][d2];
	defense_move = semeai_move[d1][d2];
	defense_certain = semeai_certain[d1][d2];
	gg_assert(board[dragon2[d2].origin] == OTHER_COLOR(board[dragon2[d1].origin]));
	semeai_defense_target = dragon2[d2].origin;
      }
      if (best_attack < semeai_results_second[d2][d1]
	  || (best_attack == semeai_results_second[d2][d1]
	      && attack_certain < semeai_certain[d2][d1])) {
	best_attack = semeai_results_second[d2][d1];
	attack_move = semeai_move[d2][d1];
	attack_certain = semeai_certain[d2][d1];
	semeai_attack_target = dragon2[d2].origin;
      }
    }
    
    if (semeais_found) {
      dragon2[d1].semeais = semeais_found;
      if (best_defense != 0 && best_attack != 0)
	update_status(DRAGON(d1).origin, CRITICAL, CRITICAL);
      else if (best_attack == 0 && attack_certain)
	update_status(DRAGON(d1).origin, ALIVE, ALIVE);
      dragon2[d1].semeai_defense_code = best_defense;
      dragon2[d1].semeai_defense_point = defense_move;
      dragon2[d1].semeai_defense_certain = defense_certain;
      ASSERT1(board[semeai_defense_target]
	      == OTHER_COLOR(board[dragon2[d1].origin]),
	      dragon2[d1].origin);
      dragon2[d1].semeai_defense_target = semeai_defense_target;
      dragon2[d1].semeai_attack_code = best_attack;
      dragon2[d1].semeai_attack_point = attack_move;
      dragon2[d1].semeai_attack_certain = attack_certain;
      dragon2[d1].semeai_attack_target = semeai_attack_target;
    }
  }
  find_moves_to_make_seki();
}
Esempio n. 28
0
/* Find moves turning supposed territory into seki. This is not
 * detected above since it either involves an ALIVE dragon adjacent to
 * a CRITICAL dragon, or an ALIVE dragon whose eyespace can be invaded
 * and turned into a seki.
 *
 * Currently we only search for tactically critical strings with
 * dragon status dead, which are neighbors of only one opponent
 * dragon, which is alive. Through semeai analysis we then determine
 * whether such a string can in fact live in seki. Relevant testcases
 * include gunnar:42 and gifu03:2.
 */
static void
find_moves_to_make_seki()
{
  int str;
  int defend_move;
  int resulta, resultb;
  
  for (str = BOARDMIN; str < BOARDMAX; str++) {
    if (IS_STONE(board[str]) && is_worm_origin(str, str)
	&& attack_and_defend(str, NULL, NULL, NULL, &defend_move)
	&& dragon[str].status == DEAD
	&& DRAGON2(str).hostile_neighbors == 1) {
      int k;
      int color = board[str];
      int opponent = NO_MOVE;
      int certain;
      struct eyevalue reduced_genus;

      for (k = 0; k < DRAGON2(str).neighbors; k++) {
	opponent = dragon2[DRAGON2(str).adjacent[k]].origin;
	if (board[opponent] != color)
	  break;
      }

      ASSERT1(opponent != NO_MOVE, opponent);

      if (dragon[opponent].status != ALIVE)
	continue;

      /* FIXME: These heuristics are used for optimization.  We don't
       *        want to call expensive semeai code if the opponent
       *        dragon has more than one eye elsewhere.  However, the
       *        heuristics might still need improvement.
       */
      compute_dragon_genus(opponent, &reduced_genus, str);
      
      if (min_eyes(&reduced_genus) > 1
	  || DRAGON2(opponent).moyo_size > 10
	  || DRAGON2(opponent).moyo_territorial_value > 2.999
	  || DRAGON2(opponent).escape_route > 0
	  || DRAGON2(str).escape_route > 0)
	continue;

      owl_analyze_semeai_after_move(defend_move, color, opponent, str,
				    &resulta, &resultb, NULL, 1, &certain, 0);

      if (resultb == WIN) {
	owl_analyze_semeai(str, opponent, &resultb, &resulta,
			   &defend_move, 1, &certain);
	resulta = REVERSE_RESULT(resulta);
	resultb = REVERSE_RESULT(resultb);
      }
      
      /* Do not trust uncertain results. In fact it should only take a
       * few nodes to determine the semeai result, if it is a proper
       * potential seki position.
       */
      if (resultb != WIN && certain) {
	int d = dragon[str].id;
	DEBUG(DEBUG_SEMEAI, "Move to make seki at %1m (%1m vs %1m)\n",
	      defend_move, str, opponent);
	dragon2[d].semeais++;
	update_status(str, CRITICAL, CRITICAL);
	dragon2[d].semeai_defense_code = REVERSE_RESULT(resultb);
	dragon2[d].semeai_defense_point = defend_move;
	dragon2[d].semeai_defense_certain = certain;
	gg_assert(board[opponent] == OTHER_COLOR(board[dragon2[d].origin]));
	dragon2[d].semeai_defense_target = opponent;

	/* We need to determine a proper attack move (the one that
	 * prevents seki).  Currently we try the defense move first,
	 * and if it doesn't work -- all liberties of the string.
	 */
	owl_analyze_semeai_after_move(defend_move, OTHER_COLOR(color),
				      str, opponent, &resulta, NULL,
				      NULL, 1, NULL, 0);
	if (resulta != WIN) {
	  dragon2[d].semeai_attack_code = REVERSE_RESULT(resulta);
	  dragon2[d].semeai_attack_point = defend_move;
	}
	else {
	  int k;
	  int libs[MAXLIBS];
	  int liberties = findlib(str, MAXLIBS, libs);

	  for (k = 0; k < liberties; k++) {
	    owl_analyze_semeai_after_move(libs[k], OTHER_COLOR(color),
					  str, opponent, &resulta, NULL,
					  NULL, 1, NULL, 0);
	    if (resulta != WIN) {
	      dragon2[d].semeai_attack_code = REVERSE_RESULT(resulta);
	      dragon2[d].semeai_attack_point = libs[k];
	      break;
	    }
	  }

	  if (k == liberties) {
	    DEBUG(DEBUG_SEMEAI,
		  "No move to attack in semeai (%1m vs %1m), seki assumed.\n",
		  str, opponent);
	    dragon2[d].semeai_attack_code = 0;
	    dragon2[d].semeai_attack_point = NO_MOVE;
	    update_status(str, ALIVE, ALIVE_IN_SEKI);
	  }
	}

	DEBUG(DEBUG_SEMEAI, "Move to prevent seki at %1m (%1m vs %1m)\n",
	      dragon2[d].semeai_attack_point, opponent, str);

	dragon2[d].semeai_attack_certain = certain;
	dragon2[d].semeai_attack_target = opponent;
      }
    }
  }

  /* Now look for dead strings inside a single eyespace of a living dragon.
   *
   * FIXME: Clearly this loop should share most of its code with the
   *        one above. It would also be good to reimplement so that
   *        moves invading a previously empty single eyespace to make
   *        seki can be found.
   */
  for (str = BOARDMIN; str < BOARDMAX; str++) {
    if (IS_STONE(board[str]) && is_worm_origin(str, str)
	&& !find_defense(str, NULL)
	&& dragon[str].status == DEAD
	&& DRAGON2(str).hostile_neighbors == 1) {
      int k;
      int color = board[str];
      int opponent = NO_MOVE;
      int certain;
      struct eyevalue reduced_genus;

      for (k = 0; k < DRAGON2(str).neighbors; k++) {
	opponent = dragon2[DRAGON2(str).adjacent[k]].origin;
	if (board[opponent] != color)
	  break;
      }

      ASSERT1(opponent != NO_MOVE, opponent);

      if (dragon[opponent].status != ALIVE)
	continue;

      /* FIXME: These heuristics are used for optimization.  We don't
       *        want to call expensive semeai code if the opponent
       *        dragon has more than one eye elsewhere.  However, the
       *        heuristics might still need improvement.
       */
      compute_dragon_genus(opponent, &reduced_genus, str);
      if (DRAGON2(opponent).moyo_size > 10 || min_eyes(&reduced_genus) > 1)
	continue;

      owl_analyze_semeai(str, opponent, &resulta, &resultb,
			 &defend_move, 1, &certain);

      /* Do not trust uncertain results. In fact it should only take a
       * few nodes to determine the semeai result, if it is a proper
       * potential seki position.
       */
      if (resulta != 0 && certain) {
	int d = dragon[str].id;
	DEBUG(DEBUG_SEMEAI, "Move to make seki at %1m (%1m vs %1m)\n",
	      defend_move, str, opponent);
	dragon2[d].semeais++;
	update_status(str, CRITICAL, CRITICAL);
	dragon2[d].semeai_defense_code = resulta;
	dragon2[d].semeai_defense_point = defend_move;
	dragon2[d].semeai_defense_certain = certain;
	gg_assert(board[opponent] == OTHER_COLOR(board[dragon2[d].origin]));
	dragon2[d].semeai_defense_target = opponent;

	/* We need to determine a proper attack move (the one that
	 * prevents seki).  Currently we try the defense move first,
	 * and if it doesn't work -- all liberties of the string.
	 */
	owl_analyze_semeai_after_move(defend_move, OTHER_COLOR(color),
				      str, opponent, &resulta, NULL,
				      NULL, 1, NULL, 0);
	if (resulta != WIN) {
	  dragon2[d].semeai_attack_code = REVERSE_RESULT(resulta);
	  dragon2[d].semeai_attack_point = defend_move;
	}
	else {
	  int k;
	  int libs[MAXLIBS];
	  int liberties = findlib(str, MAXLIBS, libs);

	  for (k = 0; k < liberties; k++) {
	    owl_analyze_semeai_after_move(libs[k], OTHER_COLOR(color),
					  str, opponent, &resulta, NULL,
					  NULL, 1, NULL, 0);
	    if (resulta != WIN) {
	      dragon2[d].semeai_attack_code = REVERSE_RESULT(resulta);
	      dragon2[d].semeai_attack_point = libs[k];
	      break;
	    }
	  }

	  if (k == liberties) {
	    DEBUG(DEBUG_SEMEAI,
		  "No move to attack in semeai (%1m vs %1m), seki assumed.\n",
		  str, opponent);
	    dragon2[d].semeai_attack_code = 0;
	    dragon2[d].semeai_attack_point = NO_MOVE;
	    update_status(str, ALIVE, ALIVE_IN_SEKI);
	  }
	}

	DEBUG(DEBUG_SEMEAI, "Move to prevent seki at %1m (%1m vs %1m)\n",
	      dragon2[d].semeai_attack_point, opponent, str);

	dragon2[d].semeai_attack_certain = certain;
	dragon2[d].semeai_attack_target = opponent;
      }
    }
  }
}
Esempio n. 29
0
int
compute_surroundings(int pos, int apos, int showboard, int *surround_size)
{
  int i, j;
  int m, n;
  int k;
  int dpos;
  int surrounded;
  
  int left_corner[MAX_BOARD];
  int right_corner[MAX_BOARD];
  int corner[BOARDMAX];
  int left_corners = 0, right_corners = 0;
  int corners = 0;
  int top_row, bottom_row;
  int color = board[pos];
  int other = OTHER_COLOR(color);
  int gi = 0;
  int gj = 0;
  int stones = 0;
  int found_some;
  
  signed char mf[BOARDMAX]; /* friendly dragon  */
  signed char mn[BOARDMAX]; /* neighbor dragons */
  int  sd[BOARDMAX]; /* distances to the goal */
  
  if (DRAGON2(pos).hostile_neighbors == 0)
    return(0);
  
  memset(mf, 0, sizeof(mf));
  memset(mn, 0, sizeof(mn));
  memset(sd, 0, sizeof(sd));
  
  mark_dragon(pos, mf, 1);

  /* mark hostile neighbors */

  for (k = 0; k < DRAGON2(pos).neighbors; k++) {
    int nd = DRAGON(DRAGON2(pos).adjacent[k]).origin;
    
    if (board[nd] != color) {
      if (0)
	gprintf("neighbor: %1m\n", nd);
      mark_dragon(nd, mn, 1);
    }
  }

  /* descend markings from stones lying on the 2nd and third lines */

  for (dpos = BOARDMIN; dpos < BOARDMAX; dpos++)
    if (ON_BOARD(dpos) && mn[dpos]) {
      for (k = 0; k < 4; k++) {
        int d = delta[k];
        if (!ON_BOARD(dpos + d))
          continue;
        if (!ON_BOARD(dpos + 2*d)) {
          if (board[dpos + d] == EMPTY)
            mn[dpos + d] = 1;
        }
        else if (!ON_BOARD(dpos + 3*d)) {
          if (board[dpos + d] == EMPTY
              && board[dpos + 2*d] == EMPTY)
            mn[dpos + 2*d] = 1;
        }
      }
    }

  /* compute minimum distances to the goal */

  for (dpos = BOARDMIN; dpos < BOARDMAX; dpos++)
    if (ON_BOARD(dpos) && mn[dpos]) 
      sd[dpos] = goal_dist(dpos, mf);

  /* revise markings */

  do {
    found_some = 0;
    for (dpos = BOARDMIN; dpos < BOARDMAX; dpos++)
      if (ON_BOARD(dpos) && mn[dpos] && sd[dpos] > 8) {
        /* discard markings if we can find 2 stones
         * that verify :
         * - it is closer to the goal than we are
         * - it is closer to us than the goal is
         * - they are closer to each other than we are to the goal
         */
        for (i = BOARDMIN; i < BOARDMAX; i++)
	  if (ON_BOARD(i) && mn[i] && i != dpos
              && sd[i] < sd[dpos]
              && square_dist(i, dpos) < sd[dpos]) {
            for (j = i + 1; j < BOARDMAX; j++)
	      if (ON_BOARD(j) && mn[j] && j != dpos
                  && sd[j] < sd[dpos]
                  && square_dist(j, dpos) < sd[dpos]
                  && square_dist(i, j) < sd[dpos]) {
	        mn[dpos] = 0;
                found_some = 1;
                break;
              }
            if (mn[dpos] == 0)
              break;
          }
      }
  } while (found_some);

  /* prepare corner array */

  for (dpos = BOARDMIN; dpos < BOARDMAX; dpos++)
    if (ON_BOARD(dpos) && mn[dpos])
      corner[corners++] = dpos;

  /* compute gravity center of the goal */

  for (dpos = BOARDMIN; dpos < BOARDMAX; dpos++)
    if (ON_BOARD(dpos) && mf[dpos]) {
      gi += I(dpos);
      gj += J(dpos);
      stones++;
    }
  gi /= stones;
  gj /= stones;
  gg = POS(gi, gj);

  /* sort the corner array */

  gg_sort(corner, corners, sizeof(int), compare_angles);

  /* if apos is not NO_MOVE, mark it. */

  if (apos != NO_MOVE) {
    ASSERT_ON_BOARD1(apos);
    mn[apos] = 1;
  }
  
  if (showboard == 1) {
    show_surround_map(mf, mn);
  }

  /* find top row of surrounding polyhedron */
  
  top_row = -1;
  for (m = 0; m < board_size; m++) {
    if (top_row != -1)
      break;
    for (n = 0; n < board_size; n++)
      if (mn[POS(m, n)]) {
	left_corner[0] = POS(m, n);
	top_row = m;
	break;
      }
  }

  /* find bottom row */
  
  bottom_row = -1;
  for (m = board_size - 1; m >= 0; m--) {
    if (bottom_row != -1)
      break;
    for (n = 0; n < board_size; n++)
      if (mn[POS(m, n)]) {
	bottom_row = m;
	break;
      }
  }
  
  /* find the corners on the left side */
  
  for (left_corners = 1; I(left_corner[left_corners-1]) < bottom_row; 
       left_corners++) {
    int best_found = 0;
    float best_slope = 0.;
    int m = I(left_corner[left_corners-1]);
    int n = J(left_corner[left_corners-1]);
    
    for (i = m + 1; i <= bottom_row; i++)
      for (j = 0; j < board_size; j++)
	if (mn[POS(i, j)]) {
	  float slope = ((float) (j - n))/((float) (i - m));
	  if (0)
	    gprintf("(left) at %m, last %m, slope=%f\n", i, j, m, n, slope);
	  
	  if (!best_found || slope < best_slope) {
	    best_found = POS(i, j);
	    best_slope = slope;
	  }
	}
    ASSERT_ON_BOARD1(best_found);
    left_corner[left_corners] = best_found;
  }
  
  for (n = board_size-1; n >= 0; n--)
    if (mn[POS(top_row, n)]) {
      right_corner[0] = POS(top_row, n);
      break;
    }
  
  /* find the corners on the right side */
  
  for (right_corners = 1; I(right_corner[right_corners-1]) < bottom_row; 
       right_corners++) {
    int best_found = 0;
    float best_slope = 0.;
    int m = I(right_corner[right_corners-1]);
    int n = J(right_corner[right_corners-1]);
    
    for (i = m + 1; i <= bottom_row; i++) {
      for (j = board_size - 1; j >= 0; j--) {
	if (mn[POS(i, j)]) {
	  float slope = ((float) (j - n))/((float) (i - m));
	  if (0)
	    gprintf("(right) at %m, last %m, slope=%f\n", i, j, m, n, slope);
	  if (!best_found || slope > best_slope) {
	    best_found = POS(i, j);
	    best_slope = slope;
	  }
	}
      }
    }
    ASSERT_ON_BOARD1(best_found);
    right_corner[right_corners] = best_found;
  }
  
  if (0) {
    for (k = 0; k < left_corners; k++)
      gprintf("left corner %d: %1m\n", k, left_corner[k]);
    
    for (k = 0; k < right_corners; k++)
      gprintf("right corner %d: %1m\n", k, right_corner[k]);
  }

  /* Now mark the interior of the convex hull */
  
  for (n = J(left_corner[0]); n <= J(right_corner[0]); n++)
    mn[POS(top_row, n)] = 1;

  for (n = J(left_corner[left_corners-1]); 
       n <= J(right_corner[right_corners-1]); n++)
    mn[POS(bottom_row, n)] = 1;

  for (m = top_row+1; m < bottom_row; m++) {
    int left_boundary = -1, right_boundary = -1;
    for (k = 1; k < left_corners; k++) {
      if (I(left_corner[k]) > m) {
	float ti = I(left_corner[k-1]);
	float tj = J(left_corner[k-1]);
	float bi = I(left_corner[k]);
	float bj = J(left_corner[k]);
	
	if (0)
	  gprintf("(left) %d: %1m %1m\n", 
		  m, left_corner[k-1], left_corner[k]);
	/* left edge in this row is on segment (ti,tj) -> (bi, bj) */
	
	/* FIXME: Rewrite this to avoid floating point arithmetic */
	left_boundary = ceil(tj + (m - ti) * (bj - tj) / (bi - ti));
	break;
      }
    }

    for (k = 1; k < right_corners; k++) {
      if (I(right_corner[k]) > m) {
	float ti = I(right_corner[k-1]);
	float tj = J(right_corner[k-1]);
	float bi = I(right_corner[k]);
	float bj = J(right_corner[k]);
	
	if (0)
	  gprintf("(right) %d: %1m %1m\n", 
		  m, right_corner[k-1], right_corner[k]);

	/* FIXME: Rewrite this to avoid floating point arithmetic */
	right_boundary = floor(tj + (m - ti) * (bj - tj) / (bi - ti));
	break;
      }
    }

    for (n = left_boundary; n <= right_boundary; n++)
      mn[POS(m, n)] = 1;
  }
  
  /* mark the expanded region */

  for (dpos = BOARDMIN; dpos < BOARDMAX; dpos++)
    if (ON_BOARD(dpos) && mn[dpos] == 1)
      for (k = 0; k < 4; k++)
	if (ON_BOARD(dpos + delta[k]) && !mn[dpos + delta[k]])
	  mn[dpos + delta[k]] = 2;
      
  /* Mark allied dragons that intersect the (unexpanded) hull.
   * These must all lie entirely within the hull for the
   * dragon to be considered surrounded. 
   *
   * Only neighbor dragons are considered since dragons that
   * are not neighbors are less likely to be helpful.
   */

  for (dpos = BOARDMIN; dpos < BOARDMAX; dpos++) {
    int mpos;
    if (ON_BOARD(dpos) 
	&& mn[dpos] == 1
	&& board[dpos] == color
	&& are_neighbor_dragons(pos, dpos)
	&& !mf[dpos]) {

      for (mpos = BOARDMIN; mpos < BOARDMAX; mpos++)
	if (ON_BOARD(mpos) && is_same_dragon(mpos, dpos))
	  mf[mpos] = 2;
    }
    /* A special case
     *
     *  . X X .
     *  X O . X
     *  X . O O
     *  . O . .
     *
     * The O stone hasn't been amalgamated and the surround computations
     * might think this single stone dragon is surrounded, which in turn
     * can generate overvaluation of moves around this stone.
     * Consequently, we allow inclusion of the stones at kosumi distance
     * in the mf (friendly) array.
     */
    if (ON_BOARD(dpos) 
	&& mn[dpos] == 2
	&& board[dpos] == color
	&& are_neighbor_dragons(pos, dpos)
	&& !mf[dpos]) {
      for (k = 4; k < 8; k++)
	if (ON_BOARD(dpos + delta[k]) && board[dpos + delta[k]] == color
	    && mn[dpos + delta[k]] == 1
	    && board[dpos + delta[k-4]] == EMPTY
	    && board[dpos + delta[(k-3)%4]] == EMPTY) {
	  for (mpos = BOARDMIN; mpos < BOARDMAX; mpos++)
	    if (ON_BOARD(mpos) && is_same_dragon(mpos, dpos))
	      mf[mpos] = 2;
	}
    }
  }

  /* determine the surround status of the dragon */

  surrounded = SURROUNDED;

  /* Compute the maximum surround status awarded
   * If distances between enclosing stones are large, reduce to
   * WEAKLY_SURROUNDED. If (really) too large, then reduce to 0
   * FIXME: constants chosen completely ad hoc. Possibly better tunings
   *        can be found.
   */

  for (k = 0; k < corners - 1; k++) {
    if (is_edge_vertex(corner[k])
        && is_edge_vertex(corner[k+1]))
      continue;
    if (square_dist(corner[k], corner[k+1]) > 60) {
      surrounded = 0;
      break;
    }
    else if (square_dist(corner[k], corner[k+1]) > 27)
      surrounded = WEAKLY_SURROUNDED;
  }
  if (surrounded
      && (!is_edge_vertex(corner[0])
          || !is_edge_vertex(corner[corners-1]))) {
    if (square_dist(corner[0], corner[corners-1]) > 60)
      surrounded = 0;
    else if (square_dist(corner[0], corner[corners-1]) > 27)
      surrounded = WEAKLY_SURROUNDED;
  }

  if (surrounded)
    for (dpos = BOARDMIN; dpos < BOARDMAX; dpos++)
      if (mf[dpos]) {
	if (mn[dpos] == 0) {
	  surrounded = 0;
	  break;
	}
	else if (mn[dpos] == 2)
	  surrounded = WEAKLY_SURROUNDED;
      }

  /* revise the status for single stone dragons. */

  if (stones == 1
      && surrounded == WEAKLY_SURROUNDED
      && mn[pos] == 2)
    surrounded = 0;
      
  /* revise the status if an ikken tobi jumps out. */

  if (surrounded) {
    for (dpos = BOARDMIN; dpos < BOARDMAX && surrounded; dpos++) {
      if (!ON_BOARD(dpos) || !mf[dpos])
	continue;

      for (k = 0; k < 4; k++) {
	int up = delta[k];
	int right = delta[(k + 1) % 4];
	if (board[dpos + up] == EMPTY
	    && board[dpos + 2*up] == color
	    && mn[dpos + 2*up] != 1
	    && ON_BOARD(dpos + up + right)
	    && board[dpos + up + right] != other
	    && ON_BOARD(dpos + up - right)
	    && board[dpos + up - right] != other) {
	  surrounded = 0;
	  break;
	}
      }
    }
  }

  if (showboard == 1 || (showboard == 2 && surrounded)) {
    show_surround_map(mf, mn);
  }

  if (!apos && surrounded && surround_pointer < MAX_SURROUND) {
    memcpy(surroundings[surround_pointer].surround_map, mn, sizeof(mn));
    surroundings[surround_pointer].dragon_number = dragon[pos].id;
    surround_pointer++;
  }

  if (surround_size) {
    int pos;

    *surround_size = 0;
    for (pos = BOARDMIN; pos < BOARDMAX; pos++)
      if (ON_BOARD(pos) && mn[pos] == 1)
	(*surround_size)++;
  }

  return surrounded;
}
Esempio n. 30
0
static void cut_connect_callback(int m, int n, int color,
				 struct pattern *pattern, int ll)
{
  int stari, starj;
  int k;
  int first_dragoni=-1, first_dragonj=-1;
  int second_dragoni=-1, second_dragonj=-1;

  int other=OTHER_COLOR(color);

  if ((pattern->movei) == -1) {
    stari = -1;
    starj = -1;
  } 
  else {
    TRANSFORM(pattern->movei, pattern->movej, &stari, &starj, ll);
    stari += m;
    starj += n;
    if (!safe_move(stari, starj, other))
      return;
  }

  if (pattern->helper) {
    if (!pattern->helper(pattern, ll, stari, starj, color))
      return;
  }

  if (pattern->klass & CLASS_B) {
    /* Require that the X stones in the pattern are tactically safe. */
    for (k = 0; k < pattern->patlen; ++k) { /* match each point */
      if (pattern->patn[k].att == ATT_X) {
	/* transform pattern real coordinate */
	int x, y;
	TRANSFORM(pattern->patn[k].x,pattern->patn[k].y,&x,&y,ll);
	x += m;
	y += n;

	if ((worm[x][y].attacki != -1)
	    && ((stari == -1)
		|| !does_defend(stari, starj, x, y)))
	  return; /* Match failed */
      }
    }
  }

  /* get here => pattern matches */

  if (pattern->klass & CLASS_B) {
    TRACE("Cutting pattern %s+%d found at %m\n",
	  pattern->name, ll, m, n);
    if (stari != -1)
      TRACE("cutting point %m\n", stari, starj);
  }
  else
    TRACE("Connecting pattern %s+%d found at %m\n",
	  pattern->name, ll, m, n);

  /* If it is a B pattern, set cutting point in worm data and make eye
   * space marginal.
   */
  
  if (pattern->klass & CLASS_B) {
    if (stari != -1) {
      if (color == WHITE)
	white_eye[stari][starj].cut = 1;
      else
	black_eye[stari][starj].cut = 1;
      if (color == WHITE && white_eye[stari][starj].color == WHITE_BORDER)
	white_eye[stari][starj].marginal = 1;
      else if (color == BLACK && black_eye[stari][starj].color == BLACK_BORDER)
	black_eye[stari][starj].marginal = 1;
    }
  }
  else if (!(pattern->klass & CLASS_C))
    return; /* Nothing more to do, up to the helper or autohelper
	       to amalgamate dragons. */

  /* If it is a C pattern, find the dragons to connect.
   * If it is a B pattern, find eye space points to inhibit connection
   * through.
   */
  for (k = 0; k < pattern->patlen; ++k) { /* match each point */
    int x, y; /* absolute (board) co-ords of (transformed) pattern element */

    /* transform pattern real coordinate */
    TRANSFORM(pattern->patn[k].x,pattern->patn[k].y,&x,&y,ll);
    x+=m;
    y+=n;

    /* Look for dragons to amalgamate. Never amalgamate stones which
     * can be attacked.
     */
    if ((pattern->klass & CLASS_C) && (p[x][y] == color)
	&& (worm[x][y].attacki == -1)) {
      if (first_dragoni == -1) {
	first_dragoni = dragon[x][y].origini;
	first_dragonj = dragon[x][y].originj;
      }
      else if ((second_dragoni == -1)
	       && ((dragon[x][y].origini != first_dragoni)
		   || (dragon[x][y].originj != first_dragonj))) {
	second_dragoni = dragon[x][y].origini;
	second_dragonj = dragon[x][y].originj;
	/* A second dragon found, we amalgamate them at once. */
	join_dragons(second_dragoni, second_dragonj,
		     first_dragoni, first_dragonj);
	TRACE("Joining dragon %m to %m\n",
	      second_dragoni, second_dragonj,
	      first_dragoni, first_dragonj);
	/* Now look for another second dragon */
	second_dragoni = -1;
	second_dragonj = -1;
////assert(dragon[x][y].origini == first_dragoni &&
	       //dragon[x][y].originj == first_dragonj);
      }
    }

    /* Inhibit connections */
    if (pattern->klass & CLASS_B) {
      if (pattern->patn[k].att != ATT_not)
	break; /* The inhibition points are guaranteed to come first. */
      if (color == WHITE && white_eye[x][y].color == WHITE_BORDER)
	white_eye[x][y].type |= INHIBIT_CONNECTION;
      else if (color == BLACK && black_eye[x][y].color == BLACK_BORDER)
	black_eye[x][y].type |= INHIBIT_CONNECTION;
    }
  } /* loop over elements */
}