diff -N -r -u -X .ignore gnugo-copy/engine/board.c gnugo/engine/board.c
--- gnugo-copy/engine/board.c	2007-01-22 16:53:16.703125000 +0100
+++ gnugo/engine/board.c	2007-02-01 18:23:58.593750000 +0100
@@ -874,7 +874,7 @@
       return 0;
     
     /* 4. The location must not be the ko point, unless ignore_ko == 1. */
-	if (!ignore_ko && pos == board_ko_pos) {
+    if (!ignore_ko && pos == board_ko_pos) {
       /*    The ko position is guaranteed to have all neighbors of the
        *    same color, or off board. If that color is the same as the
        *    move the ko is being filled, it is always allowed. This
@@ -884,7 +884,7 @@
       if (board[WEST(pos)] == OTHER_COLOR(color)
           || board[EAST(pos)] == OTHER_COLOR(color))
         return 0;
-	}
+    }
 
     /* 5. Test for suicide. */
     if (is_suicide(pos, color))
diff -N -r -u -X .ignore gnugo-copy/engine/liberty.h gnugo/engine/liberty.h
--- gnugo-copy/engine/liberty.h	2007-01-24 17:53:20.031250000 +0100
+++ gnugo/engine/liberty.h	2007-02-01 19:40:54.578125000 +0100
@@ -336,11 +336,13 @@
 /* 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, 
-			   int points[], int codes[]);
+			   int points[], int codes[], int discarded_points[]);
+int discarded_move_known(int move, int max_points, int points[]);
+void movelist_change_discarded(int move, int max_points, int points[]);
 
 /* worm.c */
 void change_tactical_point(int str, int move, int code,
-			   int points[], int codes[]);
+			   int points[], int codes[], int discarded_points[]);
 
 /* surround.c */
 int compute_surroundings(int pos, int apos, int showboard,
@@ -837,12 +839,16 @@
    */
   int attack_points[MAX_TACTICAL_POINTS];
   int attack_codes[MAX_TACTICAL_POINTS];
+  int discarded_attacks[MAX_TACTICAL_POINTS];
   int defense_points[MAX_TACTICAL_POINTS];
   int defense_codes[MAX_TACTICAL_POINTS];
+  int discarded_defenses[MAX_TACTICAL_POINTS];
   int attack_threat_points[MAX_TACTICAL_POINTS];
   int attack_threat_codes[MAX_TACTICAL_POINTS]; 
+  int discarded_att_threats[MAX_TACTICAL_POINTS];
   int defense_threat_points[MAX_TACTICAL_POINTS];
   int defense_threat_codes[MAX_TACTICAL_POINTS];
+  int discarded_def_threats[MAX_TACTICAL_POINTS];
 };
 
 extern struct worm_data worm[BOARDMAX];
diff -N -r -u -X .ignore gnugo-copy/engine/movelist.c gnugo/engine/movelist.c
--- gnugo-copy/engine/movelist.c	2007-01-07 22:54:09.957875000 +0100
+++ gnugo/engine/movelist.c	2007-02-01 19:40:14.078125000 +0100
@@ -41,7 +41,7 @@
   int k;
 
   for (k = 0; k < max_points; k++) {
-    if (codes[k] == 0)
+    if (points[k] == 0)
       return 0;
     if (points[k] == move)
       return codes[k];
@@ -50,6 +50,23 @@
 }
 
 
+/* Return true if the move was previously discarded.
+ */
+int
+discarded_move_known(int move, int max_points, int points[])
+{
+  int k;
+
+  for (k = 0; k < max_points; k++) {
+    if (points[k] == 0)
+      return 0;
+    if (points[k] == move)
+      return 1;
+  }
+  return 0;
+}
+
+
 /*
  * This function does the real work for change_attack(),
  * change_defense(), change_attack_threat(), and
@@ -58,15 +75,24 @@
 
 void
 movelist_change_point(int move, int code, int max_points,
-		      int points[], int codes[])
+		      int points[], int codes[], int discarded_points[])
 {
   int k;
 
   /* First see if we already know about this point. */
-  for (k = 0; k < max_points; k++)
+  for (k = 0; k < max_points; k++) {
     if (points[k] == move)
       break;
 
+    if (points[k] == 0) {
+      /* We have empty space to store the move. */
+      points[k] = move;
+      codes[k] = code;
+      move_up_list(points, codes, k);
+      return;
+    }
+  }
+
   /* Yes, we do. */
   if (k < max_points) {
     if (code >= codes[k])
@@ -78,8 +104,10 @@
   /* This tactical point is new to us. */
   else {
     k--; /* last index */
-    if (code <= codes[k])
+    if (code <= codes[k]) {
+      movelist_change_discarded(move, max_points, discarded_points);
       return; /* Too bad move to store. */
+    }
 
     points[k] = move;
     codes[k] = code;
@@ -88,6 +116,27 @@
 }
 
 
+/*
+ * This function changes discarded moves list.
+ */
+void
+movelist_change_discarded(int move, int max_points, int points[])
+{
+  int k;
+
+  /* We store only max_points discarded moves. */
+  for (k = 0; k < max_points; k++) {
+    if (points[k] == move)
+      return;
+
+    if (points[k] == 0) {
+      points[k] = move;
+      return;
+    }
+  }
+}
+
+
 /* Sort the changed tactical point. We set it to the higher value, so
  * we just have to move it up the table.
  */
diff -N -r -u -X .ignore gnugo-copy/engine/owl.c gnugo/engine/owl.c
--- gnugo-copy/engine/owl.c	2007-01-27 18:56:03.750000000 +0100
+++ gnugo/engine/owl.c	2007-02-05 15:54:36.562500000 +0100
@@ -927,6 +927,9 @@
 	case 5:
 	  checked_moves = shape_offensive_moves;
 	  break;
+	default:
+	  checked_moves = NULL;
+	  gg_assert(0);
 	}
 
 	for (k = 0; k < MAX_MOVES; k++) {
diff -N -r -u -X .ignore gnugo-copy/engine/reading.c gnugo/engine/reading.c
--- gnugo-copy/engine/reading.c	2007-01-22 17:12:18.640625000 +0100
+++ gnugo/engine/reading.c	2007-02-02 19:01:45.578125000 +0100
@@ -1035,6 +1035,7 @@
   int k;
   int l;
   int r;
+  int discard;
 
   ASSERT1(IS_STONE(board[str]), str);
 
@@ -1060,7 +1061,8 @@
       if (trymove(aa, other, "attack_threats-A", str)) {
        int acode = attack(str, NULL);
        if (acode != 0)
-	 change_tactical_point(str, aa, acode, moves, codes);
+	 change_tactical_point(str, aa, acode, moves, codes,
+			       worm[str].discarded_att_threats);
        popgo();
       }
 
@@ -1070,15 +1072,27 @@
 
        if (!ON_BOARD(bb)
            || IS_STONE(board[bb])
+	   || attack_threat_move_known(bb, str)
+	   || discarded_move_known(bb, MAX_TACTICAL_POINTS,
+				   worm[str].discarded_att_threats)
            || liberty_of_string2(bb, str))
          continue;
 
+       discard = 1;
+
        if (trymove(bb, other, "attack_threats-B", str)) {
          int acode = attack(str, NULL);
-         if (acode != 0)
-	   change_tactical_point(str, bb, acode, moves, codes);
+	 if (acode != 0) {
+	   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);
       }
     }
   }
@@ -1087,33 +1101,18 @@
   num_adj = chainlinks(str, adjs);
   for (k = 0; k < num_adj; k++) {
     int bb;
-    int dd;  /* Defense point of weak neighbor. */
     int acode;
     int dcode;
 
-    attack_and_defend(adjs[k], &acode, NULL, &dcode, &dd);
+    attack_and_defend(adjs[k], &acode, NULL, &dcode, NULL);
     if (acode == 0 || dcode == 0)
       continue;
 
-    /* The strange code using r == -1 below is only avoid duplication
-     * of the code starting with "if (trymove..)" below.
-     * If r == -1 and stackp == 0 then use the defense point what we got from
-     * attack_and_defend above. Otherwise step through all defense points.
-     */
-    for (r = -1; r < max_points; r++) {
-      if (stackp == 0) {
-	if (r == -1)
-	  continue;
-	if (worm[adjs[k]].defense_codes[r] == 0)
-	  break;
-	bb = worm[adjs[k]].defense_points[r];
-      }
-      else {
-	if (r == -1)
-	  bb = dd;
-	else
-	  break;
-      }
+    /* Step through all defense points. */
+    for (r = 0; r < max_points; r++) {
+      if (worm[adjs[k]].defense_codes[r] == 0)
+	break;
+      bb = worm[adjs[k]].defense_points[r];
 
       /* Test the move and see if it is a threat. */
       if (trymove(bb, other, "attack_threats-C", str)) {
@@ -1122,15 +1121,14 @@
 	else
 	  acode = attack(str, NULL);
 	if (acode != 0)
-	  change_tactical_point(str, bb, acode, moves, codes);
+	  change_tactical_point(str, bb, acode, moves, codes,
+				worm[str].discarded_att_threats);
 	popgo();
       }
     }
   }
 
   /* Return actual number of threats found regardless of attack code. */
-  if (codes[max_points - 1] > 0)
-    return max_points;
   for (num_threats = 0; num_threats < max_points; num_threats++)
     if (codes[num_threats] == 0)
       break;
diff -N -r -u -X .ignore gnugo-copy/engine/unconditional.c gnugo/engine/unconditional.c
--- gnugo-copy/engine/unconditional.c	2007-01-24 19:29:56.906250000 +0100
+++ gnugo/engine/unconditional.c	2007-01-25 13:24:25.046875000 +0100
@@ -676,13 +676,8 @@
 void
 clear_unconditionally_meaningless_moves()
 {
-  int pos;
-  
-  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
-    if (ON_BOARD(pos)) {
-      meaningless_black_moves[pos] = -1;
-      meaningless_white_moves[pos] = -1;
-    }
+  memset(meaningless_black_moves, -1, sizeof(meaningless_black_moves));
+  memset(meaningless_white_moves, -1, sizeof(meaningless_black_moves));
 }
 
 /* Pick up antisuji and replacement move reasons found by analysis
diff -N -r -u -X .ignore gnugo-copy/engine/worm.c gnugo/engine/worm.c
--- gnugo-copy/engine/worm.c	2007-01-30 23:05:05.937500000 +0100
+++ gnugo/engine/worm.c	2007-02-01 19:01:08.187500000 +0100
@@ -884,6 +884,7 @@
   int k;
   int l;
   int color;
+  int discard;
   
   for (str = BOARDMIN; str < BOARDMAX; str++) {
     color = board[str];
@@ -951,17 +952,28 @@
 	  
 	  if (!ON_BOARD(bb)
 	      || IS_STONE(board[bb])
+	      || defense_threat_move_known(bb, str)
+	      || discarded_move_known(bb, MAX_TACTICAL_POINTS,
+				      worm[str].discarded_def_threats)
 	      || liberty_of_string2(bb, str))
 	    continue;
+
+	  discard = 1;
 	  
 	  if (trymove(bb, color, "threaten defense", str)) {
 	    if (attack(str, NULL) == WIN) {
 	      int dcode = find_defense(str, NULL);
-	      if (dcode != 0)
+	      if (dcode != 0) {
 		change_defense_threat(str, bb, dcode);
+		discard = 0;
+	      }
 	    }
 	    popgo();
 	  }
+
+	  if (discard)
+	    movelist_change_discarded(bb, MAX_TACTICAL_POINTS,
+				      worm[str].discarded_def_threats);
 	}
       }
       
@@ -1063,9 +1075,9 @@
 void
 change_defense(int str, int move, int dcode)
 {
-  str = worm[str].origin;
   change_tactical_point(str, move, dcode,
-			worm[str].defense_points, worm[str].defense_codes);
+			worm[str].defense_points, worm[str].defense_codes,
+			worm[str].discarded_defenses);
 }
 
 
@@ -1080,10 +1092,10 @@
 void
 change_attack(int str, int move, int acode)
 {
-  str = worm[str].origin;
   DEBUG(DEBUG_WORMS, "change_attack: %1m %1m %d\n", str, move, acode);
   change_tactical_point(str, move, acode,
-			worm[str].attack_points, worm[str].attack_codes);
+			worm[str].attack_points, worm[str].attack_codes,
+			worm[str].discarded_attacks);
 }
 
 
@@ -1099,10 +1111,10 @@
 void
 change_defense_threat(int str, int move, int dcode)
 {
-  str = worm[str].origin;
   change_tactical_point(str, move, dcode,
 			worm[str].defense_threat_points,
-			worm[str].defense_threat_codes);
+			worm[str].defense_threat_codes,
+			worm[str].discarded_def_threats);
 }
 
 
@@ -1118,10 +1130,10 @@
 void
 change_attack_threat(int str, int move, int acode)
 {
-  str = worm[str].origin;
   change_tactical_point(str, move, acode,
 			worm[str].attack_threat_points,
-			worm[str].attack_threat_codes);
+			worm[str].attack_threat_codes,
+			worm[str].discarded_att_threats);
 }
 
 
@@ -1181,12 +1193,13 @@
 void
 change_tactical_point(int str, int move, int code,
 		      int points[MAX_TACTICAL_POINTS],
-		      int codes[MAX_TACTICAL_POINTS])
+		      int codes[MAX_TACTICAL_POINTS],
+		      int discarded_points[MAX_TACTICAL_POINTS])
 {
   ASSERT_ON_BOARD1(str);
-  ASSERT1(str == worm[str].origin, str);
   
-  movelist_change_point(move, code, MAX_TACTICAL_POINTS, points, codes);
+  movelist_change_point(move, code, MAX_TACTICAL_POINTS, points, codes,
+			discarded_points);
   propagate_worm2(str);
 }
 
@@ -1602,7 +1615,9 @@
       if (countlib(str) > 4)
 	continue;
 
-      if (attack_move_known(move, str))
+      if (attack_move_known(move, str)
+	  || discarded_move_known(move, MAX_TACTICAL_POINTS,
+				  worm[str].discarded_attacks))
 	continue;
 
       /* No defenses are known at this time, so defend_code is always 0. */
@@ -1612,9 +1627,7 @@
 	continue;
 #endif
       
-      /* FIXME: Don't attack the same string more than once.
-       * Play (move) and see if there is a defense.
-       */
+      /* Play (move) and see if there is a defense. */
       if (trymove(move, color, "attack_callback", str)) {
 	int dcode;
 	if (!board[str])
@@ -1637,7 +1650,13 @@
 		"Attack pattern %s+%d found attack on %1m at %1m with code %d\n",
 		pattern->name, ll, str, move, REVERSE_RESULT(dcode));
 	}
+	else
+	  movelist_change_discarded(move, MAX_TACTICAL_POINTS,
+				    worm[str].discarded_attacks);
       }
+      else
+        movelist_change_discarded(move, MAX_TACTICAL_POINTS,
+				  worm[str].discarded_attacks);
     }
   }
 }
@@ -1688,11 +1707,12 @@
       int str = worm[pos].origin;
 
       if (worm[str].attack_codes[0] == 0
-	  || defense_move_known(move, str))
+	  || defense_move_known(move, str)
+	  || discarded_move_known(move, MAX_TACTICAL_POINTS,
+				  worm[str].discarded_defenses))
 	continue;
       
-      /* FIXME: Don't try to defend the same string more than once.
-       * FIXME: For all attacks on this string, we should test whether
+      /* FIXME: For all attacks on this string, we should test whether
        *        the proposed move happens to refute the attack.
        * Play (move) and see if there is an attack.
        */
@@ -1707,7 +1727,13 @@
 		"Defense pattern %s+%d found defense of %1m at %1m with code %d\n",
 		pattern->name, ll, str, move, REVERSE_RESULT(acode));
 	}
+	else
+	  movelist_change_discarded(move, MAX_TACTICAL_POINTS,
+				    worm[str].discarded_defenses);
       }
+      else
+	movelist_change_discarded(move, MAX_TACTICAL_POINTS,
+				  worm[str].discarded_defenses);
     }
   }
 }
diff -N -r -u -X .ignore gnugo-copy/interface/gnugo.vcproj gnugo/interface/gnugo.vcproj
--- gnugo-copy/interface/gnugo.vcproj	2007-01-18 19:29:17.593750000 +0100
+++ gnugo/interface/gnugo.vcproj	2007-02-01 20:11:07.468750000 +0100
@@ -400,6 +400,10 @@
 				RelativePath=".\interface.h"
 				>
 			</File>
+			<File
+				RelativePath="..\engine\liberty.h"
+				>
+			</File>
 		</Filter>
 		<Filter
 			Name="Resource Files"
