[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnugo-devel] tuning patch
From: |
Gunnar Farneback |
Subject: |
[gnugo-devel] tuning patch |
Date: |
Sun, 11 May 2003 23:26:44 +0200 |
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 includes various tuning, mostly of the owl and endgame
varieties.
- new static function endgame_find_backfilling_dame() in endgame.c
- new static function endgame_find_liberties() broken out of
endgame_analyze_worm_liberties()
- new functions owl_mineye() and owl_maxeye() and corresponding
autohelper functions
- move_connects_strings revised not to care about connecting
invincible strings
- ALL_MOVE move reasons no longer considered to guarantee a safe move
- never give cutstone bonus for an inessential string
- tuning
- owl tuning
In endgame.c some comments have been revised but more importantly the
code has been reorganized so that the analysis of liberties can be
reused in a new function which finds backfilling dame. These are
described in a code comment like this:
/* A backfilling dame is a defense move, usually within potential own
* territory, which does not have to be played immediately but after
* outer liberties of some string have been filled. If those outer
* liberties are dame points (here inessential liberties), it is
* usually better to play the backfilling moves before filling the
* dame points. If nothing else it reduces the risk for making stupid
* blunders while filling dame.
*/
An example of the latter is blunder:25 where R5 is found as a
backfilling dame.
Regression changes are:
filllib:41 PASS M8 [M8]
blunder:25 PASS R5 [!P9|M6|T9]
trevorc:1420 FAIL A11 [J4]
nngs3:1180 PASS A6 [A6]
nngs4:340 PASS D2 [D2]
gunnar:24 PASS K3 [K3]
gunnar:26 PASS F9 [F9]
nando:25 PASS A14 [!P16]
The trevorc:1420 failure is mostly accidental. The correct move was
not valued correctly before either but was chosen thanks to the
connect strings bonus. Now a backfilling dame gets valued higher from
this heuristic. The passes all look real except for nando:25, which is
most likely accidental.
/Gunnar
Index: engine/endgame.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/endgame.c,v
retrieving revision 1.4
diff -u -r1.4 endgame.c
--- engine/endgame.c 26 Feb 2003 19:43:50 -0000 1.4
+++ engine/endgame.c 11 May 2003 21:18:26 -0000
@@ -26,7 +26,14 @@
static void endgame_analyze_worm_liberties(int pos, int color);
-
+static void endgame_find_backfilling_dame(int str, int color);
+static int endgame_find_liberties(int str, int *essential_liberties,
+ int essential_libs[MAXLIBS],
+ int *inessential_liberties,
+ int inessential_libs[MAXLIBS],
+ int *false_eye_liberties,
+ int false_eye_libs[MAXLIBS]);
+
/* Generate endgame moves. These are typically moves in settled positions,
* they aren't worth many points. Currently, we generate such moves using
@@ -52,8 +59,11 @@
&& dragon[pos].status == ALIVE
&& !worm[pos].invincible
&& !worm[pos].inessential
- && worm[pos].attack_codes[0] == 0)
+ && worm[pos].attack_codes[0] == 0) {
endgame_analyze_worm_liberties(pos, color);
+ if (board[pos] == color)
+ endgame_find_backfilling_dame(pos, color);
+ }
}
}
@@ -68,17 +78,18 @@
* .OXX..X X..XOOO|
* .OOOXX. XXXXXXX|
*
- * The two marked with `*' moves are worth one point in gote each (for both
- * colors). The first one is obvious - once black runs short on liberties,
- * he'll have to defend in his own eyespace, wasting one point. In the second
- * position, although black sacrifice one point by playing in white's
territory,
- * he forces white to eventually capture the black string, losing three points.
- * However, white has to play at `*' sooner or later if black doesn't take
- * that vertex, so the move is worth 3 - 1 - 1 = 1 point only, not two.
+ * The two marked with `*' moves are worth one point in gote each (for
+ * both colors). The first one is obvious - once black runs short on
+ * liberties, he'll have to defend in his own eyespace, wasting one
+ * point. In the second position, although black sacrifices one point
+ * by playing in white's territory, he forces white to eventually
+ * capture the black string, losing three points. However, white has
+ * to play at `*' sooner or later if black doesn't take that vertex, so
+ * the move is worth 3 - 1 - 1 = 1 point only, not two.
*
- * This function is able to find such moves. Algorithm is based on finding
- * such called "inessential liberties". These are defined as liberties, which
- * satisfy five conditions:
+ * This function is able to find such moves. The algorithm is based on
+ * finding so called "inessential liberties". These are defined as
+ * liberties, which satisfy five conditions:
*
* 1) they are not within an eye (not in someone's territory),
* 2) all their adjacent worms and dragons are alive,
@@ -90,7 +101,7 @@
* Such liberties are supposed to never become territory (they can't become
* an additional eye for the worm under consideration), the worm cannot
* connect to something via such a liberty and they will (or at least can)
- * be eventually filled by either of the players.
+ * eventually be filled by either of the players.
*
* FIXME: This function can probably be improved to handle more cases.
*/
@@ -100,13 +111,11 @@
int k;
int worm_color = board[pos];
int other = OTHER_COLOR(worm_color);
- int liberties;
- int libs[MAXLIBS];
- int essential_liberties = 0;
+ int essential_liberties;
int essential_libs[MAXLIBS];
- int inessential_liberties = 0;
+ int inessential_liberties;
int inessential_libs[MAXLIBS];
- int false_eye_liberties = 0;
+ int false_eye_liberties;
int false_eye_libs[MAXLIBS];
int num_attacks;
int num_attacks2;
@@ -115,54 +124,11 @@
int apos;
int value;
- /* Find all worm liberties. */
- liberties = findlib(pos, MAXLIBS, libs);
-
- /* Loop over the liberties and find inessential and essential ones. The
- * latter are defined as those, which are not inside an eye space, but
- * don't otherwise qualify as inessential. If we find a non-alive (dead
- * or critical) worm or dragon around, we stop looking for liberties and
- * skip the current worm (position is unstable).
- */
- for (k = 0; k < liberties; k++) {
- int lib = libs[k];
-
- if (!is_proper_eye_space(lib)) {
- int i;
- int essential = 0;
- int found_other = 0;
-
- for (i = 0; i < 4; i++) {
- int pos2 = lib + delta[i];
-
- if (!IS_STONE(board[pos2]))
- continue;
-
- if (worm[pos2].attack_codes[0] != 0 || dragon[pos2].status != ALIVE)
- return;
-
- if (board[pos2] == worm_color) {
- if (worm[pos2].origin != pos)
- essential = 1;
- }
- else
- found_other = 1;
- }
-
- if (i < 4)
- break;
-
- if (found_other) {
- if (essential)
- essential_libs[essential_liberties++] = lib;
- else
- inessential_libs[inessential_liberties++] = lib;
- }
- else if (is_false_eye(half_eye, lib) && !false_eye_territory[lib])
- false_eye_libs[false_eye_liberties++] = lib;
- }
- }
-
+ if (!endgame_find_liberties(pos, &essential_liberties, essential_libs,
+ &inessential_liberties, inessential_libs,
+ &false_eye_liberties, false_eye_libs))
+ return;
+
apos = NO_MOVE;
num_attacks = 0;
@@ -180,9 +146,9 @@
* worm have appeared.
*/
if (k == inessential_liberties && board[pos] != EMPTY) {
- /* Try to look for moves as in position 1. If the worm still has more than
- * one liberty, try to play on every essential liberty and see if an attack
- * appears.
+ /* Try to look for moves as in position 1. If the worm still has
+ * more than one liberty, try to play on every essential liberty
+ * and see if an attack appears.
*/
if (countlib(pos) > 1) {
for (k = 0; k < essential_liberties; k++) {
@@ -269,8 +235,9 @@
}
}
else {
- /* We were unable to fill all the liberties. Modify `inessential_liberties'
- * in order to undo the right number of moves.
+ /* We were unable to fill all the liberties. Modify
+ * `inessential_liberties' in order to undo the right number of
+ * moves.
*/
inessential_liberties = k;
}
@@ -338,9 +305,10 @@
value = 0;
if (apos != NO_MOVE) {
- /* We use the number of string's liberties minus 2 as the value of the
- * move. Minus 2 is explained in the comment before the function. In
- * some rare cases the value may differ, but this must be a good guess.
+ /* We use the number of string's liberties minus 2 as the value of
+ * the move. Minus 2 is explained in the comment before the
+ * function. In some rare cases the value may differ, but this
+ * should be a good guess.
*/
value = accuratelib(apos, other, MAXLIBS, NULL) - 2;
}
@@ -409,3 +377,137 @@
popgo();
ASSERT1(stackp == 0, pos);
}
+
+/* A backfilling dame is a defense move, usually within potential own
+ * territory, which does not have to be played immediately but after
+ * outer liberties of some string have been filled. If those outer
+ * liberties are dame points (here inessential liberties), it is
+ * usually better to play the backfilling moves before filling the
+ * dame points. If nothing else it reduces the risk for making stupid
+ * blunders while filling dame.
+ */
+static void
+endgame_find_backfilling_dame(int str, int color)
+{
+ int k;
+ int other = OTHER_COLOR(color);
+ int essential_liberties;
+ int essential_libs[MAXLIBS];
+ int inessential_liberties;
+ int inessential_libs[MAXLIBS];
+ int false_eye_liberties;
+ int false_eye_libs[MAXLIBS];
+ int dpos;
+ int loop_again = 1;
+ int move = NO_MOVE;
+
+ while (loop_again) {
+ loop_again = 0;
+ if (!endgame_find_liberties(str, &essential_liberties, essential_libs,
+ &inessential_liberties, inessential_libs,
+ &false_eye_liberties, false_eye_libs))
+ break;
+ for (k = 0; k < inessential_liberties; k++) {
+ if (!safe_move(inessential_libs[k], other)
+ || !trymove(inessential_libs[k], other,
+ "endgame", str, EMPTY, NO_MOVE))
+ continue;
+ if (board[str] == EMPTY)
+ break;
+ if (attack_and_defend(str, NULL, NULL, NULL, &dpos)) {
+ if (worm[dpos].color == EMPTY)
+ move = dpos;
+ trymove(move, color, "endgame", str, EMPTY, NO_MOVE);
+ loop_again = 1;
+ break;
+ }
+ }
+ }
+
+ while (stackp > 0)
+ popgo();
+
+ if (move != NO_MOVE && safe_move(move, color)) {
+ TRACE(" backfilling dame found at %1m for string %1m\n", move, str);
+ add_expand_territory_move(move);
+ set_minimum_territorial_value(move, 0.1);
+ }
+}
+
+/* Find liberties of the string str with various characteristics. See
+ * the comments above endgame_analyze_worm_liberties() for more
+ * information.
+ */
+static int
+endgame_find_liberties(int str,
+ int *essential_liberties, int essential_libs[MAXLIBS],
+ int *inessential_liberties,
+ int inessential_libs[MAXLIBS],
+ int *false_eye_liberties, int false_eye_libs[MAXLIBS])
+{
+ int liberties;
+ int libs[MAXLIBS];
+ int k;
+
+ ASSERT1(IS_STONE(board[str]), str);
+
+ *essential_liberties = 0;
+ *inessential_liberties = 0;
+ *false_eye_liberties = 0;
+
+ /* Find all string liberties. */
+ liberties = findlib(str, MAXLIBS, libs);
+
+ /* Loop over the liberties and find inessential and essential ones. The
+ * latter are defined as those, which are not inside an eye space, but
+ * don't otherwise qualify as inessential. If we find a non-alive (dead
+ * or critical) worm or dragon around, we stop looking for liberties and
+ * skip the current worm (position is unstable).
+ */
+ for (k = 0; k < liberties; k++) {
+ int lib = libs[k];
+
+ if (!is_proper_eye_space(lib)) {
+ int i;
+ int essential = 0;
+ int found_other = 0;
+
+ for (i = 0; i < 4; i++) {
+ int pos = lib + delta[i];
+
+ if (!IS_STONE(board[pos]) || !IS_STONE(worm[pos].color))
+ continue;
+
+ if (worm[pos].attack_codes[0] != 0 || dragon[pos].status != ALIVE)
+ return 0;
+
+ if (board[pos] == board[str]) {
+ if (find_origin(pos) != find_origin(str))
+ essential = 1;
+ }
+ else
+ found_other = 1;
+ }
+
+ if (i < 4)
+ break;
+
+ if (found_other) {
+ if (essential)
+ essential_libs[(*essential_liberties)++] = lib;
+ else
+ inessential_libs[(*inessential_liberties)++] = lib;
+ }
+ else if (is_false_eye(half_eye, lib) && !false_eye_territory[lib])
+ false_eye_libs[(*false_eye_liberties)++] = lib;
+ }
+ }
+ return 1;
+}
+
+/*
+ * Local Variables:
+ * tab-width: 8
+ * c-basic-offset: 2
+ * End:
+ */
Index: engine/liberty.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/liberty.h,v
retrieving revision 1.169
diff -u -r1.169 liberty.h
--- engine/liberty.h 9 May 2003 22:44:38 -0000 1.169
+++ engine/liberty.h 11 May 2003 21:18:26 -0000
@@ -505,6 +505,8 @@
int owl_goal_dragon(int pos);
int owl_eyespace(int pos);
int owl_big_eyespace(int pos);
+int owl_mineye(int pos);
+int owl_maxeye(int pos);
int owl_proper_eye(int pos);
int owl_eye_size(int pos);
int owl_strong_dragon(int pos);
Index: engine/owl.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/owl.c,v
retrieving revision 1.161
diff -u -r1.161 owl.c
--- engine/owl.c 11 May 2003 12:44:57 -0000 1.161
+++ engine/owl.c 11 May 2003 21:18:29 -0000
@@ -5137,6 +5137,46 @@
/* Used by autohelpers.
+ * Returns 1 if (pos) is an eyespace for the color of the dragon currently
+ * under owl investigation.
+ */
+int
+owl_mineye(int pos)
+{
+ int origin;
+ ASSERT_ON_BOARD1(pos);
+
+ origin = current_owl_data->my_eye[pos].origin;
+ if (!ON_BOARD(origin)
+ || (current_owl_data->my_eye[origin].color
+ != BORDER_COLOR(current_owl_data->color)))
+ return 0;
+
+ return min_eyes(¤t_owl_data->my_eye[origin].value);
+}
+
+
+/* Used by autohelpers.
+ * Returns 1 if (pos) is an eyespace for the color of the dragon currently
+ * under owl investigation.
+ */
+int
+owl_maxeye(int pos)
+{
+ int origin;
+ ASSERT_ON_BOARD1(pos);
+
+ origin = current_owl_data->my_eye[pos].origin;
+ if (!ON_BOARD(origin)
+ || (current_owl_data->my_eye[origin].color
+ != BORDER_COLOR(current_owl_data->color)))
+ return 0;
+
+ return max_eyes(¤t_owl_data->my_eye[origin].value);
+}
+
+
+/* Used by autohelpers.
* Returns 1 if (pos) is a non-marginal eyespace for the color of the
* dragon currently under owl investigation.
*/
Index: engine/value_moves.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/value_moves.c,v
retrieving revision 1.91
diff -u -r1.91 value_moves.c
--- engine/value_moves.c 8 May 2003 18:34:11 -0000 1.91
+++ engine/value_moves.c 11 May 2003 21:18:31 -0000
@@ -65,6 +65,8 @@
}
for (k = 0; k < strings; k++) {
+ if (worm[ss[k]].invincible)
+ continue;
if (board[ss[k]] == color) {
int newlibs = approxlib(pos, color, MAXLIBS, NULL);
own_strings++;
@@ -691,10 +693,14 @@
case MY_ATARI_ATARI_MOVE:
case YOUR_ATARI_ATARI_MOVE:
case EITHER_MOVE: /* FIXME: More advanced handling? */
- case ALL_MOVE: /* FIXME: More advanced handling? */
tactical_safety = 1;
safety = 1;
break;
+ case ALL_MOVE:
+ /* We don't trust these, unless some other move reason
+ * indicates safety.
+ */
+ break;
case EXPAND_TERRITORY_MOVE:
case EXPAND_MOYO_MOVE:
case INVASION_MOVE: /* A real invasion should be safe.
@@ -873,7 +879,7 @@
if (aa == bb)
continue;
-
+
if (DRAGON2(aa).owl_status == ALIVE
|| DRAGON2(bb).owl_status == ALIVE) {
tactical_safety = 1;
@@ -2042,7 +2048,7 @@
* FIXME: When worm[aa].cutstone2 == 1 we should probably add
* a followup value.
*/
- if (worm[aa].cutstone2 > 1) {
+ if (worm[aa].cutstone2 > 1 && !worm[aa].inessential) {
double ko_factor = 1;
if (move_reasons[r].type == ATTACK_MOVE_GOOD_KO
|| move_reasons[r].type == DEFEND_MOVE_GOOD_KO) {
Index: patterns/mkpat.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/mkpat.c,v
retrieving revision 1.119
diff -u -r1.119 mkpat.c
--- patterns/mkpat.c 8 May 2003 18:34:11 -0000 1.119
+++ patterns/mkpat.c 11 May 2003 21:18:32 -0000
@@ -330,6 +330,8 @@
{"owl_goal_dragon", 1, 0, 0.01, "owl_goal_dragon(%s)"},
{"owl_eyespace", 1, 0, 0.01, "owl_eyespace(%s)"},
{"owl_big_eyespace", 1, 0, 0.01, "owl_big_eyespace(%s)"},
+ {"owl_mineye", 1, 0, 0.01, "owl_mineye(%s)"},
+ {"owl_maxeye", 1, 0, 0.01, "owl_maxeye(%s)"},
{"owl_proper_eye", 1, 0, 0.01, "owl_proper_eye(%s)"},
{"owl_eye_size", 1, 0, 0.01, "owl_eye_size(%s)"},
{"owl_strong_dragon", 1, 0, 0.01, "owl_strong_dragon(%s)"},
Index: patterns/owl_attackpats.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/owl_attackpats.db,v
retrieving revision 1.81
diff -u -r1.81 owl_attackpats.db
--- patterns/owl_attackpats.db 11 May 2003 06:41:38 -0000 1.81
+++ patterns/owl_attackpats.db 11 May 2003 21:18:34 -0000
@@ -2614,6 +2614,22 @@
; 1 || oplay_attack(*,A)
+Pattern A719
+# gf New pattern. (3.3.20)
+
+?XXO reduce eye space
+X.*X
+?XX.
+
+:8,s,value(50)
+
+?XXc
+Xa*X
+?XXb
+
+;owl_mineye(*)==1 && xlib(b)==2 && !attack(c) && !oplay_attack(*,a,b,b)
+
+
#########################################################
# #
# Invade the eye space #
@@ -5040,6 +5056,56 @@
+---
;lib(a)==1 && lib(B)<=3 && defend(a)!=WIN
+
+
+Pattern A1349a
+# gf New pattern. (3.3.20)
+
+|.O? only chance to kill, sometimes tesuji
+|.XO
+|X.Y
+|..X
+|*.X
+|.X?
++---
+
+:8,s,value(75)
+
+|.O?
+|.XO
+|X.Y
+|..X
+|*.X
+|.A?
++---
+
+;!attack(A)
+
+
+Pattern A1349b
+# gf New pattern. (3.3.20)
+
+|oO? only chance to kill, sometimes tesuji
+|OXO
+|.XO
+|X.Y
+|..X
+|*.X
+|.X?
++---
+
+:8,s,value(75)
+
+|oO?
+|OXO
+|.XO
+|X.Y
+|..X
+|*.X
+|.A?
++---
+
+;!attack(A)
#########################################################
Index: patterns/owl_defendpats.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/owl_defendpats.db,v
retrieving revision 1.95
diff -u -r1.95 owl_defendpats.db
--- patterns/owl_defendpats.db 11 May 2003 06:41:38 -0000 1.95
+++ patterns/owl_defendpats.db 11 May 2003 21:18:35 -0000
@@ -999,11 +999,12 @@
Pattern D230
# tm New Pattern (3.1.16)
+# gf Not down to edge. (3.3.20)
-?OO... expand along edge.
-o....*
-?.....
-------
+?OO...? expand along edge.
+o....*?
+?.....?
+-------
:8,-,value(80)
@@ -1804,6 +1805,24 @@
;lib(A)>1 && lib(B)>1 && lib(c)==2
+Pattern D515
+# gf New pattern. (3.3.20)
+
+?OOX? hanging connection to defend and make eye
+O..OX
+.*...
+-----
+
+:8,-,value(61)
+
+?OOX?
+O.aOX
+.*.b.
+-----
+
+;!oplay_defend(*,a,b,a)
+
+
#########################################################
# #
# Make eyeshape on the edge #
@@ -2516,6 +2535,23 @@
;!obvious_false_oeye(a) && !oplay_attack(b,*,*)
+Pattern D640
+# gf New pattern. (3.3.20)
+# Compare D720.
+
+?O*.o try to make eye on edge
+O....
+-----
+
+:8,-,value(56)
+
+?O*bo
+Oa...
+-----
+
+;!obvious_false_oeye(a) && !oplay_attack(b,*,*)
+
+
#########################################################
# #
# Make eyeshape in the center #
@@ -2792,6 +2828,22 @@
;xlib(a)>1 && owl_big_eyespace(a) && oplay_attack(*,a,a)
+Pattern D715c
+# gf New pattern. (3.3.20)
+
+.X* defend against atari inside eyespace
+O.O
+XOo
+
+:8,-,value(60)
+
+.B*
+a.O
+XOo
+
+;lib(a)==2 && lib(B)==3 && owl_big_eyespace(*)
+
+
Pattern D716
# gf New pattern. (3.1.11)
@@ -3582,6 +3634,7 @@
Pattern D900
+# gf Added constraint. (3.3.20)
??O? block on edge
XO.?
@@ -3590,6 +3643,13 @@
:8,-,value(75)
+??O?
+Xa.?
+xX*o
+----
+
+;oplay_attack(*,a)!=WIN
+
# tm removed (3.1.15)
# This pattern way to generic. Too many bad matches.
@@ -3943,6 +4003,7 @@
Pattern D1007
# gf Revised constraint. (3.1.14)
+# gf Revised constraint. (3.3.20)
OXO connect underneath in sente
.*.
@@ -3954,7 +4015,7 @@
.*.
---
-;lib(A) == 2 && lib(b)>1 && lib(c)>1 && !attack(A)
+;lib(A) == 2 && lib(b)>1 && lib(c)>1 && !attack(A) && !oplay_disconnect(*,b,c)
#########################################################
Index: patterns/owl_vital_apats.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/owl_vital_apats.db,v
retrieving revision 1.36
diff -u -r1.36 owl_vital_apats.db
--- patterns/owl_vital_apats.db 1 Jan 2003 20:05:25 -0000 1.36
+++ patterns/owl_vital_apats.db 11 May 2003 21:18:35 -0000
@@ -868,4 +868,33 @@
:8,-,value(35)
+Pattern VA52a
+# gf New pattern. (3.3.20)
+
+|*Oo make X short of liberties
+|.XO
+|X.X
+|..X
+|OXX
+|.X?
++---
+
+:8,-,value(65)
+
+
+Pattern VA52b
+# gf New pattern. (3.3.20)
+
+|*Oo
+|OXO make X short of liberties
+|.XO
+|X.X
+|..X
+|OXX
+|.X?
++---
+
+:8,-,value(65)
+
+
# END OF FILE
Index: patterns/patterns2.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/patterns2.db,v
retrieving revision 1.58
diff -u -r1.58 patterns2.db
--- patterns/patterns2.db 8 May 2003 18:34:11 -0000 1.58
+++ patterns/patterns2.db 11 May 2003 21:18:36 -0000
@@ -2168,24 +2168,25 @@
:8,-,shape(-2)
-Pattern Shape52
-# Questionable --- ponnuki capture leaves less aji.
-# Whether direct or indirect capture is best depends
-# entirely on the context.
-
-?.? Better to capture indirectly
-*.O
-OXO
-?O?
-
-:8,-,shape(1)
-
-?.?
-*.a
-bXa
-?c?
-
-;lib(a)>1 && lib(b)>1 && lib(c)>1
+# Pattern Shape52
+# # Questionable --- ponnuki capture leaves less aji.
+# # Whether direct or indirect capture is best depends
+# # entirely on the context.
+# # gf Removed. (3.3.20)
+#
+# ?.? Better to capture indirectly
+# *.O
+# OXO
+# ?O?
+#
+# :8,-,shape(1)
+#
+# ?.?
+# *.a
+# bXa
+# ?c?
+#
+# ;lib(a)>1 && lib(b)>1 && lib(c)>1
Pattern Shape53
- [gnugo-devel] tuning patch,
Gunnar Farneback <=