/* Remove persistent cache entries which are no longer compatible with
 * the board. For efficient use of the cache, it's recommended to call
 * this function once per move, before starting the owl reading. It's
 * not required for correct operation though. 
 */
void
purge_persistent_owl_cache()
{
  int k;
  static int last_purge_position_number = -1;
  gg_assert(stackp == 0);

  /* Never do this more than once per move. */
  if (last_purge_position_number == position_number)
    return;
  else
    last_purge_position_number = position_number;

  for (k = 0; k < persistent_owl_cache_size; k++) {
    if (persistent_owl_cache[k].boardsize != board_size
	|| !verify_stored_board(persistent_owl_cache[k].board)) {
      /* Move the last entry in the cache here and back up the loop
       * counter to redo the test at this position in the cache.
       */
      if (k < persistent_owl_cache_size - 1)
	persistent_owl_cache[k] 
	  = persistent_owl_cache[persistent_owl_cache_size - 1];
      k--;
      persistent_owl_cache_size--;
    }
  }
}
int
search_persistent_owl_cache(int routine, int apos, int bpos, int cpos,
			    int *result, int *move, int *move2, int *certain)
{
  int k;
  gg_assert(stackp == 0 || stackp == 1);

  for (k = 0; k < persistent_owl_cache_size; k++) {
    if (persistent_owl_cache[k].routine == routine
	&& persistent_owl_cache[k].apos == apos
	&& persistent_owl_cache[k].bpos == bpos
	&& persistent_owl_cache[k].cpos == cpos
	&& verify_stored_board(persistent_owl_cache[k].board)) {
      *result = persistent_owl_cache[k].result;
      if (move)
	*move = persistent_owl_cache[k].move;
      if (move2)
	*move2 = persistent_owl_cache[k].move2;
      if (certain)
	*certain = persistent_owl_cache[k].result_certain;

      TRACE_OWL_PERSISTENT_CACHE(
	    "persistent owl cache hit: routine %s at %1m result %d\n",
	    routine_to_string(routine), apos, bpos, cpos, 
	    result_to_string(persistent_owl_cache[k].result));
      return 1;
    }
  }
  return 0;
}
Ejemplo n.º 3
0
/* 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;
  int best_fuseki_value;

  /* 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. */
  /* Do not choose moves with less value than 20% of the best move */
  best_fuseki_value = fuseki_value[0];
  q = gg_rand() % fuseki_total_value;
  for (k = 0; k < num_fuseki_moves; k++) {
    if (fuseki_value[k] < (best_fuseki_value / 5))
      break;
    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;
}
Ejemplo n.º 4
0
/* Remove persistent cache entries which are no longer compatible with
 * the board. For efficient use of the cache, it's recommended to call
 * this function once per move, before starting the owl reading. It's
 * not required for correct operation though. 
 */
static void
purge_persistent_cache(struct persistent_cache *cache)
{
  int k;
  int r;
  gg_assert(stackp == 0);

  /* Never do this more than once per move. */
  if (cache->last_purge_position_number == position_number)
    return;
  else
    cache->last_purge_position_number = position_number;

  for (k = 0; k < cache->current_size; k++) {
    int played_moves = 0;
    int entry_ok = 1;
    struct persistent_cache_entry *entry = &(cache->table[k]);

    if (entry->boardsize != board_size)
      entry_ok = 0;
    else {
      for (r = 0; r < MAX_CACHE_DEPTH; r++) {
	int apos = entry->stack[r];
	int color = entry->move_color[r];
	if (apos == 0)
	  break;
	if (board[apos] == EMPTY
	    && trymove(apos, color, "purge_persistent_cache", 0))
	  played_moves++;
	else {
	  entry_ok = 0;
	  break;
	}
      }
    }

    if (!entry_ok 
	|| !verify_stored_board(entry->board)) {
      /* Move the last entry in the cache here and back up the loop
       * counter to redo the test at this position in the cache.
       */
      if (0)
	gprintf("Purging entry %d from cache.\n", k);
      if (k < cache->current_size - 1)
	*entry = cache->table[cache->current_size - 1];
      k--;
      cache->current_size--;
    }
    else {
      /* Reduce score here to penalize entries getting old. */
      entry->score *= cache->age_factor;
    }

    while (played_moves > 0) {
      popgo();
      played_moves--;
    }
  }
}
/* Remove persistent cache entries which are no longer current. */
void
purge_persistent_reading_cache()
{
  int k;
  int r;
  static int last_purge_position_number = -1;
  gg_assert(stackp == 0);

  /* Never do this more than once per move. */
  if (last_purge_position_number == position_number)
    return;
  else
    last_purge_position_number = position_number;

  for (k = 0; k < persistent_reading_cache_size; k++) {
    int played_moves = 0;
    int entry_ok = 1;

    if (persistent_reading_cache[k].boardsize != board_size)
      entry_ok = 0;
    else {
      for (r = 0; r < MAX_READING_CACHE_DEPTH; r++) {
	int apos = persistent_reading_cache[k].stack[r];
	int color = persistent_reading_cache[k].move_color[r];
	if (apos == 0)
	  break;
	if (board[apos] == EMPTY
	    && trymove(apos, color, "purge_persistent_reading_cache", 0,
		       EMPTY, 0))
	  played_moves++;
	else {
	  entry_ok = 0;
	  break;
	}
      }
    }

    if (!entry_ok 
	|| !verify_stored_board(persistent_reading_cache[k].board)) {
      /* Move the last entry in the cache here and back up the loop
       * counter to redo the test at this position in the cache.
       */
      if (0)
	gprintf("Purging entry %d from cache.\n", k);
      if (k < persistent_reading_cache_size - 1)
	persistent_reading_cache[k] 
	  = persistent_reading_cache[persistent_reading_cache_size - 1];
      k--;
      persistent_reading_cache_size--;
    }

    while (played_moves > 0) {
      popgo();
      played_moves--;
    }
  }
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
char *
hashdata_to_string(Hash_data *hashdata)
{
  static char buffer[BUFFER_SIZE];
  int n = 0;
  int k;
  
  for (k = 0; k < NUM_HASHVALUES; k++) {
    n += sprintf(buffer + n, HASHVALUE_PRINT_FORMAT,
		 HASHVALUE_NUM_DIGITS, hashdata->hashval[k]);
    gg_assert(n < BUFFER_SIZE);
  }

  return buffer;
}
Ejemplo n.º 8
0
static void
showcapture(char *line)
{
  int str;
  int move;

  gg_assert(line);
  str = string_to_location(board_size, line);
  if (str == NO_MOVE || board[str] == EMPTY) {
    printf("\ninvalid point!\n");
    return;
  }
  
  if (attack(str, &move))
    mprintf("\nSuccessful attack of %1m at %1m\n", str, move);
  else
    mprintf("\n%1m cannot be attacked\n", str);
}
Ejemplo n.º 9
0
static void
showdefense(char *line)
{
  int str;
  int move;
  
  gg_assert(line);
  str = string_to_location(board_size, line);
  if (str == NO_MOVE || board[str] == EMPTY) {
    printf("\ninvalid point!\n");
    return;
  }

  if (attack(str, NULL)) {
      if (find_defense(str, &move))
        mprintf("\nSuccessful defense of %1m at %1m\n", str, move);
      else
        mprintf("\n%1m cannot be defended\n", str);
    }
    else
      mprintf("\nThere is no need to defend %1m\n", str);
}
Ejemplo n.º 10
0
/* Based on the entries in the reading cache and their nodes field,
 * compute where the relatively most expensive tactical reading is
 * going on.
 */
void
reading_hotspots(float values[BOARDMAX])
{
  int pos;
  int k;
  int sum_nodes = 0;

  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    values[pos] = 0.0;
  
  /* Compute the total number of nodes for the cached entries. */
  for (k = 0; k < reading_cache.current_size; k++)
    sum_nodes += reading_cache.table[k].cost;

  if (sum_nodes <= 100)
    return;

  /* Loop over all entries and increase the value of vertices adjacent
   * to dragons involving expensive tactical reading.
   */
  for (k = 0; k < reading_cache.current_size; k++) {
    struct persistent_cache_entry *entry = &(reading_cache.table[k]);
    float contribution = entry->cost / (float) sum_nodes;
    if (0) {
      gprintf("Reading hotspots: %d %1m %f\n", entry->routine, entry->apos,
	      contribution);
    }
    switch (entry->routine) {
    case ATTACK:
    case FIND_DEFENSE:
      mark_string_hotspot_values(values, I(entry->apos), J(entry->apos),
				 contribution);
      break;
    default:
      gg_assert(0); /* Shouldn't happen. */
      break;
    }
  }
}
/* Based on the entries in the reading cache and their nodes field,
 * compute where the relatively most expensive tactical reading is
 * going on.
 */
void
reading_hotspots(float values[BOARDMAX])
{
  int m, n, k;
  int sum_nodes = 0;

  for (m = 0; m < board_size; m++)
    for (n = 0; n < board_size; n++)
      values[POS(m, n)] = 0.0;
  
  /* Compute the total number of nodes for the cached entries. */
  for (k = 0; k < persistent_reading_cache_size; k++)
    sum_nodes += persistent_reading_cache[k].nodes;

  if (sum_nodes <= 100)
    return;

  /* Loop over all entries and increase the value of vertices adjacent
   * to dragons involving expensive tactical reading.
   */
  for (k = 0; k < persistent_reading_cache_size; k++) {
    struct reading_cache *entry = &(persistent_reading_cache[k]);
    float contribution = entry->nodes / (float) sum_nodes;
    if (0) {
      gprintf("Reading hotspots: %d %1m %f\n", entry->routine, entry->str,
	      contribution);
    }
    switch (entry->routine) {
    case ATTACK:
    case FIND_DEFENSE:
      mark_string_hotspot_values(values, I(entry->str), J(entry->str),
				 contribution);
      break;
    default:
      gg_assert(0); /* Shouldn't happen. */
      break;
    }
  }
}
Ejemplo n.º 12
0
/*
 * 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;
}
Ejemplo n.º 13
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];
  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!!! Might disregard some semeais.");
    num_dragons = MAX_DRAGONS;
  }

  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 = 0;
    int attack_certain = 0;
    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];
	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_point = defense_move;
      dragon2[d1].semeai_defense_certain = defense_certain;
      dragon2[d1].semeai_defense_target = semeai_defense_target;
      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();
}
Ejemplo n.º 14
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;
      }
    }
  }
}
Ejemplo n.º 15
0
void
tt_update(Transposition_table *table,
	  enum routine_id routine, int target1, int target2,
	  int remaining_depth, Hash_data *extra_hash, 
	  int value1, int value2, int move)
{
  Hash_data hashval;
  Hashentry *entry;
  Hashnode *deepest;
  Hashnode *newest;
  unsigned int data;
  /* Get routine costs definitions from liberty.h. */
  static const int routine_costs[] = { ROUTINE_COSTS };
  gg_assert(routine_costs[NUM_CACHE_ROUTINES] == -1);

  /* Sanity check. */
  if (remaining_depth < 0 || remaining_depth > HN_MAX_REMAINING_DEPTH)
    return;

  /* Get the combined hash value. */
  calculate_hashval_for_tt(&hashval, routine, target1, target2, extra_hash);

  data = hn_create_data(remaining_depth, value1, value2, move,
      		        routine_costs[routine]);

  /* Get the entry and nodes. */ 
  entry = &table->entries[hashdata_remainder(hashval, table->num_entries)];
  deepest = &entry->deepest;
  newest  = &entry->newest;
 
  /* See if we found an already existing node. */
  if (hashdata_is_equal(hashval, deepest->key)
      && remaining_depth >= (int) hn_get_remaining_depth(deepest->data)) {

    /* Found deepest */
    deepest->data = data;

  }
  else if (hashdata_is_equal(hashval, newest->key)
           && remaining_depth >= (int) hn_get_remaining_depth(newest->data)) {

    /* Found newest */
    newest->data = data;

    /* If newest has become deeper than deepest, then switch them. */
    if (hn_get_remaining_depth(newest->data)
	> hn_get_remaining_depth(deepest->data)) {
      Hashnode temp;

      temp = *deepest;
      *deepest = *newest;
      *newest = temp;
    }

  }
  else if (hn_get_total_cost(data) > hn_get_total_cost(deepest->data)) {
    if (hn_get_total_cost(newest->data) < hn_get_total_cost(deepest->data))
      *newest = *deepest;
    deepest->key  = hashval;
    deepest->data = data;
  } 
  else {
    /* Replace newest. */
    newest->key  = hashval;
    newest->data = data;
  }

  stats.read_result_entered++;
  table->is_clean = 0;
}
Ejemplo n.º 16
0
static void
play_aftermath(int color)
{
  int pos;
  struct board_state saved_board;
  struct aftermath_data *a = &aftermath;
  static int current_board[BOARDMAX];
  static int current_color = EMPTY;
  int cached_board = 1;
  gg_assert(color == BLACK || color == WHITE);

  if (current_color != color) {
    current_color = color;
    cached_board = 0;
  }

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (ON_BOARD(pos) && board[pos] != current_board[pos]) {
      current_board[pos] = board[pos];
      cached_board = 0;
    }
  }

  /* If this is exactly the same position as the one we analyzed the
   * last time, the content of the aftermath struct is up to date.
   */
  if (cached_board)
    return;

  a->white_captured = white_captured;
  a->black_captured = black_captured;
  a->white_prisoners = 0;
  a->black_prisoners = 0;
  a->white_territory = 0;
  a->black_territory = 0;
  a->white_area = 0;
  a->black_area = 0;
  
  store_board(&saved_board);
  do_play_aftermath(color, a);
  restore_board(&saved_board);
  
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;
    if (a->black_control[pos]) {
      a->black_area++;
      if (board[pos] == WHITE) {
	a->black_territory++;
	a->white_prisoners++;
	a->final_status[pos] = DEAD;
      }
      else if (board[pos] == EMPTY) {
	a->black_territory++;
	a->final_status[pos] = BLACK_TERRITORY;
      }
      else
	a->final_status[pos] = ALIVE;
    }
    else if (a->white_control[pos]) {
      a->white_area++;
      if (board[pos] == BLACK) {
	a->white_territory++;
	a->black_prisoners++;
	a->final_status[pos] = DEAD;
      }
      else if (board[pos] == EMPTY) {
	a->white_territory++;
	a->final_status[pos] = WHITE_TERRITORY;
      }
      else
	a->final_status[pos] = ALIVE;
    }
    else {
      if (board[pos] == EMPTY)
	a->final_status[pos] = DAME;
      else {
	a->final_status[pos] = ALIVE_IN_SEKI;
	if (board[pos] == WHITE)
	  a->white_area++;
	else
	  a->black_area++;
      }
    }
  }

  if (debug & DEBUG_AFTERMATH) {
    gprintf("White captured: %d\n", a->white_captured);
    gprintf("Black captured: %d\n", a->black_captured);
    gprintf("White prisoners: %d\n", a->white_prisoners);
    gprintf("Black prisoners: %d\n", a->black_prisoners);
    gprintf("White territory: %d\n", a->white_territory);
    gprintf("Black territory: %d\n", a->black_territory);
    gprintf("White area: %d\n", a->white_area);
    gprintf("Black area: %d\n", a->black_area);
  }
}
Ejemplo n.º 17
0
/* This is a substitute for genmove_conservative() which only does
 * what is required when doing the aftermath. Notice though that this
 * generates an "ordinary" move, in contrast to aftermath_genmove().
 * Usually this should turn up a pass, but when it doesn't it's
 * important not to miss the move.
 */
static int
reduced_genmove(int *move, int color)
{
  float val;
  int save_verbose;
  float upper_bound, lower_bound;
  float our_score;

  /* no move is found yet. */
  *move = NO_MOVE;  
  val = -1; 
  
  /* Prepare pattern matcher and reading code. */
  reset_engine();

  /* Find out information about the worms and dragons. */
  examine_position(EXAMINE_ALL);

  /* Make a score estimate. This can be used in later stages of the 
   * move generation.  If we are ahead, we can play safely and if
   * we are behind, we have to play more daringly.
   */
  estimate_score(&upper_bound, &lower_bound);
  if (verbose || showscore) {
    if (lower_bound == upper_bound)
      gprintf("\nScore estimate: %s %f\n",
	      lower_bound > 0 ? "W " : "B ", gg_abs(lower_bound));
    else
      gprintf("\nScore estimate: %s %f to %s %f\n",
	      lower_bound > 0 ? "W " : "B ", gg_abs(lower_bound),
	      upper_bound > 0 ? "W " : "B ", gg_abs(upper_bound));
    fflush(stderr);
  }

  /* The score will be used to determine when we are safely
   * ahead. So we want the most conservative score.
   */
  if (color == WHITE)
    our_score = lower_bound;
  else
    our_score = -upper_bound;

  gg_assert(stackp == 0);
  
  /*
   * Ok, information gathering is complete. Now start to find some moves!
   */

  /* Pick up moves that we know of already. */
  save_verbose = verbose;
  if (verbose > 0)
    verbose--;
  collect_move_reasons(color);
  verbose = save_verbose;
  
  /* Look for combination attacks and defenses against them. */
  combinations(color);
  gg_assert(stackp == 0);

  /* Review the move reasons and estimate move values. */
  if (review_move_reasons(move, &val, color, 0.0, our_score, NULL))
    TRACE("Move generation likes %1m with value %f\n", *move, val);
  gg_assert(stackp == 0);

  /* If no move is found then pass. */
  if (val < 0.0) {
    TRACE("I pass.\n");
    *move = NO_MOVE;
  }
  else
    TRACE("reduced_genmove() recommends %1m with value %f\n", *move, val);
 
  return val;
}
Ejemplo n.º 18
0
int
free_handicap_remaining_stones()
{
  gg_assert(remaining_handicap_stones >= 0);
  return remaining_handicap_stones;
}
Ejemplo n.º 19
0
/* Based on the entries in the owl cache and their tactical_nodes
 * field, compute where the relatively most expensive owl reading is
 * going on.
 */
void
owl_hotspots(float values[BOARDMAX])
{
  int pos;
  int k, r;
  int libs[MAXLIBS];
  int liberties;
  int sum_tactical_nodes = 0;

  /* Don't bother checking out of board. Set values[] to zero there too. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    values[pos] = 0.0;
  
  /* Compute the total number of tactical nodes for the cached entries. */
  for (k = 0; k < owl_cache.current_size; k++)
    sum_tactical_nodes += owl_cache.table[k].score;

  if (sum_tactical_nodes <= 100)
    return;

  /* Loop over all entries and increase the value of vertices adjacent
   * to dragons involving expensive owl reading.
   */
  for (k = 0; k < owl_cache.current_size; k++) {
    struct persistent_cache_entry *entry = &(owl_cache.table[k]);
    float contribution = entry->score / (float) sum_tactical_nodes;
    if (debug & DEBUG_PERSISTENT_CACHE) {
      gprintf("Owl hotspots: %d %1m %f\n", entry->routine, entry->apos,
	      contribution);
    }
    switch (entry->routine) {
    case OWL_ATTACK:
    case OWL_THREATEN_ATTACK:
    case OWL_DEFEND:
    case OWL_THREATEN_DEFENSE:
      mark_dragon_hotspot_values(values, entry->apos,
				 contribution, entry->board);
      break;
    case OWL_DOES_DEFEND:
    case OWL_DOES_ATTACK:
    case OWL_CONFIRM_SAFETY:
      mark_dragon_hotspot_values(values, entry->bpos,
				 contribution, entry->board);
      break;
    case OWL_CONNECTION_DEFENDS:
      mark_dragon_hotspot_values(values, entry->bpos,
				 contribution, entry->board);
      mark_dragon_hotspot_values(values, entry->cpos,
				 contribution, entry->board);
      break;
    case OWL_SUBSTANTIAL:
      /* Only consider the liberties of (apos). */
      liberties = findlib(entry->apos, MAXLIBS, libs);
      for (r = 0; r < liberties; r++)
	values[libs[r]] += contribution;
      break;
    default:
      gg_assert(0); /* Shouldn't happen. */
      break;
    }
  }
}
void
store_persistent_owl_cache(int routine, int apos, int bpos, int cpos,
			   int result, int move, int move2, int certain,
			   int tactical_nodes,
			   char goal[BOARDMAX], int goal_color)
{
  char active[BOARDMAX];
  int pos;
  int k;
  int r;
  int other = OTHER_COLOR(goal_color);
  gg_assert(stackp == 0);

  /* If cache is full, first try to purge it. */
  if (persistent_owl_cache_size == MAX_OWL_CACHE_SIZE)
    purge_persistent_owl_cache();

  /* FIXME: Kick out oldest or least expensive entry instead of giving up. */
  if (persistent_owl_cache_size == MAX_OWL_CACHE_SIZE) {
    TRACE_OWL_PERFORMANCE("Persistent owl cache full.\n");
    return;
  }

  persistent_owl_cache[persistent_owl_cache_size].boardsize  	 = board_size;
  persistent_owl_cache[persistent_owl_cache_size].routine    	 = routine;
  persistent_owl_cache[persistent_owl_cache_size].apos	     	 = apos;
  persistent_owl_cache[persistent_owl_cache_size].bpos	     	 = bpos;
  persistent_owl_cache[persistent_owl_cache_size].cpos	     	 = cpos;
  persistent_owl_cache[persistent_owl_cache_size].result     	 = result;
  persistent_owl_cache[persistent_owl_cache_size].result_certain = certain;
  persistent_owl_cache[persistent_owl_cache_size].move	         = move;
  persistent_owl_cache[persistent_owl_cache_size].move2	         = move2;
  persistent_owl_cache[persistent_owl_cache_size].tactical_nodes =
    tactical_nodes;
  persistent_owl_cache[persistent_owl_cache_size].movenum = movenum;
  
  /* Remains to set the board. We let the active area be
   * the goal +
   * distance four expansion through empty intersections and own stones +
   * adjacent opponent strings +
   * liberties of adjacent opponent strings with less than five liberties +
   * liberties of low liberty neighbors of adjacent opponent strings
   * with less than five liberties.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    if (ON_BOARD(pos))
      active[pos] = (goal[pos] != 0);

  /* Also add critical moves to the active area. */
  if (ON_BOARD1(move))
    active[move] = 1;

  if (ON_BOARD1(move2))
    active[move2] = 1;

  /* Distance four expansion through empty intersections and own stones. */
  for (k = 1; k < 5; k++) {
    for (pos = BOARDMIN; pos < BOARDMAX; pos++){
      if (!ON_BOARD(pos) || board[pos] == other || active[pos] != 0) 
	continue;
      if ((ON_BOARD(SOUTH(pos)) && active[SOUTH(pos)] == k)
	  || (ON_BOARD(WEST(pos)) && active[WEST(pos)] == k)
	  || (ON_BOARD(NORTH(pos)) && active[NORTH(pos)] == k)
	  || (ON_BOARD(EAST(pos)) && active[EAST(pos)] == k)) {
	if (board[pos] == EMPTY)
	  active[pos] = k + 1;
	else
	  mark_string(pos, active, (char) (k + 1));
      }
    }
  }
  
  /* Adjacent opponent strings. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (board[pos] != other || active[pos] != 0) 
      continue;
    for (r = 0; r < 4; r++) {
      int pos2 = pos + delta[r];
      if (ON_BOARD(pos2) && board[pos2] != other && active[pos2] != 0) {
	mark_string(pos, active, (char) 1);
	break;
      }
    }
  }
  
  /* Liberties of adjacent opponent strings with less than five liberties +
   * liberties of low liberty neighbors of adjacent opponent strings
   * with less than five liberties.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (board[pos] == other && active[pos] != 0 && countlib(pos) < 5) {
      int libs[4];
      int liberties = findlib(pos, 4, libs);
      int adjs[MAXCHAIN];
      int adj;
      for (r = 0; r < liberties; r++)
	active[libs[r]] = 1;
      
      /* Also add liberties of neighbor strings if these are three
       * or less.
       */
      adj = chainlinks(pos, adjs);
      for (r = 0; r < adj; r++) {
	if (countlib(adjs[r]) <= 3) {
	  int s;
	  liberties = findlib(adjs[r], 3, libs);
	  for (s = 0; s < liberties; s++)
	    active[libs[s]] = 1;
	}
      }
    }
  }
  
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    int value = board[pos];
    if (!ON_BOARD(pos))
      continue;
    if (!active[pos])
      value = GRAY;
    else if (IS_STONE(board[pos]) && countlib(pos) > 4)
      value |= HIGH_LIBERTY_BIT;
    
    persistent_owl_cache[persistent_owl_cache_size].board[pos] = value;
  }

  if (debug & DEBUG_OWL_PERSISTENT_CACHE) {
    gprintf("%o Stored result in cache (entry %d):\n",
	    persistent_owl_cache_size);
    print_persistent_owl_cache_entry(persistent_owl_cache_size);
  }
  
  persistent_owl_cache_size++;
}
Ejemplo n.º 21
0
/* Capture as many strings of the given color as we can. Played stones
 * are left on the board and the number of played stones is returned.
 * Strings marked in the exceptions array are excluded from capturing
 * attempts. If all non-excepted strings are successfully captured,
 * *none_invincible is set to one. Set none_invincible to NULL if you
 * don't need that information.
 */
static int
capture_non_invincible_strings(int color, int exceptions[BOARDMAX],
			       int *none_invincible)
{
  int other = OTHER_COLOR(color);
  int something_captured = 1; /* To get into the first turn of the loop. */
  int string_found = 0;
  int moves_played = 0;
  int save_moves;
  int libs[MAXLIBS];
  int liberties;
  int pos;
  int k;
  
  while (something_captured) {
    /* Nothing captured so far in this turn of the loop. */
    something_captured = 0;

    /* Is there something left to try to capture? */
    string_found = 0;
    
    /* Visit all friendly strings on the board. */
    for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
      if (board[pos] != color || find_origin(pos) != pos)
	continue;

      if (exceptions && exceptions[pos])
	continue;

      string_found = 1;
      
      /* Try to capture the string at pos. */
      liberties = findlib(pos, MAXLIBS, libs);
      save_moves = moves_played;
      for (k = 0; k < liberties; k++) {
	if (trymove(libs[k], other, "unconditional_life", pos))
	  moves_played++;
      }
      
      /* Successful if already captured or a single liberty remains.
       * Otherwise we must rewind and take back the last batch of moves.
       */
      if (board[pos] == EMPTY)
	something_captured = 1;
      else if (findlib(pos, 2, libs) == 1) {
	/* Need to use tryko as a defense against the extreme case
	 * when the only opponent liberty that is not suicide is an
	 * illegal ko capture, like in this 5x5 position:
	 * +-----+
	 * |.XO.O|
	 * |XXOO.|
	 * |X.XOO|
	 * |XXOO.|
	 * |.XO.O|
	 * +-----+
	 */
	int success = tryko(libs[0], other, "unconditional_life");
	gg_assert(success);
	moves_played++;
	something_captured = 1;
      }
      else
	while (moves_played > save_moves) {
	  popgo();
	  moves_played--;
	}
    }
  }

  if (none_invincible)
    *none_invincible = !string_found;
  
  return moves_played;
}
Ejemplo n.º 22
0
/* By unconditional status analysis we can statically find some moves
 * which there is never any need to play. Those belong to three
 * different categories:
 *
 * 1. A move on a vertex which is already unconditional territory for
 *    either color.
 * 2. A move which after having been made ends up as unconditional
 *    territory for the opponent.
 * 3. If a move at vertex A makes vertex B become unconditional
 *    territory, there is no need to consider a move at B, since A has
 *    all the positive effects that B would have.
 *
 * Moves in categories 1 and 2 are never any better than passing and
 * often worse (with territory scoring always worse). Moves in
 * category three can be either better or worse than passing, but it's
 * always true that a move at A is at least as good as a move at B.
 * Occasionally they are identically good (A makes B unconditional
 * territory and B makes A unconditional territory) but there is never
 * any need to analyze both.
 *
 * In meaningless_black_moves[] and meaningless_white_moves[] a value
 * of -1 means it is not meaningless, 0 (NO_MOVE) means it belongs to
 * category 1 or 2, and a value greater than zero points to the
 * preferred move in category 3.
 *
 * The parameter unconditional_territory should contain the result of
 * calling unconditional_life() in the original position. Meaningless
 * moves are computed for the given color.
 */
void
find_unconditionally_meaningless_moves(int unconditional_territory[BOARDMAX],
				       int color)
{
  int *meaningless_moves;
  int other = OTHER_COLOR(color);
  int friendly_unconditional[BOARDMAX];
  int opponent_unconditional[BOARDMAX];
  int pos;
  int pos2;

  gg_assert(color == BLACK || color == WHITE);

  if (color == BLACK)
    meaningless_moves = meaningless_black_moves;
  else
    meaningless_moves = meaningless_white_moves;

  /* Initialize meaningless_moves and detect moves of category 1, but
   * only for own unconditional territory.
   *
   * FIXME: We would save some time by detecting all category 1 moves
   * here but then we would need to have the initial unconditional
   * territory for the opponent as well. This can of course be done,
   * the question is how we get it in the nicest way.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    if (board[pos] == EMPTY) {
      if (unconditional_territory[pos])
	meaningless_moves[pos] = NO_MOVE;
      else
	meaningless_moves[pos] = -1;
    }

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (board[pos] != EMPTY || meaningless_moves[pos] != -1)
      continue;

    if (!tryko(pos, color, "find_unconditionally_meaningless_moves"))
      continue;

    unconditional_life(opponent_unconditional, other);
    if (opponent_unconditional[pos]) {
      /* Move of category 1 or 2. */
      meaningless_moves[pos] = NO_MOVE;
    }
    else {
      unconditional_life(friendly_unconditional, color);
      if (friendly_unconditional[pos])
	for (pos2 = BOARDMIN; pos2 < BOARDMAX; pos2++)
	  if (board[pos2] == EMPTY
	      && meaningless_moves[pos2] == -1
	      && friendly_unconditional[pos2]) {
	    /* Move of category 3. */
	    meaningless_moves[pos2] = pos;
	  }
    }

    popgo();
  }

  /* Meaningless moves of category 3 may have been found in multiple
   * steps. Normalize to the final replacement move.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    if (board[pos] == EMPTY && meaningless_moves[pos] > 0)
      while (meaningless_moves[meaningless_moves[pos]] > 0)
	meaningless_moves[pos] = meaningless_moves[meaningless_moves[pos]];
}
Ejemplo n.º 23
0
int
free_handicap_total_stones()
{
  gg_assert(total_handicap_stones >= 0);
  return total_handicap_stones;
}
Ejemplo n.º 24
0
/* Allocate the actual cache table. */
static void
init_cache(struct persistent_cache *cache)
{
  cache->table = malloc(cache->max_size*sizeof(struct persistent_cache_entry));
  gg_assert(cache->table);
}