gnugo-devel
[Top][All Lists]
Advanced

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

[gnugo-devel] connect and cut move reasons


From: Gunnar Farneback
Subject: [gnugo-devel] connect and cut move reasons
Date: Fri, 03 May 2002 17:43:10 +0200
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 revises the handling of connect and cut move reasons, but
only to a minor extent the valuation. Currently these move reasons are
stored together with the two dragons they connect or cut. This doesn't
fit very well with the string-based connection reader. Instead we want
to store two worms with the move reason, which is implemented by this
patch. Since a worm uniquely determines a dragon, no information is
lost compared to the current implementation. In fact, when it comes to
valuation, only the dragons are considered.

We do, however, now have the potential to find moves which connect
strings within the (supposedly) same dragon. These can be very
powerful as reinforcement moves when we are ahead, in particular if
there is an adjacent dead opponent dragon.

The patch also reimplements induce_secondary_move_reasons() using
connection reading. This significantly simplifies the function and
makes it robuster.

Cutting and connecting worms instead of dragons has the side effect of
increasing the number of move reasons generated. MAX_REASONS has been
increased from 40 to 80.

There is one minor tuning of a shape pattern in the patch, and one new
test case.

The new function extended_chainlinks() works like chainlinks(), except
that it in addition to adjacent opponent strings also reports opponent
strings with a liberty in common with the given string. This comes in
handy in the revised induce_secondary_move_reasons().

Finally the patch removes the unused worm_pairs from the move reasons
code.

- new function extended_chainlinks() in board.c
- connection and cut move reasons are relative to worms instead of
  dragons
- unused function find_worm_pair() and related variables removed from
  move_reasons.c 
- MAX_REASONS increased from 40 to 80
- induce_secondary_move_reasons() reimplemented
- tuning
- new test case

The regression results for the patch are:

./regress.sh . strategy.tst
50 unexpected PASS!
./regress.sh . rosebud.tst
1 unexpected PASS!
./regress.sh . nicklas2.tst
2103 unexpected FAIL: Correct 'PASS', got 'C1'
./regress.sh . nicklas4.tst
1201 unexpected PASS!
./regress.sh . 13x13.tst
15 unexpected FAIL: Correct 'C7|B7|C6|B6|B5|C5', got 'E8'

The nicklas2:2103 failure is due to playing a move when all opponent
stones already are dead. Presumably some fix is needed here, but this
is by no means a serious failure.

The failure of 13x13:15 is directly caused by an owl reading mistake,
added as owl:262.

/Gunnar

Index: engine/board.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/board.c,v
retrieving revision 1.46
diff -u -r1.46 board.c
--- engine/board.c      1 Apr 2002 19:42:33 -0000       1.46
+++ engine/board.c      3 May 2002 09:29:40 -0000
@@ -2483,6 +2483,57 @@
 }
 
 
+/* extended_chainlinks() returns (in the (adj) array) the opponent
+ * strings being directly adjacent to (str) or having a common liberty
+ * with (str). The number of such strings is returned.
+ */
+
+int 
+extended_chainlinks(int str, int adj[MAXCHAIN])
+{
+  struct string_data *s;
+  int n;
+  int k;
+  int r;
+  int libs[MAXLIBS];
+  int liberties;
+
+  ASSERT1(IS_STONE(board[str]), str);
+
+  if (!strings_initialized)
+    init_board();
+
+  /* We already have the list of directly adjacent strings ready, just
+   * copy it and mark the strings.
+   */
+  s = &string[string_number[str]];
+  string_mark++;
+  for (n = 0; n < s->neighbors; n++) {
+    adj[n] = string[s->neighborlist[n]].origin;
+    MARK_STRING(adj[n]);
+  }
+
+  /* Get the liberties. */
+  liberties = findlib(str, MAXLIBS, libs);
+
+  /* Look for unmarked opponent strings next to a liberty and add the
+   * ones which are found to the output.
+   */
+  for (r = 0; r < liberties; r++) {
+    for (k = 0; k < 4; k++) {
+      if (board[libs[r] + delta[k]] == OTHER_COLOR(board[str])
+         && UNMARKED_STRING(libs[r] + delta[k])) {
+       adj[n] = string[string_number[libs[r] + delta[k]]].origin;
+       MARK_STRING(adj[n]);
+       n++;
+      }
+    }
+  }
+  
+  return n;
+}
+
+
 /*
  * Find the origin of a worm or a cavity, i.e. the point with the
  * smallest 1D board coordinate. The idea is to have a canonical
Index: engine/liberty.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/liberty.h,v
retrieving revision 1.97
diff -u -r1.97 liberty.h
--- engine/liberty.h    2 May 2002 18:33:36 -0000       1.97
+++ engine/liberty.h    3 May 2002 09:29:40 -0000
@@ -140,6 +140,7 @@
 int chainlinks(int str, int adj[MAXCHAIN]);
 int chainlinks2(int str, int adj[MAXCHAIN], int lib);
 int chainlinks3(int str, int adj[MAXCHAIN], int lib);
+int extended_chainlinks(int str, int adj[MAXCHAIN]);
 
 
 /* This is increased by one anytime a move is (permanently) played or
Index: engine/move_reasons.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/move_reasons.c,v
retrieving revision 1.84
diff -u -r1.84 move_reasons.c
--- engine/move_reasons.c       2 May 2002 18:33:36 -0000       1.84
+++ engine/move_reasons.c       3 May 2002 09:29:40 -0000
@@ -47,15 +47,10 @@
 int next_dragon;
 
 /* Connections */
-int conn_dragon1[MAX_CONNECTIONS];
-int conn_dragon2[MAX_CONNECTIONS];
+int conn_worm1[MAX_CONNECTIONS];
+int conn_worm2[MAX_CONNECTIONS];
 int next_connection;
 
-/* Unordered worm pairs */
-int worm_pair1[MAX_WORM_PAIRS];
-int worm_pair2[MAX_WORM_PAIRS];
-int next_worm_pair;
-
 /* Unordered sets (currently pairs) of move reasons / targets */
 Reason_set either_data[MAX_EITHER];
 int        next_either;
@@ -98,7 +93,6 @@
   next_worm = 0;
   next_dragon = 0;
   next_connection = 0;
-  next_worm_pair = 0;
   next_either = 0;
   next_all = 0;
   next_eye = 0;
@@ -188,62 +182,30 @@
  * If necessary, add a new entry.
  */
 static int
-find_connection(int dragon1, int dragon2)
+find_connection(int worm1, int worm2)
 {
   int k;
   
-  if (dragon1 > dragon2) {
+  if (worm1 > worm2) {
     /* Swap to canonical order. */
-    int tmp = dragon1;
-    dragon1 = dragon2;
-    dragon2 = tmp;
+    int tmp = worm1;
+    worm1 = worm2;
+    worm2 = tmp;
   }
   
   for (k = 0; k < next_connection; k++)
-    if ((conn_dragon1[k] == dragon1) && (conn_dragon2[k] == dragon2))
+    if (conn_worm1[k] == worm1 && conn_worm2[k] == worm2)
       return k;
   
   /* Add a new entry. */
   gg_assert(next_connection < MAX_CONNECTIONS);
-  conn_dragon1[next_connection] = dragon1;
-  conn_dragon2[next_connection] = dragon2;
+  conn_worm1[next_connection] = worm1;
+  conn_worm2[next_connection] = worm2;
   next_connection++;
   return next_connection - 1;
 }
 
 
-#if 0
-
-/*
- * Find the index of an unordered pair of worms in the list of worm pairs.
- * If necessary, add a new entry.
- */
-static int
-find_worm_pair(int worm1, int worm2)
-{
-  int k;
-  
-  /* Make sure the worms are ordered canonically. */
-  if (worm1 > worm2) {
-    int tmp = worm1;
-    worm1 = worm2;
-    worm2 = tmp;
-  }
-  
-  for (k = 0; k < next_worm_pair; k++)
-    if ((worm_pair1[k] == worm1) && (worm_pair2[k] == worm2))
-      return k;
-  
-  /* Add a new entry. */
-  gg_assert(next_worm_pair < MAX_WORM_PAIRS);
-  worm_pair1[next_worm_pair] = worm1;
-  worm_pair2[next_worm_pair] = worm2;
-  next_worm_pair++;
-  return next_worm_pair - 1;
-}
-
-#endif
-
 static int
 find_either_data(int reason1, int what1, int reason2, int what2)
 {
@@ -369,7 +331,7 @@
     return worms[all_data[what].what1];
   case CONNECT_MOVE:
   case CUT_MOVE:
-    return dragons[conn_dragon1[what]];
+    return dragons[conn_worm1[what]];
   case ANTISUJI_MOVE:
   case EXPAND_TERRITORY_MOVE:
   case EXPAND_MOYO_MOVE:
@@ -461,8 +423,12 @@
       return;  /* Reason already listed. */
   }
 
-  /* Reason not found, add it if there is place left in both lists. */
-  gg_assert(k<MAX_REASONS);
+  /* Reason not found, add it if there is place left in both lists.
+   *
+   * FIXME: We should just drop the move reason silently in stable
+   *        releases if some list is full.
+   */
+  gg_assert(k < MAX_REASONS);
   gg_assert(next_reason < MAX_MOVE_REASONS);
   /* Add a new entry. */
   move[pos].reason[k] = next_reason;
@@ -827,19 +793,26 @@
  * distinct.
  */
 void
-add_connection_move(int pos, int dr1, int dr2)
+add_connection_move(int pos, int w1, int w2)
 {
-  int dragon1 = find_dragon(dragon[dr1].origin);
-  int dragon2 = find_dragon(dragon[dr2].origin);
+  int worm1 = find_worm(worm[w1].origin);
+  int worm2 = find_worm(worm[w2].origin);
   int connection;
 
-  ASSERT_ON_BOARD1(dr1);
-  ASSERT_ON_BOARD1(dr2);
-  gg_assert(dragon[dr1].color == dragon[dr2].color);
-  if (dragon1 == dragon2)
+  ASSERT_ON_BOARD1(w1);
+  ASSERT_ON_BOARD1(w2);
+  gg_assert(worm[w1].color == worm[w2].color);
+  if (worm1 == worm2)
     return;
-  connection = find_connection(dragon1, dragon2);
+  
+  connection = find_connection(worm1, worm2);
   add_move_reason(pos, CONNECT_MOVE, connection);
+
+  /* We do this only for the side effect of being sure that the
+   * corresponding dragon gets into the list of dragons.
+   */
+  (void) find_dragon(dragon[w1].origin);
+  (void) find_dragon(dragon[w2].origin);
 }
 
 /*
@@ -848,28 +821,34 @@
  * distinct.
  */
 void
-add_cut_move(int pos, int dr1, int dr2)
+add_cut_move(int pos, int w1, int w2)
 {
-  int dragon1 = find_dragon(dragon[dr1].origin);
-  int dragon2 = find_dragon(dragon[dr2].origin);
+  int worm1 = find_worm(worm[w1].origin);
+  int worm2 = find_worm(worm[w2].origin);
   int connection;
 
-  ASSERT_ON_BOARD1(dr1);
-  ASSERT_ON_BOARD1(dr2);
-  gg_assert(dragon[dr1].color == dragon[dr2].color);
-  if (dragon1 == dragon2)
+  ASSERT_ON_BOARD1(w1);
+  ASSERT_ON_BOARD1(w2);
+  gg_assert(worm[w1].color == worm[w2].color);
+  if (worm1 == worm2)
     return;
-  connection = find_connection(dragon1, dragon2);
+  connection = find_connection(worm1, worm2);
   
   /*
-   * Ignore the cut or connection if either (dr1) or (dr2)
+   * Ignore the cut or connection if either (w1) or (w2)
    * points to a tactically captured worm.
    */
-  if ((worm[dr1].attack_codes[0] != 0 && worm[dr1].defend_codes[0] == 0)
-      || (worm[dr2].attack_codes[0] != 0 && worm[dr2].defend_codes[0] == 0))
+  if ((worm[w1].attack_codes[0] != 0 && worm[w1].defend_codes[0] == 0)
+      || (worm[w2].attack_codes[0] != 0 && worm[w2].defend_codes[0] == 0))
     return;
   
   add_move_reason(pos, CUT_MOVE, connection);
+
+  /* We do this only for the side effect of being sure that the
+   * corresponding dragon gets into the list of dragons.
+   */
+  (void) find_dragon(dragon[w1].origin);
+  (void) find_dragon(dragon[w2].origin);
 }
 
 /*
@@ -1475,8 +1454,6 @@
   int reason2;
   int aa = NO_MOVE;
   int bb = NO_MOVE;
-  int dragon1 = -1;
-  int dragon2 = -1;
   int worm1 = -1;
   int worm2 = -1;
   int ecolor = 0;
@@ -1545,10 +1522,10 @@
 
        case CONNECT_MOVE:
        case CUT_MOVE:
-         dragon1 = conn_dragon1[move_reasons[r].what];
-         dragon2 = conn_dragon2[move_reasons[r].what];
-         aa = dragons[dragon1];
-         bb = dragons[dragon2];
+         worm1 = conn_worm1[move_reasons[r].what];
+         worm2 = conn_worm2[move_reasons[r].what];
+         aa = worms[worm1];
+         bb = worms[worm2];
          if (move_reasons[r].type == CONNECT_MOVE)
            gprintf("Move at %1m connects %1m and %1m\n", pos, aa, bb);
          else
Index: engine/move_reasons.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/move_reasons.h,v
retrieving revision 1.18
diff -u -r1.18 move_reasons.h
--- engine/move_reasons.h       2 May 2002 18:33:36 -0000       1.18
+++ engine/move_reasons.h       3 May 2002 09:29:40 -0000
@@ -72,7 +72,7 @@
 #define REDUNDANT               (TERRITORY_REDUNDANT | STRATEGICALLY_REDUNDANT)
 #define SECONDARY               4
 
-#define MAX_REASONS 40
+#define MAX_REASONS 80
 
 #define MAX_TRACE_LENGTH  160
 
@@ -130,7 +130,6 @@
 #define MAX_WORMS         2*MAX_BOARD*MAX_BOARD/3
 #define MAX_DRAGONS       MAX_WORMS
 #define MAX_CONNECTIONS   4*MAX_WORMS
-#define MAX_WORM_PAIRS    MAX_WORMS
 #define MAX_EYES          MAX_BOARD*MAX_BOARD/2
 #define MAX_LUNCHES       MAX_WORMS
 #define MAX_EITHER        100
@@ -150,14 +149,9 @@
 extern int next_dragon;
 
 /* Connections */
-extern int conn_dragon1[MAX_CONNECTIONS];
-extern int conn_dragon2[MAX_CONNECTIONS];
+extern int conn_worm1[MAX_CONNECTIONS];
+extern int conn_worm2[MAX_CONNECTIONS];
 extern int next_connection;
-
-/* Unordered worm pairs */
-extern int worm_pair1[MAX_WORM_PAIRS];
-extern int worm_pair2[MAX_WORM_PAIRS];
-extern int next_worm_pair;
 
 /* Unordered sets (currently pairs) of move reasons / targets */
 typedef struct {
Index: engine/value_moves.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/value_moves.c,v
retrieving revision 1.34
diff -u -r1.34 value_moves.c
--- engine/value_moves.c        2 May 2002 18:40:52 -0000       1.34
+++ engine/value_moves.c        3 May 2002 09:29:41 -0000
@@ -287,10 +287,12 @@
          continue;
       }      
       else if (move_reasons[r].type == CONNECT_MOVE) {
-       int dragon1 = conn_dragon1[move_reasons[r].what];
-       int dragon2 = conn_dragon2[move_reasons[r].what];
-       dd1 = dragons[dragon1];
-       dd2 = dragons[dragon2];
+       int worm1 = conn_worm1[move_reasons[r].what];
+       int worm2 = conn_worm2[move_reasons[r].what];
+       dd1 = dragon[worms[worm1]].origin;
+       dd2 = dragon[worms[worm2]].origin;
+       if (dd1 == dd2)
+         dd2 = NO_MOVE;
       }
       else
        continue;
@@ -411,202 +413,111 @@
 
 
 /*
- * Any move that captures or defends a worm also connects or cuts
- * the surrounding dragons. Find these secondary move reasons.
+ * Any move that captures or defends a worm also potentially connects
+ * or cuts the surrounding strings. Find these secondary move reasons
+ * and verify them by connection reading.
  *
  * We also let an owl attack count as a strategical defense of our
  * neighbors of the owl attacked dragon. We only do this for
  * tactically safe dragons, however, because otherwise the effects of
- * capturing has already been taken into account elsewhere.
- *
- * FIXME: There is a certain amount of optimizations that could be
- *        done here.
- *
- * FIXME: Even when we defend a worm, it's possible that the opponent
- *        still can secure a connection, e.g. underneath a string with
- *        few liberties. Thus a defense move isn't necessarily a cut
- *        move. This problem can be solved when we have a working
- *        connection reader.
- *
+ * capturing have already been taken into account elsewhere.
  */
 
 static void
 induce_secondary_move_reasons(int color)
 {
-  int m;
-  int n;
   int pos;
   int k;
-  int i;
+  int i, j;
   int aa;
-  int dd = NO_MOVE;
-  int biggest;
   
-  for (m = 0; m < board_size; m++)
-    for (n = 0; n < board_size; n++) {
-      pos = POS(m, n);
-
-      for (k = 0; k < MAX_REASONS; k++) {
-       int r = move[pos].reason[k];
+  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
+    if (!ON_BOARD(pos))
+      continue;
+    
+    for (k = 0; k < MAX_REASONS; k++) {
+      int r = move[pos].reason[k];
+      
+      if (r < 0)
+       break;
+      
+      if (move_reasons[r].type == ATTACK_MOVE
+         || move_reasons[r].type == DEFEND_MOVE) {
+       int attack_move;
+       int color_to_move;
+       int num_adj, adjs[MAXCHAIN];
+       
+       aa = worms[move_reasons[r].what];
+       
+       if (move_reasons[r].type == ATTACK_MOVE) {
+         attack_move = 1;
+         color_to_move = OTHER_COLOR(board[aa]);
+       }
+       else {
+         attack_move = 0;
+         color_to_move = board[aa];
+       }
+               
+       if (worm[aa].defend_codes[0] == 0)
+         continue; /* No defense. */
        
-       if (r < 0)
-         break;
+       /* Don't care about inessential dragons. */
+       if (DRAGON2(aa).safety == INESSENTIAL)
+         continue;
        
-       if (move_reasons[r].type == ATTACK_MOVE
-           || move_reasons[r].type == DEFEND_MOVE) {
-         aa = worms[move_reasons[r].what];
-
-         if (worm[aa].defend_codes[0] == 0)
-           continue; /* No defense. */
+       /*
+        * If this is a defense move and the defense is futile for
+        * strategical reasons, we shouldn't induce a cutting move
+        * reason.
+        *
+        * FIXME: We may want to revise this policy.
+        */
+       if (!attack_move && !strategically_sound_defense(aa, pos))
+         continue;
+       
+       num_adj = extended_chainlinks(aa, adjs);
+       
+       for (i = 0; i < num_adj; i++) {
+         for (j = i+1; j < num_adj; j++) {
+           int adj1 = adjs[i];
+           int adj2 = adjs[j];
 
-         /* Don't care about inessential dragons. */
-         if (DRAGON2(aa).safety == INESSENTIAL)
-           continue;
-         
-         /*
-          * If this is a defense move and the defense is futile for
-          * strategical reasons, we shouldn't induce a cutting move
-          * reason.
-          */
-         if (move_reasons[r].type == DEFEND_MOVE
-             && !strategically_sound_defense(aa, pos))
-           continue;
-         
-         /*
-          * Find the biggest of the surrounding dragons and say that
-          * all other dragons are connected or cut with respect to that
-          * one. We might want to use some other property than size, or
-          * still better induce cuts/connections for all combinations.
-          */
-         biggest = 0;
-         
-         /* A tactically unstable worm should never be amalgamated into
-          * a larger dragon. Occasionally this does still happen and in
-          * that case we need a workaround. Eventually this workaround
-          * should become unnecessary.
-          */
-         if (dragon[aa].size == worm[aa].size) {
-           for (i = 0; i < DRAGON2(aa).neighbors; i++) {
-             int d = DRAGON2(aa).adjacent[i];
-             if (DRAGON(d).color == dragon[aa].color)
-               continue;
-             
-             if (DRAGON(d).size > biggest) {
-               dd = DRAGON(d).origin;
-               biggest = DRAGON(d).size;
-             }
-           }
-           
-           if (biggest == 0)
+           if (attack_move && !disconnect(adj1, adj2, NULL))
              continue;
-           
-           for (i = 0; i < DRAGON2(aa).neighbors; i++) {
-             int d = DRAGON2(aa).adjacent[i];
-             int ee = DRAGON(d).origin;
-             
-             if (DRAGON(d).color == dragon[aa].color)
-               continue;
-             
-             if (dd != ee) {
-               if (move_reasons[r].type == ATTACK_MOVE) {
-                 /* Exclude the case when (aa) is dead and both
-                  * (dd) and (ee) are strongly alive or
-                  * better. Then the move would only be losing
-                  * points.
-                  */
-                 if (dragon[aa].matcher_status != DEAD
-                     || (DRAGON2(dd).safety != STRONGLY_ALIVE
-                         && DRAGON2(dd).safety != INVINCIBLE)
-                     || (DRAGON2(ee).safety != STRONGLY_ALIVE
-                         && DRAGON2(ee).safety != INVINCIBLE)) {
-                   /* If one of the strings can be attacked and the
-                     * move at (pos) does not defend, do not induce a
-                     * connection move.
-                    */
-                   if ((worm[dd].attack_codes[0] == 0
-                        || does_defend(pos, dd))
-                       && (worm[ee].attack_codes[0] == 0
-                           || does_defend(pos, ee)))
-                     add_connection_move(pos, dd, ee);
-                 }
-               }
-               else
-                 add_cut_move(pos, dd, ee);
-             }
-           }
-         }
-         else {
-           /* Workaround. If the unstable worm has been amalgamated
-            * with stable worms, it would be incorrect to add
-            * cut/connect move reasons for all neighbors of this
-            * dragon. Instead we fall back to using chainlinks() to
-            * find the neighbors of the worm. The shortcoming of this
-            * is that it only counts neighbors in direct contact with
-            * the worm, which is not always sufficient.
-            */
-           int num_adj, adjs[MAXCHAIN];
-           
-           num_adj = chainlinks(aa, adjs);
-           for (i = 0; i < num_adj; i++) {
-             int adj = adjs[i];
-             
-             if (dragon[adj].color == dragon[aa].color)
-               continue;
-             if (dragon[adj].size > biggest) {
-               dd = dragon[adj].origin;
-               biggest = dragon[adj].size;
-             }
-           }
-           
-           if (biggest == 0)
+           if (!attack_move && !string_connect(adj1, adj2, NULL))
              continue;
-           
-           for (i = 0; i < num_adj; i++) {
-             int adj = adjs[i];
-             int ee  = dragon[adj].origin;
-             
-             if (dragon[adj].color == dragon[aa].color)
-               continue;
-             
-             if (dd != ee) {
-               if (move_reasons[r].type == ATTACK_MOVE) {
-                 /* Exclude the case when (aa) is dead and both
-                  * (dd) and (ee) are strongly alive or
-                  * better. Then the move would only be losing
-                  * points.
-                  */
-                 if (dragon[aa].matcher_status != DEAD
-                     || (DRAGON2(dd).safety != STRONGLY_ALIVE
-                         && DRAGON2(dd).safety != INVINCIBLE)
-                     || (DRAGON2(ee).safety != STRONGLY_ALIVE
-                         && DRAGON2(ee).safety != INVINCIBLE)) {
-                   /* If one of the strings can be attacked and the
-                     * move at (pos) does not defend, do not induce a
-                     * connection move.
-                    */
-                   if ((worm[dd].attack_codes[0] == 0
-                        || does_defend(pos, dd))
-                       && (worm[ee].attack_codes[0] == 0
-                           || does_defend(pos, ee)))
-                     add_connection_move(pos, dd, ee);
-                 }
-               }
-               else
-                 add_cut_move(pos, dd, ee);
+
+           if (trymove(pos, color_to_move, "induce_secondary_move_reasons",
+                       aa, EMPTY, NO_MOVE)) {
+             if (attack_move && !disconnect(adj1, adj2, NULL)) {
+               DEBUG(DEBUG_MOVE_REASONS,
+                     "Connection move at %1m induced for %1m/%1m due to attack 
of %1m\n",
+                     pos, adj1, adj2, aa);
+               add_connection_move(pos, adj1, adj2);
              }
+                 
+             if (!attack_move && !string_connect(adj1, adj2, NULL)) {
+               DEBUG(DEBUG_MOVE_REASONS,
+                     "Cut move at %1m induced for %1m/%1m due to defense of 
%1m\n",
+                     pos, adj1, adj2, aa);
+               add_cut_move(pos, adj1, adj2);
+             }
+
+             popgo();
            }
          }
-       }
-       else if (move_reasons[r].type == OWL_ATTACK_MOVE) {
-         aa = dragons[move_reasons[r].what];
-         for (i = 0; i < DRAGON2(aa).neighbors; i++) {
-           int bb = dragon2[DRAGON2(aa).adjacent[i]].origin;
-           if (dragon[bb].color == color && worm[bb].attack_codes[0] == 0)
-             add_strategical_defense_move(pos, bb);
-         }
+       }  
+      }
+      else if (move_reasons[r].type == OWL_ATTACK_MOVE) {
+       aa = dragons[move_reasons[r].what];
+       for (i = 0; i < DRAGON2(aa).neighbors; i++) {
+         int bb = dragon2[DRAGON2(aa).adjacent[i]].origin;
+         if (dragon[bb].color == color && worm[bb].attack_codes[0] == 0)
+           add_strategical_defense_move(pos, bb);
        }
       }
     }
+  }
 }
 
 
@@ -818,10 +729,13 @@
 
       case CONNECT_MOVE:
         {
-         int dragon1 = conn_dragon1[move_reasons[r].what];
-         int dragon2 = conn_dragon2[move_reasons[r].what];
-         int aa = dragons[dragon1];
-         int bb = dragons[dragon2];
+         int worm1 = conn_worm1[move_reasons[r].what];
+         int worm2 = conn_worm2[move_reasons[r].what];
+         int aa = dragon[worms[worm1]].origin;
+         int bb = dragon[worms[worm2]].origin;
+
+         if (aa == bb)
+           continue;
          
          if (dragon[aa].owl_status == ALIVE
              || dragon[bb].owl_status == ALIVE) {
@@ -1997,11 +1911,17 @@
        if (doing_scoring && !move[pos].move_safety)
          break;
 
-       d1 = conn_dragon1[move_reasons[r].what];
-       d2 = conn_dragon2[move_reasons[r].what];
-       aa = dragons[d1];
-       bb = dragons[d2];
+       worm1 = conn_worm1[move_reasons[r].what];
+       worm2 = conn_worm2[move_reasons[r].what];
+       aa = dragon[worms[worm1]].origin;
+       bb = dragon[worms[worm2]].origin;
+
+       if (aa == bb)
+         continue;
 
+       d1 = find_dragon(aa);
+       d2 = find_dragon(bb);
+       
        /* If we are ahead by more than 20, value connections more strongly */
        if ((color == WHITE && score > 20.0)
            || (color == BLACK && score < -20.0))
@@ -2198,7 +2118,7 @@
        && dragon[aa].size == worm[aa].size
        && (attack_move_reason_known(pos, find_worm(aa))
            || defense_move_reason_known(pos, find_worm(aa)))) {
-      TRACE("  %1m:   %f - %1m strategic value already counted.\n",
+      TRACE("  %1m:   %f - %1m strategic value already counted - A.\n",
            pos, dragon_value[k], aa);
       continue;
     }
@@ -2221,7 +2141,7 @@
        tot_value += excess_value;
       }
       else {
-       TRACE("  %1m:   %f - %1m strategic value already counted.\n",
+       TRACE("  %1m:   %f - %1m strategic value already counted - B.\n",
              pos, dragon_value[k], aa);
       }
       
@@ -2760,15 +2680,16 @@
   }
   verbose = save_verbose;
 
-  induce_secondary_move_reasons(color);
-  time_report(2, "  induce_secondary_move_reasons", NO_MOVE, 1.0);
-  
   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);
 
Index: patterns/patterns2.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/patterns2.db,v
retrieving revision 1.36
diff -u -r1.36 patterns2.db
--- patterns/patterns2.db       27 Apr 2002 22:08:58 -0000      1.36
+++ patterns/patterns2.db       3 May 2002 09:29:41 -0000
@@ -1656,16 +1656,17 @@
 
 
 Pattern Shape19
+# gf Revised constraint. (3.3.2)
 
 X.X     stop enemy tiger
 .*O
 
 :8,-,shape(2)
 
-XaX
+XaB
 .*O
 
-;xdefend_against(*,a)
+;lib(B)>1 && xdefend_against(*,a)
 
 
 Pattern Shape20
Index: regression/13x13.tst
===================================================================
RCS file: /cvsroot/gnugo/gnugo/regression/13x13.tst,v
retrieving revision 1.13
diff -u -r1.13 13x13.tst
--- regression/13x13.tst        14 Apr 2002 05:40:50 -0000      1.13
+++ regression/13x13.tst        3 May 2002 09:29:41 -0000
@@ -87,6 +87,7 @@
 
 #CATEGORY=STRATEGY
 # New failure (comparing 3.1.15 and 3.0.0)
+# See also owl:262.
 loadsgf games/mertin13x13/gointellect-gnugo2.W+8.sgf 32
 15 gg_genmove white
 #? [C7|B7|C6|B6|B5|C5]
Index: regression/owl.tst
===================================================================
RCS file: /cvsroot/gnugo/gnugo/regression/owl.tst,v
retrieving revision 1.49
diff -u -r1.49 owl.tst
--- regression/owl.tst  30 Apr 2002 16:46:47 -0000      1.49
+++ regression/owl.tst  3 May 2002 09:29:41 -0000
@@ -946,6 +946,11 @@
 261 owl_attack E5
 #? [1 (O6|Q7|T4|T5|R4)]
 
+# See also 13x13:15.
+loadsgf games/mertin13x13/gointellect-gnugo2.W+8.sgf 32
+262 owl_defend E7
+#? [0]*
+
 ########### end of tests #####################
 
 # Report number of nodes visited by the tactical reading



reply via email to

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