Exemplo n.º 1
0
// group must be base of a group
inline static bool try_delete_group(board_t *b, index_t group)
{
    if (b->pseudo_liberties[group] != 0) {
        maybe_in_atari_now(b, group);
        return false;
    }
    index_t ptr = group;
    index_swap(b, b->list_pos[ptr], b->group_ptr++);
    do {
        clear_atari_bits_3x3(b->nbr3x3[ptr]);
        index_swap(b, b->list_pos[ptr], b->empty_ptr++);
        b->hash -= (hash_t)b->stones[ptr] * p4423[ptr];
        b->stones[ptr] = STONE_EMPTY;
        delete_stone_update_liberties(b, ptr);
        delete_stone_update_3x3(b, ptr);
        ptr = b->next_in_group[ptr];
    } while (ptr != group);
    do {
        if (IS_STONE(b->stones[N(b, ptr)])) {
            maybe_not_in_atari_now(b, b->base_of_group[N(b, ptr)]);
        }
        if (IS_STONE(b->stones[S(b, ptr)])) {
            maybe_not_in_atari_now(b, b->base_of_group[S(b, ptr)]);
        }
        if (IS_STONE(b->stones[W(b, ptr)])) {
            maybe_not_in_atari_now(b, b->base_of_group[W(b, ptr)]);
        }
        if (IS_STONE(b->stones[E(b, ptr)])) {
            maybe_not_in_atari_now(b, b->base_of_group[E(b, ptr)]);
        }
        ptr = b->next_in_group[ptr];
    } while (ptr != group);
    return true;
}
Exemplo n.º 2
0
/* Helper for the owl_hotspots() function below. */
static void
mark_dragon_hotspot_values(float values[BOARDMAX], int dr,
			   float contribution, char active_board[BOARDMAX])
{
  int pos;
  int k;
  ASSERT1(IS_STONE(board[dr]), dr);
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (board[pos] != EMPTY)
      continue;
    for (k = 0; k < 8; k++) {
      int pos2 = pos + delta[k];
      if (IS_STONE(board[pos2])
	  && (is_same_dragon(pos2, dr)
	      || (are_neighbor_dragons(pos2, dr)
		  && board[pos2] == board[dr]))
	  && (countlib(pos2) <= 4
	      || is_edge_vertex(pos))) {
	if (k < 4) {
	  if (is_same_dragon(pos2, dr))
	    values[pos] += contribution;
	  else
	    values[pos] += 0.5 * contribution;
	  break;
	}
	else {
	  /* If pos2 = SOUTHWEST(pos), this construction makes
	   *    pos3 = SOUTH(pos) and
	   *    pos4 = WEST(pos)
	   * and corresponding for all other diagonal movements.
	   */
	  int pos3 = pos + delta[k % 4];
	  int pos4 = pos + delta[(k+1) % 4];
	  if (board[pos3] == EMPTY || countlib(pos3) <= 2
	      || board[pos4] == EMPTY || countlib(pos4) <= 2)
	    values[pos] += 0.5 * contribution;
	  break;
	}
      }
    }
    /* If not close to the dragon, but within the active area, give
     * negative hotspot contribution.
     */
    if (k == 8 && active_board[pos] == EMPTY) {
      values[pos] -= 0.5 * contribution;
    }
  }
}
Exemplo n.º 3
0
/* Change the status and safety of a dragon.  In addition, if the new
 * status is not DEAD, make all worms of the dragon essential, so that
 * results found by semeai code don't get ignored.
 */
static void
update_status(int dr, enum dragon_status new_status,
    	      enum dragon_status new_safety)
{
  int pos;

  if (dragon[dr].status != new_status
      && (dragon[dr].status != CRITICAL || new_status != DEAD)) {
    DEBUG(DEBUG_SEMEAI, "Changing status of %1m from %s to %s.\n", dr,
	  status_to_string(dragon[dr].status),
	  status_to_string(new_status));
    for (pos = BOARDMIN; pos < BOARDMAX; pos++)
      if (IS_STONE(board[pos]) && is_same_dragon(dr, pos)) {
	dragon[pos].status = new_status;
	if (new_status != DEAD)
	  worm[pos].inessential = 0;
      }
  }

  if (DRAGON2(dr).safety != new_safety
      && (DRAGON2(dr).safety != CRITICAL || new_safety != DEAD)) {
    DEBUG(DEBUG_SEMEAI, "Changing safety of %1m from %s to %s.\n", dr,
	  status_to_string(DRAGON2(dr).safety), status_to_string(new_safety));
    DRAGON2(dr).safety = new_safety;
  }
}
Exemplo n.º 4
0
void
decide_eye(int pos)
{
  int color;
  struct eyevalue value;
  int attack_point;
  int defense_point;
  int eyepos;
  SGFTree tree;

  reset_engine();
  silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL);
  
  color = black_eye[pos].color;
  if (!IS_STONE(color)) {
    gprintf("The eye at %1m is not of a single color.\n", pos);
    return;
  }

  if (printboard)
    showboard(0);

  /* Enable sgf output. */
  if (*outfilename)
    sgffile_begindump(&tree);
  count_variations = 1;
  
  if (black_eye[pos].color == BLACK) {
    eyepos = black_eye[pos].origin;
    compute_eyes(eyepos, &value, &attack_point, &defense_point,
		 black_eye, half_eye, 0);
    gprintf("Black eyespace at %1m: %s\n", eyepos, eyevalue_to_string(&value));
    if (eye_move_urgency(&value) > 0) {
      gprintf("  vital points: %1m (attack) %1m (defense)\n", attack_point,
	      defense_point);
    }
  }
  
  if (white_eye[pos].color == WHITE) {
    eyepos = white_eye[pos].origin;
    compute_eyes(eyepos, &value, &attack_point, &defense_point,
		 white_eye, half_eye, 0);
    gprintf("White eyespace at %1m: %s\n", eyepos, eyevalue_to_string(&value));
    if (eye_move_urgency(&value) > 0) {
      gprintf("  vital points: %1m (attack) %1m (defense)\n", attack_point,
	      defense_point);
    }
  }
  
  /* Finish sgf output. */
  sgffile_enddump(outfilename);
  count_variations = 0;
}
Exemplo n.º 5
0
inline static void delete_stone_update_liberties(board_t *b, index_t pos)
{
    index_t z, pos2 = pos * pos;
    if (IS_STONE(b->stones[N(b, pos)])) {
        b->pseudo_liberties[z=b->base_of_group[N(b, pos)]] ++;
        b->group_liberties_sum[z] += pos;
        b->group_liberties_sum_squared[z] += pos2;
    }
    if (IS_STONE(b->stones[S(b, pos)])) {
        b->pseudo_liberties[z=b->base_of_group[S(b, pos)]] ++;
        b->group_liberties_sum[z] += pos;
        b->group_liberties_sum_squared[z] += pos2;
    }
    if (IS_STONE(b->stones[W(b, pos)])) {
        b->pseudo_liberties[z=b->base_of_group[W(b, pos)]] ++;
        b->group_liberties_sum[z] += pos;
        b->group_liberties_sum_squared[z] += pos2;
    }
    if (IS_STONE(b->stones[E(b, pos)])) {
        b->pseudo_liberties[z=b->base_of_group[E(b, pos)]] ++;
        b->group_liberties_sum[z] += pos;
        b->group_liberties_sum_squared[z] += pos2;
    }
}
Exemplo n.º 6
0
/* Check whether two dragons are directly adjacent or have at least
 * one common liberty.
 */
static int
close_enough_for_proper_semeai(int apos, int bpos)
{
  int pos;
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (board[pos] == EMPTY
	&& neighbor_of_dragon(pos, apos)
	&& neighbor_of_dragon(pos, bpos))
      return 1;
    else if (IS_STONE(board[pos])) {
      if (is_same_dragon(pos, apos) && neighbor_of_dragon(pos, bpos))
	return 1;
      if (is_same_dragon(pos, bpos) && neighbor_of_dragon(pos, apos))
	return 1;
    }
  }
  
  return 0;
}
Exemplo n.º 7
0
/* Helper for the reading_hotspots() function below. */
static void
mark_string_hotspot_values(float values[BOARDMAX],
			   int m, int n, float contribution)
{
  int i, j, k;
  
  /* If p[m][n] is EMPTY, we just give the contribution to close empty
   * vertices. This is a rough simplification.
   */
  if (BOARD(m, n) == EMPTY) {
    for (i = -1; i <= 1; i++)
      for (j = -1; j <= 1; j++)
	if (BOARD(m+i, n+j) == EMPTY)
	  values[POS(m+i, n+j)] += contribution;
    return;
  }
  
  /* Otherwise we give contribution to liberties and diagonal
   * neighbors of the string at (m, n).
   */
  for (i = 0; i < board_size; i++)
    for (j = 0; j < board_size; j++) {
      if (BOARD(i, j) != EMPTY)
	continue;
      for (k = 0; k < 8; k++) {
	int di = deltai[k];
	int dj = deltaj[k];
	if (IS_STONE(BOARD(i+di, j+dj))
	    && same_string(POS(i+di, j+dj), POS(m, n))) {
	  if (k < 4) {
	    values[POS(i, j)] += contribution;
	    break;
	  }
	  else {
	    if (BOARD(i+di, j) == EMPTY || countlib(POS(i+di, j)) <= 2
		|| BOARD(i, j+dj) == EMPTY || countlib(POS(i, j+dj)) <= 2)
	      values[POS(i, j)] += contribution;
	    break;
	  }
	}
      }
    }
}
Exemplo n.º 8
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;
      }
    }
  }
}
Exemplo n.º 9
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;
      }
    }
  }
}
Exemplo n.º 10
0
bool check_board(board_t *b)
{
    static bool flag[max_len];
    memset(flag, false, sizeof(bool) * max_len);
    for (index_t i = 0; i < b->len; i++) {
        if (IS_STONE(b->stones[i])) {
            flag[b->next_in_group[i]] = true;
            //check next[i] in the same group
            if (b->base_of_group[i] != b->base_of_group[b->next_in_group[i]])
                return false;
            //check same color
            if (b->stones[i] != b->stones[b->next_in_group[i]])
                return false;
        }
    }
    // check uniqueness of next[i]
    for (index_t i = 0; i < b->len; i++) {
        if (IS_STONE(b->stones[i]) != flag[i]) {
            return false;
        }
    }
    //check pseudo_liberties information
    board_t *b2 = new board_t;
    fork_board(b2, b);
    memset(b2->group_size, 0, sizeof(index_t) * b->len);
    for (index_t i = 0; i < b2->len; i++) {
        if (IS_STONE(b2->stones[i])) {
            index_t j = b2->base_of_group[i];
            b2->group_size[j] ++;
            b2->pseudo_liberties[j] = 0;
            b2->group_liberties_sum[j] = 0;
            b2->group_liberties_sum_squared[j] = 0;
        }
    }
    for (index_t i = 0; i < b2->len; i++) {
        if (IS_STONE(b2->stones[i])) {
            update_empty_neighbour(b2, i);
        }
    }
    for (index_t i = 0; i < b2->len; i++) {
        if (IS_STONE(b2->stones[i])) {
            index_t p = b->base_of_group[i];
            index_t q = b2->base_of_group[i];
            if (b->group_size[p] != b2->group_size[q] ||
                    b->pseudo_liberties[p] != b2->pseudo_liberties[q]) {
                delete b2;
                return false;
            }
            if (b->group_liberties_sum[p] != b2->group_liberties_sum[q] ||
                    b->group_liberties_sum_squared[p] !=
                    b2->group_liberties_sum_squared[q]) {
                delete b2;
                return false;
            }
        }
    }
    delete b2;
    // check hash value
    hash_t nhash = 0;
    for (index_t i = 0; i < b->len; i++) {
        if (IS_STONE(b->stones[i]))
            nhash += (hash_t)b->stones[i] * p4423[i];
    }
    if (nhash != b->hash)
        return false;
    // check list
    for (index_t i = 0; i < b->len; i++) {
        if (b->stones[i] != STONE_BORDER) {
            index_t j = b->list_pos[i];
            if (j < 0 || j >= b->size * b->size)
                return false;
            if (b->list[j] != i)
                return false;
            int tA = b->stones[i] == STONE_EMPTY ? 0 :
                     i == b->base_of_group[i] ? 2 : 1;
            int tB = j < b->empty_ptr ? 0 : j < b->group_ptr ? 1 : 2;
            if (tA != tB)
                return false;
        }
    }
    // check nbr3x3
    nbr3x3_t new_nbr[max_len];
    for (index_t i = 0; i < b->len; i++) {
        if (b->stones[i] != STONE_BORDER) {
            new_nbr[i] = recalc_nbr3x3(b, i);
        }
    }
    for (index_t i = 0; i < b->len; i++) {
        if (IS_STONE(b->stones[i]) && b->base_of_group[i] == i && find_atari(b, i) >= 0) {
            index_t av = find_atari(b, i);
            if (av < 0)
                return false;
            set_atari_bits_3x3(new_nbr[av],
                               IN_GROUP(b, N(b, av), i),
                               IN_GROUP(b, S(b, av), i),
                               IN_GROUP(b, W(b, av), i),
                               IN_GROUP(b, E(b, av), i));
        }
    }
    for (index_t i = 0; i < b->len; i++) {
        if (b->stones[i] != STONE_BORDER) {
            if (new_nbr[i] != b->nbr3x3[i])
                return false;
        }
    }
    // check prob
    for (int i = 0; i < 2; i++)
        for (int j = 0; j < b->len; j++) {
            double diff = get_prob(b, j, (stone_t)(i+1)) - b->prob_sum2[i][j];
            if (diff > epsilon || diff < -epsilon)
                return false;
        }
    return true;
}
Exemplo n.º 11
0
/* Computes the active area for the current board position and the
 * read result that has just been stored in *entry.
 */
static void
compute_active_breakin_area(struct persistent_cache_entry *entry,
			    const char breakin_shadow[BOARDMAX], int dummy)
{
  int pos;
  int k, r;
  signed char active[BOARDMAX];
  int other = OTHER_COLOR(board[entry->apos]);
  UNUSED(dummy);

  /* We let the active area be
   * the string to connect +
   * the breakin shadow (which contains the goal) +
   * distance two expansion through empty intersections and own stones +
   * adjacent opponent strings +
   * liberties and neighbors of adjacent opponent strings with less than
   * five liberties +
   * liberties and neighbors of low liberty neighbors of adjacent opponent
   * strings with less than five liberties.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    active[pos] = breakin_shadow[pos];

  signed_mark_string(entry->apos, active, 1);

  /* To be safe, also add the successful move. */
  if (entry->result != 0 && entry->move != 0)
    active[entry->move] = 1;

  /* Distance two expansion through empty intersections and own stones. */
  for (k = 1; k < 3; 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
	  signed_mark_string(pos, active, (signed 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] && active[pos2] <= 2) {
	signed_mark_string(pos, active, 1);
	break;
      }
    }
  }
  
  /* Liberties of adjacent opponent strings with less than four 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) < 4) {
      int libs[4];
      int liberties = findlib(pos, 3, 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++) {
	signed_mark_string(adjs[r], active, -1);
	if (countlib(adjs[r]) <= 3) {
	  int s;
	  int adjs2[MAXCHAIN];
	  int adj2;
	  liberties = findlib(adjs[r], 3, libs);
	  for (s = 0; s < liberties; s++)
	    active[libs[s]] = 1;
	  adj2 = chainlinks(pos, adjs2);
	  for (s = 0; s < adj2; s++)
	    signed_mark_string(adjs2[s], active, -1);
	}
      }
    }
  }
  
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    char value = board[pos];
    if (!ON_BOARD(pos))
      continue;
    if (!active[pos])
      value = GRAY;
    else if (IS_STONE(board[pos]) && countlib(pos) > 3 && active[pos] > 0)
      value |= HIGH_LIBERTY_BIT2;
    
    entry->board[pos] = value;
  }
}
Exemplo n.º 12
0
static void
compute_active_reading_area(struct persistent_cache_entry *entry,
			    const char goal[BOARDMAX], int dummy)
{
  signed char active[BOARDMAX];
  int pos, r;
  UNUSED(dummy);

  /* Remains to set the board. We let the active area be the contested
   * string and reading shadow + adjacent empty and strings +
   * neighbors of active area so far + one more expansion from empty
   * to empty.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    active[pos] = goal[pos];

  signed_mark_string(entry->apos, active, 1);

  /* To be safe, also add the successful move. */
  if (entry->result != 0 && entry->move != 0)
    active[entry->move] = 1;

  /* Add adjacent strings and empty. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;
    if (active[pos] != 0) 
      continue;
    if ((ON_BOARD(SOUTH(pos)) && active[SOUTH(pos)] == 1)
	|| (ON_BOARD(WEST(pos)) && active[WEST(pos)] == 1)
	|| (ON_BOARD(NORTH(pos)) && active[NORTH(pos)] == 1)
	|| (ON_BOARD(EAST(pos)) && active[EAST(pos)] == 1)) {
      if (IS_STONE(board[pos]))
	signed_mark_string(pos, active, 2);
      else
	active[pos] = 2;
    }
  }

  /* Remove invincible strings. No point adding their liberties and
   * neighbors.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;
    if (IS_STONE(board[pos]) && worm[pos].invincible)
      active[pos] = 0;
  }
  
  /* Expand empty to empty. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (IS_STONE(board[pos]) || active[pos] != 0) 
      continue;
    if ((board[SOUTH(pos)] == EMPTY && active[SOUTH(pos)] == 2)
	|| (board[WEST(pos)] == EMPTY && active[WEST(pos)] == 2)
	|| (board[NORTH(pos)] == EMPTY && active[NORTH(pos)] == 2)
	|| (board[EAST(pos)] == EMPTY && active[EAST(pos)] == 2))
      active[pos] = 3;
  }
  
  /* Add neighbors of active area so far. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;
    if (active[pos] != 0) 
      continue;
    if ((ON_BOARD(SOUTH(pos)) && active[SOUTH(pos)] > 0
	 && active[SOUTH(pos)] < 4)
	|| (ON_BOARD(WEST(pos)) && active[WEST(pos)] > 0
	    && active[WEST(pos)] < 4)
	|| (ON_BOARD(NORTH(pos)) && active[NORTH(pos)] > 0
	    && active[NORTH(pos)] < 4)
	|| (ON_BOARD(EAST(pos)) && active[EAST(pos)] > 0
	    && active[EAST(pos)] < 4))
      active[pos] = 4;
  }

  /* Also add the previously played stones to the active area. */
  for (r = 0; r < stackp; r++)
    active[entry->stack[r]] = 5;

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;
    entry->board[pos] = 
      active[pos] != 0 ? board[pos] : GRAY;
  }
}
Exemplo n.º 13
0
static void compute_active_owl_area(struct persistent_cache_entry *entry,
				    const char goal[BOARDMAX],
				    int goal_color)
{
  int k, r;
  int pos;
  int other = OTHER_COLOR(goal_color);
  signed char active[BOARDMAX];

  /* We let the active area be the goal +
   * distance four expansion through empty intersections and own stones +
   * adjacent opponent strings +
   * liberties and neighbors of adjacent opponent strings with less than
   * five liberties +
   * liberties and neighbors 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(entry->move))
    active[entry->move] = 1;

  if (ON_BOARD1(entry->move2))
    active[entry->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
	  signed_mark_string(pos, active, (signed 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) {
	signed_mark_string(pos, active, 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++) {
	signed_mark_string(adjs[r], active, -1);
	if (countlib(adjs[r]) <= 3) {
	  int s;
	  int adjs2[MAXCHAIN];
	  int adj2;
	  liberties = findlib(adjs[r], 3, libs);
	  for (s = 0; s < liberties; s++)
	    active[libs[s]] = 1;
	  adj2 = chainlinks(pos, adjs2);
	  for (s = 0; s < adj2; s++)
	    signed_mark_string(adjs2[s], active, -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 && active[pos] > 0)
      value |= HIGH_LIBERTY_BIT;
    
    entry->board[pos] = value;
  }
}
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++;
}
/* Store a new read result in the persistent cache. */
void
store_persistent_reading_cache(int routine, int str, int result, int move,
			       int nodes)
{
  char active[BOARDMAX];
  int k;
  int r;
  int score = nodes;
  struct reading_cache *entry;

  ASSERT1(result == 0 || (move == 0) || ON_BOARD(move), move);

  /* Never cache results at too great depth. */
  if (stackp > MAX_READING_CACHE_DEPTH)
    return;

  /* If cache is still full, consider kicking out an old entry. */
  if (persistent_reading_cache_size == MAX_READING_CACHE_SIZE) {
    int worst_entry = -1;
    int worst_score = score;
    
    for (k = 1; k < persistent_reading_cache_size; k++) {
      if (persistent_reading_cache[k].score < worst_score) {
	worst_score = persistent_reading_cache[k].score;
	worst_entry = k;
      }
    }

    if (worst_entry != -1) {
      /* Move the last entry in the cache here to make space.
       */
      if (worst_entry < persistent_reading_cache_size - 1)
	persistent_reading_cache[worst_entry] 
	  = persistent_reading_cache[persistent_reading_cache_size - 1];
      persistent_reading_cache_size--;
    }
    else
      return;
  }

  entry = &(persistent_reading_cache[persistent_reading_cache_size]);
  entry->boardsize       = board_size;
  entry->movenum         = movenum;
  entry->nodes           = nodes;
  entry->score           = score;
  entry->remaining_depth = depth - stackp;
  entry->routine         = routine;
  entry->str	         = str;
  entry->result          = result;
  entry->move	         = move;

  for (r = 0; r < MAX_READING_CACHE_DEPTH; r++) {
    if (r < stackp)
      get_move_from_stack(r, &(entry->stack[r]), &(entry->move_color[r]));
    else {
      entry->stack[r] = 0;
      entry->move_color[r] = EMPTY;
    }
  }
  
  /* Remains to set the board. We let the active area be the contested
   * string and reading shadow + adjacent empty and strings +
   * neighbors of active area so far + one more expansion from empty
   * to empty.
   */
  for (k = BOARDMIN; k < BOARDMAX; k++)
    active[k] = shadow[k];

  mark_string(str, active, 1);

  /* To be safe, also add the successful move. */
  if (result != 0 && move != 0)
    active[move] = 1;

  /* Add adjacent strings and empty. */
  for (k = BOARDMIN; k < BOARDMAX; k++) {
    if (!ON_BOARD(k))
      continue;
    if (active[k] != 0) 
      continue;
    if ((ON_BOARD(SOUTH(k)) && active[SOUTH(k)] == 1)
	|| (ON_BOARD(WEST(k)) && active[WEST(k)] == 1)
	|| (ON_BOARD(NORTH(k)) && active[NORTH(k)] == 1)
	|| (ON_BOARD(EAST(k)) && active[EAST(k)] == 1)) {
      if (IS_STONE(board[k]))
	mark_string(k, active, 2);
      else
	active[k] = 2;
    }
  }

  /* Remove invincible strings. No point adding their liberties and
   * neighbors.
   */
  for (k = BOARDMIN; k < BOARDMAX; k++) {
    if (!ON_BOARD(k))
      continue;
    if (IS_STONE(board[k]) && worm[k].invincible)
      active[k] = 0;
  }
  
  /* Expand empty to empty. */
  for (k = BOARDMIN; k < BOARDMAX; k++) {
    if (IS_STONE(board[k]) || active[k] != 0) 
      continue;
    if ((board[SOUTH(k)] == EMPTY && active[SOUTH(k)] == 2)
	|| (board[WEST(k)] == EMPTY && active[WEST(k)] == 2)
	|| (board[NORTH(k)] == EMPTY && active[NORTH(k)] == 2)
	|| (board[EAST(k)] == EMPTY && active[EAST(k)] == 2))
      active[k] = 3;
  }
  
  /* Add neighbors of active area so far. */
  for (k = BOARDMIN; k < BOARDMAX; k++) {
    if (!ON_BOARD(k))
      continue;
    if (active[k] != 0) 
      continue;
    if ((ON_BOARD(SOUTH(k)) && active[SOUTH(k)] > 0 && active[SOUTH(k)] < 4)
	|| (ON_BOARD(WEST(k)) && active[WEST(k)] > 0 && active[WEST(k)] < 4)
	|| (ON_BOARD(NORTH(k)) && active[NORTH(k)] > 0 && active[NORTH(k)] < 4)
	|| (ON_BOARD(EAST(k)) && active[EAST(k)] > 0 && active[EAST(k)] < 4))
      active[k] = 4;
  }

  /* Also add the previously played stones to the active area. */
  for (r = 0; r < stackp; r++)
    active[entry->stack[r]] = 5;

  for (k = BOARDMIN; k < BOARDMAX; k++) {
    if (!ON_BOARD(k))
      continue;
    entry->board[k] = 
      active[k] != 0 ? board[k] : GRAY;
  }

  if (0) {
    gprintf("%o Stored result in cache (entry %d):\n",
	    persistent_reading_cache_size);
    print_persistent_reading_cache_entry(persistent_reading_cache_size);
    gprintf("%o Reading shadow was:\n");
    draw_reading_shadow();
  }
  
  persistent_reading_cache_size++;
}