Index: engine/dragon.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/dragon.c,v
retrieving revision 1.160
diff -u -r1.160 dragon.c
--- engine/dragon.c	5 Feb 2006 19:27:40 -0000	1.160
+++ engine/dragon.c	9 Feb 2006 18:44:40 -0000
@@ -49,8 +49,12 @@
 
 #include "liberty.h"
 #include "gg_utils.h"
+#include "readconnect.h"
 
 static void initialize_supplementary_dragon_data(void);
+static void amalgamate_dragons(int color);
+static void build_dragon(int w, int amalgamated[BOARDMAX],
+			 float effective_sizes[BOARDMAX]);
 static void find_lunches(void);
 static void eye_computations(void);
 static void revise_inessentiality(void);
@@ -113,8 +117,15 @@
    * involved dragons.
    */
   memset(cutting_points, 0, sizeof(cutting_points));
+  if (experimental_connections)
+    initialize_static_worm_connections();
   find_cuts();
   find_connections();
+  if (experimental_connections) {
+    analyze_worm_connections();
+    amalgamate_dragons(BLACK);
+    amalgamate_dragons(WHITE);
+  }
 
   /* At this time, all dragons have been finalized and we can
    * initialize the dragon2[] array. After that we can no longer allow
@@ -744,6 +755,117 @@
 }
  
 
+static void
+amalgamate_dragons(int color)
+{
+  int amalgamated[BOARDMAX];
+  int pos;
+  float effective_sizes[BOARDMAX];
+
+  if (stackp == 0) {
+    for (pos = BOARDMIN; pos < BOARDMAX; pos++)
+      if (IS_STONE(board[pos]))
+	effective_sizes[pos] = worm[pos].effective_size;
+  }
+  else {
+    compute_effective_string_sizes(effective_sizes);
+    for (pos = BOARDMIN; pos < BOARDMAX; pos++)
+      if (IS_STONE(board[pos]))
+	effective_sizes[pos] = effective_sizes[find_origin(pos)];
+  }
+
+  memset(amalgamated, 0, sizeof(amalgamated));
+  while (1) {
+    int biggest_remaining_string = NO_MOVE;
+    float biggest_remaining_string_size = 0.0;
+    for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
+      if (board[pos] == color
+	  && find_origin(pos) == pos
+	  && !amalgamated[pos]
+	  && effective_sizes[pos] > biggest_remaining_string_size) {
+	biggest_remaining_string = pos;
+	biggest_remaining_string_size = effective_sizes[pos];
+      }
+    }
+    if (biggest_remaining_string == NO_MOVE)
+      break;
+
+    build_dragon(biggest_remaining_string, amalgamated, effective_sizes);
+  }
+}
+
+static void
+build_dragon(int starting_worm, int amalgamated[BOARDMAX],
+	     float effective_sizes[BOARDMAX])
+{
+  int color = board[starting_worm];
+  int worms_in_this_dragon[BOARDMAX];
+  int num_worms;
+  int aspiring_worms[BOARDMAX];
+  int num_aspiring;
+  int considered[BOARDMAX];
+  int k;
+  int pos;
+
+  DEBUG(DEBUG_DRAGONS, "Build dragon starting at %1m.\n", starting_worm);
+  
+  amalgamated[starting_worm] = 1;
+  worms_in_this_dragon[0] = starting_worm;
+  num_worms = 1;
+  num_aspiring = 0;
+  memset(considered, 0, sizeof(considered));
+
+  for (k = 0; k < num_worms; k++) {
+    int this_worm = worms_in_this_dragon[k];
+    for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
+      if (board[pos] != color || find_origin(pos) != pos
+	  || amalgamated[pos] || considered[pos]
+	  || !worms_are_connected(this_worm, pos))
+	continue;
+
+      DEBUG(DEBUG_DRAGONS, "  queueing %1m.\n", pos);
+      considered[pos] = 1;
+      aspiring_worms[num_aspiring] = pos;
+      num_aspiring++;
+    }
+    
+    while (num_aspiring > 0) {
+      int j;
+      int w;
+      int largest_worm = -1;
+      float largest_size = 0.0;
+      
+      for (j = 0; j < num_aspiring; j++) {
+	if (effective_sizes[aspiring_worms[j]] > largest_size) {
+	  largest_worm = j;
+	  largest_size = effective_sizes[aspiring_worms[j]];
+	}
+      }
+
+      w = aspiring_worms[largest_worm];
+      num_aspiring--;
+      aspiring_worms[largest_worm] = aspiring_worms[num_aspiring];
+      
+      for (j = 0; j < num_worms; j++) {
+	if (worms_are_intransitively_connected(w, worms_in_this_dragon[j])) {
+	  DEBUG(DEBUG_DRAGONS,
+		"  intransitive connection to %1m, discarding %1m.\n",
+		worms_in_this_dragon[j], w);
+	  break;
+	}
+      }
+      
+      if (j == num_worms) {
+	worms_in_this_dragon[num_worms] = w;
+	num_worms++;
+	amalgamated[w] = 1;
+	join_dragons(starting_worm, w);
+	break;
+      }
+    }
+  }
+}
+
 /* Examine which dragons are adjacent to each other. This is
  * complicated by the fact that adjacency may involve a certain
  * amount of empty space.
@@ -1601,9 +1723,10 @@
   int pos;
   int saved_cutting_points[BOARDMAX];
 
-  /* This is currently necessary in order not to mess up the
-   * worm[].cutstone2 field. See cutstone2_helper in
-   * patterns/helpers.c. On the other hand it shouldn't be very
+  /* This is currently necessary in order not to mess up various
+   * information such as the worm[].cutstone2 field (see
+   * cutstone2_helper in patterns/helpers.c) and the static worm
+   * connection analysis. On the other hand it shouldn't be very
    * interesting to recompute dragons in the original position.
    */
   gg_assert(stackp > 0);
@@ -1618,8 +1741,15 @@
 	new_dragon_origins[pos] = find_origin(pos);
     }
   
+  if (experimental_connections)
+    initialize_static_worm_connections();
   find_cuts();
   find_connections();
+  if (experimental_connections) {
+    analyze_worm_connections();
+    amalgamate_dragons(BLACK);
+    amalgamate_dragons(WHITE);
+  }
 
   memcpy(cutting_points, saved_cutting_points, sizeof(cutting_points));
   memcpy(dragon_origins, new_dragon_origins, sizeof(new_dragon_origins));
Index: engine/liberty.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/liberty.h,v
retrieving revision 1.255
diff -u -r1.255 liberty.h
--- engine/liberty.h	5 Feb 2006 20:50:41 -0000	1.255
+++ engine/liberty.h	9 Feb 2006 18:44:40 -0000
@@ -325,12 +325,21 @@
 void mark_inessential_stones(int color, signed char safe_stones[BOARDMAX]);
 
 void get_lively_stones(int color, signed char safe_stones[BOARDMAX]);
+void compute_effective_string_sizes(float effective_sizes[BOARDMAX]);
 int is_same_worm(int w1, int w2);
 int is_worm_origin(int w, int pos);
 void propagate_worm(int pos);
 void find_cuts(void);
 void find_connections(void);
 
+void initialize_static_worm_connections(void);
+void analyze_worm_connections(void);
+void register_potential_worm_connection(int w1, int w2);
+int worms_are_connected(int w1, int w2);
+int worms_are_connected_with_ko(int w1, int w2);
+int worms_are_close(int w1, int w2);
+int worms_are_intransitively_connected(int w1, int w2);
+
 /* movelist.c */
 int movelist_move_known(int move, int max_points, int points[], int codes[]);
 void movelist_change_point(int move, int code, int max_points, 
Index: engine/owl.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/owl.c,v
retrieving revision 1.246
diff -u -r1.246 owl.c
--- engine/owl.c	4 Feb 2006 10:38:43 -0000	1.246
+++ engine/owl.c	9 Feb 2006 18:44:42 -0000
@@ -72,6 +72,9 @@
   /* Same as goal, except never anything is removed from it. */
   signed char cumulative_goal[BOARDMAX];
 
+  int original_goal_strings[MAX_GOAL_WORMS];
+  int num_original_goal_strings;
+  
   /* FIXME: neighbors[] and escape_values[] are never recomputed.
    *	    Consider moving these arrays from stack to a static or
    *	    dynamic variable so it is not copied around in
@@ -1839,6 +1842,9 @@
   int value1;
   int value2;
   int this_variation_number = count_variations - 1;
+  int all_original_strings_tactically_unstable;
+  SGFTree *save_sgf_dumptree;
+  int save_count_variations;
   
   SETUP_TRACE_INFO("owl_attack", str);
 
@@ -1880,6 +1886,22 @@
     READ_RETURN(OWL_ATTACK, str, depth - stackp, move, 0, 0);
   }
 
+  /* Do not give up if all original strings are tactically unstable.
+   */
+  save_sgf_dumptree = sgf_dumptree;
+  save_count_variations = count_variations;
+  sgf_dumptree = NULL;
+  count_variations = 0;
+  all_original_strings_tactically_unstable = 1;
+  for (k = 0; k < owl->num_original_goal_strings; k++) {
+    if (!attack(owl->original_goal_strings[k], NULL)) {
+      all_original_strings_tactically_unstable = 0;
+      break;
+    }
+  }
+  sgf_dumptree = save_sgf_dumptree;
+  count_variations = save_count_variations;
+  
   memset(mw, 0, sizeof(mw));
   global_owl_node_counter++;
   local_owl_node_counter++;
@@ -1889,7 +1911,8 @@
 
   /* First see whether there is any chance to kill. */
   if (owl_estimate_life(owl, NULL, vital_moves, &live_reason, 1,
-			&probable_eyes, &eyemin, &eyemax)) {
+			&probable_eyes, &eyemin, &eyemax)
+      && !all_original_strings_tactically_unstable) {
     /*
      * We need to check here if there's a worm under atari. If yes,
      * locate it and report a (gote) GAIN.
@@ -1931,21 +1954,22 @@
 
   /* We try moves in five passes.
    *                                stackp==0   stackp>0
-   * 0. Vital moves in the interval  [70..]      [45..]
-   * 1. Shape moves
-   * 2. Vital moves in the interval  [..69]      [..44]
-   * 3. Tactical attack moves (except certain kos)
-   * 4. Moves found by the defender
-   * 5. Tactical ko attack moves which were not tried in pass 3
+   * 1. Tactical attack moves if all original strings are unstable.
+   * 2. Vital moves in the interval  [70..]      [45..]
+   * 3. Shape moves
+   * 4. Vital moves in the interval  [..69]      [..44]
+   * 5. Tactical attack moves (except certain kos)
+   * 6. Moves found by the defender
+   * 7. Tactical ko attack moves which were not tried in pass 3
    */
-  for (pass = 0; pass < 6; pass++) {
+  for (pass = 1; pass < 8; pass++) {
     moves = NULL;
     move_cutoff = 1;
     
     current_owl_data = owl;
     /* Get the shape moves if we are in the right pass. */
     switch (pass) {
-    case 1:
+    case 3:
       if (stackp > owl_branch_depth && number_tried_moves > 0)
 	continue;
       
@@ -1953,13 +1977,13 @@
       moves = shape_moves;
       break;
 
-    case 0:
     case 2:
+    case 4:
       if (stackp > owl_branch_depth && number_tried_moves > 0)
 	continue;
       
       moves = vital_moves;
-      if (pass == 0 || stackp > owl_distrust_depth) {
+      if (pass == 2 || stackp > owl_distrust_depth) {
 	if (stackp == 0)
 	  move_cutoff = 70;
 	else
@@ -1969,8 +1993,12 @@
 	move_cutoff = 99; /* Effectively disable vital moves. */
       break;
 
-    case 3:
+    case 1:
+      if (!all_original_strings_tactically_unstable)
+	break;
+      /* Otherwise fall through. */
     case 5:
+    case 7:
       {
 	/* Look for a tactical attack. This is primarily intended for
 	 * the case where the whole dragon is a single string, therefore
@@ -1979,8 +2007,8 @@
 	 * We must be wary with attacks giving ko. Unless the dragon
 	 * otherwise looks alive, this may turn a dead dragon into one
 	 * which can live by ko. Such moves will be tried anyway in
-	 * pass 5. Notice though that we can only reach there if an owl
-	 * defense was found in pass 4.
+	 * pass 7. Notice though that we can only reach there if an owl
+	 * defense was found in pass 6.
 	 */
 	int apos;
 	int result;
@@ -1992,7 +2020,7 @@
 	result = attack(str, &apos);
 	if (result == WIN
 	    || (result != 0 && (min_eyes(&probable_eyes) >= 2
-				|| pass == 5))) {
+				|| pass == 7))) {
 	  set_single_owl_move(shape_moves, apos, "tactical attack");
 	  moves = shape_moves;
 	}
@@ -2004,7 +2032,7 @@
     /* If we found no move in the first four passes we ask the defender
      * for a move suggestion.
      */
-    case 4:
+    case 6:
       if (number_tried_moves == 0) {
 	int dpos;
 	int dcode = do_owl_defend(str, &dpos, NULL, owl, escape);
@@ -2091,7 +2119,7 @@
       current_owl_data = owl;
 
       /* Shape moves are selected on demand. */
-      if (pass == 1) {
+      if (pass == 3) {
         if (!get_next_move_from_list(&shape_patterns, other,
 	                             shape_moves, move_cutoff, owl))
           break;
@@ -2134,8 +2162,10 @@
        */
       if (IS_STONE(board[str]))
 	origin = str;
+      else if (owl->num_original_goal_strings > 0)
+	origin = owl->original_goal_strings[0];
       else
-	origin = select_new_goal_origin(NO_MOVE, owl);
+	origin = NO_MOVE;
 
       /* Test whether the move cut the goal dragon apart. */
       if (moves[k].cuts[0] != NO_MOVE) {
@@ -2147,8 +2177,9 @@
 
       if (origin == NO_MOVE)
 	dcode = 0;
-      else
+      else {
 	dcode = do_owl_defend(origin, NULL, &wid, owl, escape);
+      }
 
       if (!ko_move) {
 	if (dcode == 0) {
@@ -2473,13 +2504,15 @@
   int eyemin = -1;               /* Lower bound on the number of eyes. */
   int eyemax = -1;               /* Upper bound on the number of eyes. */
   struct eyevalue probable_eyes; /* Best guess of eyevalue. */
-  int escape_route;
   const char *live_reason;
   int move_cutoff;
   int xpos;
   int value1;
   int value2;
   int this_variation_number = count_variations - 1;
+  int all_original_strings_tactically_unstable;
+  SGFTree *save_sgf_dumptree;
+  int save_count_variations;
 
   SETUP_TRACE_INFO("owl_defend", str);
 
@@ -2514,6 +2547,23 @@
     return value1;
   }
 
+  /* Do not declare victory if all original strings are tactically
+   * unstable.
+   */
+  save_sgf_dumptree = sgf_dumptree;
+  save_count_variations = count_variations;
+  sgf_dumptree = NULL;
+  count_variations = 0;
+  all_original_strings_tactically_unstable = 1;
+  for (k = 0; k < owl->num_original_goal_strings; k++) {
+    if (!attack(owl->original_goal_strings[k], NULL)) {
+      all_original_strings_tactically_unstable = 0;
+      break;
+    }
+  }
+  sgf_dumptree = save_sgf_dumptree;
+  count_variations = save_count_variations;
+  
   /* In order to get a defense move even if we seem to already have
    * escaped and to reduce the impact of overestimated escape
    * possibilities, we don't declare escape victory on the first move.
@@ -2521,18 +2571,20 @@
    * FIXME: Should introduce a new owl depth value rather than having
    *        this hardwired value.
    */
-  escape_route = owl_escape_route(owl);
-  if (stackp > 2 && escape_route >= 5) {
-    /* FIXME: We probably should make distinction in the returned
-     * result whether the dragon lives by making two eyes or by
-     * escaping.
-     */
-    TRACE("%oVariation %d: ALIVE (escaped)\n", this_variation_number);
-    SGFTRACE(0, WIN, "escaped");
-    READ_RETURN(OWL_DEFEND, str, depth - stackp, move, 0, WIN);
+  if (stackp > 2 && !all_original_strings_tactically_unstable) {
+    int escape_route = owl_escape_route(owl);
+    if (escape_route >= 5) {
+      /* FIXME: We probably should make distinction in the returned
+       * result whether the dragon lives by making two eyes or by
+       * escaping.
+       */
+      TRACE("%oVariation %d: ALIVE (escaped)\n", this_variation_number);
+      SGFTRACE(0, WIN, "escaped");
+      READ_RETURN(OWL_DEFEND, str, depth - stackp, move, 0, WIN);
+    }
   }
 
-  /* If reading goes to deep or we run out of nodes, we assume life. */
+  /* If reading goes too deep or we run out of nodes, we assume life. */
   if (reading_limit_reached(&live_reason, this_variation_number)) {
     SGFTRACE(0, WIN, live_reason);
     READ_RETURN(OWL_DEFEND, str, depth - stackp, move, 0, WIN);
@@ -2545,10 +2597,11 @@
   current_owl_data = owl;
   memset(owl->safe_move_cache, 0, sizeof(owl->safe_move_cache));
 
-  /* First see whether we might already be alife. */
+  /* First see whether we might already be alive. */
   if (escape < MAX_ESCAPE) {
     if (owl_estimate_life(owl, NULL, vital_moves, &live_reason, 0,
-	  		  &probable_eyes, &eyemin, &eyemax)) {
+	  		  &probable_eyes, &eyemin, &eyemax)
+	&& !all_original_strings_tactically_unstable) {
       SGFTRACE(0, WIN, live_reason);
       TRACE("%oVariation %d: ALIVE (%s)\n",
 	    this_variation_number, live_reason);
@@ -2571,19 +2624,20 @@
 
   /* We try moves in four passes.
    *                                stackp==0   stackp>0
-   * 0. Vital moves in the interval  [70..]      [45..]
-   * 1. Shape moves
-   * 2. Vital moves in the interval  [..69]      [..44]
-   * 3. Tactical defense moves
+   * 1. Tactical defense moves if all original strings unstable
+   * 2. Vital moves in the interval  [70..]      [45..]
+   * 3. Shape moves
+   * 4. Vital moves in the interval  [..69]      [..44]
+   * 5. Tactical defense moves
    */
-  for (pass = 0; pass < 4; pass++) {
+  for (pass = 1; pass < 6; pass++) {
     moves = NULL;
     move_cutoff = 1;
     
     current_owl_data = owl;
     switch (pass) {
     /* Get the shape moves if we are in the right pass. */
-    case 1:
+    case 3:
       
       if (stackp > owl_branch_depth && number_tried_moves > 0)
 	continue;
@@ -2592,13 +2646,13 @@
       moves = shape_moves;
       break;
 
-    case 0:
     case 2:
+    case 4:
       if (stackp > owl_branch_depth && number_tried_moves > 0)
 	continue;
       
       moves = vital_moves;
-      if (pass == 0 || stackp > owl_distrust_depth) {
+      if (pass == 2 || stackp > owl_distrust_depth) {
 	if (stackp == 0)
 	  move_cutoff = 70;
 	else if (eyemin + min_eyes(&probable_eyes) > 3)
@@ -2612,7 +2666,8 @@
 	move_cutoff = 99; /* Effectively disable vital moves. */
       break;
 
-    case 3:
+    case 1:
+    case 5:
       {
 	int goalcount = 0;
 
@@ -2622,7 +2677,8 @@
 	  if (ON_BOARD(k))
 	    goalcount += owl->goal[k];
 
-	if (goalcount < 5) {
+	if ((pass == 5 && all_original_strings_tactically_unstable)
+	    || (pass == 5 && goalcount < 5)) {
 
 	  /* Look for a tactical defense. This is primarily intended for
 	   * the case where the whole dragon is a single string, therefore
@@ -2635,8 +2691,8 @@
 	   * confusing the eye analysis.
 	   */
 	  int dpos;
-	  SGFTree *save_sgf_dumptree = sgf_dumptree;
-	  int save_count_variations = count_variations;
+	  save_sgf_dumptree = sgf_dumptree;
+	  save_count_variations = count_variations;
 
 	  sgf_dumptree = NULL;
 	  count_variations = 0;
@@ -2676,7 +2732,7 @@
       
       current_owl_data = owl;
       
-      if (pass == 1) {
+      if (pass == 3) {
         if (!get_next_move_from_list(&shape_patterns, color, shape_moves,
 	                             move_cutoff, owl))
 	  break;
@@ -4452,7 +4508,7 @@
   int color = board[apos];
   
   ASSERT1(bpos == NO_MOVE || board[bpos] == color, bpos);
-
+  
   if (new_dragons == NULL) {
     for (pos = BOARDMIN; pos < BOARDMAX; pos++)
       if (ON_BOARD(pos)) {
@@ -4474,9 +4530,21 @@
       }
   }
 
+  owl->num_original_goal_strings = 0;
+  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
+    if (IS_STONE(board[pos])
+	&& owl->goal[pos]
+	&& owl->num_original_goal_strings < MAX_GOAL_WORMS
+	&& find_origin(pos) == pos) {
+      owl->original_goal_strings[owl->num_original_goal_strings] = pos;
+      owl->num_original_goal_strings++;
+    }
+
   memcpy(owl->cumulative_goal, owl->goal, sizeof(owl->goal));
   owl->color = color;
   owl_mark_boundary(owl);
+  if (owl->num_original_goal_strings == 1)
+    owl_update_goal(apos, 2, NO_MOVE, owl, 0);
 }
 
 
@@ -4620,6 +4688,7 @@
   int stones[MAX_BOARD * MAX_BOARD];
   int num_stones;
   int k;
+  int m;
   int do_add = 1;
   SGFTree *save_sgf_dumptree = sgf_dumptree;
   int save_count_variations = count_variations;
@@ -4676,6 +4745,34 @@
       }
   }
 
+  /* Update the list of original goal strings. This move may have
+   * merged two or more of the strings.
+   */
+  for (k = 0; k < owl->num_original_goal_strings; k++) {
+    for (m = 0; m < k; m++)
+      if (same_string(owl->original_goal_strings[k],
+		      owl->original_goal_strings[m])) {
+	owl->num_original_goal_strings--;
+	owl->original_goal_strings[k] =
+	  owl->original_goal_strings[owl->num_original_goal_strings];
+	k--;
+	break;
+      }
+  }
+
+  /*  It might also have recreated a previously captured string. */
+  if (owl->goal[pos] == 1
+      && owl->num_original_goal_strings < MAX_GOAL_WORMS) {
+    for (k = 0; k < owl->num_original_goal_strings; k++) {
+      if (same_string(owl->original_goal_strings[k], pos))
+	break;
+    }
+    if (k == owl->num_original_goal_strings) {
+      owl->original_goal_strings[owl->num_original_goal_strings] = pos;
+      owl->num_original_goal_strings++;
+    }
+  }
+
   if (1 && verbose)
     goaldump(owl->goal);
 }
@@ -4898,6 +4995,16 @@
   }
 
   mark_string(pos, owl->boundary, boundary_mark);
+
+  /* The move may have captured one or more of the original goal strings. */
+  for (k = 0; k < owl->num_original_goal_strings; k++) {
+    if (board[owl->original_goal_strings[k]] == EMPTY) {
+      owl->num_original_goal_strings--;
+      owl->original_goal_strings[k] =
+	owl->original_goal_strings[owl->num_original_goal_strings];
+      k--;
+    }
+  }
 }
 
 /* Lists the goal array. For use in GDB:
@@ -5964,6 +6071,7 @@
   int result;
   double start = 0.0;
   struct local_owl_data *owl;
+  int pos;
   int num_moves = 0;
 
   if (debug & DEBUG_OWL_PERFORMANCE)
@@ -6041,6 +6149,16 @@
   /* FIXME: We would want to use init_owl() here too, but it doesn't
    * fit very well with the construction of the goal array above.
    */
+  owl->num_original_goal_strings = 0;
+  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
+    if (ON_BOARD(pos)
+	&& owl->goal[pos]
+	&& owl->num_original_goal_strings < MAX_GOAL_WORMS
+	&& find_origin(pos) == pos) {
+      owl->original_goal_strings[owl->num_original_goal_strings] = pos;
+      owl->num_original_goal_strings++;
+    }
+
   memcpy(owl->cumulative_goal, owl->goal, BOARDMAX);
   compute_owl_escape_values(owl);
   owl_mark_boundary(owl);
@@ -6651,6 +6769,9 @@
   memcpy(new_owl->cumulative_goal, (*owl)->cumulative_goal,
          sizeof(new_owl->cumulative_goal));
   memcpy(new_owl->boundary, (*owl)->boundary, sizeof(new_owl->boundary));
+  memcpy(new_owl->original_goal_strings, (*owl)->original_goal_strings,
+	 sizeof(new_owl->original_goal_strings));
+  new_owl->num_original_goal_strings = (*owl)->num_original_goal_strings;
   memcpy(new_owl->neighbors, (*owl)->neighbors, sizeof(new_owl->neighbors));
   memcpy(new_owl->escape_values, (*owl)->escape_values,
 	 sizeof(new_owl->escape_values));
Index: engine/worm.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/worm.c,v
retrieving revision 1.71
diff -u -r1.71 worm.c
--- engine/worm.c	23 Jan 2006 18:15:51 -0000	1.71
+++ engine/worm.c	9 Feb 2006 18:44:43 -0000
@@ -32,7 +32,8 @@
 static void compute_effective_worm_sizes(void);
 static void do_compute_effective_worm_sizes(int color,
 					    int (*cw)[MAX_CLOSE_WORMS],
-					    int *ncw, int max_distance);
+					    int *ncw, float *effective_sizes,
+					    int max_distance);
 static void compute_unconditional_status(void);
 static void find_worm_attacks_and_defenses(void);
 static void find_worm_threats(void);
@@ -566,17 +567,37 @@
 static void
 compute_effective_worm_sizes()
 {
+  float effective_sizes[BOARDMAX];
+  int pos;
+  
   do_compute_effective_worm_sizes(BLACK | WHITE, close_worms,
-				  number_close_worms, 3);
+				  number_close_worms, effective_sizes, 3);
+  
+  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
+    if (IS_STONE(board[pos]))
+      worm[pos].effective_size = effective_sizes[worm[pos].origin];
+  
   do_compute_effective_worm_sizes(BLACK, close_black_worms,
-				  number_close_black_worms, 5);
+				  number_close_black_worms, NULL, 5);
+  
   do_compute_effective_worm_sizes(WHITE, close_white_worms,
-				  number_close_white_worms, 5);
+				  number_close_white_worms, NULL, 5);
+}
+
+/* Same as above but does not write to the worm array or update the
+ * close worms arrays. Intended to be used when stackp > 0.
+ */
+void
+compute_effective_string_sizes(float effective_sizes[BOARDMAX])
+{
+  do_compute_effective_worm_sizes(BLACK | WHITE, NULL, NULL,
+				   effective_sizes, 3);
 }
 
 static void
 do_compute_effective_worm_sizes(int color, int (*cw)[MAX_CLOSE_WORMS],
-				int *ncw, int max_distance)
+				int *ncw, float *effective_sizes,
+				int max_distance)
 {
   int pos;
 
@@ -647,8 +668,12 @@
     }
   }
 
-  /* Compute the effective sizes but only when all worms are considered. */
-  if (color == (BLACK | WHITE)) {
+  /* Compute the effective sizes if caller wants them. */
+  if (effective_sizes) {
+    for (pos = BOARDMIN; pos < BOARDMAX; pos++)
+      if (ON_BOARD(pos))
+	effective_sizes[pos] = 0.0;
+      
     /* Distribute (fractional) contributions to the worms. */
     for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
       if (!ON_BOARD(pos))
@@ -657,16 +682,11 @@
       for (k = 0; k < nworms[pos]; k++) {
 	int w = worms[pos][k];
 	if (board[pos] == EMPTY)
-	  worm[w].effective_size += 0.5/nworms[pos];
+	  effective_sizes[w] += 0.5/nworms[pos];
 	else
-	  worm[w].effective_size += 1.0;
+	  effective_sizes[w] += 1.0;
       }
     }
-    
-    /* Propagate the effective size values all over the worms. */
-    for (pos = BOARDMIN; pos < BOARDMAX; pos++)
-      if (IS_STONE(board[pos]) && is_worm_origin(pos, pos))
-	propagate_worm(pos);
   }
 
   /* Fill in the appropriate close_*_worms (cw) and
@@ -676,13 +696,16 @@
     if (!ON_BOARD(pos))
       continue;
 
-    if (nworms[pos] > MAX_CLOSE_WORMS)
-      ncw[pos] = 0;
-    else
-      ncw[pos] = nworms[pos];
+    if (ncw) {
+      if (nworms[pos] > MAX_CLOSE_WORMS)
+	ncw[pos] = 0;
+      else
+	ncw[pos] = nworms[pos];
+    }
 
-    for (k = 0; k < ncw[pos]; k++)
-      cw[pos][k] = worms[pos][k];
+    if (cw)
+      for (k = 0; k < ncw[pos]; k++) 
+	cw[pos][k] = worms[pos][k];
   }
 }
 
@@ -1621,7 +1644,8 @@
          * possible that it only has a ko defense and then we would
          * risk to find an irrelevant move to attack with ko.
 	 */
-	if (dcode != WIN && REVERSE_RESULT(dcode) >= worm[str].attack_codes[0]) {
+	if (dcode != WIN
+	    && REVERSE_RESULT(dcode) >= worm[str].attack_codes[0]) {
 	  change_attack(str, move, REVERSE_RESULT(dcode));
 	  DEBUG(DEBUG_WORMS,
 		"Attack pattern %s+%d found attack on %1m at %1m with code %d\n",
@@ -1732,6 +1756,222 @@
 }
 
 /* ================================================================ */
+/*                Static worm connection analysis                   */
+/* ================================================================ */
+
+/* The static worm connection analysis uses the C patterns in conn.db
+ * to find worms which are close enough so that the connection reading
+ * can be expected to determine that they are expected unless there is
+ * a serious connection problem. Then connection reading is done to
+ * see which of these are indeed connected and intransitive
+ * connections (A connected to B and B connected to C but A not
+ * connected to C) are detected.
+ *
+ * The results are stored in a two-dimensional array for each pair of
+ * worms. Only the worm origins have valid data. Each entry is an
+ * unsigned char where the three lowest bits contain the disconnect
+ * result code, the fourth bit marks potential connections (as found
+ * by the C patterns in conn.db) and the fifth bit is set for pairs of
+ * strings being intransitively connected.
+ *
+ * There are two parallel worm connection matrices. The primary one is
+ * used at stackp==0 and contains the static information for the
+ * original position. The secondary array is used when stackp>0,
+ * primarily to allow computation of a new set of dragons after a move
+ * has been made.
+ */
+static unsigned char primary_worm_connection_matrix[BOARDMAX][BOARDMAX];
+static unsigned char secondary_worm_connection_matrix[BOARDMAX][BOARDMAX];
+/* The three lowest bits in the matrix store the disconnect result. */
+#define POTENTIAL_WORM_CONNECTION     8
+#define INTRANSITIVE_WORM_CONNECTION 16
+
+void
+initialize_static_worm_connections(void)
+{
+  int w1;
+  int w2;
+
+  unsigned char (*worm_connection_matrix)[BOARDMAX][BOARDMAX];
+  if (stackp == 0)
+    worm_connection_matrix = &primary_worm_connection_matrix;
+  else
+    worm_connection_matrix = &secondary_worm_connection_matrix;
+  
+#if 0
+  memset(worm_connection_matrix, 0, sizeof(worm_connection_matrix));
+#else
+  for (w1 = BOARDMIN; w1 < BOARDMAX; w1++)
+    for (w2 = BOARDMIN; w2 < BOARDMAX; w2++)
+      (*worm_connection_matrix)[w1][w2] = 0;
+#endif
+}
+
+#define DEBUG_EXPERIMENTAL_CONNECTIONS \
+  ((debug & DEBUG_DRAGONS) && experimental_connections)
+
+
+void
+analyze_worm_connections()
+{
+  int w1;
+  int w2;
+  int w3;
+
+  unsigned char (*worm_connection_matrix)[BOARDMAX][BOARDMAX];
+  if (stackp == 0)
+    worm_connection_matrix = &primary_worm_connection_matrix;
+  else
+    worm_connection_matrix = &secondary_worm_connection_matrix;
+  
+  if (DEBUG_EXPERIMENTAL_CONNECTIONS)
+    gprintf("analyze_worm_connections\n");
+  
+  for (w1 = BOARDMIN; w1 < BOARDMAX; w1++) {
+    if (!IS_STONE(board[w1]) || w1 != find_origin(w1))
+      continue;
+    
+    for (w2 = w1 + 1; w2 < BOARDMAX; w2++) {
+      if (board[w2] != board[w1] || w2 != find_origin(w2))
+	continue;
+
+      if ((*worm_connection_matrix)[w1][w2] & POTENTIAL_WORM_CONNECTION) {
+	int nodes = get_connection_node_counter();
+	unsigned char disconnect_result = disconnect(w1, w2, NULL);
+	gg_assert(disconnect_result < 8);
+	(*worm_connection_matrix)[w1][w2] |= disconnect_result;
+	(*worm_connection_matrix)[w2][w1] |= disconnect_result;
+	if (DEBUG_EXPERIMENTAL_CONNECTIONS)
+	  gprintf("disconnect %1m %1m = %d, %d nodes %d %d\n", w1, w2,
+		  disconnect_result, get_connection_node_counter() - nodes,
+		  (*worm_connection_matrix)[w1][w2],
+		  (*worm_connection_matrix)[w2][w1]);
+      }
+    }
+  }
+
+  for (w1 = BOARDMIN; w1 < BOARDMAX; w1++) {
+    if (!IS_STONE(board[w1]) || w1 != find_origin(w1))
+      continue;
+    
+    for (w2 = BOARDMIN; w2 < BOARDMAX; w2++) {
+      if (w2 == w1 || board[w2] != board[w1] || w2 != find_origin(w2))
+	continue;
+
+      if (!((*worm_connection_matrix)[w1][w2] & POTENTIAL_WORM_CONNECTION)
+	  || ((*worm_connection_matrix)[w1][w2] & 7) != 0)
+	continue;
+
+      for (w3 = BOARDMIN; w3 < BOARDMAX; w3++) {
+	if (w3 == w2 || board[w3] != board[w2] || w3 != find_origin(w3))
+	  continue;
+      
+	if (!((*worm_connection_matrix)[w2][w3] & POTENTIAL_WORM_CONNECTION)
+	    || ((*worm_connection_matrix)[w2][w3] & 7) != 0)
+	  continue;
+
+	if (((*worm_connection_matrix)[w1][w3] & POTENTIAL_WORM_CONNECTION)
+	    && ((*worm_connection_matrix)[w1][w3] & 7) != 0) {
+	  if (DEBUG_EXPERIMENTAL_CONNECTIONS)
+	    gprintf("intransitive connection %1m - %1m - %1m %d\n", w1, w2, w3,
+		    (*worm_connection_matrix)[w1][w3],
+		    (*worm_connection_matrix)[w3][w1]);
+	  (*worm_connection_matrix)[w1][w3] |= INTRANSITIVE_WORM_CONNECTION;
+	  (*worm_connection_matrix)[w3][w1] |= INTRANSITIVE_WORM_CONNECTION;
+	}
+      }
+    }
+  }
+}
+
+static void
+normalize_worms(int *w1, int *w2)
+{
+  *w1 = find_origin(*w1);
+  *w2 = find_origin(*w2);
+}
+
+void
+register_potential_worm_connection(int w1, int w2)
+{
+  unsigned char (*worm_connection_matrix)[BOARDMAX][BOARDMAX];
+  if (stackp == 0)
+    worm_connection_matrix = &primary_worm_connection_matrix;
+  else
+    worm_connection_matrix = &secondary_worm_connection_matrix;
+  
+  normalize_worms(&w1, &w2);
+  if (DEBUG_EXPERIMENTAL_CONNECTIONS
+      && !((*worm_connection_matrix)[w1][w2] & POTENTIAL_WORM_CONNECTION))
+    gprintf("Registered potential worm connection %1m - %1m %d %d\n", w1, w2,
+	    (*worm_connection_matrix)[w1][w2],
+	    (*worm_connection_matrix)[w2][w1]);
+  (*worm_connection_matrix)[w1][w2] |= POTENTIAL_WORM_CONNECTION;
+  (*worm_connection_matrix)[w2][w1] |= POTENTIAL_WORM_CONNECTION;
+}
+
+int
+worms_are_connected(int w1, int w2)
+{
+  unsigned char (*worm_connection_matrix)[BOARDMAX][BOARDMAX];
+  if (stackp == 0)
+    worm_connection_matrix = &primary_worm_connection_matrix;
+  else
+    worm_connection_matrix = &secondary_worm_connection_matrix;
+  
+  normalize_worms(&w1, &w2);
+  if (((*worm_connection_matrix)[w1][w2] & POTENTIAL_WORM_CONNECTION)
+      && ((*worm_connection_matrix)[w1][w2] & 7) == 0)
+    return 1;
+
+  return 0;
+}
+
+int
+worms_are_connected_with_ko(int w1, int w2)
+{
+  unsigned char (*worm_connection_matrix)[BOARDMAX][BOARDMAX];
+  if (stackp == 0)
+    worm_connection_matrix = &primary_worm_connection_matrix;
+  else
+    worm_connection_matrix = &secondary_worm_connection_matrix;
+  
+  normalize_worms(&w1, &w2);
+  if (((*worm_connection_matrix)[w1][w2] & POTENTIAL_WORM_CONNECTION)
+      && (((*worm_connection_matrix)[w1][w2] & 7) == KO_A
+	  || ((*worm_connection_matrix)[w1][w2] & 7) == KO_B))
+    return 1;
+
+  return 0;
+}
+
+int
+worms_are_close(int w1, int w2)
+{
+  unsigned char (*worm_connection_matrix)[BOARDMAX][BOARDMAX];
+  if (stackp == 0)
+    worm_connection_matrix = &primary_worm_connection_matrix;
+  else
+    worm_connection_matrix = &secondary_worm_connection_matrix;
+  
+  normalize_worms(&w1, &w2);
+  return ((*worm_connection_matrix)[w1][w2] & POTENTIAL_WORM_CONNECTION);
+}
+
+int
+worms_are_intransitively_connected(int w1, int w2)
+{
+  unsigned char (*worm_connection_matrix)[BOARDMAX][BOARDMAX];
+  if (stackp == 0)
+    worm_connection_matrix = &primary_worm_connection_matrix;
+  else
+    worm_connection_matrix = &secondary_worm_connection_matrix;
+  
+  normalize_worms(&w1, &w2);
+  return ((*worm_connection_matrix)[w1][w2] & INTRANSITIVE_WORM_CONNECTION);
+}
+
+/* ================================================================ */
 /*                      Debugger functions                          */
 /* ================================================================ */
 
Index: patterns/conn.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/conn.db,v
retrieving revision 1.43
diff -u -r1.43 conn.db
--- patterns/conn.db	23 Jan 2006 18:15:51 -0000	1.43
+++ patterns/conn.db	9 Feb 2006 18:44:44 -0000
@@ -77,7 +77,15 @@
 #
 ########################
 
-callback_data X!
+callback_data OX!
+
+Pattern EB1
+
+OxOxO
+..*..
+-----
+
+:|,BY
 
 
 ##########################
@@ -86,7 +94,7 @@
 #
 ##########################
 
-callback_data X!
+callback_data OX!
 
 
 Pattern CB1b
@@ -443,6 +451,17 @@
 #
 ############################
 
+Pattern CC330
+
+O.o
+XOo
+O.o
+XOo
+
+:8,CY
+
+
+
 ####################################################################
 #
 # CC4xx -  fragile double connections
@@ -489,7 +508,7 @@
 Xaf
 ?e.
 
-;xcut(a)
+;xcut(a) && !experimental_connections
 
 >if (!xplay_attack_either(b,c,d,b,d) || !xplay_attack_either(c,b,a,c,a))
 >  amalgamate(e,f);
Index: patterns/connections.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/connections.c,v
retrieving revision 1.44
diff -u -r1.44 connections.c
--- patterns/connections.c	23 Jan 2006 18:15:51 -0000	1.44
+++ patterns/connections.c	9 Feb 2006 18:44:44 -0000
@@ -40,15 +40,23 @@
   int first_dragon  = NO_MOVE;
   int second_dragon = NO_MOVE;
 
+  /* Strings of our color. */
+  int o_strings[BOARDMAX];
+  int o_nstrings = 0;
+
   int other = OTHER_COLOR(color);
   UNUSED(data);
 
   move = AFFINE_TRANSFORM(pattern->move_offset, ll, anchor);
   
-  if ((pattern->class & CLASS_B) && !safe_move(move, other))
+  if ((pattern->class & CLASS_B) && !safe_move(move, other)
+      && !experimental_connections)
+    return;
+
+  if ((pattern->class & CLASS_Y) && !experimental_connections)
     return;
 
-  if (pattern->class & CLASS_C) {
+  if ((pattern->class & CLASS_C) || experimental_connections) {
     /* If C pattern, test if there are more than one dragon in this
      * pattern so that there is something to connect, before doing any
      * expensive reading.
@@ -57,20 +65,36 @@
     for (k = 0; k < pattern->patlen; ++k) { /* match each point */
       /* transform pattern real coordinate */
       int pos = AFFINE_TRANSFORM(pattern->patn[k].offset, ll, anchor);
-      
-      /* Look for distinct dragons. */
+
       if (pattern->patn[k].att == ATT_O) {
-	if (first_dragon == NO_MOVE)
-	  first_dragon = dragon[pos].origin;
-	else if (second_dragon == NO_MOVE
-		 && dragon[pos].origin != first_dragon) {
-	  second_dragon = dragon[pos].origin;
-	  /* A second dragon found, no need to continue looping. */
-	  break;
+	if (!experimental_connections) {
+	  /* Look for distinct dragons. */
+	  if (first_dragon == NO_MOVE)
+	    first_dragon = dragon[pos].origin;
+	  else if (second_dragon == NO_MOVE
+		   && dragon[pos].origin != first_dragon) {
+	    second_dragon = dragon[pos].origin;
+	    /* A second dragon found, no need to continue looping. */
+	    break;
+	  }
+	}
+	else {
+	  int origin = find_origin(pos);
+	  int l;
+	  for (l = 0; l < o_nstrings; l++) {
+	    if (o_strings[l] == origin)
+	      break;
+	  }
+	  if (l == o_nstrings) {
+	    for (l = 0; l < o_nstrings; l++)
+	      register_potential_worm_connection(o_strings[l], origin);
+	    o_strings[l] = origin;
+	    o_nstrings++;
+	  }
 	}
       }
     }
-    if (second_dragon == NO_MOVE)
+    if (!experimental_connections && second_dragon == NO_MOVE)
       return; /* Nothing to amalgamate. */
   }
     
@@ -144,6 +168,7 @@
      * can be attacked.
      */
     if ((pattern->class & CLASS_C)
+	&& !experimental_connections
 	&& board[pos] == color
 	&& pattern->patn[k].att == ATT_O
 	&& ((pattern->class & CLASS_s) || attack(pos, NULL) == 0)) {
Index: patterns/helpers.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/helpers.c,v
retrieving revision 1.72
diff -u -r1.72 helpers.c
--- patterns/helpers.c	23 Jan 2006 18:15:51 -0000	1.72
+++ patterns/helpers.c	9 Feb 2006 18:44:44 -0000
@@ -428,6 +428,8 @@
 void
 amalgamate_most_valuable_helper(int apos, int bpos, int cpos)
 {
+  if (experimental_connections)
+    return;
   if (!is_same_dragon(apos, bpos) && !is_same_dragon(bpos, cpos)) {
     if (dragon[apos].effective_size >= dragon[cpos].effective_size)
       join_dragons(apos, bpos);
Index: patterns/owl_defendpats.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/owl_defendpats.db,v
retrieving revision 1.133
diff -u -r1.133 owl_defendpats.db
--- patterns/owl_defendpats.db	23 Jan 2006 18:15:51 -0000	1.133
+++ patterns/owl_defendpats.db	9 Feb 2006 18:44:46 -0000
@@ -7074,6 +7074,23 @@
 ;&& oplay_disconnect(*,a,b) != WIN
 
 
+Pattern D1396
+# gf New pattern. (3.7.5)
+# See gifu03:404
+
+O...O
+.*.O.
+-----
+
+:8,b,value(80)
+
+B...A
+.*.O.
+-----
+
+;attack(A) && owl_strong_dragon(B) && !oplay_disconnect(*,A,B)
+
+
 #########################################################
 #                                                       #
 #                          Ko                           #
Index: patterns/patterns2.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/patterns2.db,v
retrieving revision 1.82
diff -u -r1.82 patterns2.db
--- patterns/patterns2.db	23 Jan 2006 18:15:51 -0000	1.82
+++ patterns/patterns2.db	9 Feb 2006 18:44:46 -0000
@@ -852,6 +852,14 @@
 ;oplay_attack_either(*,a,B,a) && !oplay_attack(*,c) && !oplay_attack(*,d)
 
 
+Pattern Conn612
+
+O*O.
+XOXO
+
+:8,C
+
+
 ##############################################
 #
 # Connection of two space jump from two stones
