Index: engine/optics.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/optics.c,v
retrieving revision 1.101
diff -u -r1.101 optics.c
--- engine/optics.c	8 Oct 2005 08:17:31 -0000	1.101
+++ engine/optics.c	26 Feb 2006 23:21:13 -0000
@@ -2675,21 +2675,55 @@
  * 3 - result has been computed and was a success (1)
  */
 
+/* Types of vertices under consideration. */
+#define REGULAR       0
+#define OUTER_LIBERTY 1
+#define EXTRA_EYE     2
+#define KO_THREAT     3
+
+struct vertex_data {
+  int pos[MAX_BOARD];
+  int type[MAX_BOARD];
+  int num;
+  int ko_threat_pos;
+};
+
 /* Like trymove() except that it does a superko check. This does,
  * however, only disallow repetition (besides simple ko) if the move
  * does not capture any stones.
+ *
+ * In order to cope with bent four in the corner, white is given one
+ * free virtual ko threat, which black can eliminate by spending a
+ * move playing on the "ko threat vertex". This has the side effect
+ * that white wins all direct kos, which is mostly okay until ko
+ * dependent eye values are implemented. It can, however, cause attack
+ * and defense points dependent on ko to be recommended together with
+ * non-ko vital points.
  */
+
+static int forced_ko_threat_stackp = -1;
+
 static int
-eyegraph_trymove(int pos, int color, const char *message, int str)
+eyegraph_trymove(int pos, int color, const char *message, int str,
+		 int ko_threat_pos)
 {
   static Hash_data remembered_board_hashes[MAXSTACK];
   int k;
   int does_capture = does_capture_something(pos, color);
   
   remembered_board_hashes[stackp] = board_hash;
-  
-  if (!trymove(pos, color, message, str))
-    return 0;
+
+  if (!trymove(pos, color, message, str)) {
+    if (color == WHITE
+	&& !is_suicide(pos, color)
+	&& board[ko_threat_pos] == EMPTY) {
+      trymove(ko_threat_pos, BLACK, "answering virtual ko threat", str);
+      forced_ko_threat_stackp = stackp;
+      trymove(pos, color, message, str);
+    }
+    else
+      return 0;
+  }
 
   if (does_capture)
     return 1;
@@ -2700,9 +2734,24 @@
       return 0;
     }
 
+  
   return 1;
 }
 
+static void
+eyegraph_popgo(void)
+{
+  popgo();
+
+  if (forced_ko_threat_stackp == stackp) {
+    popgo();
+    forced_ko_threat_stackp = -1;
+  }
+}
+
+/* Has the vertex a neighbor which is connected to an invincible black
+ * string?
+ */
 static int
 eyegraph_is_margin_or_outer_liberty(int vertex)
 {
@@ -2729,7 +2778,8 @@
 }
 
 static int
-eyegraph_order_moves(int num_vertices, int *vertices, int color_to_move, int *moves)
+eyegraph_order_moves(struct vertex_data *vertices, int color_to_move,
+		     int *moves)
 {
   int num_moves = 0;
   int scores[BOARDMAX];
@@ -2738,28 +2788,27 @@
   int k;
   int r;
 
-  for (k = 0; k < num_vertices; k++) {
-    if (k >= num_vertices - 3) {
+  for (k = 0; k < vertices->num; k++) {
+    if (vertices->type[k] != REGULAR) {
       /* Never useful for white to fill in outer liberties or a second eye. */
       if (color_to_move == WHITE)
 	break;
-      /* No use playing the second outer liberty before the first one. */
-      if (k == num_vertices - 2 && board[vertices[num_vertices - 3]] == EMPTY)
+      /* No use playing a later outer liberty before the previous one. */
+      if (vertices->type[k] == OUTER_LIBERTY
+	  && vertices->type[k - 1] == OUTER_LIBERTY
+	  && board[vertices->pos[k - 1]] == EMPTY)
 	continue;
     }
     
-    move = vertices[k];
+    move = vertices->pos[k];
     score = 0;
 
     if (board[move] != EMPTY)
       continue;
     
-    if (eyegraph_is_margin_or_outer_liberty(move)) {
-      if (k < num_vertices - 3)
+    if (eyegraph_is_margin_or_outer_liberty(move))
+      if (vertices->type[k] == REGULAR)
 	score = 5; /* margin */
-      else
-	score = -10; /* outer liberty */
-    }
 
     if (accuratelib(move, color_to_move, 2, NULL) == 1)
       score -= 3;
@@ -2861,12 +2910,12 @@
     return (result); \
   } while (0);
 
-static int tactical_life_defend(int str, int num_vertices, int *vertices,
+static int tactical_life_defend(int str, struct vertex_data *vertices,
 				unsigned char *results);
 
 /* Determine whether black can capture all white stones. */
 static int
-tactical_life_attack(int str, int num_vertices, int *vertices,
+tactical_life_attack(int str, struct vertex_data *vertices,
 		     unsigned char *results)
 {
   int k;
@@ -2877,9 +2926,9 @@
   int moves[BOARDMAX];
 
   /* Compute hash value to index the result cache with. */
-  for (k = 0; k < num_vertices; k++) {
+  for (k = 0; k < vertices->num; k++) {
     hash *= 3;
-    hash += board[vertices[k]];
+    hash += board[vertices->pos[k]];
   }
   hash *= 2;
   hash += (board_ko_pos != NO_MOVE);
@@ -2903,21 +2952,21 @@
   results[hash] |= 1;
 
   /* Try to play on all relevant vertices. */
-  num_moves = eyegraph_order_moves(num_vertices, vertices,
-				   OTHER_COLOR(board[str]), moves);
+  num_moves = eyegraph_order_moves(vertices, OTHER_COLOR(board[str]), moves);
   for (k = 0; k < num_moves; k++) {
     int move = moves[k];
     if (eyegraph_trymove(move, OTHER_COLOR(board[str]),
-	  		 "tactical_life_attack", str)) {
+	  		 "tactical_life_attack", str,
+			 vertices->ko_threat_pos)) {
       /* We were successful if the white stones were captured or if no
        * defense can be found.
        */
       if (board[str] == EMPTY)
 	result = 1;
       else
-	result = !tactical_life_defend(str, num_vertices, vertices, results);
+	result = !tactical_life_defend(str, vertices, results);
       
-      popgo();
+      eyegraph_popgo();
 
       if (result == 1) {
 	/* Store the result (success) in the cache. */
@@ -2934,7 +2983,7 @@
 
 /* Determine whether white can live with all stones. */
 static int
-tactical_life_defend(int str, int num_vertices, int *vertices,
+tactical_life_defend(int str, struct vertex_data *vertices,
 		     unsigned char *results)
 {
   int k;
@@ -2945,10 +2994,10 @@
   int moves[BOARDMAX];
   
   /* Compute hash value to index the result cache with. */
-  for (k = 0; k < num_vertices; k++) {
+  for (k = 0; k < vertices->num; k++) {
     hash *= 3;
-    ASSERT1(board[vertices[k]] <= 2, vertices[k]);
-    hash += board[vertices[k]];
+    ASSERT1(board[vertices->pos[k]] <= 2, vertices->pos[k]);
+    hash += board[vertices->pos[k]];
   }
   hash *= 2;
   hash += (board_ko_pos != NO_MOVE);
@@ -2972,16 +3021,17 @@
   results[hash] |= (1 << 2);
 
   /* Try to play on all relevant vertices. */
-  num_moves = eyegraph_order_moves(num_vertices, vertices, board[str], moves);
+  num_moves = eyegraph_order_moves(vertices, board[str], moves);
   for (k = 0; k < num_moves; k++) {
     int move = moves[k];
     if ((!is_suicide(move, OTHER_COLOR(board[str]))
 	 || does_capture_something(move, board[str]))
-	&& eyegraph_trymove(move, board[str], "tactical_life_defend", str)) {
+	&& eyegraph_trymove(move, board[str], "tactical_life_defend", str,
+			    vertices->ko_threat_pos)) {
       /* We were successful if no attack can be found. */
-      result = !tactical_life_attack(str, num_vertices, vertices, results);
+      result = !tactical_life_attack(str, vertices, results);
       
-      popgo();
+      eyegraph_popgo();
 
       if (result == 1) {
 	/* Store the result (success) in the cache. */
@@ -2992,7 +3042,7 @@
   }
 
   /* If no move worked, also try passing. */
-  if (!tactical_life_attack(str, num_vertices, vertices, results)) {
+  if (!tactical_life_attack(str, vertices, results)) {
     /* Store the result (success) in the cache. */
     results[hash] = (results[hash] & (~12)) | (3 << 2);
     EYEGRAPH_RETURN(1, "tactical_life_defend: win");
@@ -3009,7 +3059,7 @@
  * used or filled in before starting reading.
  */
 static void
-tactical_life(int have_eye, int num_vertices, int *vertices,
+tactical_life(int have_eye, struct vertex_data *vertices,
 	      int *attack_code, int *num_attacks, int *attack_points,
 	      int *defense_code, int *num_defenses, int *defense_points,
 	      unsigned char *results)
@@ -3038,7 +3088,8 @@
    * suicide the white stones can be considered dead.
    */
   if (!have_eye) {
-    if (!eyegraph_trymove(POS(0, 0), WHITE, "tactical_life-A", NO_MOVE)) {
+    if (!eyegraph_trymove(POS(0, 0), WHITE, "tactical_life-A", NO_MOVE,
+			  NO_MOVE)) {
       *attack_code = WIN;
       *defense_code = 0;
       return;
@@ -3051,9 +3102,9 @@
   /* Call tactical_life_attack() and tactical_life_defend() to
    * determine status.
    */
-  if (tactical_life_attack(str, num_vertices, vertices, results)) {
+  if (tactical_life_attack(str, vertices, results)) {
     *attack_code = WIN;
-    if (tactical_life_defend(str, num_vertices, vertices, results))
+    if (tactical_life_defend(str, vertices, results))
       *defense_code = WIN;
   }
   else
@@ -3067,30 +3118,30 @@
   if (*attack_code != 0 && *defense_code != 0) {
     if (num_attacks != NULL && attack_points != NULL) {
       *num_attacks = 0;
-      num_moves = eyegraph_order_moves(num_vertices, vertices,
+      num_moves = eyegraph_order_moves(vertices,
 				       OTHER_COLOR(board[str]), moves);
       for (k = 0; k < num_moves; k++) {
 	int move = moves[k];
 	if (eyegraph_trymove(move, OTHER_COLOR(board[str]), "tactical_life-B",
-			     str)) {
+			     str, vertices->ko_threat_pos)) {
 	  if (board[str] == EMPTY
-	      || !tactical_life_defend(str, num_vertices, vertices, results))
+	      || !tactical_life_defend(str, vertices, results))
 	    attack_points[(*num_attacks)++] = move;
-	  popgo();
+	  eyegraph_popgo();
 	}
       }
     }
 
     if (num_defenses != NULL && defense_points != NULL) {
       *num_defenses = 0;
-      num_moves = eyegraph_order_moves(num_vertices, vertices, board[str],
-				       moves);
+      num_moves = eyegraph_order_moves(vertices, board[str], moves);
       for (k = 0; k < num_moves; k++) {
 	int move = moves[k];
-	if (eyegraph_trymove(move, board[str], "tactical_life-C", str)) {
-	  if (!tactical_life_attack(str, num_vertices, vertices, results))
+	if (eyegraph_trymove(move, board[str], "tactical_life-C", str,
+			     vertices->ko_threat_pos)) {
+	  if (!tactical_life_attack(str, vertices, results))
 	    defense_points[(*num_defenses)++] = move;
-	  popgo();
+	  eyegraph_popgo();
 	}
       }
     }
@@ -3098,7 +3149,7 @@
 
   /* Unfill the extra eye if we didn't use it. */
   if (!have_eye)
-    popgo();
+    eyegraph_popgo();
 }
 
 /* Determine the eye value of the eyespace for the big white group on
@@ -3113,7 +3164,7 @@
  * need to recursively call ourselves after a move has been made.
  */
 static void
-evaluate_eyespace(struct eyevalue *result, int num_vertices, int *vertices,
+evaluate_eyespace(struct eyevalue *result, struct vertex_data *vertices,
 		  int *num_vital_attacks, int *vital_attacks,
 		  int *num_vital_defenses, int *vital_defenses,
 		  unsigned char *tactical_life_results)
@@ -3141,7 +3192,7 @@
   *num_vital_defenses = 0;
 
   /* Determine tactical life without an extra eye. */
-  tactical_life(0, num_vertices, vertices,
+  tactical_life(0, vertices,
 		&attack_code, &num_attacks, attack_points,
 		&defense_code, &num_defenses, defense_points,
 		tactical_life_results);
@@ -3157,15 +3208,16 @@
     if (sgf_dumptree)
       sgftreeAddComment(sgf_dumptree, "Alive without extra eye.\n");
     
-    num_moves = eyegraph_order_moves(num_vertices, vertices, BLACK, moves);
+    num_moves = eyegraph_order_moves(vertices, BLACK, moves);
     for (k = 0; k < num_moves; k++) {
       int acode, dcode;
       int move = moves[k];
-      if (eyegraph_trymove(move, BLACK, "evaluate_eyespace-A", NO_MOVE)) {
-	tactical_life(0, num_vertices, vertices, &acode, NULL, NULL,
+      if (eyegraph_trymove(move, BLACK, "evaluate_eyespace-A", NO_MOVE,
+			   vertices->ko_threat_pos)) {
+	tactical_life(0, vertices, &acode, NULL, NULL,
 		      &dcode, NULL, NULL, tactical_life_results);
 	if (acode != 0) {
-	  tactical_life(1, num_vertices, vertices, &acode, NULL, NULL,
+	  tactical_life(1, vertices, &acode, NULL, NULL,
 			&dcode, NULL, NULL, tactical_life_results);
 	  if (acode != 0) {
 	    if (a == 1)
@@ -3185,7 +3237,7 @@
 	      sgftreeAddComment(sgf_dumptree, "Ko threat to remove one eye.\n");
 	  }
 	}
-	popgo();
+	eyegraph_popgo();
       }
     }
     set_eyevalue(result, a, 2, 2, 2);
@@ -3204,7 +3256,7 @@
      */
     if (sgf_dumptree)
       sgftreeAddComment(sgf_dumptree, "Critical without extra eye.\n");
-    tactical_life(1, num_vertices, vertices,
+    tactical_life(1, vertices,
 		  &attack_code2, &num_attacks2, attack_points2,
 		  &defense_code2, NULL, NULL, tactical_life_results);
     for (k = 0; k < num_defenses; k++)
@@ -3221,8 +3273,9 @@
       int a = 1;
       for (k = 0; k < num_attacks; k++) {
 	int move = attack_points[k];
-	if (eyegraph_trymove(move, BLACK, "evaluate_eyespace-B", NO_MOVE)) {
-	  evaluate_eyespace(&result2, num_vertices, vertices,
+	if (eyegraph_trymove(move, BLACK, "evaluate_eyespace-B", NO_MOVE,
+			     vertices->ko_threat_pos)) {
+	  evaluate_eyespace(&result2, vertices,
 			    &num_vital_attacks2, vital_attacks2,
 			    &num_vital_defenses2, vital_defenses2,
 			    tactical_life_results);
@@ -3239,7 +3292,7 @@
 	  }
 	  else if (a == 1)
 	    vital_attacks[(*num_vital_attacks)++] = move;
-	  popgo();
+	  eyegraph_popgo();
 	}
       }
       set_eyevalue(result, a, 1, 2, 2);
@@ -3259,7 +3312,7 @@
      */
     if (sgf_dumptree)
       sgftreeAddComment(sgf_dumptree, "Dead without extra eye.\n");
-    tactical_life(1, num_vertices, vertices,
+    tactical_life(1, vertices,
 		  &attack_code, &num_attacks, attack_points,
 		  &defense_code, &num_defenses, defense_points,
 		  tactical_life_results);
@@ -3271,15 +3324,16 @@
       int d = 1;
       if (sgf_dumptree)
 	sgftreeAddComment(sgf_dumptree, "Alive with extra eye.\n");
-      num_moves = eyegraph_order_moves(num_vertices, vertices, BLACK, moves);
+      num_moves = eyegraph_order_moves(vertices, BLACK, moves);
       for (k = 0; k < num_moves; k++) {
 	int acode, dcode;
 	int move = moves[k];
-	if (eyegraph_trymove(move, BLACK, "evaluate_eyespace-C", NO_MOVE)) {
-	  tactical_life(1, num_vertices, vertices, &acode, NULL, NULL,
+	if (eyegraph_trymove(move, BLACK, "evaluate_eyespace-C", NO_MOVE,
+			     vertices->ko_threat_pos)) {
+	  tactical_life(1, vertices, &acode, NULL, NULL,
 			&dcode, NULL, NULL, tactical_life_results);
 	  if (acode != 0) {
-	    evaluate_eyespace(&result2, num_vertices, vertices,
+	    evaluate_eyespace(&result2, vertices,
 			      &num_vital_attacks2, vital_attacks2,
 			      &num_vital_defenses2, vital_defenses2,
 			      tactical_life_results);
@@ -3291,19 +3345,20 @@
 		sgftreeAddComment(sgf_dumptree, "Attacking ko threat.\n");
 	    }
 	  }
-	  popgo();
+	  eyegraph_popgo();
 	}
       }
       
-      num_moves = eyegraph_order_moves(num_vertices, vertices, WHITE, moves);
+      num_moves = eyegraph_order_moves(vertices, WHITE, moves);
       for (k = 0; k < num_moves; k++) {
 	int acode, dcode;
 	int move = moves[k];
-	if (eyegraph_trymove(move, WHITE, "evaluate_eyespace-D", NO_MOVE)) {
-	  tactical_life(0, num_vertices, vertices, &acode, NULL, NULL,
+	if (eyegraph_trymove(move, WHITE, "evaluate_eyespace-D", NO_MOVE,
+			     vertices->ko_threat_pos)) {
+	  tactical_life(0, vertices, &acode, NULL, NULL,
 			&dcode, NULL, NULL, tactical_life_results);
 	  if (dcode != 0) {
-	    evaluate_eyespace(&result2, num_vertices, vertices,
+	    evaluate_eyespace(&result2, vertices,
 			      &num_vital_attacks2, vital_attacks2,
 			      &num_vital_defenses2, vital_defenses2,
 			      tactical_life_results);
@@ -3315,7 +3370,7 @@
 		sgftreeAddComment(sgf_dumptree, "Defending ko threat.\n");
 	    }
 	  }
-	  popgo();
+	  eyegraph_popgo();
 	}
       }
       set_eyevalue(result, a, 1, 1, d);
@@ -3341,8 +3396,9 @@
 	vital_attacks[(*num_vital_attacks)++] = attack_points[k];
       for (k = 0; k < num_defenses; k++) {
 	int move = defense_points[k];
-	if (eyegraph_trymove(move, WHITE, "evaluate_eyespace-E", NO_MOVE)) {
-	  evaluate_eyespace(&result2, num_vertices, vertices,
+	if (eyegraph_trymove(move, WHITE, "evaluate_eyespace-E", NO_MOVE,
+			     vertices->ko_threat_pos)) {
+	  evaluate_eyespace(&result2, vertices,
 			    &num_vital_attacks2, vital_attacks2,
 			    &num_vital_defenses2, vital_defenses2,
 			    tactical_life_results);
@@ -3359,7 +3415,7 @@
 	  }
 	  else if (d == 1)
 	    vital_defenses[(*num_vital_defenses)++] = move;
-	  popgo();
+	  eyegraph_popgo();
 	}
       }
       set_eyevalue(result, 0, 0, 1, d);
@@ -3379,15 +3435,16 @@
       int d = 0;
       if (sgf_dumptree)
 	sgftreeAddComment(sgf_dumptree, "Dead with extra eye.\n");
-      num_moves = eyegraph_order_moves(num_vertices, vertices, WHITE, moves);
+      num_moves = eyegraph_order_moves(vertices, WHITE, moves);
       for (k = 0; k < num_moves; k++) {
 	int acode, dcode;
 	int move = moves[k];
-	if (eyegraph_trymove(move, WHITE, "evaluate_eyespace-F", NO_MOVE)) {
-	  tactical_life(1, num_vertices, vertices, &acode, NULL, NULL,
+	if (eyegraph_trymove(move, WHITE, "evaluate_eyespace-F", NO_MOVE,
+			     vertices->ko_threat_pos)) {
+	  tactical_life(1, vertices, &acode, NULL, NULL,
 			&dcode, NULL, NULL, tactical_life_results);
 	  if (dcode != 0) {
-	    tactical_life(0, num_vertices, vertices, &acode, NULL, NULL,
+	    tactical_life(0, vertices, &acode, NULL, NULL,
 			  &dcode, NULL, NULL, tactical_life_results);
 	    if (dcode != 0) {
 	      if (d == 1)
@@ -3408,7 +3465,7 @@
 				  "Ko threat to make one eye.\n");
 	    }
 	  }
-	  popgo();
+	  eyegraph_popgo();
 	}
       }
       set_eyevalue(result, 0, 0, 0, d);
@@ -3521,9 +3578,8 @@
   int num_margins;
   int margins[BOARDMAX]; /* Way larger than necessary. */
 
-  int num_vertices;
-  int vertices[BOARDMAX]; /* Way larger than necessary. */
-
+  struct vertex_data vertices;
+  
   int table_size;
   unsigned char *tactical_life_results;
 
@@ -3567,7 +3623,7 @@
 
   /* Cut out the eyespace from the solid white string. */
   num_margins = 0;
-  num_vertices = 0;
+  vertices.num = 0;
   
   if (horizontal_edge == 0)
     mini = -1;
@@ -3601,8 +3657,10 @@
     if (c == '!' || c == '@' || c == '(' || c == ')' || c == '$')
       margins[num_margins++] = POS(i, j);
     if (c != '|' && c != '-' && c != '+' && c != '%'
-	&& ON_BOARD(POS(i, j)) && mx[POS(i, j)] != WHITE)
-      vertices[num_vertices++] = POS(i, j);
+	&& ON_BOARD(POS(i, j)) && mx[POS(i, j)] != WHITE) {
+      vertices.type[vertices.num] = REGULAR;
+      vertices.pos[vertices.num++] = POS(i, j);
+    }
     j++;
   }
 
@@ -3624,26 +3682,55 @@
   mx[SW(pos)] = EMPTY;
   mx[SOUTH(pos)] = BLACK;
   mx[SE(pos)] = BLACK;
-  if (ON_BOARD(NN(pos)))
+  if (ON_BOARD(NN(pos))) {
     mx[NN(pos)] = EMPTY;
-  else
+    mx[EE(pos)] = EMPTY;
+    mx[WEST(NN(pos))] = EMPTY;
+  }
+  else {
     mx[SS(pos)] = EMPTY;
+    mx[WW(pos)] = EMPTY;
+    mx[EAST(SS(pos))] = EMPTY;
+  }
 
-  /* Add the two outer liberties in the lower left or upper right to
+  /* Use one of the liberties of this invincible group to mark ko
+   * threat status. Playing there obviously destroys the
+   * invincibility, but that doesn't actually matter as nobody is
+   * going to play on the other liberty anyway.
+   */
+  vertices.type[vertices.num] = KO_THREAT;
+  vertices.pos[vertices.num++] = pos;
+  vertices.ko_threat_pos = pos;
+
+  
+  /* Add the three outer liberties in the lower left or upper right to
    * the list of vertices.
    */
   if (ON_BOARD(NN(pos))) {
-    vertices[num_vertices++] = NE(pos);
-    vertices[num_vertices++] = NN(pos);
+    vertices.type[vertices.num] = OUTER_LIBERTY;
+    vertices.pos[vertices.num++] = NE(pos);
+    vertices.type[vertices.num] = OUTER_LIBERTY;
+    vertices.pos[vertices.num++] = NN(pos);
+    vertices.type[vertices.num] = OUTER_LIBERTY;
+    vertices.pos[vertices.num++] = EE(pos);
+    vertices.type[vertices.num] = OUTER_LIBERTY;
+    vertices.pos[vertices.num++] = WEST(NN(pos));
   }
   else {
-    vertices[num_vertices++] = SW(pos);
-    vertices[num_vertices++] = SS(pos);
+    vertices.type[vertices.num] = OUTER_LIBERTY;
+    vertices.pos[vertices.num++] = SW(pos);
+    vertices.type[vertices.num] = OUTER_LIBERTY;
+    vertices.pos[vertices.num++] = SS(pos);
+    vertices.type[vertices.num] = OUTER_LIBERTY;
+    vertices.pos[vertices.num++] = WW(pos);
+    vertices.type[vertices.num] = OUTER_LIBERTY;
+    vertices.pos[vertices.num++] = EAST(SS(pos));
   }
 
   /* Add an extra eye in the upper left corner. */
   mx[POS(0, 0)] = EMPTY;
-  vertices[num_vertices++] = POS(0, 0);
+  vertices.type[vertices.num] = EXTRA_EYE;
+  vertices.pos[vertices.num++] = POS(0, 0);
 
   if (!add_margins(num_margins, margins, mx))
     return 0;
@@ -3666,33 +3753,36 @@
    */
   for (pos = BOARDMIN; pos < BOARDMAX; pos++)
     if (board[pos] == WHITE && !same_string(pos, POS(1, 0))) {
-      vertices[num_vertices] = vertices[num_vertices - 1];
-      vertices[num_vertices - 1] = vertices[num_vertices - 2];
-      vertices[num_vertices - 2] = vertices[num_vertices - 3];
-      vertices[num_vertices - 3] = pos;
-      num_vertices++;
+      int k;
+      for (k = vertices.num; vertices.type[k - 1] != REGULAR; k--) {
+	vertices.type[k] = vertices.type[k - 1];
+	vertices.pos[k] = vertices.pos[k - 1];
+      }
+      vertices.type[k] = REGULAR;
+      vertices.pos[k] = pos;
+      vertices.num++;
     }
 
   if (verbose) {
     int k;
     gprintf("\nPlayable vertices:\n");
-    for (k = 0; k < num_vertices; k++)
-      gprintf("%1m ", vertices[k]);
+    for (k = 0; k < vertices.num; k++)
+      gprintf("%1m(%d) ", vertices.pos[k], vertices.type[k]);
     gprintf("\n\n");
   }
   
   /* Disable this test if you need to evaluate larger eyespaces, have
    * no shortage of memory, and know what you're doing.
    */
-  if (num_vertices > 17) {
+  if (vertices.num > 17) {
     gprintf("analyze_eyegraph: too large eyespace, %d vertices\n",
-	    num_vertices);
-    gg_assert(num_vertices <= 17);
+	    vertices.num);
+    gg_assert(vertices.num <= 17);
   }
 
-  /* The cache must have 2*3^num_vertices entries. */
+  /* The cache must have 2*3^vertices.num entries. */
   table_size = 2;
-  for (k = 0; k < num_vertices; k++)
+  for (k = 0; k < vertices.num; k++)
     table_size *= 3;
 
   /* Allocate memory for the cache. */
@@ -3707,7 +3797,7 @@
     sgffile_printboard(sgf_dumptree);
   
   /* Evaluate the eyespace on the board. */
-  evaluate_eyespace(value, num_vertices, vertices,
+  evaluate_eyespace(value, &vertices,
 		    &num_vital_attacks, vital_attacks,
 		    &num_vital_defenses, vital_defenses,
 		    tactical_life_results);
@@ -3726,8 +3816,8 @@
   /* Encode the attack and defense points with symbols in the mg[] array. */
   memset(mg, ' ', sizeof(mg));
 
-  for (k = 0; k < num_vertices - 2; k++)
-    mg[vertices[k]] = (board[vertices[k]] == BLACK ? 'X' : '.');
+  for (k = 0; k < vertices.num - 3; k++)
+    mg[vertices.pos[k]] = (board[vertices.pos[k]] == BLACK ? 'X' : '.');
 
   for (k = 0; k < num_margins; k++)
     mg[margins[k]] = (mg[margins[k]] == 'X' ? '$' : '!');
