Exemple #1
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;
      }
    }
  }
}
Exemple #2
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]);
	      }
            }
	  }
	}
      }
    }
}
Exemple #3
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;
      }
    }
  }
}