Arend wrote (on November 18): > > Incidentally I have a snapback patch too, which has been lying around > > for a while without time for evaluation. Feel free to merge it with > > your patch as you like. > > Hmm, I had just put it in CVS before I got your e-mail. Your patch looks > better than mine, however. Actually it's more general than your solution, in a non-obvious way. More specifically it also finds moves like * in this position |XXXXX |XOOOX |OX.OO |.*... +----- which defends the lone O stone with ko. This is an improvement because previously the tactical reading has considered the stone as undefendable. Unfortunately this caused several regression failures but these turned out to depend on non-ko-aware constraints in certain patterns, involving does_attack and defend_both. The patch below solves these problems, leaving a regression result of nngs:1000 PASS K6 [K6] trevorc:1580 PASS C8 [C8] 13x13:39 FAIL J5 [H4|J4] The failure of 13x13:39 is accidental. The passes are also slightly accidental in that the move valuation is questionable but they do involve improved understanding of the tactical status. - new function set_up_snapback_moves() in reading.c - defend1() and restricted_defend1() revised - owl tuning - tuning - one test case revised /Gunnar Index: engine/reading.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/reading.c,v retrieving revision 1.88 diff -u -r1.88 reading.c --- engine/reading.c 4 Dec 2002 03:34:07 -0000 1.88 +++ engine/reading.c 7 Dec 2002 18:52:35 -0000 @@ -152,6 +152,8 @@ struct reading_moves *moves); static void special_rescue6_moves(int str, int libs[3], struct reading_moves *moves); +static void set_up_snapback_moves(int str, int lib, + struct reading_moves *moves); static void edge_clamp_moves(int str, struct reading_moves *moves); static int do_attack(int str, int *move, int komaster, int kom_pos); static int attack1(int str, int *move, int komaster, int kom_pos); @@ -1109,7 +1111,7 @@ SETUP_TRACE_INFO("defend1", str); reading_node_counter++; - gg_assert(IS_STONE(board[str])); + ASSERT1(IS_STONE(board[str]), str); ASSERT1(countlib(str) == 1, str); RTRACE("try to escape atari on %1m.\n", str); @@ -1120,29 +1122,14 @@ /* Collect moves to try in the first batch. * 1. First order liberty. * 2. Chain breaking moves. + * 3. Moves to set up a snapback. */ moves.pos[0] = lib; moves.score[0] = 0; moves.num = 1; break_chain_moves(str, &moves); - - /* Try to set up snap-back. */ - if (stackp <= backfill_depth && countstones(str) == 1) { - int adjs[MAXCHAIN]; - int adj = chainlinks2(str, adjs, 2); - int r; - int libs[2]; - for (r = 0; r < adj; r++) { - findlib(adjs[r], 2, libs); - if (neighbor_of_string(libs[0], str) - && !is_self_atari(libs[1], color)) - ADD_CANDIDATE_MOVE(libs[1], 0, moves); - if (neighbor_of_string(libs[1], str) - && !is_self_atari(libs[0], color)) - ADD_CANDIDATE_MOVE(libs[0], 0, moves); - } - } + set_up_snapback_moves(str, lib, &moves); order_moves(str, &moves, color, read_function_name, 0); @@ -4817,12 +4804,14 @@ /* Collect moves to try in the first batch. * 1. First order liberty. * 2. Chain breaking moves. + * 3. Moves to set up a snapback. */ moves.pos[0] = lib; moves.score[0] = 0; moves.num = 1; break_chain_moves(str, &moves); + set_up_snapback_moves(str, lib, &moves); order_moves(str, &moves, color, read_function_name, 0); for (k = 0; k < moves.num; k++) { @@ -5370,6 +5359,51 @@ gprintf("static int safe_atari_score = %d;\n", safe_atari_score); } } + +/* + * set_up_snapback_moves() is called with (str) a string having a + * single liberty at (lib). + * + * This adds moves which may defend a string in atari by capturing a + * neighbor in a snapback. One example is this position: + * + * OOOOO + * OXXXO + * OX.OX + * OXOXX + * OX*.. + * ----- + * + * This code also finds the move * to defend the lone O stone with ko + * in this position: + * + * |XXXXX + * |XOOOX + * |OX.OO + * |.*... + * +----- + * + */ + +static void +set_up_snapback_moves(int str, int lib, struct reading_moves *moves) +{ + int color = board[str]; + int other = OTHER_COLOR(color); + int libs2[2]; + + ASSERT1(countlib(str) == 1, str); + + /* This can only work if our string is a single stone and the + * opponent is short of liberties. + */ + if (stackp <= backfill_depth + && countstones(str) == 1 + && approxlib(lib, other, 2, libs2) == 1 + && !is_self_atari(libs2[0], color)) + ADD_CANDIDATE_MOVE(libs2[0], 0, *moves); +} + Index: patterns/owl_attackpats.db =================================================================== RCS file: /cvsroot/gnugo/gnugo/patterns/owl_attackpats.db,v retrieving revision 1.68 diff -u -r1.68 owl_attackpats.db --- patterns/owl_attackpats.db 1 Dec 2002 19:44:35 -0000 1.68 +++ patterns/owl_attackpats.db 7 Dec 2002 18:52:38 -0000 @@ -1207,12 +1207,13 @@ Pattern A403 +# gf Devalued. See also A403b. (3.3.13) ..* knight's move to block O.. OYX -:8,-,value(75) +:8,-,value(55) cb* Aad @@ -1221,6 +1222,25 @@ ; oplay_defend_both(*,a,b,c,A,b) && oplay_defend_both(*,b,a,d,*,A) +Pattern A403b +# gf More specific pattern and constraint. (3.3.13) + +??? +..* knight's move to block +O.. +OYX + +:8,-,value(75) + +??e +cb* +Aad +OYX + +; owl_escape_value(e)>0 +; && oplay_defend_both(*,a,b,c,A,b) && oplay_defend_both(*,b,a,d,*,A) + + Pattern A404 xYx attack eyeshape @@ -4296,6 +4316,17 @@ +---- :8,s,value(80) + + +Pattern A1311b +# gf New pattern. (3.3.13) + +|.XY?? +|XO.XO kill without ko +|.*..? ++----- + +:8,s,value(81) Pattern A1312 Index: patterns/owl_defendpats.db =================================================================== RCS file: /cvsroot/gnugo/gnugo/patterns/owl_defendpats.db,v retrieving revision 1.75 diff -u -r1.75 owl_defendpats.db --- patterns/owl_defendpats.db 25 Nov 2002 16:47:43 -0000 1.75 +++ patterns/owl_defendpats.db 7 Dec 2002 18:52:41 -0000 @@ -1605,6 +1605,7 @@ Pattern D505 #tm modified (3.1.22) +# gf Changed does_attack to xplay_defend for better ko handling. (3.3.13) OOX hanging connection to defend and make eye ..O @@ -1618,7 +1619,8 @@ .*. --- -; lib(a)>1 && safe_xmove(b) && oplay_attack(*,d,b,d) && does_attack(b,c) +; lib(a)>1 && safe_xmove(b) && oplay_attack(*,d,b,d) +; && xplay_defend(b,c) != WIN Pattern D506 Index: patterns/patterns.db =================================================================== RCS file: /cvsroot/gnugo/gnugo/patterns/patterns.db,v retrieving revision 1.90 diff -u -r1.90 patterns.db --- patterns/patterns.db 1 Dec 2002 19:44:35 -0000 1.90 +++ patterns/patterns.db 7 Dec 2002 18:52:47 -0000 @@ -6062,6 +6062,7 @@ Pattern EB1403 # gf Revised pattern. (3.1.3) +# gf Revised constraint. (3.3.13) ?oX?? sente hane ..XO? @@ -6075,7 +6076,10 @@ ?a*bo ----- -;alive(f) && xmoyo(e) && !oplay_defend_both(*,a,b,?,c,a,D) +# The oplay_defend test is a workaround for oplay_defend_both not +# correctly handling ko results. +;alive(f) && xmoyo(e) +;&& (!oplay_defend_both(*,a,b,?,c,a,D) || oplay_defend(*,a,b,?,c,a) != WIN) ;&& !oplay_attack(*,a,b,c,*) Index: regression/manyfaces1.tst =================================================================== RCS file: /cvsroot/gnugo/gnugo/regression/manyfaces1.tst,v retrieving revision 1.4 diff -u -r1.4 manyfaces1.tst --- regression/manyfaces1.tst 21 Nov 2002 00:25:10 -0000 1.4 +++ regression/manyfaces1.tst 7 Dec 2002 18:52:47 -0000 @@ -51,7 +51,7 @@ loadsgf games/nngs/ManyFaces3-gnugo-3.3.11-200211071935.sgf 101 100 gg_genmove black -#? [!B13]* +#? [!B13|A13]* loadsgf games/nngs/ManyFaces3-gnugo-3.3.11-200211071935.sgf 191 150 gg_genmove black