Appended is a patch to 2.7.231, mostly improving the speed at a few places and fixing some bugs. - bugfix in configure.in - spelling correction in gnugo.texi - same_dragon() moved from board.c to dragon.c - report_dragon() and ascii_report_dragon() moved from printutils.c to dragon.c - speed improvements in filllib.c - corrected size of defend_not_adjacent_lib_score[] array in reading.c - tuning The speed improvements in filllib.c are significant. By cutting away unnecessary tactical reading, the liberty filling is now a factor five faster than before or so. Hence GNU Go is now passing much faster. :-) The tuning includes some performance tuning. A nice trick to speed up certain patterns is to move the tactical safety test which is implicit for nonsacrifice (class s) patterns to the constraint. The reason why this works is that the implicit safety test is done before the constraint is run. But if the constraint involves some very inexpensive test, e.g. for liberties, then the safety test can be shortcut if it's placed at the end of the constraint. See for example the pattern Sente12 near the end of the patch. /Gunnar Index: gnugo/configure.in diff -u gnugo/configure.in:1.1.1.20 gnugo/configure.in:1.2 --- gnugo/configure.in:1.1.1.20 Sun Apr 22 13:05:48 2001 +++ gnugo/configure.in Sun Apr 22 21:18:53 2001 @@ -52,7 +52,7 @@ AC_ARG_ENABLE(chinese-rules, [ --enable-chinese-rules tell the Go Modem Protocol to use Chinese Rules - --disable-color use Japanese rules (default)]) + --disable-chinese-rules use Japanese rules (default)]) dnl expand any instances of @matcher@ in Makefile AC_SUBST(matcher) Index: gnugo/doc/gnugo.texi diff -u gnugo/doc/gnugo.texi:1.1.1.6 gnugo/doc/gnugo.texi:1.2 --- gnugo/doc/gnugo.texi:1.1.1.6 Sun Apr 22 13:06:08 2001 +++ gnugo/doc/gnugo.texi Sun Apr 22 21:18:53 2001 @@ -6,7 +6,7 @@ @dircategory GNU games @direntry -* gnugo: (gnugo). The Gnu Go program +* GNU go: (gnugo). The GNU Go program @end direntry @set EDITION 1.0 Index: gnugo/engine/board.c diff -u gnugo/engine/board.c:1.1.1.9 gnugo/engine/board.c:1.10 --- gnugo/engine/board.c:1.1.1.9 Sun Apr 22 13:05:50 2001 +++ gnugo/engine/board.c Sun Apr 22 21:18:53 2001 @@ -2781,22 +2781,6 @@ } -/* - * Test whether two dragons are the same. Used by autohelpers and elsewhere. - */ - -int -same_dragon(int ai, int aj, int bi, int bj) -{ - if (ai == -1 || aj == -1 || bi == -1 || bj == -1) - return (ai == bi && aj == bj); - - return (dragon[ai][aj].origini == dragon[bi][bj].origini - && dragon[ai][aj].originj == dragon[bi][bj].originj); -} - - - /* * Local Variables: * tab-width: 8 Index: gnugo/engine/dragon.c diff -u gnugo/engine/dragon.c:1.1.1.13 gnugo/engine/dragon.c:1.10 --- gnugo/engine/dragon.c:1.1.1.13 Sun Apr 22 13:05:50 2001 +++ gnugo/engine/dragon.c Sun Apr 22 21:18:53 2001 @@ -1813,6 +1813,22 @@ return dragon_escape(goal, p[m][n], escape_value); } + +/* + * Test whether two dragons are the same. Used by autohelpers and elsewhere. + */ + +int +same_dragon(int ai, int aj, int bi, int bj) +{ + if (ai == -1 || aj == -1 || bi == -1 || bj == -1) + return (ai == bi && aj == bj); + + return (dragon[ai][aj].origini == dragon[bi][bj].origini + && dragon[ai][aj].originj == dragon[bi][bj].originj); +} + + /* ================================================================ */ /* A few status functions */ /* ================================================================ */ @@ -1844,6 +1860,99 @@ else return lively_black_dragons > 0; } + + +/* ================================================================ */ +/* Debugger functions */ +/* ================================================================ */ + + +/* For use in gdb, print details of the dragon at (m,n). + * Add this to your .gdbinit file: + * + * define dragon + * set ascii_report_dragon("$arg0") + * end + * + * Now 'dragon S8' will report the details of the S8 dragon. + * + */ + +void +ascii_report_dragon(char *string) +{ + int m, n; + string_to_location(board_size, string, &m, &n); + report_dragon(m, n); +} + + +void +report_dragon(int m, int n) +{ + int i, j, k; + + if (p[m][n] == EMPTY) { + gprintf("There is no dragon at %m\n", m, n); + return; + } + gprintf("*** dragon at %m:\n", m, n); + gprintf("color: %s; origin: %m; size: %d; effective size: %f\n", + (dragon[m][n].color == WHITE) ? "White" : "Black", + dragon[m][n].origini, dragon[m][n].originj, + dragon[m][n].size, dragon[m][n].effective_size); + gprintf("strings:"); + for (i = 0; i < board_size; i++) + for (j = 0; j < board_size; j++) + if (worm[i][j].origini == i + && worm[i][j].originj == j + && same_dragon(i, j, m, n)) + gprintf(" %m", i, j); + gprintf("\nhalf eyes: %d,", dragon[m][n].heyes); + if (dragon[m][n].heyei != -1) + gprintf("half eye: %m, ", dragon[m][n].heyei, dragon[m][n].heyej); + else gprintf("half eye: NONE,"); + gprintf(" genus %d, escape_route %d,", dragon[m][n].genus, + dragon[m][n].escape_route); + if (dragon[m][n].lunchi != -1) + gprintf(" lunch at %m\n", dragon[m][n].lunchi, dragon[m][n].lunchj); + else + gprintf(" no lunch\n"); + gprintf("dragon status %s, owl status %s, matcher status %s\n", + status_to_string(dragon[m][n].status), + status_to_string(dragon[m][n].owl_status), + status_to_string(dragon[m][n].matcher_status)); + if (dragon[m][n].owl_attacki != -1) + gprintf("owl attack point %m, ", + dragon[m][n].owl_attacki, dragon[m][n].owl_attackj); + else gprintf("no owl attack point, "); + if (dragon[m][n].owl_second_attacki != -1) + gprintf("second owl attack point %m\n", + dragon[m][n].owl_second_attacki, dragon[m][n].owl_second_attackj); + else gprintf("no second owl attack point\n"); + if (dragon[m][n].owl_defendi != -1) + gprintf("owl defense point %m, ", + dragon[m][n].owl_defendi, dragon[m][n].owl_defendj); + else gprintf("no owl defense point, "); + if (dragon[m][n].owl_second_defendi != -1) + gprintf("second owl defense point %m\n", + dragon[m][n].owl_second_defendi, dragon[m][n].owl_second_defendj); + else gprintf("no second owl defense point\n"); + if (dragon[m][n].semeai) + gprintf("This dragon is involved in a semeai. Margin of safety %d\n", + dragon[m][n].semeai_margin_of_safety); + else + gprintf("This dragon is not involved in a semeai.\n"); + gprintf("age: %d; owl time: %f\n", dragon[m][n].age, dragon[m][n].owl_time); + gprintf("neighbor dragons: "); + for (k = 0; k < DRAGON2(m, n).neighbors; k++) + gprintf("%m ", + dragon2[DRAGON2(m, n).adjacent[k]].origini, + dragon2[DRAGON2(m, n).adjacent[k]].originj); + gprintf("\nmoyo: %d; safety: %d\n", + DRAGON2(m, n).moyo, DRAGON2(m, n).safety); +} + /* * Local Variables: Index: gnugo/engine/filllib.c diff -u gnugo/engine/filllib.c:1.1.1.5 gnugo/engine/filllib.c:1.2 --- gnugo/engine/filllib.c:1.1.1.5 Sun Apr 15 21:16:14 2001 +++ gnugo/engine/filllib.c Sun Apr 22 13:12:57 2001 @@ -25,6 +25,7 @@ #include #include +#include #include #include "liberty.h" @@ -60,14 +61,16 @@ { switch (p[m][n]) { case EMPTY: - if (safe_move(m, n, BLACK) == 1 - && safe_move(m, n, WHITE) == 0 - && living_neighbor(m, n, BLACK)) + if (!(*found_black) + && living_neighbor(m, n, BLACK) + && safe_move(m, n, BLACK) == 1 + && safe_move(m, n, WHITE) == 0) *found_black = 1; - if (safe_move(m, n, WHITE) == 1 - && safe_move(m, n, BLACK) == 0 - && living_neighbor(m, n, WHITE)) + if (!(*found_white) + && living_neighbor(m, n, WHITE) + && safe_move(m, n, WHITE) == 1 + && safe_move(m, n, BLACK) == 0) *found_white = 1; break; @@ -109,6 +112,46 @@ int k; int other = OTHER_COLOR(color); int di, dj; + int potential_color[MAX_BOARD][MAX_BOARD]; + + /* We first make a fast scan for intersections which are potential + * candidates for liberty filling. This is not very accurate, but it + * does filter out intersections which could never pass the real + * tests below but might still require a lot of tactical reading in + * the process. + */ + memset(potential_color, 0, sizeof(potential_color)); + for (m=0; m= 8) { + di *= 2; + dj *= 2; + } + if (ON_BOARD(m+di, n+dj) && p[m+di][n+dj] == EMPTY) + potential_color[m+di][n+dj] |= p[m][n]; + } + } + } + for (m=0; m4 */ static int defend_lib_score[6] = {-5, -4, 0, 2, 5, 20}; -static int defend_not_adjacent_lib_score[6] = { 0, 0, 2, 4, 6}; +static int defend_not_adjacent_lib_score[5] = { 0, 0, 2, 4, 6}; static int defend_capture_score[6] = { 0, 6, 9, 13, 18, 24}; static int defend_atari_score[6] = { 0, 2, 4, 6, 8, 10}; static int defend_save_score[6] = { 0, 3, 6, 8, 10, 12}; @@ -4967,25 +4967,26 @@ { int k; for (k=0; k<6; k++) { - defend_lib_score[k] = params[k]; - defend_not_adjacent_lib_score[k] = params[k + 6]; - defend_capture_score[k] = params[k + 12]; - defend_atari_score[k] = params[k + 18]; - defend_save_score[k] = params[k + 24]; - if (k < 5) { - defend_open_score[k] = params[k + 30]; - attack_own_lib_score[k] = params[k + 35]; - } - attack_string_lib_score[k] = params[k + 40]; - attack_capture_score[k] = params[k + 46]; - attack_save_score[k] = params[k + 52]; - if (k < 5) - attack_open_score[k] = params[k + 58]; - } - defend_not_edge_score = params[63]; - attack_not_edge_score = params[64]; - attack_ko_score = params[65]; - cannot_defend_penalty = params[66]; + defend_lib_score[k] = params[k]; + if (k < 5) + defend_not_adjacent_lib_score[k] = params[k + 6]; + defend_capture_score[k] = params[k + 11]; + defend_atari_score[k] = params[k + 17]; + defend_save_score[k] = params[k + 23]; + if (k < 5) { + defend_open_score[k] = params[k + 29]; + attack_own_lib_score[k] = params[k + 34]; + } + attack_string_lib_score[k] = params[k + 39]; + attack_capture_score[k] = params[k + 45]; + attack_save_score[k] = params[k + 51]; + if (k < 5) + attack_open_score[k] = params[k + 57]; + } + defend_not_edge_score = params[62]; + attack_not_edge_score = params[63]; + attack_ko_score = params[64]; + cannot_defend_penalty = params[65]; } Index: gnugo/patterns/owl_vital_apats.db diff -u gnugo/patterns/owl_vital_apats.db:1.1.1.2 gnugo/patterns/owl_vital_apats.db:1.2 --- gnugo/patterns/owl_vital_apats.db:1.1.1.2 Sun Apr 15 21:16:25 2001 +++ gnugo/patterns/owl_vital_apats.db Sun Apr 22 13:12:58 2001 @@ -221,7 +221,7 @@ ;attack(a) && !defend(a) -Pattern VA11 +Pattern VA12 # gf New pattern. (2.7.224) |xx?? @@ -233,7 +233,7 @@ :8,s,value(75) -Pattern VA12 +Pattern VA13 # gf New pattern. (2.7.224) # This is almost identical to attack pattern A1330. It is needed in # both places. @@ -255,7 +255,7 @@ ;lib(A)<=3 && oplay_attack(a,a) -Pattern VA13 +Pattern VA14 # gf New pattern. (2.7.224) ?OX force X to defend inside own eyespace @@ -265,7 +265,7 @@ :8,-,value(35) -?bX force X to defend inside own eyespace +?bX ODa *Dc --- @@ -273,7 +273,7 @@ ;lib(b)>1 && lib(c)>1 && owl_eyespace(a,D) && !attack(D) -Pattern VA14 +Pattern VA15 # gf New pattern. (2.7.227) X.? sacrifice to destroy eye @@ -289,7 +289,7 @@ ;lib(A) == 2 && lib(b) > 1 && !oplay_attack(*,c,d,d) -Pattern VA15 +Pattern VA16 # gf New pattern. (2.7.227) ---- Index: gnugo/patterns/patterns.db diff -u gnugo/patterns/patterns.db:1.1.1.19 gnugo/patterns/patterns.db:1.26 --- gnugo/patterns/patterns.db:1.1.1.19 Sun Apr 22 13:05:58 2001 +++ gnugo/patterns/patterns.db Sun Apr 22 21:18:53 2001 @@ -10199,11 +10199,12 @@ Pattern CD99 # db added (2.7.182) # gf/db Moved to its proper place in the file. (2.7.188) +# gf Changed fixed value to followup value. (2.7.232) OX if a cutting stone can't be caught, X* play in opponent's territory -:8,OX,value(3) +:8,OX,followup_value(3) OX X* Index: gnugo/patterns/patterns2.db diff -u gnugo/patterns/patterns2.db:1.1.1.14 gnugo/patterns/patterns2.db:1.16 --- gnugo/patterns/patterns2.db:1.1.1.14 Sun Apr 22 13:06:01 2001 +++ gnugo/patterns/patterns2.db Sun Apr 22 13:12:58 2001 @@ -1598,13 +1598,14 @@ Pattern Shape26 +# gf Added s classification. (2.7.231) XO? Bad shape after atari on first line O.* ... --- -:8,-,shape(-1) +:8,s,shape(-1) XO? b.* @@ -2286,44 +2287,53 @@ Pattern Sente12 # gf/db New pattern. (2.7.188) +# gf Added s classification and safety test in constraint. (2.7.231) +# For performance reason this pattern has s classification and an +# explicit safety test in the constraint. Thus the safety test can be +# shortcut by the preceding liberty test. *X -:-,X +:-,sX *A -;lib(A) == 2 && !attack(A) +;lib(A) == 2 && !attack(A) && safe_omove(*) # Action adds a followup value unless the threat is considered ineffective. >threaten_to_capture(A) Pattern Sente13 # gf/db New pattern. (2.7.192) +# gf Moved safety tests from classifications to constraint to improve speed. +# (2.7.231) *O -:-,nO +:-,sO *a -;lib(a) == 2 && !attack(a) && olib(*) > 2 +;lib(a) == 2 && !attack(a) && olib(*) > 2 && safe_xmove(*) && safe_omove(*) # Action adds a reverse followup value. >defend_against_atari(a) Pattern Sente13b # gf/db New pattern. (2.7.222) +# gf Removed n classification and moved own safety test to constraint. +# (2.7.231) .O *? -:8,nO +:8,sO ba *? -;lib(a) == 2 && !attack(a) && olib(b) > 2 && oplay_attack(*,b,b) +;lib(a) == 2 && !attack(a) && olib(b) > 2 && safe_omove(*) +;&& oplay_attack(*,b,b) # Action adds a reverse followup value. >defend_against_atari(a)