gnugo-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnugo-devel] arend_3_7.5: blunder more carefully


From: Arend Bayer
Subject: [gnugo-devel] arend_3_7.5: blunder more carefully
Date: Sun, 1 Sep 2002 17:37:41 +0200 (CEST)

This is a rather large patch substantially changing how value_moves()
handles blunders.

Instead of confirm_safety() only reporting whether there is an
unexpected attack, it instead reports its size (and hence, it is renamed
to blunder_size). Instead of using the somewhat crude
allowed_blunder_size heuristic, the move valuation tries to do the right
thing: Subtract the "blunder size" from the move value.

If it is still the highest valued move, we play it nevertheless.
Otherwise, we check the next best move for safety.  (This part,
together with the triggering of reevaluate_ko_threats(), is
moved out of review_move_reasons into a separate function
find_best_move() for clarity.)

This change allows to remove the "vital_string" array in
atari_atari_confirm_safety (now atari_atari_blunder_size). The logic
behind vital_string said that any combination attack on supposedly safe
stones is unacceptable, regardless of "allowed_blunder_size". This is
now unnecessary.
        (If a big dragon is saved, but part of it can be
        attacked via an atari-atari sequence, then find_best_move() will
        automatically find out whether there is a better move to save this
        dragon (that also prevents this combination attack), or whether we
        should still play the original move (if saving part of the dragon
        is the best we can do).)

There are 9 PASSes, a couple of them extremely urgent, and 3 FAILs.


Arend


- new functions blunder_size(), atari_atari_blunder_size() report the
  size of a blunder as exactly as possible
- remove vital_string logic in atari_atari code
- remove allowed_blunder_size heuristic in review_move_reasons()
- instead use blunder size more exactly in new function find_best_move()


Breakage:
bogus PASS: strategy:18
FAIL: nicklas5:1203 is fine (L19 is the same asK18),
nngs2:70 owl problem,
trevor:180 can hopefully be solved by a small change in
atari_atari_blunder_size (see the FIXME there), but I have to check first
whether it's a valid thing to do.

./regress.sh . blunder.tst
21 unexpected PASS!
./regress.sh . strategy.tst
18 unexpected PASS!
./regress.sh . nicklas5.tst
1202 unexpected PASS!
1203 unexpected FAIL: Correct 'K18|N18|N19|M19', got 'L19'
./regress.sh . trevor.tst
180 unexpected FAIL: Correct '!A8', got 'A8'
./regress.sh . nngs.tst
390 unexpected PASS!
1560 unexpected PASS!
./regress.sh . global.tst
1 unexpected PASS!
./regress.sh . trevord.tst
1060 unexpected PASS!
./regress.sh . handtalk.tst
21 unexpected PASS!
./regress.sh . nngs2.tst
70 unexpected FAIL: Correct '!L18', got 'L18'
./regress.sh . century2002.tst
95 unexpected PASS!


Index: engine/aftermath.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/aftermath.c,v
retrieving revision 1.31
diff -u -d -r1.31 aftermath.c
--- engine/aftermath.c  6 Aug 2002 19:04:11 -0000       1.31
+++ engine/aftermath.c  1 Sep 2002 15:04:23 -0000
@@ -560,7 +560,7 @@
        || (DRAGON2(bb).safety != INVINCIBLE
            && DRAGON2(bb).safety != STRONGLY_ALIVE
            && owl_does_defend(move, bb) != WIN)
-       || (!confirm_safety(move, color, 0, NULL, NULL, NULL))) {
+       || (!confirm_safety(move, color, NULL, NULL))) {
       score[move] = 0;
     }
     else {
@@ -731,7 +731,7 @@
       /* If we don't allow self atari, also call confirm safety to
        * avoid setting up combination attacks.
        */
-      if (!self_atari_ok && !confirm_safety(move, color, 0, NULL, NULL, NULL))
+      if (!self_atari_ok && !confirm_safety(move, color, NULL, NULL))
        continue;

       *aftermath_move = move;
Index: engine/combination.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/combination.c,v
retrieving revision 1.30
diff -u -d -r1.30 combination.c
--- engine/combination.c        6 Aug 2002 19:04:11 -0000       1.30
+++ engine/combination.c        1 Sep 2002 15:04:29 -0000
@@ -212,13 +212,10 @@
 #define AA_MAX_MOVES MAX_BOARD * MAX_BOARD
 static int aa_status[BOARDMAX]; /* ALIVE, DEAD or CRITICAL */
 static int forbidden[BOARDMAX];
-static int vital_string[BOARDMAX]; /* May not sacrifice these stones. */
 static int aa_values[BOARDMAX];
-static void compute_aa_status(int color, int saved_dragons[BOARDMAX],
-                             int saved_worms[BOARDMAX]);
+static void compute_aa_status(int color, const char safe_stones[BOARDMAX]);
 static void compute_aa_values(int color);
 static int get_aa_status(int pos);
-static int is_vital_string(int str);
 static int do_atari_atari(int color, int *attack_point,
                          int *defense_point, int last_friendly,
                          int save_verbose, int minsize,
@@ -267,7 +264,7 @@
     return 0;
   memset(forbidden, 0, sizeof(forbidden));

-  compute_aa_status(color, NULL, NULL);
+  compute_aa_status(color, NULL);
   compute_aa_values(color);

   aa_val = do_atari_atari(color, &apos, &dpos, NO_MOVE,
@@ -308,20 +305,34 @@
 }


-/* Ask the atari_atari code whether there appears any combination
- * attack which would capture at least minsize stones after playing at
- * (move). If this happens, (*defense) points to a move which prevents
- * this blunder.
- *
+/* Wrapper around atari_atari_blunder_size. Check whether a
+ * combination attack of size at least minsize appears after move
+ * at (move) has been made.
  * The arrays saved_dragons[] and saved_worms[] should be one for
  * stones belonging to dragons or worms respectively, which are
- * supposedly saved by (move). These may be NULL if no stones are
- * supposed to having been saved.
+ * supposedly saved by (move).
  */
 int
 atari_atari_confirm_safety(int color, int move, int *defense, int minsize,
-                          int saved_dragons[BOARDMAX],
-                          int saved_worms[BOARDMAX])
+                          const char saved_dragons[BOARDMAX],
+                          const char saved_worms[BOARDMAX])
+{
+  char safe_stones[BOARDMAX];
+
+  mark_safe_stones(color, move, saved_dragons, saved_worms, safe_stones);
+
+  return (atari_atari_blunder_size(color, move, defense, safe_stones)
+         >= minsize);
+}
+
+
+/* This function checks whether any new combination attack appears after
+ * move at (move) has been made, and returns its size (in points).
+ * safe_stones marks which of our stones are supposedly safe after this move.
+ */
+int
+atari_atari_blunder_size(int color, int move, int *defense,
+                        const char safe_stones[BOARDMAX])
 {
   int apos;
   int defense_point = NO_MOVE, after_defense_point = NO_MOVE;
@@ -332,11 +343,12 @@
    * so in this respect the move is safe enough.
    */
   if (aa_depth < 2)
-    return 1;
+    return 0;

   memset(forbidden, 0, sizeof(forbidden));

-  compute_aa_status(other, saved_dragons, saved_worms);
+  /* FIXME: Maybe these should be moved after the tryko() below? */
+  compute_aa_status(other, safe_stones);
   compute_aa_values(other);

   /* Accept illegal ko capture here. */
@@ -345,8 +357,7 @@
     abortgo(__FILE__, __LINE__, "trymove", I(move), J(move));
   increase_depth_values();

-  aa_val = do_atari_atari(other, &apos, &defense_point, NO_MOVE, 0, minsize,
-                         NULL);
+  aa_val = do_atari_atari(other, &apos, &defense_point, NO_MOVE, 0, 0, NULL);
   after_aa_val = aa_val;

   if (aa_val == 0 || defense_point == NO_MOVE) {
@@ -361,7 +372,7 @@

     popgo();
     decrease_depth_values();
-    return 1;
+    return 0;
   }

   while (aa_val >= after_aa_val) {
@@ -381,16 +392,16 @@
    * the original move at (aa) was really relevant. So we
    * try omitting it and see if a combination is still found.
    */
-  compute_aa_status(other, NULL, NULL);
+  compute_aa_status(other, NULL);
   compute_aa_values(other);
-  if (do_atari_atari(other, NULL, NULL, NO_MOVE,
-                    0, minsize, NULL) >= after_aa_val)
-    return 1;
-  else {
+  aa_val = do_atari_atari(other, NULL, NULL, NO_MOVE, 0, 0, NULL);
+  if (after_aa_val - aa_val > 0) {
     if (defense)
       *defense = after_defense_point;
-    return 0;
+    return after_aa_val - aa_val;
   }
+  else
+    return 0;
 }


@@ -399,13 +410,12 @@
 /* ---------------------------------------------------------------- */


-/* Helper function for computing the aa_status for a string. It also
- * sets up the vital_string[] array.
+/* Helper function for computing the aa_status for all opponent's strings.
+ * If safe_stones is given, we just copy the information from there.
  */

 static void
-compute_aa_status(int color, int saved_dragons[BOARDMAX],
-                 int saved_worms[BOARDMAX])
+compute_aa_status(int color, const char safe_stones[BOARDMAX])
 {
   int other = OTHER_COLOR(color);
   int pos;
@@ -426,23 +436,26 @@
    */
   for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
     if (board[pos] == other) {
-      if (dragon[pos].status == DEAD)
-       aa_status[pos] = DEAD;
-      else if (dragon[pos].status == CRITICAL
-              && (!saved_dragons || !saved_dragons[pos]))
-       aa_status[pos] = CRITICAL;
-      else if (worm[pos].attack_codes[0] != 0) {
-       if (worm[pos].defend_codes[0] != 0) {
-         if (saved_worms && saved_worms[pos])
-           aa_status[pos] = ALIVE;
-         else
+      if (safe_stones) {
+       if (safe_stones[pos])
+         aa_status[pos] = ALIVE;
+       else
+         aa_status[pos] = DEAD;
+      }
+      else {
+       if (dragon[pos].status == DEAD)
+         aa_status[pos] = DEAD;
+       else if (dragon[pos].status == CRITICAL)
+         aa_status[pos] = CRITICAL;
+       else if (worm[pos].attack_codes[0] != 0) {
+         if (worm[pos].defend_codes[0] != 0)
            aa_status[pos] = CRITICAL;
+         else
+           aa_status[pos] = DEAD;
        }
        else
-         aa_status[pos] = DEAD;
+         aa_status[pos] = ALIVE;
       }
-      else
-       aa_status[pos] = ALIVE;
     }
     else if (ON_BOARD(pos))
       aa_status[pos] = UNKNOWN;
@@ -483,22 +496,6 @@
     }
   }

-  /* Set up the vital strings array. This contains stones we can't be
-   * allowed to sacrifice. This is used by
-   * atari_atari_confirm_safety() to make sure that stones which are
-   * supposedly saved by the move can't be captured in a combination
-   * attack.
-   */
-
-  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
-    if (ON_BOARD(pos)
-       && ((saved_dragons && saved_dragons[pos])
-           || (saved_worms && saved_worms[pos])))
-      vital_string[pos] = 1;
-    else
-      vital_string[pos] = 0;
-  }
-
   sgf_dumptree = save_sgf_dumptree;
   count_variations = save_count_variations;
   verbose = save_verbose;
@@ -532,23 +529,6 @@
 }


-/* Helper function to examine whether a stone is part of a vital
- * string. Since the vital_string[] array was generated at stackp == 0
- * new stones may have appeared on the board.
- */
-static int
-is_vital_string(int str)
-{
-  int stones[MAX_BOARD * MAX_BOARD];
-  int n = findstones(str, MAX_BOARD * MAX_BOARD, stones);
-  int k;
-  for (k = 0; k < n; k++)
-    if (vital_string[stones[k]])
-      return 1;
-
-  return 0;
-}
-

 /* Helper function for atari_atari. Here worms is the number of
  * opponent worms involved in the combination, and (last_friendly) is
@@ -762,8 +742,7 @@
        continue;

       if (minsize > 0
-         && get_aa_value(ii) < minsize
-         && !is_vital_string(ii))
+         && get_aa_value(ii) < minsize)
        continue;

       if (get_aa_status(ii) != ALIVE)
@@ -920,8 +899,7 @@
                                             pattern->patn[k].y, ll, m, n));

       if (current_minsize > 0
-         && get_aa_value(str) < current_minsize
-         && !is_vital_string(str))
+         && get_aa_value(str) < current_minsize)
        continue;

       if (aa_move_known(current_attacks, move, str))
@@ -1076,7 +1054,7 @@
       gprintf("%o\n");
     }
   }
-
+
   return num_moves;
 }

Index: engine/filllib.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/filllib.c,v
retrieving revision 1.21
diff -u -d -r1.21 filllib.c
--- engine/filllib.c    6 Aug 2002 19:04:11 -0000       1.21
+++ engine/filllib.c    1 Sep 2002 15:04:31 -0000
@@ -548,7 +548,7 @@
     return 0;
   verbose = save_verbose;

-  return confirm_safety(move, color, 0, defense_point, NULL, NULL);
+  return confirm_safety(move, color, defense_point, NULL);
 }


Index: engine/liberty.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/liberty.h,v
retrieving revision 1.109
diff -u -d -r1.109 liberty.h
--- engine/liberty.h    18 Aug 2002 21:41:02 -0000      1.109
+++ engine/liberty.h    1 Sep 2002 15:04:36 -0000
@@ -323,8 +323,10 @@
 int obvious_false_eye(int pos, int color);
 int owl_topological_eye(int pos, int color);
 int vital_chain(int pos);
-int confirm_safety(int move, int color, int size, int *defense_point,
-                  int saved_dragons[BOARDMAX], int saved_worms[BOARDMAX]);
+int confirm_safety(int move, int color, int *defense_point,
+                  char safe_stones[BOARDMAX]);
+float blunder_size(int move, int color, int *defense_point,
+                  char safe_stones[BOARDMAX]);
 void set_depth_values(int level);
 void modify_depth_values(int n);
 void increase_depth_values(void);
@@ -406,8 +408,12 @@

 int get_attack_threats(int pos, int max_strings, int strings[]);
 int get_defense_threats(int pos, int max_strings, int strings[]);
-void get_saved_worms(int pos, int saved[BOARDMAX]);
-void get_saved_dragons(int pos, int saved[BOARDMAX]);
+void get_saved_worms(int pos, char saved[BOARDMAX]);
+void get_saved_dragons(int pos, char saved[BOARDMAX]);
+void mark_safe_stones(int color, int move_pos,
+                     const char saved_dragons[BOARDMAX],
+                     const char saved_worms[BOARDMAX],
+                     char safe_stones[BOARDMAX]);


 int owl_lively(int pos);
@@ -451,9 +457,12 @@
 void combinations(int color);
 int atari_atari(int color, int *attack_move, int *defense_move,
                int save_verbose);
-int atari_atari_confirm_safety(int color, int tpos, int *move,
-                              int minsize, int saved_dragons[BOARDMAX],
-                              int saved_worms[BOARDMAX]);
+int atari_atari_confirm_safety(int color, int tpos, int *move, int minsize,
+                              const char saved_dragons[BOARDMAX],
+                              const char saved_worms[BOARDMAX]);
+
+int atari_atari_blunder_size(int color, int tpos, int *move,
+                            const char safe_stones[BOARDMAX]);

 int review_move_reasons(int *move, float *val, int color,
                        float pure_threat_value, float lower_bound);
@@ -489,7 +498,8 @@

 int does_attack(int move, int str);
 int does_defend(int move, int str);
-int double_atari(int move, int color);
+int double_atari(int move, int color, float *value,
+                char safe_stones[BOARDMAX]);
 int play_attack_defend_n(int color, int do_attack, int num_moves, ...);
 int play_attack_defend2_n(int color, int do_attack, int num_moves, ...);
 int play_break_through_n(int color, int num_moves, ...);
Index: engine/move_reasons.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/move_reasons.c,v
retrieving revision 1.90
diff -u -d -r1.90 move_reasons.c
--- engine/move_reasons.c       25 Aug 2002 11:14:36 -0000      1.90
+++ engine/move_reasons.c       1 Sep 2002 15:04:42 -0000
@@ -1400,7 +1400,7 @@

 /* Find worms rescued by a move at (pos). */
 void
-get_saved_worms(int pos, int saved[BOARDMAX])
+get_saved_worms(int pos, char saved[BOARDMAX])
 {
   int k;
   memset(saved, 0, sizeof(saved[0]) * BOARDMAX);
@@ -1417,13 +1417,8 @@
      * confirm_safety routines spot an attack with ko and thinks the
      * move is unsafe.
      */
-    if (move_reasons[r].type == DEFEND_MOVE) {
-      int origin = worm[worms[what]].origin;
-      int ii;
-      for (ii = BOARDMIN; ii < BOARDMAX; ii++)
-       if (IS_STONE(board[ii]) && worm[ii].origin == origin)
-         saved[ii] = 1;
-    }
+    if (move_reasons[r].type == DEFEND_MOVE)
+      mark_string(worm[worms[what]].origin, saved, 1);
   }
 }

@@ -1511,7 +1506,7 @@

 /* Find dragons rescued by a move at (pos). */
 void
-get_saved_dragons(int pos, int saved[BOARDMAX])
+get_saved_dragons(int pos, char saved[BOARDMAX])
 {
   int k;
   memset(saved, 0, sizeof(saved[0]) * BOARDMAX);
@@ -1536,6 +1531,44 @@
          saved[ii] = 1;
     }
   }
+}
+
+
+/* If a move has saved the dragons in saved_dragons[] and worms in
+ * saved_worms[], this functions writes the stones now supposedly safe
+ * in the array safe_stones[].
+ *
+ * The safety of the played move itself is set according to
+ * move[pos].move_safety.
+ */
+void
+mark_safe_stones(int color, int move_pos, const char saved_dragons[BOARDMAX],
+                const char saved_worms[BOARDMAX], char safe_stones[BOARDMAX])
+{
+  int pos;
+
+  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
+    if (board[pos] == OTHER_COLOR(color)) {
+      if (dragon[pos].status == DEAD
+         || (worm[pos].attack_codes[0] != 0
+             && worm[pos].defend_codes[0] == 0))
+       safe_stones[pos] = 0;
+      else
+       safe_stones[pos] = 1;
+    }
+    else if (board[pos] == color) {
+      if (dragon[pos].status == DEAD
+         || (dragon[pos].status == CRITICAL && !saved_dragons[pos])
+         || (worm[pos].attack_codes[0] != 0
+             && (worm[pos].defend_codes[0] == 0 || !saved_worms[pos])))
+       safe_stones[pos] = 0;
+      else
+       safe_stones[pos] = 1;
+    }
+    else
+      safe_stones[pos] = 0;
+  }
+  safe_stones[move_pos] = move[move_pos].move_safety;
 }


Index: engine/utils.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/utils.c,v
retrieving revision 1.50
diff -u -d -r1.50 utils.c
--- engine/utils.c      6 Aug 2002 19:04:11 -0000       1.50
+++ engine/utils.c      1 Sep 2002 15:04:49 -0000
@@ -808,39 +808,46 @@
 }


+int
+confirm_safety(int move, int color, int *defense_point,
+              char safe_stones[BOARDMAX])
+{
+  return (blunder_size(move, color, defense_point, safe_stones)
+         == 0.0);
+}
+
 /* This function will detect some blunders. If the move reduces the
  * number of liberties of an adjacent friendly string, there is a
  * danger that the move could backfire, so the function checks that no
  * friendly worm which was formerly not attackable becomes attackable,
  * and it checks that no opposing worm which was not defendable
- * becomes defendable. Only worms with worm.size>size are checked.
+ * becomes defendable.
  *
- * The arrays saved_dragons[] and saved_worms[] should be one for
- * stones belonging to dragons or worms respectively, which are
- * supposedly saved by (move). These may be NULL if no stones are
- * supposed to gaving been saved.
+ * It returns the estimated size of the blunder, or 0.0 if nothing
+ * bad has happened.
+ *
+ * The array safe_stones[] contains the stones that are supposedly
+ * safe after (move). It may be NULL.
  *
  * For use when called from fill_liberty, this function may optionally
  * return a point of defense, which, if taken, will presumably make
  * the move at (move) safe on a subsequent turn.
- *
- * FIXME: Most TRACE calls below are ineffective because we have
- * decreased the verbose value to avoid traces in the owl code.
  */

-int
-confirm_safety(int move, int color, int size, int *defense_point,
-              int saved_dragons[BOARDMAX], int saved_worms[BOARDMAX])
+float
+blunder_size(int move, int color, int *defense_point,
+            char safe_stones[BOARDMAX])
 {
   int libs[5];
   int liberties = accurate_approxlib(move, color, 5, libs);
   int other = OTHER_COLOR(color);
-  int issafe = 1;
   int pos;
   int apos;
   int trouble = 0;
   int k;
+  int ii;
   int save_verbose = verbose;
+  float return_value = 0.0;

   if (defense_point)
     *defense_point = NO_MOVE;
@@ -849,45 +856,41 @@

   if (verbose > 0)
     verbose--;
-
-  if (!atari_atari_confirm_safety(color, move, &apos, size,
-                                 saved_dragons, saved_worms)) {
-    ASSERT_ON_BOARD1(apos);
-    if (defense_point)
-      *defense_point = apos;
-    verbose = save_verbose;
-    TRACE("Combination attack appears at %1m.\n", apos);
-    return 0;
-  }

-  if (liberties > 4) {
-    verbose = save_verbose;
-    return 1;
-  }
+  if (liberties > 4)
+    goto atari_atari;

+  /* We start by looking whether we have killed a dragon. If this happens,
+   * we mark its stones as no longer safe, and remember the dragon's size.
+   */
   for (k = 0; k < 4; k++) {
     int bpos = move + delta[k];
     if (board[bpos] == color
        && liberties <= worm[bpos].liberties) {
       trouble = 1;
       if ((dragon[bpos].status == ALIVE
-          || (dragon[bpos].status == CRITICAL
-              && saved_dragons != NULL
-              && saved_dragons[bpos]))
+          || (safe_stones
+              && safe_stones[bpos]))
          && DRAGON2(bpos).safety != INVINCIBLE
          && DRAGON2(bpos).safety != STRONGLY_ALIVE
-         && dragon[bpos].size >= size
          && !owl_confirm_safety(move, bpos, defense_point)) {
        verbose = save_verbose;
-       return 0;
+       TRACE("Dragon at %1m becomes attackable.\n", bpos);
+       if (verbose > 0)
+         verbose--;
+       return_value += 2.0 * dragon[bpos].effective_size;
+       if (safe_stones)
+         for (ii = BOARDMIN; ii < BOARDMAX; ii++)
+           if (ON_BOARD(ii) && dragon[ii].origin == dragon[bpos].origin)
+             safe_stones[ii] = 0;
       }
     }
   }

-  if (!trouble) {
-    verbose = save_verbose;
-    return 1;
-  }
+  verbose = save_verbose;
+
+  if (!trouble)
+    goto atari_atari;

   /* Need to increase the depth values during this reading to avoid
    * horizon effects.
@@ -895,63 +898,71 @@
   increase_depth_values();

   if (trymove(move, color, NULL, NO_MOVE, EMPTY, NO_MOVE)) {
-    for (pos = BOARDMIN; issafe && pos < BOARDMAX; pos++)
-      if (issafe
-         && IS_STONE(board[pos])
+    for (pos = BOARDMIN; pos < BOARDMAX; pos++)
+      if (IS_STONE(board[pos])
          && worm[pos].origin == pos
          && pos != move) {
+       /* First, we look for a new tactical attack. */
        if (board[pos] == color
-           && worm[pos].attack_codes[0] == 0
-           && worm[pos].size >= size
+           && ((safe_stones && safe_stones[pos])
+               || (!safe_stones && worm[pos].attack_codes[0] == 0))
            && attack(pos, NULL)) {
+         /* A safe worm of ours has become attackable. */
          if (defense_point)
            find_defense(pos, defense_point);
-         issafe = 0;
          TRACE("After %1m Worm at %1m becomes attackable.\n", move, pos);
+         return_value += worm[pos].effective_size;
+         if (safe_stones) /* Can't use mark_string. */
+           for (ii = BOARDMIN; ii < BOARDMAX; ii++)
+             if (worm[ii].origin == worm[pos].origin)
+               safe_stones[ii] = 0;
        }
        else if (board[pos] == other
+                && worm[pos].origin == pos
                 && worm[pos].attack_codes[0] != 0
                 && worm[pos].defend_codes[0] == 0
-                && worm[pos].size >= size
                 && find_defense(pos, NULL)) {
-         /* Also ask the owl code whether the string can live
+         /* A dead opponent's worm has become defendable.
+          * Also ask the owl code whether the string can live
           * strategically. To do this we need to temporarily undo
           * the trymove().
           */
+         int owl_attacks;
+
          popgo();
          decrease_depth_values();
-         if (owl_does_attack(move, pos) != WIN)
-           issafe = 0;
+         owl_attacks = owl_does_attack(move, pos);
+         if (owl_attacks != WIN) {
+           return_value += worm[pos].effective_size;
+           TRACE("After %1m worm at %1m becomes defendable.\n",
+                 move, pos);
+         }
          trymove(move, color, NULL, NO_MOVE, EMPTY, NO_MOVE);
          increase_depth_values();

-         if (!issafe) {
-           if (defense_point) {
-             int dpos;
-             if (attack(pos, &dpos))
-               *defense_point = dpos;
-             else
-               TRACE("No attack found (unexpectedly) on %1m after move at 
%1m.\n",
-                     pos, move);
-           }
-
-           TRACE("After %1m worm at %1m becomes defendable.\n",
-                 move, pos);
+         if (owl_attacks != WIN && defense_point) {
+           int dpos;
+           if (attack(pos, &dpos))
+             *defense_point = dpos;
+           else
+             TRACE("No attack found (unexpectedly) on %1m after move at 
%1m.\n",
+                   pos, move);
          }
        }
       }

     if (liberties == 2) {
-      if (double_atari(libs[0], other)) {
+      float d_a_blunder_size;
+      if (double_atari(libs[0], other, &d_a_blunder_size, safe_stones)) {
        if (defense_point && safe_move(libs[0], color) == WIN)
          *defense_point = libs[0];
-       issafe = 0;
+       return_value += d_a_blunder_size;
        TRACE("Double threat appears at %1m.\n", libs[0]);
       }
-      else if (double_atari(libs[1], other)) {
+      else if (double_atari(libs[1], other, &d_a_blunder_size, safe_stones)) {
        if (defense_point && safe_move(libs[1], color) == WIN)
          *defense_point = libs[1];
-       issafe = 0;
+       return_value += d_a_blunder_size;
        TRACE("Double threat appears at %1m.\n", libs[1]);
       }
     }
@@ -960,8 +971,21 @@

   /* Reset the depth values. */
   decrease_depth_values();
-  verbose = save_verbose;
-  return issafe;
+
+  /* We call the atari-atari code last to avoid duplicate blunder reports. */
+atari_atari:
+  {
+    int atari = atari_atari_blunder_size(color, move, &apos, safe_stones);
+    if (atari) {
+      ASSERT_ON_BOARD1(apos);
+      if (defense_point)
+       *defense_point = apos;
+      TRACE("Combination attack appears at %1m.\n", apos);
+      return_value += (float) atari;
+    }
+  }
+
+  return return_value;
 }


@@ -975,10 +999,16 @@
  * misnomer since this includes attacks which are not necessarily
  * double ataris, though the common double atari is the most
  * important special case.
+ *
+ * If safe_stones != NULL, then only attacks on stones marked as safe are
+ * tried.
+ *
+ * The value of the double atari attack is returned in *value (unless
+ * value is NULL), and the attacked stones are marked unsafe.
  */

 int
-double_atari(int move, int color)
+double_atari(int move, int color, float *value, char safe_stones[BOARDMAX])
 {
   int other = OTHER_COLOR(color);
   int k;
@@ -996,15 +1026,32 @@
     /* because (m, n) and (m+dm, n+dn) are opposite
      * corners of a square, ON_BOARD2(m, n) && ON_BOARD2(m+dm, n+dn)
      * implies ON_BOARD2(m+dm, n) and ON_BOARD2(n, n+dn)
+     *
+     * Only try to attack supposedly safe stones.
      */
     if (BOARD(m+dm, n+dn) == color
        && BOARD(m, n+dn) == other
        && BOARD(m+dm, n) == other
+       && (!safe_stones
+           || (safe_stones[POS(m, n+dn)] && safe_stones[POS(m+dm, n)]))
        && trymove(move, color, "double_atari", NO_MOVE, EMPTY, NO_MOVE)) {
       if (countlib(move) > 1
          && (BOARD(m, n+dn) == EMPTY || BOARD(m+dm, n) == EMPTY
              || !defend_both(POS(m, n+dn), POS(m+dm, n)))) {
        popgo();
+       if (value) {
+         if (worm[POS(m, n+dn)].effective_size
+             > worm[POS(m+dm, n)].effective_size) {
+           *value = 2.0 * worm[POS(m, n+dn)].effective_size;
+           if (safe_stones)
+             mark_string(POS(m, n+dn), safe_stones, 0);
+         }
+         else {
+           *value = 2.0 * worm[POS(m+dm, n)].effective_size;
+           if (safe_stones)
+             mark_string(POS(m+dm, n), safe_stones, 0);
+         }
+       }
        return 1;
       }
       popgo();
Index: engine/value_moves.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/value_moves.c,v
retrieving revision 1.49
diff -u -d -r1.49 value_moves.c
--- engine/value_moves.c        25 Aug 2002 11:14:36 -0000      1.49
+++ engine/value_moves.c        1 Sep 2002 15:05:01 -0000
@@ -91,19 +91,26 @@
   return own_strings + fewlibs;
 }

-
-/* Find saved dragons and worms, then call confirm_safety(). */
-static int
-value_moves_confirm_safety(int move, int color, int minsize)
+/* Find saved dragons and worms, then call blunder_size(). */
+static float
+value_moves_get_blunder_size(int move, int color)
 {
-  int saved_dragons[BOARDMAX];
-  int saved_worms[BOARDMAX];
+  char saved_dragons[BOARDMAX];
+  char saved_worms[BOARDMAX];
+  char safe_stones[BOARDMAX];

   get_saved_dragons(move, saved_dragons);
   get_saved_worms(move, saved_worms);
+
+  mark_safe_stones(color, move, saved_dragons, saved_worms, safe_stones);

-  return confirm_safety(move, color, minsize, NULL,
-                       saved_dragons, saved_worms);
+  return blunder_size(move, color, NULL, safe_stones);
+}
+
+static int
+value_moves_confirm_safety(int move, int color)
+{
+  return (value_moves_get_blunder_size(move, color) == 0.0);
 }


@@ -2452,7 +2459,7 @@
       && board[pos] == EMPTY
       && move[pos].additional_ko_value > 0.0
       && is_legal(pos, color)
-      && value_moves_confirm_safety(pos, color, 0)) {
+      && value_moves_confirm_safety(pos, color)) {
     float new_tot_value = gg_min(pure_threat_value,
                                 tot_value
                                 + 0.25 * move[pos].additional_ko_value);
@@ -2577,7 +2584,7 @@

 /* Add a move to the list of top moves (if it is among the top ten) */

-  void
+void
 record_top_move(int move, float val)
 {
   int k;
@@ -2771,68 +2778,23 @@
   }
 }

-
-/*
- * Review the move reasons to find which (if any) move we want to play.
- *
- * The parameter pure_threat_value is the value assigned to a move
- * which only threatens to capture or kill something. The reason for
- * playing these is that the move may be effective because we have
- * misevaluated the dangers or because the opponent misplays.
+/* This selects the best move available according to their valuations.
+ * If the best move is an illegal ko capture, we add ko threat values.
+ * If the best move is a blunder, it gets devalued and continue to look
+ * for the best move.
  */
-int
-review_move_reasons(int *the_move, float *val, int color,
-                   float pure_threat_value, float score)
+static int
+find_best_move(int *the_move, float *val, int color)
 {
-  int pos;
-  float tval;
-  float bestval = 0.0;
-  int best_move = NO_MOVE;
-  int ko_values_have_been_added = 0;
-  int allowed_blunder_size = 0;
-
   int good_move_found = 0;
-  int save_verbose;
-
-  start_timer(2);
-  if (!urgent || allpats) {
-    find_more_attack_and_defense_moves(color);
-    time_report(2, "  find_more_attack_and_defense_moves", NO_MOVE, 1.0);
-  }
-
-  save_verbose = verbose;
-  if (verbose > 0)
-    verbose--;
-  if (level > 5) {
-    find_more_owl_attack_and_defense_moves(color);
-    time_report(2, "  find_more_owl_attack_and_defense_moves", NO_MOVE, 1.0);
-  }
-  verbose = save_verbose;
-
-  if (verbose > 0)
-    verbose--;
-  examine_move_safety(color);
-  time_report(2, "  examine_move_safety", NO_MOVE, 1.0);
-  verbose = save_verbose;
-
-  /* We can't do this until move_safety is known. */
-  induce_secondary_move_reasons(color);
-  time_report(2, "  induce_secondary_move_reasons", NO_MOVE, 1.0);
-
-  if (printworms || verbose)
-    list_move_reasons(color);
-
-  /* Evaluate all moves with move reasons. */
-  value_moves(color, pure_threat_value, score);
-  time_report(2, "  value_moves", NO_MOVE, 1.0);
+  int ko_values_have_been_added = 0;
+  char blunder_tested[BOARDMAX];
+  float bestval;
+  int best_move;
+  int pos;

-  /* Perform point redistribution */
-  redistribute_points();
+  memset(blunder_tested, 0, sizeof(blunder_tested));

-  /* Search through all board positions for the 10 highest valued
-   * moves and print them.
-   */
-  print_top_moves();
   while (!good_move_found) {
     bestval = 0.0;
     best_move = NO_MOVE;
@@ -2842,7 +2804,7 @@
       if (!ON_BOARD(pos) || move[pos].final_value == 0.0)
          continue;

-      tval = move[pos].final_value;
+      float tval = move[pos].final_value;

       if (tval > bestval) {
        if (is_legal(pos, color) || is_illegal_ko_capture(pos, color)) {
@@ -2858,20 +2820,6 @@
       }
     }

-    /* Compute the size of strings we can allow to lose due to blunder
-     * effects. If ko threat values have been added, only the base
-     * value of the move must be taken into account here.
-     */
-    if (!ko_values_have_been_added || !ON_BOARD(best_move))
-      allowed_blunder_size = (int) (bestval / 2 - 1);
-    else {
-      int base_value;
-
-      ASSERT_ON_BOARD1(best_move);
-      base_value = bestval - move[best_move].additional_ko_value;
-      allowed_blunder_size = (int) (base_value / 2 - 1);
-    }
-
     /* If the best move is an illegal ko capture, reevaluate ko
      * threats and search again.
      */
@@ -2887,21 +2835,33 @@
       print_top_moves();
       good_move_found = 0;
     }
-    /* Call confirm_safety() to check that we're not about to make a
-     * blunder. Otherwise reject this move and scan through all move
+    /* Call blunder_size() to check that we're not about to make a
+     * blunder. Otherwise devalue this move and scan through all move
      * values once more.
      */
-    else if (bestval > 0.0
-            && !value_moves_confirm_safety(best_move, color,
-                                           allowed_blunder_size)) {
-      TRACE("Move at %1m would be a blunder.\n", best_move);
-      remove_top_move(best_move);
-      move[best_move].value = 0.0;
-      move[best_move].final_value = 0.0;
-      good_move_found = 0;
+    else if (bestval > 0.0) {
+      if (!blunder_tested[best_move]) {
+       float blunder_size = value_moves_get_blunder_size(best_move, color);
+       if (blunder_size > 0.0) {
+         TRACE("Move at %1m is a blunder, subtracting %f.\n", best_move,
+               blunder_size);
+         remove_top_move(best_move);
+         move[best_move].value -= blunder_size;
+         move[best_move].final_value -= blunder_size;
+         TRACE("Move at %1m is now valued %f.\n", best_move,
+               move[best_move].final_value);
+         record_top_move(best_move, move[best_move].final_value);
+         good_move_found = 0;
+         blunder_tested[best_move] = 1;
+       }
+       else
+         good_move_found = 1; /* Best move was not a blunder. */
+      }
+      else /* The move apparently was a blunder, but still the best move. */
+       good_move_found = 1;
     }
     else
-      good_move_found = 1;
+      good_move_found = 1; /* It's best to pass. */
   }

   if (bestval > 0.0
@@ -2912,6 +2872,65 @@
   }

   return 0;
+}
+
+
+/*
+ * Review the move reasons to find which (if any) move we want to play.
+ *
+ * The parameter pure_threat_value is the value assigned to a move
+ * which only threatens to capture or kill something. The reason for
+ * playing these is that the move may be effective because we have
+ * misevaluated the dangers or because the opponent misplays.
+ */
+int
+review_move_reasons(int *the_move, float *val, int color,
+                   float pure_threat_value, float score)
+{
+  int save_verbose;
+
+  start_timer(2);
+  if (!urgent || allpats) {
+    find_more_attack_and_defense_moves(color);
+    time_report(2, "  find_more_attack_and_defense_moves", NO_MOVE, 1.0);
+  }
+
+  save_verbose = verbose;
+  if (verbose > 0)
+    verbose--;
+  if (level > 5) {
+    find_more_owl_attack_and_defense_moves(color);
+    time_report(2, "  find_more_owl_attack_and_defense_moves", NO_MOVE, 1.0);
+  }
+  verbose = save_verbose;
+
+  if (verbose > 0)
+    verbose--;
+  examine_move_safety(color);
+  time_report(2, "  examine_move_safety", NO_MOVE, 1.0);
+  verbose = save_verbose;
+
+  /* We can't do this until move_safety is known. */
+  induce_secondary_move_reasons(color);
+  time_report(2, "  induce_secondary_move_reasons", NO_MOVE, 1.0);
+
+  if (printworms || verbose)
+    list_move_reasons(color);
+
+  /* Evaluate all moves with move reasons. */
+  value_moves(color, pure_threat_value, score);
+  time_report(2, "  value_moves", NO_MOVE, 1.0);
+
+  /* Perform point redistribution */
+  redistribute_points();
+
+  /* Search through all board positions for the 10 highest valued
+   * moves and print them.
+   */
+  print_top_moves();
+
+  /* Select the highest valued move and return it. */
+  return find_best_move(the_move, val, color);
 }


Index: interface/play_gtp.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/interface/play_gtp.c,v
retrieving revision 1.85
diff -u -d -r1.85 play_gtp.c
--- interface/play_gtp.c        18 Aug 2002 21:41:02 -0000      1.85
+++ interface/play_gtp.c        1 Sep 2002 15:05:10 -0000
@@ -1677,8 +1677,8 @@
   int minsize = 0;
   int result;
   int defense_point = NO_MOVE;
-  int saved_dragons[BOARDMAX];
-  int saved_worms[BOARDMAX];
+  char saved_dragons[BOARDMAX];
+  char saved_worms[BOARDMAX];

   n = gtp_decode_move(s, &color, &i, &j);
   if (n == 0)





reply via email to

[Prev in Thread] Current Thread [Next in Thread]