diff -N -r -u -X .ignore gnugo-copy/engine/reading.c gnugo/engine/reading.c
--- gnugo-copy/engine/reading.c	2007-03-06 17:11:34.531250000 +0100
+++ gnugo/engine/reading.c	2007-03-07 21:30:10.015625000 +0100
@@ -484,7 +484,7 @@
  * is a frontend to the attack() and find_defense() functions, which
  * guarantees a consistent result. If a string cannot be attacked, 0
  * is returned and acode is 0. If a string can be attacked and
- * defended, WIN is returned, acode and dcode are both non-zero, and
+ * defended, 1 is returned, acode and dcode are both non-zero, and
  * (attack_point), (defense_point) both point to vertices on the board. 
  * If a string can be attacked but not defended, 0 is again returned, 
  * acode is non-zero, dcode is 0, and (attack_point) points to a vertex 
@@ -507,7 +507,7 @@
   int dpos = NO_MOVE;
 
   acode = attack(str, &apos);
-  if (acode != 0)
+  if (acode)
     dcode = find_defense(str, &dpos);
 
   ASSERT1(!(acode != 0 && dcode == WIN && dpos == NO_MOVE), str);
@@ -521,7 +521,7 @@
   if (defense_point)
     *defense_point = dpos;
 
-  return acode != 0 && dcode != 0;
+  return acode && dcode;
 }
 
 
@@ -1021,38 +1021,35 @@
 
 
 /* Return up to max_threats threats to capture the string at str.
- * If the string is directly attackable the number of threats
- * is reported to be 0.
+ * The function should be called only when the string is not directly
+ * attackable.
  *
  * NOTE:  You can call attack_threats with moves[] and codes[] 
  *        already partly filled in. So if you want to get the
  *        threats from scratch, you have to set them to 0
  *        yourself.
- *
- * FIXME: Shall we report upgrades, like we can capture in ko but
- *        have a threat to capture unconditionally?
  */
 
 int
 attack_threats(int str, int max_points, int moves[], int codes[])
 {
   int other;
-  int num_threats;
   int liberties;
   int libs[MAXLIBS];
-  int num_adj;
+  int *cur_lib, *last_lib;
   int adjs[MAXCHAIN];
-  int k;
-  int l;
+  int *cur_adj, *last_adj;
   int r;
   int discard;
+  int aa, bb;
+  int acode;
+  const int *cur_delta;
 
   ASSERT1(IS_STONE(board[str]), str);
+  ASSERT1(!attack(str, NULL), str);
 
-  /* Only handle strings with no way to capture immediately.
-   * For now, we treat ko the same as unconditionally. */
-  if (attack(str, NULL) != 0)
-    return 0;
+  other = OTHER_COLOR(board[str]);
+  liberties = findlib(str, MAXLIBS, libs);
 
   /* This test would seem to be unnecessary since we only threaten
    * strings with attack_code == 0, but it turns out that single
@@ -1061,27 +1058,30 @@
    *
    * The test against 6 liberties is just an optimization.
    */
-  other = OTHER_COLOR(board[str]);
-  liberties = findlib(str, MAXLIBS, libs);
-  if (liberties > 1 && liberties < 6) {
-    for (k = 0; k < liberties; k++) {
-      int aa = libs[k];
+  if (liberties == 1)
+    change_tactical_point(str, libs[0], WIN, moves, codes,
+			  worm[str].discarded_att_threats);
+  else if (liberties < 6) {
+    cur_lib = libs;
+    last_lib = cur_lib + liberties;
+
+    for (; cur_lib < last_lib; cur_lib++) {
+      aa = *cur_lib;
 
       /* Try to threaten on the liberty. */
       if (trymove(aa, other, "attack_threats-A", str)) {
-       int acode = attack(str, NULL);
-       if (acode != 0)
+       acode = attack(str, NULL);
+       if (acode)
 	 change_tactical_point(str, aa, acode, moves, codes,
 			       worm[str].discarded_att_threats);
        popgo();
       }
 
       /* Try to threaten on second order liberties. */
-      for (l = 0; l < 4; l++) {
-       int bb = libs[k] + delta[l];
+      for (cur_delta = delta; cur_delta < last_delta_4; cur_delta++) {
+       bb = aa + *cur_delta;
 
-       if (!ON_BOARD(bb)
-           || IS_STONE(board[bb])
+       if (board[bb]
 	   || attack_threat_move_known(bb, str)
 	   || discarded_move_known(bb, MAX_TACTICAL_POINTS,
 				   worm[str].discarded_att_threats)
@@ -1091,8 +1091,8 @@
        discard = 1;
 
        if (trymove(bb, other, "attack_threats-B", str)) {
-         int acode = attack(str, NULL);
-	 if (acode != 0) {
+         acode = attack(str, NULL);
+	 if (acode) {
 	   change_tactical_point(str, bb, acode, moves, codes,
 				 worm[str].discarded_att_threats);
 	   discard = 0;
@@ -1108,41 +1108,52 @@
   }
 
   /* Threaten to attack by saving weak neighbors. */
-  num_adj = chainlinks(str, adjs);
-  for (k = 0; k < num_adj; k++) {
-    int bb;
-    int acode;
+  cur_adj = adjs;
+  last_adj = cur_adj + chainlinks(str, adjs);
+  for (; cur_adj < last_adj; cur_adj++) {
     int dcode;
 
-    attack_and_defend(adjs[k], &acode, NULL, &dcode, NULL);
-    if (acode == 0 || dcode == 0)
+    if (!attack_and_defend(*cur_adj, &acode, NULL, &dcode, NULL))
       continue;
 
     /* Step through all defense points. */
     for (r = 0; r < max_points; r++) {
-      if (worm[adjs[k]].defense_codes[r] == 0)
+      if (!worm[*cur_adj].defense_codes[r])
 	break;
-      bb = worm[adjs[k]].defense_points[r];
+      bb = worm[*cur_adj].defense_points[r];
+
+      if (attack_threat_move_known(bb, str)
+	  || discarded_move_known(bb, MAX_TACTICAL_POINTS,
+				  worm[str].discarded_att_threats))
+        continue;
+
+      discard = 1;
 
       /* Test the move and see if it is a threat. */
       if (trymove(bb, other, "attack_threats-C", str)) {
-	if (board[str] == EMPTY)
+	if (!board[str])
 	  acode = WIN;
 	else
 	  acode = attack(str, NULL);
-	if (acode != 0)
+	if (acode) {
 	  change_tactical_point(str, bb, acode, moves, codes,
 				worm[str].discarded_att_threats);
+	  discard = 0;
+	}
 	popgo();
       }
+
+      if (discard)
+	movelist_change_discarded(bb, MAX_TACTICAL_POINTS,
+				  worm[str].discarded_att_threats);
     }
   }
 
   /* Return actual number of threats found regardless of attack code. */
-  for (num_threats = 0; num_threats < max_points; num_threats++)
-    if (codes[num_threats] == 0)
+  for (r = 0; r < max_points; r++)
+    if (!codes[r])
       break;
-  return num_threats;
+  return r;
 }
 
 
diff -N -r -u -X .ignore gnugo-copy/engine/worm.c gnugo/engine/worm.c
--- gnugo-copy/engine/worm.c	2007-03-06 14:58:57.953125000 +0100
+++ gnugo/engine/worm.c	2007-03-07 21:31:08.000000000 +0100
@@ -822,8 +822,8 @@
 	 */
 	if (!send_two_return_one(pos, other)
 	    && trymove(pos, other, "make_worms", str)) {
-	  if (board[str] == EMPTY || attack(str, NULL)) {
-	    if (board[str] == EMPTY)
+	  if (!board[str] || attack(str, NULL)) {
+	    if (!board[str])
 	      dcode = 0;
 	    else
 	      dcode = find_defense(str, NULL);
@@ -863,79 +863,61 @@
 {
   int str;
   static int libs[MAXLIBS];
-  int liberties;
-  
-  int k;
-  int l;
+  int *cur_lib, *last_lib;
+  int aa, bb;
   int color;
   int discard;
+  int dcode, worm_dcode;
+  const int *cur_delta;
   
   for (str = BOARDMIN; str < BOARDMAX; str++) {
-    color = board[str];
-    if (!IS_STONE(color) || !is_worm_origin(str, str))
+    if (!IS_STONE(board[str]) || !is_worm_origin(str, str))
       continue;
 
+    color = board[str];
+
     /* 1. Start with finding attack threats. */
     /* Only try those worms that have no attack. */
-    if (worm[str].attack_codes[0] == 0) {
+    if (!worm[str].attack_codes[0]) {
       attack_threats(str, MAX_TACTICAL_POINTS,
 		     worm[str].attack_threat_points,
 		     worm[str].attack_threat_codes);
-#if 0
-      /* Threaten to attack by saving weak neighbors. */
-      num_adj = chainlinks(str, adjs);
-      for (k = 0; k < num_adj; k++) {
-	if (worm[adjs[k]].attack_codes[0] != 0
-	    && worm[adjs[k]].defense_codes[0] != 0)
-	  for (r = 0; r < MAX_TACTICAL_POINTS; r++) {
-	    int bb;
-	    
-	    if (worm[adjs[k]].defense_codes[r] == 0)
-	      break;
-	    bb = worm[adjs[k]].defense_points[r];
-	    if (trymove(bb, other, "threaten attack", str,
-			EMPTY, NO_MOVE)) {
-	      int acode;
-	      if (board[str] == EMPTY)
-		acode = WIN;
-	      else
-		acode = attack(str, NULL);
-	      if (acode != 0)
-		change_attack_threat(str, bb, acode);
-	      popgo();
-	    }
-	  }
-      }
-#endif
+
       /* FIXME: Try other moves also (patterns?). */
     }
     
     /* 2. Continue with finding defense threats. */
     /* Only try those worms that have an attack. */
-    if (worm[str].attack_codes[0] != 0
-	&& worm[str].defense_codes[0] == 0) {
+    else {
+      worm_dcode = worm[str].defense_codes[0];
 
-      liberties = findlib(str, MAXLIBS, libs);
+      cur_lib = libs;
+      last_lib = cur_lib + findlib(str, MAXLIBS, libs);
       
-      for (k = 0; k < liberties; k++) {
-	int aa = libs[k];
+      for (; cur_lib < last_lib; cur_lib++) {
+	aa = *cur_lib;
+
+	discard = 1;
 	
 	/* Try to threaten on the liberty. */
 	if (trymove(aa, color, "threaten defense", NO_MOVE)) {
-	  if (attack(str, NULL) == WIN) {
-	    int dcode = find_defense(str, NULL);
-	    if (dcode != 0)
-	      change_defense_threat(str, aa, dcode);
+	  dcode = find_defense(str, NULL);
+	  if (dcode > worm_dcode) {
+	    change_defense_threat(str, aa, dcode);
+	    discard = 0;
 	  }
 	  popgo();
 	}
+
+	if (discard)
+	  movelist_change_discarded(aa, MAX_TACTICAL_POINTS,
+				    worm[str].discarded_def_threats);
 	
 	/* Try to threaten on second order liberties. */
-	for (l = 0; l < 4; l++) {
-	  int bb = libs[k] + delta[l];
+	for (cur_delta = delta; cur_delta < last_delta_4; cur_delta++) {
+	  bb = aa + *cur_delta;
 	  
-	  if (!ON_BOARD(bb)
-	      || IS_STONE(board[bb])
+	  if (board[bb]
 	      || defense_threat_move_known(bb, str)
 	      || discarded_move_known(bb, MAX_TACTICAL_POINTS,
 				      worm[str].discarded_def_threats)
@@ -945,12 +927,10 @@
 	  discard = 1;
 	  
 	  if (trymove(bb, color, "threaten defense", str)) {
-	    if (attack(str, NULL) == WIN) {
-	      int dcode = find_defense(str, NULL);
-	      if (dcode != 0) {
-		change_defense_threat(str, bb, dcode);
-		discard = 0;
-	      }
+	    dcode = find_defense(str, NULL);
+	    if (dcode > worm_dcode) {
+	      change_defense_threat(str, bb, dcode);
+	      discard = 0;
 	    }
 	    popgo();
 	  }
@@ -961,7 +941,7 @@
 	}
       }
       
-      /* It might be interesting to look for defense threats by
+      /* FIXME: It might be interesting to look for defense threats by
        * attacking weak neighbors, similar to threatening attack by
        * defending a weak neighbor. However, in this case it seems
        * probable that if there is such an attack, it's a real
diff -N -r -u -X .ignore gnugo-copy/regression/nngs.tst gnugo/regression/nngs.tst
--- gnugo-copy/regression/nngs.tst	2006-03-10 21:03:22.000000000 +0100
+++ gnugo/regression/nngs.tst	2007-03-07 17:34:59.796875000 +0100
@@ -993,4 +993,4 @@
 #CATEGORY=OWL_TUNING
 loadsgf games/nngs/LordOfPi-gnugo-3.1.20-200201202014.sgf 36
 2060 reg_genmove black
-#? [C4|C1|F1]
+#? [C4]
