[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnugo-devel] Optics patch
From: |
Gunnar Farneback |
Subject: |
[gnugo-devel] Optics patch |
Date: |
Wed, 28 Nov 2001 21:57:16 +0100 |
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 implements the topological eye with ko concept. As a side
effect the topological sum is now stored in struct half_eye_data. This
has important consequences for the owl tuning. Previously
owl_topological_eye was a very expensive autohelper. This is now
reversed since it only looks up already computed data. However, in
some situations (vertices which are at most marginal eyespaces)
owl_topological_eye() won't be effective at all. We may want to change
this, but for now I want to see if this is a real problem.
I also removed the functions make_proper_eye_space() and
remove_eyepoint(). Those were once used in some failed experiments and
are not very interesting with the owl paradigm in place.
- new field value in struct half_eye_data
- topological_eye() and evaluate_diagonal_intersection() reimplemented
to take ko into account
- improved debug output in compute_eyes() and compute_eyes_pessimistic()
- add_half_eye() renamed add_false_eye
- make_proper_eye_space() and remove_eyepoint() removed
- owl_topological_eye() reimplemented to look up stored data
/Gunnar
Index: engine/dragon.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/dragon.c,v
retrieving revision 1.30
diff -u -r1.30 dragon.c
--- engine/dragon.c 2001/11/26 14:54:28 1.30
+++ engine/dragon.c 2001/11/28 20:22:38
@@ -97,9 +97,10 @@
dragon[ii].owl_threat_status = UNCHECKED;
dragon[ii].owl_second_attack_point = NO_MOVE;
dragon[ii].owl_second_defense_point = NO_MOVE;
- half_eye[ii].type = 0;
+ half_eye[ii].type = 0;
+ half_eye[ii].value = 10.0; /* Something big. */
- if (worm[ii].origin == ii)
+ if (IS_STONE(board[ii]) && worm[ii].origin == ii)
DEBUG(DEBUG_DRAGONS,
"Initialising dragon from worm at %1m, size %d\n",
ii, worm[ii].size);
@@ -225,11 +226,14 @@
/* Find topological half eyes and false eyes by analyzing the
* diagonal intersections, as described in the Texinfo
* documentation (Eyes/Eye Topology).
+ *
+ * FIXME: Consolidate this piece of code with the very similar one
+ * in owl_determine_life().
*/
for (m = 0; m < board_size; m++)
for (n = 0; n < board_size; n++) {
- int sum;
+ float sum;
ii = POS(m, n);
if (black_eye[ii].color == BLACK_BORDER
@@ -237,14 +241,14 @@
&& black_eye[ii].neighbors <= 1
&& black_eye[ii].dragon != NO_MOVE) {
sum = topological_eye(ii, BLACK, black_eye, white_eye, half_eye);
- if (sum >= 4) {
+ if (sum >= 4.0) {
half_eye[ii].type = FALSE_EYE;
if (black_eye[ii].esize == 1
|| is_legal(ii, WHITE)
|| board[ii] == WHITE)
- add_half_eye(ii, black_eye, half_eye);
+ add_false_eye(ii, black_eye, half_eye);
}
- else if (sum == 3)
+ else if (sum > 2.0)
half_eye[ii].type = HALF_EYE;
}
@@ -253,14 +257,14 @@
&& white_eye[ii].neighbors <= 1
&& white_eye[ii].dragon != NO_MOVE) {
sum = topological_eye(ii, WHITE, black_eye, white_eye, half_eye);
- if (sum >= 4) {
+ if (sum >= 4.0) {
half_eye[ii].type = FALSE_EYE;
if (white_eye[ii].esize == 1
|| is_legal(ii, BLACK)
|| board[ii] == BLACK)
- add_half_eye(ii, white_eye, half_eye);
+ add_false_eye(ii, white_eye, half_eye);
}
- else if (sum == 3)
+ else if (sum > 2.0)
half_eye[ii].type = HALF_EYE;
}
}
Index: engine/liberty.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/liberty.h,v
retrieving revision 1.49
diff -u -r1.49 liberty.h
--- engine/liberty.h 2001/11/28 18:35:18 1.49
+++ engine/liberty.h 2001/11/28 20:22:39
@@ -484,8 +484,6 @@
int is_proper_eye_space(int pos);
int is_marginal_eye_space(int pos);
int max_eye_value(int pos);
-void make_proper_eye_space(int pos, int color);
-void remove_eyepoint(int pos, int color);
void test_eyeshape(int eyesize, int *eye_vertices);
@@ -560,6 +558,7 @@
struct half_eye_data {
+ float value; /* Topological eye value. */
int type; /* HALF_EYE or FALSE_EYE; */
int num_attacks; /* number of attacking points */
int attack_point[4]; /* the move to attack a topological halfeye */
@@ -744,12 +743,12 @@
struct half_eye_data heye[BOARDMAX],
int add_moves, int color);
void propagate_eye(int pos, struct eye_data eye[BOARDMAX]);
-int topological_eye(int pos, int color,
- struct eye_data b_eye[BOARDMAX],
- struct eye_data w_eye[BOARDMAX],
- struct half_eye_data heye[BOARDMAX]);
-void add_half_eye(int pos, struct eye_data eye[BOARDMAX],
- struct half_eye_data heye[BOARDMAX]);
+float topological_eye(int pos, int color,
+ struct eye_data b_eye[BOARDMAX],
+ struct eye_data w_eye[BOARDMAX],
+ struct half_eye_data heye[BOARDMAX]);
+void add_false_eye(int pos, struct eye_data eye[BOARDMAX],
+ struct half_eye_data heye[BOARDMAX]);
void make_domains(struct eye_data b_eye[BOARDMAX],
struct eye_data w_eye[BOARDMAX],
int owl_call);
Index: engine/optics.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/optics.c,v
retrieving revision 1.28
diff -u -r1.28 optics.c
--- engine/optics.c 2001/11/26 14:54:28 1.28
+++ engine/optics.c 2001/11/28 20:22:41
@@ -79,7 +79,7 @@
static int next_map(int *q, int map[MAXEYE], int esize);
static void print_eye(struct eye_data eye[BOARDMAX],
struct half_eye_data heye[BOARDMAX], int pos);
-static int
+static float
evaluate_diagonal_intersection(int m, int n, int color,
int *attack_point, int *defense_point,
struct eye_data b_eye[BOARDMAX],
@@ -116,9 +116,9 @@
/*
- * make_domains() is called from make_dragons(). It marks the black
- * and white domains (eyeshape regions) and collects some statistics
- * about each one.
+ * make_domains() is called from make_dragons() and from
+ * owl_determine_life(). It marks the black and white domains
+ * (eyeshape regions) and collects some statistics about each one.
*/
void
@@ -572,7 +572,7 @@
*/
void
-propagate_eye (int origin, struct eye_data eye[BOARDMAX])
+propagate_eye(int origin, struct eye_data eye[BOARDMAX])
{
int pos;
@@ -652,7 +652,7 @@
/*
- * Given an eyespace with origin (i,j), this function computes the
+ * Given an eyespace with origin (pos), this function computes the
* minimum and maximum numbers of eyes the space can yield. If max and
* min are different, then vital points of attack and defense are also
* generated.
@@ -689,14 +689,24 @@
if (eye[pos2].marginal && IS_STONE(board[pos2]))
DEBUG(DEBUG_EYES, "%1m (X!)\n", pos2);
- else if (eye[pos2].marginal && board[pos2] == EMPTY)
- DEBUG(DEBUG_EYES, "%1m (!)\n", pos2);
+ else if (is_halfeye(heye, pos2) && IS_STONE(board[pos2])) {
+ if (heye[pos2].value == 3.0)
+ DEBUG(DEBUG_EYES, "%1m (XH)\n", pos2);
+ else
+ DEBUG(DEBUG_EYES, "%1m (XH) (topological eye value = %f\n", pos2,
+ heye[pos2].value);
+ }
else if (!eye[pos2].marginal && IS_STONE(board[pos2]))
DEBUG(DEBUG_EYES, "%1m (X)\n", pos2);
- else if (is_halfeye(heye, pos2) && board[pos2] == EMPTY)
- DEBUG(DEBUG_EYES, "%1m (H)\n", pos2);
- else if (is_halfeye(heye, pos2) && IS_STONE(board[pos2]))
- DEBUG(DEBUG_EYES, "%1m (XH)\n", pos2);
+ else if (eye[pos2].marginal && board[pos2] == EMPTY)
+ DEBUG(DEBUG_EYES, "%1m (!)\n", pos2);
+ else if (is_halfeye(heye, pos2) && board[pos2] == EMPTY) {
+ if (heye[pos2].value == 3.0)
+ DEBUG(DEBUG_EYES, "%1m (H)\n", pos2);
+ else
+ DEBUG(DEBUG_EYES, "%1m (H) (topological eye value = %f\n", pos2,
+ heye[pos2].value);
+ }
else
DEBUG(DEBUG_EYES, "%1m\n", pos2);
}
@@ -854,14 +864,24 @@
if (eye[pos2].marginal && IS_STONE(board[pos2]))
DEBUG(DEBUG_EYES, "%1m (X!)\n", pos2);
- else if (eye[pos2].marginal && board[pos2] == EMPTY)
- DEBUG(DEBUG_EYES, "%1m (!)\n", pos2);
+ else if (is_halfeye(heye, pos2) && IS_STONE(board[pos2])) {
+ if (heye[pos2].value == 3.0)
+ DEBUG(DEBUG_EYES, "%1m (XH)\n", pos2);
+ else
+ DEBUG(DEBUG_EYES, "%1m (XH) (topological eye value = %f\n", pos2,
+ heye[pos2].value);
+ }
else if (!eye[pos2].marginal && IS_STONE(board[pos2]))
DEBUG(DEBUG_EYES, "%1m (X)\n", pos2);
- else if (is_halfeye(heye, pos2) && board[pos2] == EMPTY)
- DEBUG(DEBUG_EYES, "%1m (H)\n", pos2);
- else if (is_halfeye(heye, pos2) && IS_STONE(board[pos2]))
- DEBUG(DEBUG_EYES, "%1m (XH)\n", pos2);
+ else if (eye[pos2].marginal && board[pos2] == EMPTY)
+ DEBUG(DEBUG_EYES, "%1m (!)\n", pos2);
+ else if (is_halfeye(heye, pos2) && board[pos2] == EMPTY) {
+ if (heye[pos2].value == 3.0)
+ DEBUG(DEBUG_EYES, "%1m (H)\n", pos2);
+ else
+ DEBUG(DEBUG_EYES, "%1m (H) (topological eye value = %f\n", pos2,
+ heye[pos2].value);
+ }
else
DEBUG(DEBUG_EYES, "%1m\n", pos2);
}
@@ -1373,7 +1393,7 @@
/* recognize_eye(pos, *attack_point, *defense_point, *max, *min, eye_data,
* half_eye_data, add_moves, color), where pos is the origin of an eyespace,
- * returns 1 if there is a pattern in eyes.c matching the eyespace, or
+ * returns 1 if there is a pattern in eyes.db matching the eyespace, or
* 0 if no match is found. If there is a key point for attack, (*attack_point)
* is set to its location, or NO_MOVE if there is none.
* Similarly (*defense_point) is the location of a vital defense point. *min
@@ -1716,31 +1736,26 @@
}
-/* add_half_eye adds a half eye or false eye to an eye shape. */
+/* add_false_eye() turns a proper eyespace into a margin. */
void
-add_half_eye(int pos, struct eye_data eye[BOARDMAX],
- struct half_eye_data heye[BOARDMAX])
+add_false_eye(int pos, struct eye_data eye[BOARDMAX],
+ struct half_eye_data heye[BOARDMAX])
{
int k;
- if (heye[pos].type)
- DEBUG(DEBUG_EYES, "half or false eye found at %1m\n", pos);
+ ASSERT1(heye[pos].type == FALSE_EYE, pos);
+ DEBUG(DEBUG_EYES, "false eye found at %1m\n", pos);
- if (heye[pos].type == FALSE_EYE) {
- DEBUG(DEBUG_EYES, "false eye at %1m for dragon at %1m\n",
- pos, eye[pos].dragon);
- if (eye[pos].color != GRAY) {
- if (eye[pos].marginal == 0) {
- eye[pos].marginal = 1;
- eye[eye[pos].origin].msize++;
- for (k = 0; k < 4; k++)
- if (ON_BOARD(pos + delta[k])
- && eye[pos + delta[k]].origin == eye[pos].origin)
- eye[pos + delta[k]].marginal_neighbors++;
- propagate_eye(eye[pos].origin, eye);
- }
- }
- }
+ if (eye[pos].color == GRAY || eye[pos].marginal != 0)
+ return;
+
+ eye[pos].marginal = 1;
+ eye[eye[pos].origin].msize++;
+ for (k = 0; k < 4; k++)
+ if (ON_BOARD(pos + delta[k])
+ && eye[pos + delta[k]].origin == eye[pos].origin)
+ eye[pos + delta[k]].marginal_neighbors++;
+ propagate_eye(eye[pos].origin, eye);
}
@@ -1795,68 +1810,38 @@
return heye[pos].type == HALF_EYE;
}
-/* Turn a marginal eye space into a proper eye space. */
-void
-make_proper_eye_space(int pos, int color)
-{
- struct eye_data *eye;
- int k;
-
- if (color == WHITE)
- eye = white_eye;
- else
- eye = black_eye;
-
- gg_assert(eye[pos].color != GRAY_BORDER);
- gg_assert(eye[pos].marginal == 1);
-
- eye[pos].marginal = 0;
-
- eye[eye[pos].origin].msize--;
- for (k = 0; k < 4; k++)
- if (ON_BOARD(pos + delta[k])
- && eye[pos + delta[k]].origin == eye[pos].origin)
- eye[pos + delta[k]].marginal_neighbors--;
-
- propagate_eye(eye[pos].origin, eye);
-}
-
-/* Remove an eye point. This function can only be used before the
- * segmentation into eyespaces.
- */
-void
-remove_eyepoint(int pos, int color)
-{
- if (color == WHITE)
- white_eye[pos].color = GRAY_BORDER;
- else
- black_eye[pos].color = GRAY_BORDER;
-}
-
-
/* See Texinfo documentation (Eyes:Eye Topology). Returns:
- * 2 or less if (m, n) is a proper eye for (color);
- * 3 if (m, n) is a half eye;
- * 4 if (m, n) is a false eye.
+ * - 2 or less if (pos) is a proper eye for (color);
+ * - between 2 and 3 if the eye can be made false only by ko
+ * - 3 if (pos) is a half eye;
+ * - between 3 and 4 if the eye can be made real only by ko
+ * - 4 or more if (pos) is a false eye.
*
- * (*ai, *aj) and (*di, *dj) returns the coordinates of an empty
- * unsettled diagonal intersection, or an attack and defense point
- * respectively of an unsettled diagonal opponent worm.
+ * Attack and defense points for control of the diagonals are stored
+ * in the heye[] array.
*/
-int
+float
topological_eye(int pos, int color,
struct eye_data b_eye[BOARDMAX],
struct eye_data w_eye[BOARDMAX],
struct half_eye_data heye[BOARDMAX])
{
- int sum = 0;
- int val;
+ float sum = 0.0;
+ float val;
int num_attacks = 0;
int num_defenses = 0;
+ int attack_values[4];
+ int defense_values[4];
int k;
+ int r;
int attack_point;
int defense_point;
+ int attack_value;
+ int defense_value;
+
+ memset(attack_values, 0, sizeof(attack_values));
+ memset(defense_values, 0, sizeof(defense_values));
/* Loop over the diagonal directions. */
for (k = 4; k < 8; k++) {
@@ -1865,15 +1850,60 @@
&attack_point, &defense_point,
b_eye, w_eye);
sum += val;
- if (val == 1) {
- if (attack_point != NO_MOVE) {
+ if (val > 0.0 && val < 2.0) {
+ /* Diagonals off the edge has value 1.0 but no attack or defense
+ * point.
+ */
+ if (attack_point != NO_MOVE && defense_point != NO_MOVE) {
ASSERT_ON_BOARD1(attack_point);
- heye[pos].attack_point[num_attacks] = attack_point;
- num_attacks++;
- }
- if (defense_point != NO_MOVE) {
ASSERT_ON_BOARD1(defense_point);
- heye[pos].defense_point[num_defenses] = defense_point;
+ /* Store these in sorted (descending) order. We remap val
+ * differently for attack and defense points according to:
+ *
+ * val attack_value defense_value
+ * --- ------------ -------------
+ * 1.0 3 3
+ * <1.0 2 1
+ * >1.0 1 2
+ *
+ * This means that we primarily want to take control of
+ * diagonals without ko and secondarily of diagonals we can
+ * take unconditionally but not the opponent.
+ */
+ if (val == 1.0) {
+ attack_value = 3;
+ defense_value = 3;
+ }
+ else if (val < 1.0) {
+ attack_value = 2;
+ defense_value = 3;
+ }
+ else {
+ attack_value = 3;
+ defense_value = 2;
+ }
+
+ for (r = 0; r < 4; r++) {
+ if (attack_values[r] < attack_value) {
+ int tmp_value = attack_values[r];
+ int tmp_point = heye[pos].attack_point[r];
+ attack_values[r] = attack_value;
+ heye[pos].attack_point[r] = attack_point;
+ attack_value = tmp_value;
+ attack_point = tmp_point;
+ }
+
+ if (defense_values[r] < defense_value) {
+ int tmp_value = defense_values[r];
+ int tmp_point = heye[pos].defense_point[r];
+ defense_values[r] = defense_value;
+ heye[pos].defense_point[r] = defense_point;
+ defense_value = tmp_value;
+ defense_point = tmp_point;
+ }
+ }
+
+ num_attacks++;
num_defenses++;
}
}
@@ -1881,40 +1911,53 @@
heye[pos].num_attacks = num_attacks;
heye[pos].num_defends = num_defenses;
+ heye[pos].value = sum;
+
return sum;
}
-/* Evaluate an intersection (m, n) which is diagonal to an eye space
- * (i, j), as described in the Texinfo documentation (Eyes/Eye
- * Topology).
- * Returns 0 if the opponent cannot safely play at the vertex;
- * Returns 1 if empty and the opponent can safely play on it;
- * Returns 2 if safely occupied by the opponent.
+/* Evaluate an intersection (m, n) which is diagonal to an eye space,
+ * as described in the Texinfo documentation (Eyes/Eye Topology).
+ *
+ * Returns:
*
- * Exception: if one coordinate is off the board, returns 1;
- * if both are off the board, returns 0. This guarantees
- * correct behavior for diagonal intersections of points
- * on the edge or in the corner.
+ * 0 if both coordinates are off the board
+ * 1 if one coordinate is off the board
*
+ * 0 if (color) has control over the vertex
+ * a if (color) can take control over the vertex unconditionally and
+ * the opponent can take control by winning a ko.
+ * 1 if both (color) and the opponent can take control of the vertex
+ * unconditionally
+ * b if (color) can take control over the vertex by winning a ko and
+ * the opponent can take control unconditionally.
+ * 2 if the opponent has control over the vertex
+ *
+ * The values a and b are discussed in the documentation. We are
+ * currently using a = 0.75 and b = 1.25.
+ *
* Notice that it's necessary to pass the coordinates separately
* instead of as a 1D coordinate. The reason is that the 1D mapping
* can't uniquely identify "off the corner" points.
*/
-static int
+static float
evaluate_diagonal_intersection(int m, int n, int color,
int *attack_point, int *defense_point,
struct eye_data b_eye[BOARDMAX],
struct eye_data w_eye[BOARDMAX])
{
- int value = 0;
+ float value = 0;
int other = OTHER_COLOR(color);
int pos = POS(m, n);
int acode = 0;
int apos = NO_MOVE;
int dcode = 0;
int dpos = NO_MOVE;
+ int off_edge = 0;
+ const float a = 0.75;
+ const float b = 2 - a;
*attack_point = NO_MOVE;
*defense_point = NO_MOVE;
@@ -1924,13 +1967,14 @@
* are special cases.
*/
if (m < 0 || m >= board_size)
- value++;
+ off_edge++;
if (n < 0 || n >= board_size)
- value++;
+ off_edge++;
- if (value > 0)
- return value % 2; /* Must return 0 if both coordinates out of bounds. */
+ /* Must return 0 if both coordinates out of bounds. */
+ if (off_edge > 0)
+ return (float) (off_edge % 2);
/* Discard points within own eyespace, unless marginal or ko point.
*
@@ -1973,64 +2017,96 @@
&& !b_eye[pos].marginal
&& b_eye[pos].marginal_neighbors < 2
&& !(board[pos] == EMPTY && does_capture_something(pos, WHITE)))
- return 0;
+ return 0.0;
if (color == WHITE
&& w_eye[pos].color == WHITE_BORDER
&& !w_eye[pos].marginal
&& w_eye[pos].marginal_neighbors < 2
&& !(board[pos] == EMPTY && does_capture_something(pos, BLACK)))
- return 0;
+ return 0.0;
- if (board[pos] == EMPTY && safe_move(pos, other) != 0)
- value = 1;
- else {
+ if (board[pos] == EMPTY) {
+ /* We should normally have a safe move, but occasionally it may
+ * happen that it's not safe. There are complications, however,
+ * with a position like this
+ *
+ * .XXXX|
+ * XXOO.|
+ * XO.O.|
+ * XXO.O|
+ * -----+
+ *
+ */
+
+ int our_safety = safe_move(pos, color);
+ int your_safety = safe_move(pos, other);
+
+ if (your_safety == 0)
+ value = 0.0;
+ else if (our_safety == 0 && your_safety == WIN)
+ value = 2.0;
+ else if (our_safety == WIN && your_safety == WIN)
+ value = 1.0;
+ else if (our_safety == WIN && your_safety != WIN)
+ value = a;
+ else if (our_safety != WIN && your_safety == WIN)
+ value = b;
+ else
+ value = 1.0; /* Both contingent on ko. Probably can't happen. */
+
+ apos = pos;
+ dpos = pos;
+ }
+ else if (board[pos] == color) {
+ /* This stone had better be safe, otherwise we wouldn't have an
+ * eyespace in the first place.
+ */
+ value = 0.0;
+ }
+ else if (board[pos] == other) {
if (stackp == 0) {
- if (board[pos] == other) {
- if (worm[pos].attack_codes[0] == 0)
- value = 2;
- else if (worm[pos].defend_codes[0] != 0) {
- value = 1;
- apos = worm[pos].attack_points[0];
- dpos = worm[pos].defense_points[0];
- }
- }
+ acode = worm[pos].attack_codes[0];
+ apos = worm[pos].attack_points[0];
+ dcode = worm[pos].defend_codes[0];
+ dpos = worm[pos].defense_points[0];
}
- else {
- if (board[pos] == other) {
- attack_and_defend(pos, &acode, &apos, &dcode, &dpos);
- if (acode == 0)
- value = 2;
- else if (dcode != 0)
- value = 1;
- }
- }
- }
-
- if (value == 1) {
- if (board[pos] == EMPTY) {
- if (!safe_move(pos, color))
- value = 2;
- else {
- *attack_point = pos;
- *defense_point = pos;
- }
- }
- else {
- /* FIXME:
- * Usually there are several attack and defense moves that would
- * be equally valid. It's not good that we make an arbitrary
- * choice at this point.
- */
- ASSERT_ON_BOARD1(apos);
- ASSERT_ON_BOARD1(dpos);
- /* Notice:
- * The point to ATTACK the half eye is the point which DEFENDS
- * the stones on the diagonal intersection and vice versa. Thus
- * we must switch attack and defense points here.
- */
- *attack_point = dpos;
- *defense_point = apos;
- }
+ else
+ attack_and_defend(pos, &acode, &apos, &dcode, &dpos);
+
+ /* Must test acode first since dcode only is reliable if acode is
+ * non-zero.
+ */
+ if (acode == 0)
+ value = 2.0;
+ else if (dcode == 0)
+ value = 0.0;
+ else if (acode == WIN && dcode == WIN)
+ value = 1.0;
+ else if (acode == WIN && dcode != WIN)
+ value = a;
+ else if (acode != WIN && dcode == WIN)
+ value = b;
+ else if (acode != WIN && dcode != WIN)
+ value = 1.0; /* Both contingent on ko. Probably can't happen. */
+ }
+
+ if (value > 0.0 && value < 2.0) {
+ /* FIXME:
+ * Usually there are several attack and defense moves that would
+ * be equally valid. It's not good that we make an arbitrary
+ * choice at this point.
+ */
+ ASSERT_ON_BOARD1(apos);
+ ASSERT_ON_BOARD1(dpos);
+ /* Notice:
+ * The point to ATTACK the half eye is the point which DEFENDS
+ * the stones on the diagonal intersection and vice versa. Thus
+ * we must switch attack and defense points here.
+ * If the vertex is empty, dpos == apos and it doesn't matter
+ * whether we switch.
+ */
+ *attack_point = dpos;
+ *defense_point = apos;
}
return value;
Index: engine/owl.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/owl.c,v
retrieving revision 1.37
diff -u -r1.37 owl.c
--- engine/owl.c 2001/11/28 18:35:18 1.37
+++ engine/owl.c 2001/11/28 20:22:46
@@ -1939,10 +1939,13 @@
}
}
+ /* Reset halfeye data. Set topological eye value to something big. */
for (m = 0; m < board_size; m++)
- for (n = 0; n < board_size; n++)
+ for (n = 0; n < board_size; n++) {
owl->half_eye[POS(m, n)].type = 0;
-
+ owl->half_eye[POS(m, n)].value = 10.0;
+ }
+
/* Find topological half eyes and false eyes by analyzing the
* diagonal intersections, as described in the Texinfo
* documentation (Eyes/Eye Topology).
@@ -1968,7 +1971,7 @@
for (m = 0; m<board_size; m++)
for (n = 0; n<board_size; n++) {
int pos = POS(m, n);
- int sum;
+ float sum;
if (mx[pos] <= 0)
continue;
@@ -1978,14 +1981,15 @@
sum = topological_eye(pos, color, owl->black_eye, owl->white_eye,
owl->half_eye);
-
- if (sum >= 4) {
+
+ if (sum >= 4.0) {
+ /* False eye. */
int previously_marginal = eye[pos].marginal;
owl->half_eye[pos].type = FALSE_EYE;
if (eye[pos].esize == 1
|| is_legal(pos, OTHER_COLOR(color))
|| board[pos] == OTHER_COLOR(color)) {
- add_half_eye(pos, eye, owl->half_eye);
+ add_false_eye(pos, eye, owl->half_eye);
/* Marginal status may have changed. This can change the
* topological eye evaluation for diagonal neighbors, so
@@ -2005,7 +2009,7 @@
}
}
}
- else if (sum == 3) {
+ else if (sum > 2.0) {
owl->half_eye[pos].type = HALF_EYE;
ASSERT1(owl->half_eye[pos].num_attacks > 0, pos);
ASSERT_ON_BOARD1(owl->half_eye[pos].attack_point[0]);
@@ -2122,12 +2126,12 @@
*
* In both cases * is the vital point according to the graph
* matching. The significant difference is that in the first
- * case the vital point is a margin.
+ * case the vital point is adjacent to stones in the goal.
*/
else if (!does_attack
&& defense_point != NO_MOVE
&& board[defense_point] == EMPTY
- && (!eye[attack_point].marginal
+ && (!liberty_of_goal(defense_point, owl)
|| !is_self_atari(defense_point, color)
|| is_ko(defense_point, color, NULL)
|| safe_move(defense_point, color) != 0)) {
@@ -2152,11 +2156,10 @@
}
}
}
- /* sniff each lunch for nutritional value. The
- assumption is that capturing the lunch is gote,
- therefore the number of half eyes equals the
- MINIMUM number of eyes yielded by the resulting
- eye space.
+ /* Sniff each lunch for nutritional value. The assumption is that
+ * capturing the lunch is gote, therefore the number of half eyes
+ * equals the MINIMUM number of eyes yielded by the resulting eye
+ * space.
*/
{
for (lunch = 0; (lunch < MAX_LUNCHES); lunch++)
@@ -3621,14 +3624,25 @@
}
-/* Trampoline to topological_eye(). */
+/* Retrieve topological eye values stored in the half_eye[] array of
+ * the current owl data.
+ *
+ * FIXME: Sooner or later we'll want this to return a non-rounded
+ * value. When we change this, we have to review all patterns using
+ * the autohelper owl_topological_eye().
+ */
int
owl_topological_eye(int pos, int color)
{
- return topological_eye(pos, color,
- current_owl_data->black_eye,
- current_owl_data->white_eye,
- current_owl_data->half_eye);
+ float value;
+ UNUSED(color);
+ value = current_owl_data->half_eye[pos].value;
+ if (value > 2.0 && value < 4.0)
+ return 3;
+ else if (value <= 2.0)
+ return (int) (value + 0.99); /* Round up. */
+ else
+ return (int) value; /* Round down. */
}
/* This function returns true if it is judged that the capture of the
Index: patterns/mkpat.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/mkpat.c,v
retrieving revision 1.30
diff -u -r1.30 mkpat.c
--- patterns/mkpat.c 2001/11/26 14:54:28 1.30
+++ patterns/mkpat.c 2001/11/28 20:22:50
@@ -235,8 +235,6 @@
{"amalgamate_most_valuable_helper",3,
"amalgamate_most_valuable_helper(%s,%s,%s)"},
{"amalgamate", 2, "join_dragons(%s,%s)"},
- {"make_proper_eye", 1, "make_proper_eye_space(%s,color)"},
- {"remove_eyepoint", 1, "remove_eyepoint(%s,color)"},
{"owl_escape_value",1, "owl_escape_value(%s)"},
{"owl_goal_dragon", 1, "owl_goal_dragon(%s)"},
{"owl_eyespace", 2, "owl_eyespace(%s,%s)"},