Here is a patch to 2.7.228. (Yes, I know that this is an old version but I think it will apply cleanly to more modern versions also.) Summary: - Don't award points for cut/connected groups if they are also attacked/defended at the same time. - minor cleaning - endgame performance tuning - commented some tests in NICKLAS test suite - bugfix in nicklas2.tst I have started doing some serious endgame work and have entered a number of the positions from the book "Get strong at the endgame". Unfortunately I can't make this test suite public because of copyright rules. But I find it more and more difficult to get proper endgame evaluations from GNU Go because of the "strategic effect" calculation that can be found around line 2888 in move_reason.c. I have written about this before and I am starting to feel like the roman Cato who chanted "Delenda Carthago" at the end of all his speeches in the roman senate. I think that the "strategic effect" feature is buggy as designed because it gives wrong values for the wrong reasons. It is also dangerous because it is hindering the development of better ways to detect and fix weaknesses in our position. So I propose once more that we remove it. I have a patch ready for you if you want it. -Inge ================================================================ diff -ur gnugo-2.7.228/engine/move_reasons.c gnugo-2.7.228iw/engine/move_reasons.c --- gnugo-2.7.228/engine/move_reasons.c Thu Mar 22 23:34:33 2001 +++ gnugo-2.7.228iw/engine/move_reasons.c Tue Apr 10 09:38:07 2001 @@ -393,6 +393,7 @@ || move_reasons[r].what == what)) return 1; } + return 0; } @@ -2235,8 +2236,42 @@ break; - case CONNECT_MOVE: case CUT_MOVE: + /* If one of the cut groups are also attacked or owl attacked, + don't award any points to the cut, i.e, don't set does_block */ + { + int d1, d2; + + d1 = conn_dragon1[move_reasons[r].what]; + d2 = conn_dragon2[move_reasons[r].what]; + + if (move_reason_known(m, n, OWL_ATTACK_MOVE, d1) + || move_reason_known(m, n, ATTACK_MOVE, d1) + || move_reason_known(m, n, OWL_ATTACK_MOVE, d2) + || move_reason_known(m, n, ATTACK_MOVE, d2)) + break; + } + does_block = 1; + break; + + case CONNECT_MOVE: + /* If one of the connectd groups are also defended or owl defended + don't award any points to the connection, i.e, don't set does_block */ + { + int d1, d2; + + d1 = conn_dragon1[move_reasons[r].what]; + d2 = conn_dragon2[move_reasons[r].what]; + + if (move_reason_known(m, n, OWL_DEFEND_MOVE, d1) + || move_reason_known(m, n, DEFEND_MOVE, d1) + || move_reason_known(m, n, OWL_DEFEND_MOVE, d2) + || move_reason_known(m, n, DEFEND_MOVE, d2)) + break; + } + does_block = 1; + break; + case STRATEGIC_ATTACK_MOVE: case STRATEGIC_DEFEND_MOVE: case BLOCK_TERRITORY_MOVE: @@ -2403,7 +2438,9 @@ TRACE(" %m: %f - owl attack/defend for %m\n", m, n, this_value, ai, aj); tot_value += this_value; +#if 0 does_block = 1; +#endif break; case OWL_ATTACK_THREAT: diff -ur gnugo-2.7.228/engine/owl.c gnugo-2.7.228iw/engine/owl.c --- gnugo-2.7.228/engine/owl.c Fri Mar 30 18:07:46 2001 +++ gnugo-2.7.228iw/engine/owl.c Thu Apr 5 21:55:48 2001 @@ -757,13 +757,16 @@ owl_attack(int m, int n, int *ui, int *uj) { int result; - static struct local_owl_data owl; - int reading_nodes_when_called = get_reading_node_counter(); - clock_t start=0, end; - double elapsed; + static struct local_owl_data owl; + int reading_nodes_when_called; + clock_t start=0, end; + double elapsed; - if (debug & DEBUG_OWL_PERFORMANCE) + if (debug & DEBUG_OWL_PERFORMANCE) { + reading_nodes_when_called = get_reading_node_counter(); start = clock(); + } + owl.local_owl_node_counter = 0; TRACE("owl_attack %m\n", m, n); owl.lunches_are_current = 0; @@ -771,6 +774,7 @@ compute_owl_escape_values(&owl); owl_make_domains(&owl, NULL); result = do_owl_attack(m, n, ui, uj, &owl, EMPTY, -1, -1); + if (debug & DEBUG_OWL_PERFORMANCE) { end = clock(); elapsed = ((double) (end - start)) / CLOCKS_PER_SEC; @@ -778,9 +782,11 @@ m, n, result, *ui, *uj, owl.local_owl_node_counter, get_reading_node_counter() - reading_nodes_when_called, elapsed); } + return result; } + /* Static function containing the main recursive code for * owl_attack. */ @@ -816,8 +822,7 @@ if ((stackp <= owl_branch_depth) && (hashflags & HASH_OWL_ATTACK)) { found_read_result = get_read_result(OWL_ATTACK, komaster, kom_i, kom_j, &m, &n, &read_result); - if (found_read_result) - { + if (found_read_result) { TRACE_CACHED_RESULT(*read_result); if (rr_get_result(*read_result) != 0) { if (ui) *ui = FIXUP_CACHED_VALUE(rr_get_result_i(*read_result)); @@ -861,7 +866,7 @@ SGFTRACE(-1, -1, 0, "owl node limit reached"); READ_RETURN0(read_result); } - + memset(mw, 0, sizeof(mw)); global_owl_node_counter++; owl->local_owl_node_counter++; @@ -1361,8 +1366,7 @@ if ((stackp <= owl_branch_depth) && (hashflags & HASH_OWL_DEFEND)) { found_read_result = get_read_result(OWL_DEFEND, komaster, kom_i, kom_j, &m, &n, &read_result); - if (found_read_result) - { + if (found_read_result) { TRACE_CACHED_RESULT(*read_result); if (rr_get_result(*read_result) != 0) { if (ui) *ui = FIXUP_CACHED_VALUE(rr_get_result_i(*read_result)); diff -ur gnugo-2.7.228/engine/worm.c gnugo-2.7.228iw/engine/worm.c --- gnugo-2.7.228/engine/worm.c Fri Mar 30 18:07:46 2001 +++ gnugo-2.7.228iw/engine/worm.c Tue Apr 10 09:51:22 2001 @@ -52,6 +52,7 @@ static void defense_callback(int m, int n, int color, struct pattern *pattern, int ll, void *data); + /* A STRING is a maximal connected set of stones of the same color, * black or white. A WORM is the same thing as a string, except that * its color can be empty. An empty worm is called a CAVITY. diff -ur gnugo-2.7.228/patterns/endgame.db gnugo-2.7.228iw/patterns/endgame.db --- gnugo-2.7.228/patterns/endgame.db Thu Mar 22 23:34:34 2001 +++ gnugo-2.7.228iw/patterns/endgame.db Fri Apr 13 01:19:56 2001 @@ -1301,7 +1301,7 @@ XaX one point gote ObO -;oplay_attack(a,b,b) && marginal_eye(a) && proper_eye(c) +;marginal_eye(a) && proper_eye(c) && oplay_attack(a,b,b) Pattern CE20 diff -ur gnugo-2.7.228/regression/NICKLAS gnugo-2.7.228iw/regression/NICKLAS --- gnugo-2.7.228/regression/NICKLAS Thu Mar 8 17:33:04 2001 +++ gnugo-2.7.228iw/regression/NICKLAS Sun Apr 1 00:22:53 2001 @@ -3,3 +3,39 @@ nicklas3.tst: 9x9 games with harder problems nicklas4.tst: 19x19 games (simple) nicklas5.tst: 19x19 games (harder) + + + NICKLAS2.TST + +#102: LOW PRIORITY; DIFFICULTY HARD; DEFER +gnugo --quiet -l niclas1.sgf -L29 -t + + This is not as clear cut as it may seem. 2.7.228 values the endgame + move at A7 to 6.66 points which seems about correct. The fact that + white D4 threatens both to cut off E5-F6 and to capture D3 through + a chain of threats (which is what happens in the game) is not that + easy to understand. A more general atari_atari module could + perhaps see it, but we are still far from that. I think this test + should be in nicklas3.tst (harder problems). + + +#601: ??? PRIORITY; DIFFICULTY EASY/HARD; TUNING + + This test illustrates a general weakness of GNU Go: It's poor + ability to defend its borders. White can enter blacks domains or + capture the E8 group. In the latter case, whites first move is + a sacrifice and the capture happens through a chain of threats. + + +#701 ??? PRIORITY; DIFFICULTY UNKNOWN; SEMEAI + + This is a very good test case for a semeai module. Black F1 threatens + to live and creates all the necessary liberties to win the semai. + + +#902 + + The only way to live is to attack a neighbour dragon, e.g. the one + at F8. But does the owl code really do this by default? + + diff -ur gnugo-2.7.228/regression/nicklas2.tst gnugo-2.7.228iw/regression/nicklas2.tst --- gnugo-2.7.228/regression/nicklas2.tst Thu Mar 8 17:33:04 2001 +++ gnugo-2.7.228iw/regression/nicklas2.tst Sun Apr 1 00:22:04 2001 @@ -28,11 +28,15 @@ #? [critical]* 903 dragon_status G7 -#? [critical]* +#? [critical H8 H8]* + +# E1 loses some endgame points since black must capture later anyhow, +# but it saves the group. It could be regarded as acceptable at this +# point. loadsgf games/nicklas/nicklas9.sgf 50 904 gg_genmove black -#? [B1]* +#? [B1|E1]* loadsgf games/nicklas/nicklas10.sgf 18 1001 gg_genmove black