gnugo-devel
[Top][All Lists]
Advanced

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

[gnugo-devel] Optics patch


From: Gunnar Farneback
Subject: [gnugo-devel] Optics patch
Date: Wed, 28 Nov 2001 21:57:16 +0100
User-agent: EMH/1.14.1 SEMI/1.14.3 (Ushinoya) FLIM/1.14.2 (Yagi-Nishiguchi) APEL/10.3 Emacs/20.7 (sparc-sun-solaris2.7) (with unibyte mode)

This patch implements the topological eye with ko concept. As a side
effect the topological sum is now stored in struct half_eye_data. This
has important consequences for the owl tuning. Previously
owl_topological_eye was a very expensive autohelper. This is now
reversed since it only looks up already computed data. However, in
some situations (vertices which are at most marginal eyespaces)
owl_topological_eye() won't be effective at all. We may want to change
this, but for now I want to see if this is a real problem.

I also removed the functions make_proper_eye_space() and
remove_eyepoint(). Those were once used in some failed experiments and
are not very interesting with the owl paradigm in place.

- new field value in struct half_eye_data
- topological_eye() and evaluate_diagonal_intersection() reimplemented
  to take ko into account
- improved debug output in compute_eyes() and compute_eyes_pessimistic()
- add_half_eye() renamed add_false_eye
- make_proper_eye_space() and remove_eyepoint() removed
- owl_topological_eye() reimplemented to look up stored data

/Gunnar

Index: engine/dragon.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/dragon.c,v
retrieving revision 1.30
diff -u -r1.30 dragon.c
--- engine/dragon.c     2001/11/26 14:54:28     1.30
+++ engine/dragon.c     2001/11/28 20:22:38
@@ -97,9 +97,10 @@
       dragon[ii].owl_threat_status  = UNCHECKED;
       dragon[ii].owl_second_attack_point  = NO_MOVE;
       dragon[ii].owl_second_defense_point = NO_MOVE;
-      half_eye[ii].type        =  0;
+      half_eye[ii].type             =  0;
+      half_eye[ii].value            =  10.0; /* Something big. */
       
-      if (worm[ii].origin == ii)
+      if (IS_STONE(board[ii]) && worm[ii].origin == ii)
        DEBUG(DEBUG_DRAGONS, 
              "Initialising dragon from worm at %1m, size %d\n", 
              ii, worm[ii].size);
@@ -225,11 +226,14 @@
   /* Find topological half eyes and false eyes by analyzing the
    * diagonal intersections, as described in the Texinfo
    * documentation (Eyes/Eye Topology).
+   *
+   * FIXME: Consolidate this piece of code with the very similar one
+   * in owl_determine_life().
    */
 
   for (m = 0; m < board_size; m++)
     for (n = 0; n < board_size; n++) {
-      int sum;
+      float sum;
       ii = POS(m, n);
 
       if (black_eye[ii].color == BLACK_BORDER
@@ -237,14 +241,14 @@
          && black_eye[ii].neighbors <= 1
          && black_eye[ii].dragon != NO_MOVE) {
        sum = topological_eye(ii, BLACK, black_eye, white_eye, half_eye);
-       if (sum >= 4) {
+       if (sum >= 4.0) {
          half_eye[ii].type = FALSE_EYE;
          if (black_eye[ii].esize == 1
              || is_legal(ii, WHITE)
              || board[ii] == WHITE)
-           add_half_eye(ii, black_eye, half_eye);
+           add_false_eye(ii, black_eye, half_eye);
        }
-       else if (sum == 3)
+       else if (sum > 2.0)
          half_eye[ii].type = HALF_EYE;
       }
       
@@ -253,14 +257,14 @@
          && white_eye[ii].neighbors <= 1
          && white_eye[ii].dragon != NO_MOVE) {
        sum = topological_eye(ii, WHITE, black_eye, white_eye, half_eye);
-       if (sum >= 4) {
+       if (sum >= 4.0) {
          half_eye[ii].type = FALSE_EYE;
          if (white_eye[ii].esize == 1
              || is_legal(ii, BLACK)
              || board[ii] == BLACK)
-           add_half_eye(ii, white_eye, half_eye);
+           add_false_eye(ii, white_eye, half_eye);
        }
-       else if (sum == 3)
+       else if (sum > 2.0)
          half_eye[ii].type = HALF_EYE;
       }
     }
Index: engine/liberty.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/liberty.h,v
retrieving revision 1.49
diff -u -r1.49 liberty.h
--- engine/liberty.h    2001/11/28 18:35:18     1.49
+++ engine/liberty.h    2001/11/28 20:22:39
@@ -484,8 +484,6 @@
 int is_proper_eye_space(int pos);
 int is_marginal_eye_space(int pos);
 int max_eye_value(int pos);
-void make_proper_eye_space(int pos, int color);
-void remove_eyepoint(int pos, int color);
 void test_eyeshape(int eyesize, int *eye_vertices);
 
 
@@ -560,6 +558,7 @@
 
 
 struct half_eye_data {
+  float value;      /* Topological eye value. */
   int type;         /* HALF_EYE or FALSE_EYE; */
   int num_attacks;  /* number of attacking points */
   int attack_point[4];  /* the move to attack a topological halfeye */
@@ -744,12 +743,12 @@
                   struct half_eye_data heye[BOARDMAX],
                     int add_moves, int color);
 void propagate_eye(int pos, struct eye_data eye[BOARDMAX]);
-int topological_eye(int pos, int color,
-                    struct eye_data b_eye[BOARDMAX],
-                    struct eye_data w_eye[BOARDMAX],
-                    struct half_eye_data heye[BOARDMAX]);
-void add_half_eye(int pos, struct eye_data eye[BOARDMAX], 
-                  struct half_eye_data heye[BOARDMAX]);
+float topological_eye(int pos, int color,
+                     struct eye_data b_eye[BOARDMAX],
+                     struct eye_data w_eye[BOARDMAX],
+                     struct half_eye_data heye[BOARDMAX]);
+void add_false_eye(int pos, struct eye_data eye[BOARDMAX], 
+                  struct half_eye_data heye[BOARDMAX]);
 void make_domains(struct eye_data b_eye[BOARDMAX],
                   struct eye_data w_eye[BOARDMAX],
                  int owl_call);
Index: engine/optics.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/optics.c,v
retrieving revision 1.28
diff -u -r1.28 optics.c
--- engine/optics.c     2001/11/26 14:54:28     1.28
+++ engine/optics.c     2001/11/28 20:22:41
@@ -79,7 +79,7 @@
 static int next_map(int *q, int map[MAXEYE], int esize);
 static void print_eye(struct eye_data eye[BOARDMAX],
                      struct half_eye_data heye[BOARDMAX], int pos);
-static int 
+static float 
 evaluate_diagonal_intersection(int m, int n, int color,
                               int *attack_point, int *defense_point,
                               struct eye_data b_eye[BOARDMAX],
@@ -116,9 +116,9 @@
 
 
 /*
- * make_domains() is called from make_dragons(). It marks the black
- * and white domains (eyeshape regions) and collects some statistics
- * about each one.
+ * make_domains() is called from make_dragons() and from
+ * owl_determine_life(). It marks the black and white domains
+ * (eyeshape regions) and collects some statistics about each one.
  */
 
 void
@@ -572,7 +572,7 @@
  */
 
 void
-propagate_eye (int origin, struct eye_data eye[BOARDMAX])
+propagate_eye(int origin, struct eye_data eye[BOARDMAX])
 {
   int pos;
 
@@ -652,7 +652,7 @@
 
 
 /* 
- * Given an eyespace with origin (i,j), this function computes the
+ * Given an eyespace with origin (pos), this function computes the
  * minimum and maximum numbers of eyes the space can yield. If max and
  * min are different, then vital points of attack and defense are also
  * generated.
@@ -689,14 +689,24 @@
 
        if (eye[pos2].marginal && IS_STONE(board[pos2]))
          DEBUG(DEBUG_EYES, "%1m (X!)\n", pos2);
-       else if (eye[pos2].marginal && board[pos2] == EMPTY)
-         DEBUG(DEBUG_EYES, "%1m (!)\n", pos2);
+       else if (is_halfeye(heye, pos2) && IS_STONE(board[pos2])) {
+         if (heye[pos2].value == 3.0)
+           DEBUG(DEBUG_EYES, "%1m (XH)\n", pos2);
+         else
+           DEBUG(DEBUG_EYES, "%1m (XH) (topological eye value = %f\n", pos2,
+                 heye[pos2].value);
+       }
        else if (!eye[pos2].marginal && IS_STONE(board[pos2]))
          DEBUG(DEBUG_EYES, "%1m (X)\n", pos2);
-       else if (is_halfeye(heye, pos2) && board[pos2] == EMPTY)
-         DEBUG(DEBUG_EYES, "%1m (H)\n", pos2);
-       else if (is_halfeye(heye, pos2) && IS_STONE(board[pos2]))
-         DEBUG(DEBUG_EYES, "%1m (XH)\n", pos2);
+       else if (eye[pos2].marginal && board[pos2] == EMPTY)
+         DEBUG(DEBUG_EYES, "%1m (!)\n", pos2);
+       else if (is_halfeye(heye, pos2) && board[pos2] == EMPTY) {
+         if (heye[pos2].value == 3.0)
+           DEBUG(DEBUG_EYES, "%1m (H)\n", pos2);
+         else
+           DEBUG(DEBUG_EYES, "%1m (H) (topological eye value = %f\n", pos2,
+                 heye[pos2].value);
+       }
        else
          DEBUG(DEBUG_EYES, "%1m\n", pos2);
       }
@@ -854,14 +864,24 @@
 
        if (eye[pos2].marginal && IS_STONE(board[pos2]))
          DEBUG(DEBUG_EYES, "%1m (X!)\n", pos2);
-       else if (eye[pos2].marginal && board[pos2] == EMPTY)
-         DEBUG(DEBUG_EYES, "%1m (!)\n", pos2);
+       else if (is_halfeye(heye, pos2) && IS_STONE(board[pos2])) {
+         if (heye[pos2].value == 3.0)
+           DEBUG(DEBUG_EYES, "%1m (XH)\n", pos2);
+         else
+           DEBUG(DEBUG_EYES, "%1m (XH) (topological eye value = %f\n", pos2,
+                 heye[pos2].value);
+       }
        else if (!eye[pos2].marginal && IS_STONE(board[pos2]))
          DEBUG(DEBUG_EYES, "%1m (X)\n", pos2);
-       else if (is_halfeye(heye, pos2) && board[pos2] == EMPTY)
-         DEBUG(DEBUG_EYES, "%1m (H)\n", pos2);
-       else if (is_halfeye(heye, pos2) && IS_STONE(board[pos2]))
-         DEBUG(DEBUG_EYES, "%1m (XH)\n", pos2);
+       else if (eye[pos2].marginal && board[pos2] == EMPTY)
+         DEBUG(DEBUG_EYES, "%1m (!)\n", pos2);
+       else if (is_halfeye(heye, pos2) && board[pos2] == EMPTY) {
+         if (heye[pos2].value == 3.0)
+           DEBUG(DEBUG_EYES, "%1m (H)\n", pos2);
+         else
+           DEBUG(DEBUG_EYES, "%1m (H) (topological eye value = %f\n", pos2,
+                 heye[pos2].value);
+       }
        else
          DEBUG(DEBUG_EYES, "%1m\n", pos2);
       }
@@ -1373,7 +1393,7 @@
 
 /* recognize_eye(pos, *attack_point, *defense_point, *max, *min, eye_data, 
  * half_eye_data, add_moves, color), where pos is the origin of an eyespace,
- * returns 1 if there is a pattern in eyes.c matching the eyespace, or
+ * returns 1 if there is a pattern in eyes.db matching the eyespace, or
  * 0 if no match is found. If there is a key point for attack, (*attack_point)
  * is set to its location, or NO_MOVE if there is none.
  * Similarly (*defense_point) is the location of a vital defense point. *min
@@ -1716,31 +1736,26 @@
 }     
 
 
-/* add_half_eye adds a half eye or false eye to an eye shape. */
+/* add_false_eye() turns a proper eyespace into a margin. */
 
 void
-add_half_eye(int pos, struct eye_data eye[BOARDMAX],
-            struct half_eye_data heye[BOARDMAX])
+add_false_eye(int pos, struct eye_data eye[BOARDMAX],
+             struct half_eye_data heye[BOARDMAX])
 {
   int k;
-  if (heye[pos].type)
-    DEBUG(DEBUG_EYES, "half or false eye found at %1m\n", pos);
+  ASSERT1(heye[pos].type == FALSE_EYE, pos);
+  DEBUG(DEBUG_EYES, "false eye found at %1m\n", pos);
 
-  if (heye[pos].type == FALSE_EYE) {
-    DEBUG(DEBUG_EYES, "false eye at %1m for dragon at %1m\n",
-         pos, eye[pos].dragon);
-    if (eye[pos].color != GRAY) {
-      if (eye[pos].marginal == 0) {
-       eye[pos].marginal = 1;
-       eye[eye[pos].origin].msize++;
-       for (k = 0; k < 4; k++)
-         if (ON_BOARD(pos + delta[k])
-             && eye[pos + delta[k]].origin == eye[pos].origin)
-           eye[pos + delta[k]].marginal_neighbors++;
-       propagate_eye(eye[pos].origin, eye);
-      }
-    }
-  }
+  if (eye[pos].color == GRAY || eye[pos].marginal != 0)
+    return;
+  
+  eye[pos].marginal = 1;
+  eye[eye[pos].origin].msize++;
+  for (k = 0; k < 4; k++)
+    if (ON_BOARD(pos + delta[k])
+       && eye[pos + delta[k]].origin == eye[pos].origin)
+      eye[pos + delta[k]].marginal_neighbors++;
+  propagate_eye(eye[pos].origin, eye);
 }
 
 
@@ -1795,68 +1810,38 @@
   return heye[pos].type == HALF_EYE;
 }
 
-/* Turn a marginal eye space into a proper eye space. */
-void
-make_proper_eye_space(int pos, int color)
-{
-  struct eye_data  *eye;
-  int k;
-
-  if (color == WHITE)
-    eye = white_eye;
-  else
-    eye = black_eye;
-
-  gg_assert(eye[pos].color != GRAY_BORDER);
-  gg_assert(eye[pos].marginal == 1);
-  
-  eye[pos].marginal = 0;
-  
-  eye[eye[pos].origin].msize--;
-  for (k = 0; k < 4; k++)
-    if (ON_BOARD(pos + delta[k])
-       && eye[pos + delta[k]].origin == eye[pos].origin)
-      eye[pos + delta[k]].marginal_neighbors--;
-
-  propagate_eye(eye[pos].origin, eye);
-}
-
-/* Remove an eye point. This function can only be used before the
- * segmentation into eyespaces.
- */
-void
-remove_eyepoint(int pos, int color)
-{
-  if (color == WHITE)
-    white_eye[pos].color = GRAY_BORDER;
-  else
-    black_eye[pos].color = GRAY_BORDER;
-}
-
-
 /* See Texinfo documentation (Eyes:Eye Topology). Returns:
- * 2 or less if (m, n) is a proper eye for (color);
- * 3 if (m, n) is a half eye;
- * 4 if (m, n) is a false eye.
+ * - 2 or less if (pos) is a proper eye for (color);
+ * - between 2 and 3 if the eye can be made false only by ko
+ * - 3 if (pos) is a half eye;
+ * - between 3 and 4 if the eye can be made real only by ko
+ * - 4 or more if (pos) is a false eye.
  *
- * (*ai, *aj) and (*di, *dj) returns the coordinates of an empty
- * unsettled diagonal intersection, or an attack and defense point
- * respectively of an unsettled diagonal opponent worm.
+ * Attack and defense points for control of the diagonals are stored
+ * in the heye[] array.
  */
 
-int
+float
 topological_eye(int pos, int color,
                struct eye_data b_eye[BOARDMAX],
                struct eye_data w_eye[BOARDMAX],
                struct half_eye_data heye[BOARDMAX])
 {
-  int sum = 0;
-  int val;
+  float sum = 0.0;
+  float val;
   int num_attacks = 0;
   int num_defenses = 0;
+  int attack_values[4];
+  int defense_values[4];
   int k;
+  int r;
   int attack_point;
   int defense_point;
+  int attack_value;
+  int defense_value;
+
+  memset(attack_values, 0, sizeof(attack_values));
+  memset(defense_values, 0, sizeof(defense_values));
   
   /* Loop over the diagonal directions. */
   for (k = 4; k < 8; k++) {
@@ -1865,15 +1850,60 @@
                                         &attack_point, &defense_point, 
                                         b_eye, w_eye);
     sum += val;
-    if (val == 1) {
-      if (attack_point != NO_MOVE) {
+    if (val > 0.0 && val < 2.0) {
+      /* Diagonals off the edge has value 1.0 but no attack or defense
+       * point.
+       */
+      if (attack_point != NO_MOVE && defense_point != NO_MOVE) {
        ASSERT_ON_BOARD1(attack_point);
-       heye[pos].attack_point[num_attacks] = attack_point;
-       num_attacks++;
-      }
-      if (defense_point != NO_MOVE) {
        ASSERT_ON_BOARD1(defense_point);
-       heye[pos].defense_point[num_defenses] = defense_point;
+       /* Store these in sorted (descending) order. We remap val
+         * differently for attack and defense points according to:
+        *
+        * val    attack_value     defense_value
+        * ---    ------------     -------------
+        * 1.0    3                3
+        * <1.0   2                1
+        * >1.0   1                2
+        *
+        * This means that we primarily want to take control of
+        * diagonals without ko and secondarily of diagonals we can
+        * take unconditionally but not the opponent.
+        */
+       if (val == 1.0) {
+         attack_value = 3;
+         defense_value = 3;
+       }
+       else if (val < 1.0) {
+         attack_value = 2;
+         defense_value = 3;
+       }
+       else {
+         attack_value = 3;
+         defense_value = 2;
+       }
+
+       for (r = 0; r < 4; r++) {
+         if (attack_values[r] < attack_value) {
+           int tmp_value = attack_values[r];
+           int tmp_point = heye[pos].attack_point[r];
+           attack_values[r] = attack_value;
+           heye[pos].attack_point[r] = attack_point;
+           attack_value = tmp_value;
+           attack_point = tmp_point;
+         }
+       
+         if (defense_values[r] < defense_value) {
+           int tmp_value = defense_values[r];
+           int tmp_point = heye[pos].defense_point[r];
+           defense_values[r] = defense_value;
+           heye[pos].defense_point[r] = defense_point;
+           defense_value = tmp_value;
+           defense_point = tmp_point;
+         }
+       }
+       
+       num_attacks++;
        num_defenses++;
       }
     }
@@ -1881,40 +1911,53 @@
 
   heye[pos].num_attacks = num_attacks;
   heye[pos].num_defends = num_defenses;
+  heye[pos].value = sum;
+  
   return sum;
 }
 
 
 
-/* Evaluate an intersection (m, n) which is diagonal to an eye space
- * (i, j), as described in the Texinfo documentation (Eyes/Eye
- * Topology).
- * Returns 0 if the opponent cannot safely play at the vertex;
- * Returns 1 if empty and the opponent can safely play on it;
- * Returns 2 if safely occupied by the opponent.
+/* Evaluate an intersection (m, n) which is diagonal to an eye space,
+ * as described in the Texinfo documentation (Eyes/Eye Topology).
+ *
+ * Returns:
  *
- * Exception: if one coordinate is off the board, returns 1;
- * if both are off the board, returns 0. This guarantees
- * correct behavior for diagonal intersections of points
- * on the edge or in the corner.
+ * 0 if both coordinates are off the board
+ * 1 if one coordinate is off the board
  *
+ * 0    if (color) has control over the vertex
+ * a    if (color) can take control over the vertex unconditionally and
+ *      the opponent can take control by winning a ko.
+ * 1    if both (color) and the opponent can take control of the vertex
+ *      unconditionally
+ * b    if (color) can take control over the vertex by winning a ko and
+ *      the opponent can take control unconditionally.
+ * 2    if the opponent has control over the vertex
+ *
+ * The values a and b are discussed in the documentation. We are
+ * currently using a = 0.75 and b = 1.25.
+ *
  * Notice that it's necessary to pass the coordinates separately
  * instead of as a 1D coordinate. The reason is that the 1D mapping
  * can't uniquely identify "off the corner" points.
  */
-static int 
+static float
 evaluate_diagonal_intersection(int m, int n, int color,
                               int *attack_point, int *defense_point,
                               struct eye_data b_eye[BOARDMAX],
                               struct eye_data w_eye[BOARDMAX])
 {
-  int value = 0;
+  float value = 0;
   int other = OTHER_COLOR(color);
   int pos = POS(m, n);
   int acode = 0;
   int apos = NO_MOVE;
   int dcode = 0;
   int dpos = NO_MOVE;
+  int off_edge = 0;
+  const float a = 0.75;
+  const float b = 2 - a;
 
   *attack_point = NO_MOVE;
   *defense_point = NO_MOVE;
@@ -1924,13 +1967,14 @@
    * are special cases.
    */
   if (m < 0 || m >= board_size)
-    value++;
+    off_edge++;
 
   if (n < 0 || n >= board_size)
-    value++;
+    off_edge++;
 
-  if (value > 0)
-    return value % 2; /* Must return 0 if both coordinates out of bounds. */
+  /* Must return 0 if both coordinates out of bounds. */
+  if (off_edge > 0)
+    return (float) (off_edge % 2);
 
   /* Discard points within own eyespace, unless marginal or ko point.
    *
@@ -1973,64 +2017,96 @@
       && !b_eye[pos].marginal
       && b_eye[pos].marginal_neighbors < 2
       && !(board[pos] == EMPTY && does_capture_something(pos, WHITE)))
-    return 0;
+    return 0.0;
   if (color == WHITE
       && w_eye[pos].color == WHITE_BORDER
       && !w_eye[pos].marginal
       && w_eye[pos].marginal_neighbors < 2
       && !(board[pos] == EMPTY && does_capture_something(pos, BLACK)))
-    return 0;
+    return 0.0;
 
-  if (board[pos] == EMPTY && safe_move(pos, other) != 0)
-    value = 1;
-  else {
+  if (board[pos] == EMPTY) {
+    /* We should normally have a safe move, but occasionally it may
+     * happen that it's not safe. There are complications, however,
+     * with a position like this
+     *
+     * .XXXX|
+     * XXOO.|
+     * XO.O.|
+     * XXO.O|
+     * -----+
+     *
+     */
+
+    int our_safety = safe_move(pos, color);
+    int your_safety = safe_move(pos, other);
+    
+    if (your_safety == 0)
+      value = 0.0;
+    else if (our_safety == 0 && your_safety == WIN)
+      value = 2.0;
+    else if (our_safety == WIN && your_safety == WIN)
+      value = 1.0;
+    else if (our_safety == WIN && your_safety != WIN)
+      value = a;
+    else if (our_safety != WIN && your_safety == WIN)
+      value = b;
+    else
+      value = 1.0; /* Both contingent on ko. Probably can't happen. */
+
+    apos = pos;
+    dpos = pos;
+  }
+  else if (board[pos] == color) {
+    /* This stone had better be safe, otherwise we wouldn't have an
+     * eyespace in the first place.
+     */
+    value = 0.0;
+  }
+  else if (board[pos] == other) {
     if (stackp == 0) {
-      if (board[pos] == other) {
-       if (worm[pos].attack_codes[0] == 0)
-         value = 2;
-       else if (worm[pos].defend_codes[0] != 0) {
-         value = 1;
-         apos = worm[pos].attack_points[0];
-         dpos = worm[pos].defense_points[0];
-       }
-      }
+      acode = worm[pos].attack_codes[0];
+      apos  = worm[pos].attack_points[0];
+      dcode = worm[pos].defend_codes[0];
+      dpos  = worm[pos].defense_points[0];
     }
-    else {
-      if (board[pos] == other) {
-       attack_and_defend(pos, &acode, &apos, &dcode, &dpos);
-       if (acode == 0)
-         value = 2;
-       else if (dcode != 0)
-         value = 1;
-      }
-    }   
-  }
-
-  if (value == 1) {
-    if (board[pos] == EMPTY) {
-      if (!safe_move(pos, color))
-       value = 2;
-      else {
-       *attack_point = pos;
-       *defense_point = pos;
-      }
-    }
-    else {
-      /* FIXME:
-       * Usually there are several attack and defense moves that would
-       * be equally valid. It's not good that we make an arbitrary
-       * choice at this point.
-       */
-      ASSERT_ON_BOARD1(apos);
-      ASSERT_ON_BOARD1(dpos);
-      /* Notice:
-       * The point to ATTACK the half eye is the point which DEFENDS
-       * the stones on the diagonal intersection and vice versa. Thus
-       * we must switch attack and defense points here.
-       */
-      *attack_point = dpos;
-      *defense_point = apos;
-    }
+    else
+      attack_and_defend(pos, &acode, &apos, &dcode, &dpos);
+
+    /* Must test acode first since dcode only is reliable if acode is
+     * non-zero.
+     */
+    if (acode == 0)
+      value = 2.0;
+    else if (dcode == 0)
+      value = 0.0;
+    else if (acode == WIN && dcode == WIN)
+      value = 1.0;
+    else if (acode == WIN && dcode != WIN)
+      value = a;
+    else if (acode != WIN && dcode == WIN)
+      value = b;
+    else if (acode != WIN && dcode != WIN)
+      value = 1.0; /* Both contingent on ko. Probably can't happen. */
+  }
+  
+  if (value > 0.0 && value < 2.0) {
+    /* FIXME:
+     * Usually there are several attack and defense moves that would
+     * be equally valid. It's not good that we make an arbitrary
+     * choice at this point.
+     */
+    ASSERT_ON_BOARD1(apos);
+    ASSERT_ON_BOARD1(dpos);
+    /* Notice:
+     * The point to ATTACK the half eye is the point which DEFENDS
+     * the stones on the diagonal intersection and vice versa. Thus
+     * we must switch attack and defense points here.
+     * If the vertex is empty, dpos == apos and it doesn't matter
+     * whether we switch.
+     */
+    *attack_point = dpos;
+    *defense_point = apos;
   }
 
   return value;
Index: engine/owl.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/owl.c,v
retrieving revision 1.37
diff -u -r1.37 owl.c
--- engine/owl.c        2001/11/28 18:35:18     1.37
+++ engine/owl.c        2001/11/28 20:22:46
@@ -1939,10 +1939,13 @@
       }
     }
 
+  /* Reset halfeye data. Set topological eye value to something big. */
   for (m = 0; m < board_size; m++)
-    for (n = 0; n < board_size; n++)
+    for (n = 0; n < board_size; n++) {
       owl->half_eye[POS(m, n)].type = 0;
-
+      owl->half_eye[POS(m, n)].value = 10.0;
+    }
+  
   /* Find topological half eyes and false eyes by analyzing the
    * diagonal intersections, as described in the Texinfo
    * documentation (Eyes/Eye Topology).
@@ -1968,7 +1971,7 @@
     for (m = 0; m<board_size; m++)
       for (n = 0; n<board_size; n++) {
        int pos = POS(m, n);
-       int sum;
+       float sum;
 
        if (mx[pos] <= 0)
          continue;
@@ -1978,14 +1981,15 @@
        
        sum = topological_eye(pos, color, owl->black_eye, owl->white_eye,
                              owl->half_eye);
-
-       if (sum >= 4) {
+       
+       if (sum >= 4.0) {
+         /* False eye. */
          int previously_marginal = eye[pos].marginal;
          owl->half_eye[pos].type = FALSE_EYE;
          if (eye[pos].esize == 1
              || is_legal(pos, OTHER_COLOR(color))
              || board[pos] == OTHER_COLOR(color)) {
-           add_half_eye(pos, eye, owl->half_eye);
+           add_false_eye(pos, eye, owl->half_eye);
            
            /* Marginal status may have changed. This can change the
              * topological eye evaluation for diagonal neighbors, so
@@ -2005,7 +2009,7 @@
            }
          }
        }
-       else if (sum == 3) {
+       else if (sum > 2.0) {
          owl->half_eye[pos].type = HALF_EYE;
          ASSERT1(owl->half_eye[pos].num_attacks > 0, pos);
          ASSERT_ON_BOARD1(owl->half_eye[pos].attack_point[0]);
@@ -2122,12 +2126,12 @@
           *
           * In both cases * is the vital point according to the graph
           * matching. The significant difference is that in the first
-          * case the vital point is a margin.
+          * case the vital point is adjacent to stones in the goal.
           */
          else if (!does_attack
                   && defense_point != NO_MOVE
                   && board[defense_point] == EMPTY
-                  && (!eye[attack_point].marginal
+                  && (!liberty_of_goal(defense_point, owl)
                       || !is_self_atari(defense_point, color)
                       || is_ko(defense_point, color, NULL)
                       || safe_move(defense_point, color) != 0)) {
@@ -2152,11 +2156,10 @@
        }
       }
     }
-  /* sniff each lunch for nutritional value. The
-     assumption is that capturing the lunch is gote,
-     therefore the number of half eyes equals the
-     MINIMUM number of eyes yielded by the resulting
-     eye space.
+  /* Sniff each lunch for nutritional value. The assumption is that
+   * capturing the lunch is gote, therefore the number of half eyes
+   * equals the MINIMUM number of eyes yielded by the resulting eye
+   * space.
    */
   {
     for (lunch = 0; (lunch < MAX_LUNCHES); lunch++)
@@ -3621,14 +3624,25 @@
 }
 
 
-/* Trampoline to topological_eye(). */
+/* Retrieve topological eye values stored in the half_eye[] array of
+ * the current owl data.
+ *
+ * FIXME: Sooner or later we'll want this to return a non-rounded
+ * value. When we change this, we have to review all patterns using
+ * the autohelper owl_topological_eye().
+ */
 int
 owl_topological_eye(int pos, int color)
 {
-  return topological_eye(pos, color,
-                        current_owl_data->black_eye,
-                        current_owl_data->white_eye,
-                        current_owl_data->half_eye);
+  float value;
+  UNUSED(color);
+  value = current_owl_data->half_eye[pos].value;
+  if (value > 2.0 && value < 4.0)
+    return 3;
+  else if (value <= 2.0)
+    return (int) (value + 0.99); /* Round up. */
+  else
+    return (int) value;          /* Round down. */
 }
 
 /* This function returns true if it is judged that the capture of the
Index: patterns/mkpat.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/mkpat.c,v
retrieving revision 1.30
diff -u -r1.30 mkpat.c
--- patterns/mkpat.c    2001/11/26 14:54:28     1.30
+++ patterns/mkpat.c    2001/11/28 20:22:50
@@ -235,8 +235,6 @@
   {"amalgamate_most_valuable_helper",3,
    "amalgamate_most_valuable_helper(%s,%s,%s)"},
   {"amalgamate",      2, "join_dragons(%s,%s)"},
-  {"make_proper_eye", 1, "make_proper_eye_space(%s,color)"},
-  {"remove_eyepoint", 1, "remove_eyepoint(%s,color)"},
   {"owl_escape_value",1, "owl_escape_value(%s)"},
   {"owl_goal_dragon", 1, "owl_goal_dragon(%s)"},
   {"owl_eyespace",    2, "owl_eyespace(%s,%s)"},



reply via email to

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