Index: engine/board.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/board.c,v retrieving revision 1.99 diff -u -p -r1.99 board.c --- engine/board.c 7 Jun 2004 17:18:54 -0000 1.99 +++ engine/board.c 2 Sep 2004 15:18:47 -0000 @@ -1338,6 +1338,25 @@ is_corner_vertex(int pos) } +/* Returns true if the empty vertex respectively the string at pos1 is + * adjacent to the empty vertex respectively the string at pos2. + */ +int are_neighbors(int pos1, int pos2) +{ + if (board[pos1] == EMPTY) { + if (board[pos2] == EMPTY) + return (gg_abs(pos1 - pos2) == NS || gg_abs(pos1 - pos2) == WE); + else + return neighbor_of_string(pos1, pos2); + } + else { + if (board[pos2] == EMPTY) + return neighbor_of_string(pos2, pos1); + else + return adjacent_strings(pos1, pos2); + } +} + /* Count the number of liberties of the string at pos. pos must not be * empty. @@ -2901,12 +2920,7 @@ does_capture_something(int pos, int colo } -/* For each stone in the string at pos, set mx to value mark. If - * some of the stones in the string are marked prior to calling this - * function, only the connected unmarked stones starting from pos - * are guaranteed to become marked. The rest of the string may or may - * not become marked. (In the current implementation, it will.) - */ +/* For each stone in the string at pos, set mx to value mark. */ void mark_string(int str, char mx[BOARDMAX], char mark) { Index: engine/board.h =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/board.h,v retrieving revision 1.13 diff -u -p -r1.13 board.h --- engine/board.h 7 Jun 2004 17:18:54 -0000 1.13 +++ engine/board.h 2 Sep 2004 15:18:48 -0000 @@ -288,6 +288,7 @@ int same_string(int str1, int str2); int adjacent_strings(int str1, int str2); void mark_string(int str, char mx[BOARDMAX], char mark); void signed_mark_string(int str, signed char mx[BOARDMAX], signed char mark); +int are_neighbors(int pos1, int pos2); /* Count and/or find liberties at (pos). */ int countlib(int str); @@ -387,6 +388,8 @@ int is_hoshi_point(int m, int n); void draw_letter_coordinates(FILE *outfile); void simple_showboard(FILE *outfile); +void mark_goal_in_sgf(char goal[BOARDMAX]); + /* ================================================================ */ /* assertions */ /* ================================================================ */ Index: engine/cache.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/cache.c,v retrieving revision 1.45 diff -u -p -r1.45 cache.c --- engine/cache.c 7 Jun 2004 17:15:52 -0000 1.45 +++ engine/cache.c 2 Sep 2004 15:18:48 -0000 @@ -224,6 +224,7 @@ tt_update(Transposition_table *table, /* Get the entry and nodes. */ entry = &table->entries[hashdata_remainder(hashval, table->num_entries)]; + gg_assert(entry >= table->entries); deepest = &entry->deepest; newest = &entry->newest; Index: engine/dragon.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/dragon.c,v retrieving revision 1.142 diff -u -p -r1.142 dragon.c --- engine/dragon.c 1 Aug 2004 17:38:07 -0000 1.142 +++ engine/dragon.c 2 Sep 2004 15:18:49 -0000 @@ -49,6 +49,7 @@ #include "liberty.h" #include "gg_utils.h" +#include "readconnect.h" static void initialize_supplementary_dragon_data(void); static void find_lunches(void); @@ -67,7 +68,7 @@ static void connected_to_eye_recurse(int int eye_color, struct eye_data *eye, char *mx, char *me, int *halfeyes); static int compute_crude_status(int pos); -static int compute_escape(int pos, int dragon_status_known); +static float compute_escape(int pos); static void compute_surrounding_moyo_sizes(const struct influence_data *q); static int dragon2_initialized; @@ -187,11 +188,6 @@ make_dragons(int stop_before_owl) #endif - /* Compute the escape route measure. */ - for (str = BOARDMIN; str < BOARDMAX; str++) - if (IS_STONE(board[str]) && dragon[str].origin == str) - DRAGON2(str).escape_route = compute_escape(str, 0); - /* Update the segmentation of the initial influence before we * compute the surrounding moyo sizes. The reason for this is that * now the eyespace inhibition found by find_cuts() can be taken @@ -200,7 +196,7 @@ make_dragons(int stop_before_owl) resegment_initial_influence(); /* Set dragon weaknesses according to initial_influence. */ - compute_refined_dragon_weaknesses(); + compute_refined_dragon_weaknesses(0); for (d = 0; d < number_of_dragons; d++) dragon2[d].weakness_pre_owl = dragon2[d].weakness; @@ -260,7 +256,7 @@ make_dragons(int stop_before_owl) /* Some dragons can be ignored but be extra careful with big dragons. */ if (crude_dragon_weakness(ALIVE, &no_eyes, 0, DRAGON2(str).moyo_territorial_value, - DRAGON2(str).escape_route - 10) + DRAGON2(str).escape_route - 3.0) < 0.00001 + gg_max(0.12, 0.32 - 0.01*dragon[str].effective_size)) { DRAGON2(str).owl_status = UNCHECKED; DRAGON2(str).owl_attack_point = NO_MOVE; @@ -387,7 +383,7 @@ make_dragons(int stop_before_owl) set_eyevalue(&no_eyes, 0, 0, 0, 0); if (crude_dragon_weakness(ALIVE, &no_eyes, 0, DRAGON2(str).moyo_territorial_value, - DRAGON2(str).escape_route - 10) + DRAGON2(str).escape_route - 3.0) < 0.00001 + gg_max(0.12, 0.32 - 0.01*dragon[str].effective_size)) { DRAGON2(str).owl_threat_status = UNCHECKED; DRAGON2(str).owl_second_attack_point = NO_MOVE; @@ -1623,7 +1619,7 @@ show_dragons(void) d2 = &(dragon2[dd->id]); if (dd->origin == pos) { - gprintf("%1m : %s dragon size %d (%f), genus %s, escape factor %d, crude status %s, status %s, moyo size %d, moyo territory value %f, safety %s, weakness pre owl %f, weakness %f", + gprintf("%1m : %s dragon size %d (%f), genus %s, escape factor %f, crude status %s, status %s, moyo size %d, moyo territory value %f, safety %s, weakness pre owl %f, weakness %f", pos, board[pos] == BLACK ? "B" : "W", dd->size, @@ -1831,7 +1827,7 @@ compute_crude_status(int pos) if (lunch != NO_MOVE && true_genus < 3 && worm[lunch].defense_codes[0] != 0 - && DRAGON2(pos).escape_route < 5) + && DRAGON2(pos).escape_route < 3.0) if (true_genus == 2 || worm[lunch].size > 2) return CRITICAL; @@ -1846,7 +1842,7 @@ compute_crude_status(int pos) return DEAD; if (true_genus == 3 - && DRAGON2(pos).escape_route < 5) + && DRAGON2(pos).escape_route < 3.0) return CRITICAL; } @@ -1857,239 +1853,169 @@ compute_crude_status(int pos) } -/* The dragon escape measure. This is defined as follows. - * - * Let a PATH be a sequence of adjacent intersections that do nowhere - * touch or include an opponent stone or touch the border. It may - * include friendly stones and those are allowed to touch opponent - * stones or the border). Let a DISTANCE N INTERSECTION be an - * intersection connected to a dragon by a path of length N, but by no - * shorter path. The connection of the path to the dragon may either - * be by direct adjacency or, in the first step, diagonally if both - * adjoining intersections are empty. - * - * It is assumed that each intersection has an escape value, which - * would typically depend on influence and (preliminary) dragon - * status. We define the escape potential as the sum of the escape - * values over the distance four intersections of the dragon. - * - * Example of distance N intersections, 1 <= N <= 4: - * - * . . . . . . . . . . . . . . . . . . - * . . . . . X . . O . . . . . X . . O - * . . X . . . . . O . . X . 2 . 4 . O - * X . . . . . . . . X . . 1 1 2 3 4 . - * X O . O . . . . O X O 1 O 1 2 3 4 O - * X O . O . . . . . X O 1 O 1 . 4 . . - * X O . . . X . O O X O 1 . . X . . O - * . . . X . . . . . . 1 . X . . . . . - * X . . . . X . . . X . . . . X . . . - * . . . . . . . . . . . . . . . . . . - * - * Additionally, a path may not pass a connection inhibited - * intersection. +/* Compute the escape potential. The dragon is marked in the goal array. */ - -#define ENQUEUE(pos) (queue[queue_end++] = (pos),\ - mx[pos] = 1) - -/* Compute the escape potential described above. The dragon is marked - * in the goal array. - */ -int -dragon_escape(char goal[BOARDMAX], int color, - char escape_value[BOARDMAX]) +float +dragon_escape(const char goal[BOARDMAX], int color, + const float escape_values[BOARDMAX], int careful) { - int ii; + int pos; int k; - static int mx[BOARDMAX]; - static int mx_initialized = 0; - int queue[MAX_BOARD * MAX_BOARD]; - int queue_start = 0; - int queue_end = 0; - int other = OTHER_COLOR(color); - int distance; - int escape_potential = 0; - - gg_assert(IS_STONE(color)); - - if (!mx_initialized) { - memset(mx, 0, sizeof(mx)); - mx_initialized = 1; - } + float optimistic = 0.0; + float pessimistic = 0.0; + int real_start; + float optimistic_escape[BOARDMAX]; + float pessimistic_escape[BOARDMAX]; + float highest_contribution = 0.0; + char private_goal[BOARDMAX]; + struct connection_data conn; + SGFTree *save_sgf_dumptree = sgf_dumptree; + int save_count_variations = count_variations; - /* Enter the stones of the dragon in the queue. */ - for (ii = BOARDMIN; ii < BOARDMAX; ii++) - if (ON_BOARD(ii) && goal[ii]) - ENQUEUE(ii); - - /* Find points at increasing distances from the dragon. At distance - * four, sum the escape values at those points to get the escape - * potential. - */ - for (distance = 0; distance <= 4; distance++) { - int save_queue_end = queue_end; - while (queue_start < save_queue_end) { - ii = queue[queue_start]; - queue_start++; + sgf_dumptree = NULL; + count_variations = 0; - /* Do not pass connection inhibited intersections. */ - if (cut_possible(ii, OTHER_COLOR(color))) - continue; - if (distance == 4) - escape_potential += escape_value[ii]; - else { - if (ON_BOARD(SOUTH(ii)) - && !mx[SOUTH(ii)] - && (board[SOUTH(ii)] == color - || (board[SOUTH(ii)] == EMPTY - && ON_BOARD(SE(ii)) && board[SE(ii)] != other - && ON_BOARD(SS(ii)) && board[SS(ii)] != other - && ON_BOARD(SW(ii)) && board[SW(ii)] != other))) - ENQUEUE(SOUTH(ii)); - - if (ON_BOARD(WEST(ii)) - && !mx[WEST(ii)] - && (board[WEST(ii)] == color - || (board[WEST(ii)] == EMPTY - && ON_BOARD(SW(ii)) && board[SW(ii)] != other - && ON_BOARD(WW(ii)) && board[WW(ii)] != other - && ON_BOARD(NW(ii)) && board[NW(ii)] != other))) - ENQUEUE(WEST(ii)); - - if (ON_BOARD(NORTH(ii)) - && !mx[NORTH(ii)] - && (board[NORTH(ii)] == color - || (board[NORTH(ii)] == EMPTY - && ON_BOARD(NW(ii)) && board[NW(ii)] != other - && ON_BOARD(NN(ii)) && board[NN(ii)] != other - && ON_BOARD(NE(ii)) && board[NE(ii)] != other))) - ENQUEUE(NORTH(ii)); - - if (ON_BOARD(EAST(ii)) - && !mx[EAST(ii)] - && (board[EAST(ii)] == color - || (board[EAST(ii)] == EMPTY - && ON_BOARD(NE(ii)) && board[NE(ii)] != other - && ON_BOARD(EE(ii)) && board[EE(ii)] != other - && ON_BOARD(SE(ii)) && board[SE(ii)] != other))) - ENQUEUE(EAST(ii)); - - /* For distance one intersections, allow kosumi to move out. I.e. - * - * ??.. - * X.*. - * ?O.? - * ??X? - * - */ - if (distance == 0) { - if (board[SOUTH(ii)] == EMPTY - && board[WEST(ii)] == EMPTY - && !mx[SW(ii)] - && (board[SW(ii)] == color - || (board[SW(ii)] == EMPTY - && ON_BOARD(SOUTH(SW(ii))) - && board[SOUTH(SW(ii))] != other - && ON_BOARD(WEST(SW(ii))) - && board[WEST(SW(ii))] != other))) - ENQUEUE(SW(ii)); - - if (board[WEST(ii)] == EMPTY - && board[NORTH(ii)] == EMPTY - && !mx[NW(ii)] - && (board[NW(ii)] == color - || (board[NW(ii)] == EMPTY - && ON_BOARD(WEST(NW(ii))) - && board[WEST(NW(ii))] != other - && ON_BOARD(NORTH(NW(ii))) - && board[NORTH(NW(ii))] != other))) - ENQUEUE(NW(ii)); - - if (board[NORTH(ii)] == EMPTY - && board[EAST(ii)] == EMPTY - && !mx[NE(ii)] - && (board[NE(ii)] == color - || (board[NE(ii)] == EMPTY - && ON_BOARD(NORTH(NE(ii))) - && board[NORTH(NE(ii))] != other - && ON_BOARD(EAST(NE(ii))) - && board[EAST(NE(ii))] != other))) - ENQUEUE(NE(ii)); - - if (board[EAST(ii)] == EMPTY - && board[SOUTH(ii)] == EMPTY - && !mx[SE(ii)] - && (board[SE(ii)] == color - || (board[SE(ii)] == EMPTY - && ON_BOARD(EAST(SE(ii))) - && board[EAST(SE(ii))] != other - && ON_BOARD(SOUTH(SE(ii))) - && board[SOUTH(SE(ii))] != other))) - ENQUEUE(SE(ii)); - } + memcpy(private_goal, goal, BOARDMAX); + + /* First we let the readconnect code compute a distance map + * from our dragon. + */ + for (pos = BOARDMIN; pos < BOARDMAX; pos++) { + if (!ON_BOARD(pos)) + continue; + optimistic_escape[pos] = 0.0; + pessimistic_escape[pos] = 0.0; + if (private_goal[pos] && !IS_STONE(board[pos])) + private_goal[pos] = 0; + if (board[pos] == color) { + ASSERT1((private_goal[find_origin(pos)] != 0) + == (private_goal[pos] != 0), pos); + } + } + init_connection_data(color, private_goal, NO_MOVE, 2.99, &conn, 0); + real_start = conn.queue_end; + spread_connection_distances(color, &conn); + /* expand_connection_queue(&conn); */ + if (1 && verbose && (debug & DEBUG_ESCAPE)) + print_connection_distances(&conn); + /* Note: Now all goal stones are in the connection queue with index + * < real_start, all other fields with finite connection are + * higher up in the queue. + */ + + /* Compute: pessimistic and optimistic escape values, and + * assign them to stones in the dragon by tracing back the + * connecting path. + */ + for (k = conn.queue_end; --k >= real_start;) { + float this_escape_value; + int small_step = 1; + int from; + float delta; + pos = conn.queue[k]; + from = conn.coming_from[pos]; + delta = conn.distances[pos] - conn.distances[from]; + this_escape_value = escape_values[pos]; + + ASSERT1(ON_BOARD(from), pos); + + if (delta > 1.201 + || (delta > 1.01 && are_neighbors(pos, from))) + small_step = 0; + if (private_goal[from]) { + optimistic_escape[from] += optimistic_escape[pos]; + optimistic += optimistic_escape[pos]; + if (small_step) { + DEBUG(DEBUG_ESCAPE, "%1m: %f ", pos, pessimistic_escape[pos]); + pessimistic_escape[from] += pessimistic_escape[pos]; + pessimistic += pessimistic_escape[pos]; + if (pessimistic_escape[pos] > highest_contribution) + highest_contribution = pessimistic_escape[pos]; } } + else { + optimistic_escape[from] += this_escape_value + optimistic_escape[pos]; + if (small_step) + pessimistic_escape[from] += this_escape_value + pessimistic_escape[pos]; + } } + if (careful) + pessimistic -= highest_contribution; - /* Reset used mx cells. */ - for (k = 0; k < queue_end; k++) { - /* The assertion fails if the same element should have been queued - * twice, which might happen if ENQUEUE() is called without - * checking mx[]. - */ - ASSERT1(mx[queue[k]] == 1, queue[k]); - mx[queue[k]] = 0; - } + DEBUG(DEBUG_ESCAPE, "\nEscape: opt. %f, pess. %f, %d fields visited\n", + optimistic, pessimistic, conn.queue_end); - return escape_potential; + sgf_dumptree = save_sgf_dumptree; + count_variations = save_count_variations; + + return pessimistic; } /* Wrapper to call the function above and compute the escape potential * for the dragon at (pos). + * This function needs + * - crude_status + * - moyo_size (according to initial influence with dragons unknown) + * - genus + * - lunches + * to have been computed earlier. */ -static int -compute_escape(int pos, int dragon_status_known) +static float +compute_escape(int pos) { int ii; - char goal[BOARDMAX]; - char escape_value[BOARDMAX]; + int d; char safe_stones[BOARDMAX]; + float strength[BOARDMAX]; + float very_crude_weakness[MAX_BOARD * MAX_BOARD]; + char goal[BOARDMAX]; + float escape_values[BOARDMAX]; + int color = board[pos]; - ASSERT1(IS_STONE(board[pos]), pos); + ASSERT1(IS_STONE(color), pos); + DEBUG(DEBUG_ESCAPE, "Computing escape measure for %1m:\n", pos); - for (ii = BOARDMIN; ii < BOARDMAX; ii++) - if (ON_BOARD(ii)) - goal[ii] = is_same_dragon(ii, pos); + get_lively_stones(OTHER_COLOR(color), safe_stones); + mark_dragon(pos, safe_stones, 0); - /* Compute escape_value array. Points are awarded for moyo (4), - * area (2) or EMPTY (1). Values may change without notice. - */ - get_lively_stones(OTHER_COLOR(board[pos]), safe_stones); - compute_escape_influence(board[pos], safe_stones, NULL, 0, escape_value); - - /* If we can reach a live group, award 6 points. */ + for (d = 0; d < number_of_dragons; d++) + very_crude_weakness[d] + = crude_dragon_weakness(ALIVE, &dragon2[d].genus, + dragon2[d].lunch != NO_MOVE, + dragon2[d].moyo_territorial_value, 0.0); for (ii = BOARDMIN; ii < BOARDMAX; ii++) { - if (!ON_BOARD(ii)) + if (!IS_STONE(board[ii])) continue; + if (!safe_stones[ii]) + strength[ii] = 0.0; + else if (board[ii] == OTHER_COLOR(color)) + strength[ii] = 100.0; + else + strength[ii] = 100.0 * (1.0 - very_crude_weakness[dragon[ii].id]); + } - if (dragon_status_known) { - if (dragon[ii].crude_status == ALIVE) - escape_value[ii] = 6; - else if (dragon[ii].crude_status == UNKNOWN - && (DRAGON2(ii).escape_route > 5 - || DRAGON2(ii).moyo_size > 5)) - escape_value[ii] = 4; - } - else { - if (board[ii] == board[pos] - && !goal[ii] - && worm[ii].attack_codes[0] == 0) - escape_value[ii] = 2; - } + memset(goal, 0, BOARDMAX); + mark_dragon(pos, goal, 1); + + /* Compute escape_value array. */ + compute_escape_influence(EMPTY, safe_stones, strength, &escape_influence); + export_escape_values(&escape_influence, color, escape_values); + + for (ii = BOARDMIN; ii < BOARDMAX; ii++) { + ASSERT1(escape_values[ii] >= 0.0, pos); + ASSERT1(1.0/escape_values[ii] > 0.0, pos); + if (board[ii] != color || is_same_dragon(ii, pos)) + continue; + if (dragon[ii].crude_status == ALIVE) + escape_values[ii] = 5.0; + else if (dragon[ii].crude_status == UNKNOWN + && worm[ii].attack_codes[0] == NO_MOVE + && ii == worm[ii].origin + && DRAGON2(ii).moyo_size > 5) + escape_values[ii] = 1.5; } - return dragon_escape(goal, board[pos], escape_value); + return dragon_escape(goal, color, escape_values, 1); } /* @@ -2171,7 +2097,7 @@ compute_surrounding_moyo_sizes(const str static struct interpolation_data moyo_value2weakness = { 5, 0.0, 15.0, {1.0, 0.65, 0.3, 0.15, 0.05, 0.0}}; static struct interpolation_data escape_route2weakness = - { 5, 0.0, 25.0, {1.0, 0.6, 0.3, 0.1, 0.05, 0.0}}; + { 5, 0.0, 8.0, {1.0, 0.7, 0.4, 0.2, 0.1, 0.0}}; static struct interpolation_data genus2weakness = { 6, 0.0, 3.0, {1.0, 0.95, 0.8, 0.5, 0.2, 0.1, 0.0}}; @@ -2188,11 +2114,13 @@ crude_dragon_weakness(int safety, struct float weakness; int i, j; + if (safety == INVINCIBLE || safety == INESSENTIAL) return 0.0; if (safety == TACTICALLY_DEAD || safety == DEAD || safety == CRITICAL) return 1.0; + escape_route2weakness.range_upperbound = max_escape_route; weakness_value[0] = gg_interpolate(&moyo_value2weakness, moyo_value); weakness_value[1] = gg_interpolate(&escape_route2weakness, escape_route); weakness_value[2] = gg_interpolate(&genus2weakness, true_genus); @@ -2276,18 +2204,26 @@ compute_dragon_weakness_value(int d) * subsequent re-run of the influence code. */ void -compute_refined_dragon_weaknesses() +compute_refined_dragon_weaknesses(int dragon_status_known) { int d; /* Compute the surrounding moyo sizes. */ for (d = 0; d < number_of_dragons; d++) dragon2[d].moyo_size = 2 * BOARDMAX; - + /* Set moyo sizes according to initial_influence. */ compute_surrounding_moyo_sizes(&initial_black_influence); compute_surrounding_moyo_sizes(&initial_white_influence); + if (!dragon_status_known) { + /* Compute the escape route measure. */ + int str; + for (str = BOARDMIN; str < BOARDMAX; str++) + if (IS_STONE(board[str]) && dragon[str].origin == str) + DRAGON2(str).escape_route = compute_escape(str); + } + for (d = 0; d < number_of_dragons; d++) dragon2[d].weakness = compute_dragon_weakness_value(d); } @@ -2412,7 +2348,6 @@ dragon_weak(int pos) - /* Returns the size of the biggest critical dragon on the board. */ int @@ -2493,7 +2428,7 @@ report_dragon(FILE *outfile, int pos) gfprintf(outfile, "genus %s\n", eyevalue_to_string(&d2->genus)); gfprintf(outfile, "heye %1m\n", d2->heye); - gfprintf(outfile, "escape_route %d\n", d2->escape_route); + gfprintf(outfile, "escape_route %f\n", d2->escape_route); gfprintf(outfile, "lunch %1m\n", d2->lunch); gfprintf(outfile, "crude_status %s\n", status_to_string(d->crude_status)); Index: engine/genmove.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/genmove.c,v retrieving revision 1.93 diff -u -p -r1.93 genmove.c --- engine/genmove.c 19 Jul 2004 12:23:08 -0000 1.93 +++ engine/genmove.c 2 Sep 2004 15:18:49 -0000 @@ -179,7 +179,7 @@ examine_position(int how_much) } if (NEEDS_UPDATE(dragons_refinedly_examined)) { - compute_refined_dragon_weaknesses(); + compute_refined_dragon_weaknesses(1); } if (how_much == FULL_EXAMINE_DRAGONS) { gg_assert(test_gray_border() < 0); @@ -696,7 +696,7 @@ revise_thrashing_dragon(int color, float compute_influence(OTHER_COLOR(color), safe_stones, strength, OPPOSITE_INFLUENCE(color), NO_MOVE, "revised thrashing dragon"); - compute_refined_dragon_weaknesses(); + compute_refined_dragon_weaknesses(1); return 1; } Index: engine/globals.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/globals.c,v retrieving revision 1.67 diff -u -p -r1.67 globals.c --- engine/globals.c 8 Jun 2004 05:06:12 -0000 1.67 +++ engine/globals.c 2 Sep 2004 15:18:49 -0000 @@ -170,3 +170,9 @@ float territorial_weight = 1.0; float strategical_weight = 1.0; float attack_dragon_weight = 1.0; float followup_weight = 1.0; + +float owl_escape = 1.704785; +float escape_exp = 0.65316103; +float escape_coeff = 2.14990993; +float max_escape_route = 3.3919487193653; +float escape_default_attenuation = 3.1491622408; Index: engine/influence.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/influence.c,v retrieving revision 1.103 diff -u -p -r1.103 influence.c --- engine/influence.c 31 May 2004 13:57:50 -0000 1.103 +++ engine/influence.c 2 Sep 2004 15:18:50 -0000 @@ -25,11 +25,13 @@ #include #include #include +#include #include "liberty.h" #include "influence.h" #include "patterns.h" #include "gg_utils.h" +#include "hash.h" static void add_influence_source(int pos, int color, float strength, float attenuation, @@ -61,7 +63,7 @@ struct influence_data move_influence; struct influence_data followup_influence; /* Influence used for estimation of escape potential. */ -static struct influence_data escape_influence; +struct influence_data escape_influence; /* Pointer to influence data used during pattern matching. */ static struct influence_data *current_influence = NULL; @@ -214,10 +216,7 @@ accumulate_influence(struct influence_da else inv_attenuation = 1.0 / q->black_attenuation[pos]; - if (q->is_territorial_influence) - inv_diagonal_damping = 1.0 / TERR_DIAGONAL_DAMPING; - else - inv_diagonal_damping = 1.0 / DIAGONAL_DAMPING; + inv_diagonal_damping = 1.0 / TERR_DIAGONAL_DAMPING; if (color == WHITE) permeability_array = q->white_permeability; @@ -415,7 +414,6 @@ init_influence(struct influence_data *q, min_infl_for_territory.values[4] = t * 20.0 + (1.0-t) * 20.0; min_infl_for_territory.values[5] = t * 15.0 + (1.0-t) * 15.0; min_infl_for_territory.values[6] = t * 10.0 + (1.0-t) * 15.0; - } else { @@ -444,21 +442,8 @@ init_influence(struct influence_data *q, } - if (q != &escape_influence) { - q->color_to_move = color; - if (q->is_territorial_influence) - attenuation = TERR_DEFAULT_ATTENUATION; - else - attenuation = DEFAULT_ATTENUATION; - } - else { - q->color_to_move = EMPTY; - if (q->is_territorial_influence) - attenuation = 2 * TERR_DEFAULT_ATTENUATION; - else - attenuation = 2 * DEFAULT_ATTENUATION; - } - + q->color_to_move = color; + attenuation = q->default_attenuation; q->intrusion_counter = 0; /* Remember this for later. */ @@ -678,15 +663,11 @@ add_marked_intrusions(struct influence_d * A - Barrier pattern, where O plays first and X tries to block influence. * D - Barrier pattern, where O plays first and O tries to block influence. * B - Intrusion patterns, adding a low intensity influence source. - * E - Enhance patterns, FIXME: document this one! * t - Non-territory patterns, marking vertices as not territory. * I - Invasion patterns, adding a low intensity influence source. - * e - Escape bonus. Used together with I to increase the value substantially - * if escape influence is being computed. * * Classes A, D, and B are matched with color as O, and it is assumed - * that O is in turn to move. Classes E and I are matched with either - * color as O. + * that O is in turn to move. Class I is matched with either color as O. */ static void influence_callback(int anchor, int color, struct pattern *pattern, int ll, @@ -696,18 +677,6 @@ influence_callback(int anchor, int color int k; struct influence_data *q = data; - /* Patterns marked T are only used in "territorial influence", - * patterns marked W get ignored other(w)ise. - */ - if (((pattern->class & CLASS_T) && !(q->is_territorial_influence)) - || ((pattern->class & CLASS_W) && q->is_territorial_influence)) - return; - - /* We also ignore enhancement patterns in territorial influence. */ - if ((pattern->class & CLASS_E) - && q->is_territorial_influence) - return; - /* Don't use invasion (I) patterns when scoring. */ if (doing_scoring && (pattern->class & CLASS_I)) return; @@ -721,7 +690,7 @@ influence_callback(int anchor, int color int blocking_color; int ii; if (pattern->patn[k].att != ATT_comma - && (!q->is_territorial_influence || pattern->patn[k].att != ATT_not)) + && pattern->patn[k].att != ATT_not) break; /* All commas are guaranteed to come first. */ /* transform pattern real coordinate */ @@ -755,22 +724,17 @@ influence_callback(int anchor, int color * * Patterns also having class s are an exception from this rule. */ - if ((pattern->class & (CLASS_D | CLASS_A | CLASS_B | CLASS_E | CLASS_t)) + if ((pattern->class & (CLASS_D | CLASS_A | CLASS_B | CLASS_t)) && !(pattern->class & CLASS_s)) { for (k = 0; k < pattern->patlen; ++k) { /* match each point */ if ((pattern->patn[k].att == ATT_O - && (pattern->class & (CLASS_D | CLASS_B | CLASS_E | CLASS_t))) + && (pattern->class & (CLASS_D | CLASS_B | CLASS_t))) || (pattern->patn[k].att == ATT_X && (pattern->class & (CLASS_A | CLASS_B | CLASS_t)))) { /* transform pattern real coordinate */ int ii = AFFINE_TRANSFORM(pattern->patn[k].offset, ll, anchor); - if (pattern->class & CLASS_E) { - if ((color == WHITE && q->white_strength[ii] == 0.0) - || (color == BLACK && q->black_strength[ii] == 0.0)) - return; /* Match failed. */ - } /* FIXME: This test is probably not necessary any more. */ - else if (!(pattern->class & (CLASS_D | CLASS_B | CLASS_t))) { + if (!(pattern->class & (CLASS_D | CLASS_B | CLASS_t))) { if ((stackp == 0 && worm[ii].attack_codes[0] != 0) || attack(ii, NULL) != 0) return; /* Match failed */ @@ -856,29 +820,13 @@ influence_callback(int anchor, int color attenuation = 1.5; } - /* Increase strength if we're computing escape influence. */ - if (q == &escape_influence && (pattern->class & CLASS_e)) - add_influence_source(pos, this_color, 20 * strength, attenuation, q); - else - add_influence_source(pos, this_color, strength, attenuation, q); + add_influence_source(pos, this_color, strength, attenuation, q); DEBUG(DEBUG_INFLUENCE, " low intensity influence source at %1m, strength %f, color %C\n", pos, strength, this_color); return; } - - /* For E patterns, add a new influence source of the same color and - * pattern defined strength at *. - */ - if (pattern->class & CLASS_E) { - add_influence_source(pos, color, - pattern->value, DEFAULT_ATTENUATION, q); - DEBUG(DEBUG_INFLUENCE, - " extra %C source at %1m, strength %f\n", color, - pos, pattern->value); - return; - } /* Loop through pattern elements and perform necessary actions * for A, D, B, and t patterns. @@ -886,8 +834,7 @@ influence_callback(int anchor, int color for (k = 0; k < pattern->patlen; ++k) { /* match each point */ if (((pattern->class & (CLASS_D | CLASS_A)) && (pattern->patn[k].att == ATT_comma - || (pattern->patn[k].att == ATT_not - && q->is_territorial_influence))) + || (pattern->patn[k].att == ATT_not))) || ((pattern->class & CLASS_B) && pattern->patn[k].att == ATT_not)) { /* transform pattern real coordinate */ @@ -930,12 +877,8 @@ influence_callback(int anchor, int color /* Low intensity influence source for the color in turn to move. */ if (pattern->class & CLASS_B) { - if (q->is_territorial_influence) - enter_intrusion_source(anchor, ii, strength, - TERR_DEFAULT_ATTENUATION, q); - else - add_influence_source(ii, color, strength, - DEFAULT_ATTENUATION, q); + enter_intrusion_source(anchor, ii, strength, + TERR_DEFAULT_ATTENUATION, q); DEBUG(DEBUG_INFLUENCE, " intrusion at %1m\n", ii); } @@ -1057,12 +1000,12 @@ find_influence_patterns(struct influence int ii; current_influence = q; - matchpat(influence_callback, ANCHOR_COLOR, &influencepat_db, q, NULL); + if (q->is_territorial_influence) + matchpat(influence_callback, ANCHOR_COLOR, &influencepat_db, q, NULL); if (color != EMPTY) matchpat(influence_callback, color, &barrierspat_db, q, NULL); - if (q->is_territorial_influence) - add_marked_intrusions(q, color); + add_marked_intrusions(q, color); /* When color == EMPTY, we introduce a weaker kind of barriers * manually instead of searching for patterns. @@ -1093,7 +1036,6 @@ find_influence_patterns(struct influence */ static void do_compute_influence(int color, const char safe_stones[BOARDMAX], - const char inhibited_sources[BOARDMAX], const float strength[BOARDMAX], struct influence_data *q, int move, const char *trace_message) { @@ -1101,14 +1043,11 @@ do_compute_influence(int color, const ch init_influence(q, color, safe_stones, strength); modify_depth_values(stackp - 1); - if (q != &escape_influence) - find_influence_patterns(q, color); - else - find_influence_patterns(q, EMPTY); + find_influence_patterns(q, color); modify_depth_values(1 - stackp); for (ii = BOARDMIN; ii < BOARDMAX; ii++) - if (ON_BOARD(ii) && !(inhibited_sources && inhibited_sources[ii])) { + if (ON_BOARD(ii)) { if (q->white_strength[ii] > 0.0) accumulate_influence(q, ii, WHITE); if (q->black_strength[ii] > 0.0) @@ -1120,7 +1059,8 @@ do_compute_influence(int color, const ch if ((move == NO_MOVE && (printmoyo & PRINTMOYO_INITIAL_INFLUENCE)) - || (debug_influence && move == debug_influence)) + || (debug_influence && move == debug_influence) + || debug & DEBUG_INFLUENCE) print_influence(q, trace_message); } @@ -1154,6 +1094,7 @@ compute_influence(int color, const char q->is_territorial_influence = 1; q->color_to_move = color; + q->default_attenuation = TERR_DEFAULT_ATTENUATION; /* Turn off DEBUG_INFLUENCE for influence computations we are not * interested in. @@ -1166,8 +1107,7 @@ compute_influence(int color, const char influence_id++; q->id = influence_id; - do_compute_influence(color, safe_stones, NULL, strength, - q, move, trace_message); + do_compute_influence(color, safe_stones, strength, q, move, trace_message); debug = save_debug; } @@ -1378,6 +1318,9 @@ value_territory(struct influence_data *q * this is to make sure that the vertex is not regarded as * moyo or area. Also notice that the non-territory * degradation below may over-rule this decision. + * + * We do not do this for escape influence, as dead stones should + * be accounted for by other means (lunch etc.) by the code using it. */ if (board[ii] == BLACK) first_guess[ii] = 1.0; @@ -1492,8 +1435,7 @@ segment_region(struct influence_data *q, queue_start++; if (!q->safe[tt] || board[tt] != color) { size++; - if (q->is_territorial_influence) - terr_val += gg_abs(q->territory_value[tt]); + terr_val += gg_abs(q->territory_value[tt]); } segmentation[tt] = q->number_of_regions; for (k = 0; k < 4; k++) { @@ -1752,7 +1694,8 @@ compute_followup_influence(const struct && q->white_strength[ii] > base->white_strength[ii])) accumulate_influence(q, ii, color); - value_territory(q); + if (q->is_territorial_influence) + value_territory(q); if (debug_influence && debug_influence == move) print_influence(q, trace_message); @@ -1765,58 +1708,16 @@ compute_followup_influence(const struct void compute_escape_influence(int color, const char safe_stones[BOARDMAX], - const char goal[BOARDMAX], const float strength[BOARDMAX], - char escape_value[BOARDMAX]) + struct influence_data *q) { - int k; - int ii; int save_debug = debug; - /* IMPORTANT: The caching relies on the fact that safe_stones[] and - * strength[] will currently always be identical for identical board[] - * states. Better check for these, too. - */ - static int cached_board[BOARDMAX]; - static char escape_values[BOARDMAX][2]; - static int active_caches[2] = {0, 0}; - - int cache_number = (color == WHITE); - - VALGRIND_MAKE_WRITABLE(&escape_influence, sizeof(escape_influence)); - - if (!goal) { - /* Encode the values of color and dragons_known into an integer - * between 0 and 3. - */ - int board_was_cached = 1; - - /* Notice that we compare the out of board markers as well, in - * case the board size should have changed between calls. - */ - for (ii = BOARDMIN; ii < BOARDMAX; ii++) { - if (cached_board[ii] != board[ii]) { - cached_board[ii] = board[ii]; - board_was_cached = 0; - } - } - - if (!board_was_cached) - for (k = 0; k < 2; k++) - active_caches[k] = 0; - - if (active_caches[cache_number]) { - for (ii = BOARDMIN; ii < BOARDMAX; ii++) - if (ON_BOARD(ii)) - escape_value[ii] = escape_values[ii][cache_number]; - - return; - } - } + VALGRIND_MAKE_WRITABLE(q, sizeof(*q)); - /* Use enhance pattern and higher attenuation for escape influence. */ - escape_influence.is_territorial_influence = 0; - escape_influence.color_to_move = EMPTY; + q->is_territorial_influence = 0; + q->color_to_move = EMPTY; + q->default_attenuation = escape_default_attenuation; /* Turn off DEBUG_INFLUENCE unless we are specifically interested in * escape computations. @@ -1824,61 +1725,156 @@ compute_escape_influence(int color, cons if (!(debug & DEBUG_ESCAPE)) debug &= ~DEBUG_INFLUENCE; - do_compute_influence(OTHER_COLOR(color), safe_stones, goal, strength, - &escape_influence, -1, NULL); + do_compute_influence(color, safe_stones, strength, q, -1, NULL); debug = save_debug; - for (ii = BOARDMIN; ii < BOARDMAX; ii++) - if (ON_BOARD(ii)) { - if (whose_moyo(&escape_influence, ii) == color) - escape_value[ii] = 4; - else if (whose_area(&escape_influence, ii) == color) - escape_value[ii] = 2; - else if (whose_area(&escape_influence, ii) == EMPTY) { - if (goal) { - escape_value[ii] = 0; - - if (!goal[ii]) { - int goal_proximity = 0; - - for (k = 0; k < 8; k++) { - if (ON_BOARD(ii + delta[k])) { - goal_proximity += 2 * goal[ii + delta[k]]; - if (k < 4 && ON_BOARD(ii + 2 * delta[k])) - goal_proximity += goal[ii + delta[k]]; - } - else - goal_proximity += 1; - } + if ((debug & DEBUG_ESCAPE) && verbose > 0) + print_influence(q, "escape influence"); +} - if (goal_proximity < 6) - escape_value[ii] = 1; - } - } - else - escape_value[ii] = 1; - } +#if 0 +static struct interpolation_data atan_to_escape_value = + { 2, 0.0, 0.5 * 3.1415926, {0.0, 1.0, 2.0}}; +#endif + +void +export_escape_values(const struct influence_data *q, int color, + float escape_values[BOARDMAX]) +{ + int pos; + + gg_assert(IS_STONE(color)); + float first_guess[BOARDMAX]; + + /* First go: computer escape value directly from influence values. */ + for (pos = BOARDMIN; pos < BOARDMAX; pos++) + if (board[pos] != EMPTY) + /* Safe opponent stones have no escape value, our own safe + * stones should be handled by the calling function. + */ + first_guess[pos] = 0.0; + else { + /* The escape value is mostly a function of the ratio + * (our influence at pos) / (opponent's influence at pos) + */ + float ours = (color == WHITE ? + q->white_influence[pos] : q->black_influence[pos]); + float yours = (color == BLACK ? + q->white_influence[pos] : q->black_influence[pos]); + int dist_i, dist_j; + float ratio, central; + first_guess[pos] + = soft_cap(escape_coeff * pow((ours + 0.0001)/(yours + 0.0001), + escape_exp), 1.0); + ASSERT1(first_guess[pos] >= 0.0, pos); + + + /* If the influence is small, the ratio isn't such a good + * measure and we increase the influence. + * What we consider "small influence" depends on how central this + * intersection lies. This is borrowed from the territory + * computations. + */ + dist_i = gg_min(I(pos), board_size - I(pos) - 1); + dist_j = gg_min(J(pos), board_size - J(pos) - 1); + if (dist_i > dist_j) + dist_i = gg_min(4, dist_i); else - escape_value[ii] = 0; + dist_j = gg_min(4, dist_j); + central = 2.0 * gg_min(dist_i, dist_j) + dist_i + dist_j; + ratio = 1.1 * yours / gg_interpolate(&min_infl_for_territory, central); + first_guess[pos] += (1.0 - gg_interpolate(&territory_correction, ratio)) + * (1.0 - first_guess[pos]); + } + + /* Second loop: Correct according to neighbors for non-edge vertices. */ + for (pos = BOARDMIN; pos < BOARDMAX; pos++) + if (board[pos] == EMPTY + && !is_edge_vertex(pos)) { + int k; + float best_neighbor = first_guess[pos]; + for (k = 0; k < 3; k++) { + int pos2 = pos + delta[k]; + float permeability = (color == BLACK) ? q->black_permeability[pos2] : + q->white_permeability[pos2]; + best_neighbor = gg_max(permeability * first_guess[pos2] + + (1.0 - permeability) * first_guess[pos], + best_neighbor); + } + escape_values[pos] = 0.4 * first_guess[pos] + 0.6 * best_neighbor; } + else + escape_values[pos] = first_guess[pos]; - if (0 && (debug & DEBUG_ESCAPE) && verbose > 0) { - print_numeric_influence(&escape_influence, - escape_influence.white_influence, - "%3.0f", 3, 1, 1); - print_numeric_influence(&escape_influence, - escape_influence.black_influence, - "%3.0f", 3, 1, 1); - } + if ((debug & DEBUG_ESCAPE) && (verbose > 0)) { + gprintf("Escape influence:\n"); + print_numeric_influence(q, escape_values, "%5.2f", 5, 0, 0); + } +} - if (!goal) { - /* Save the computed values in the cache. */ - for (ii = BOARDMIN; ii < BOARDMAX; ii++) - if (ON_BOARD(ii)) - escape_values[ii][cache_number] = escape_value[ii]; - active_caches[cache_number] = 1; + +#define ESCAPE_CACHE_SIZE 100 + +static float cached_escape_values[ESCAPE_CACHE_SIZE][BOARDMAX]; +static Hash_data escape_cache_hash[ESCAPE_CACHE_SIZE]; +static int escape_cache_color[ESCAPE_CACHE_SIZE]; +static int escape_cache_position_number; +static int num_escape_cache_entries = 0; + +void +clear_escape_cache() +{ + escape_cache_position_number = position_number; + num_escape_cache_entries = 0; +} + +int +retrieve_escape_cache(int color, const char safe_stones[BOARDMAX], + float escape_values[BOARDMAX]) +{ + Hash_data safe_stones_hash = goal_to_hashvalue(safe_stones); + int i; + + if (escape_cache_position_number != position_number) + return 0; + + for (i = 0; i < num_escape_cache_entries; i++) + if (hashdata_is_equal(escape_cache_hash[i], safe_stones_hash) + && escape_cache_color[i] == color) { + memcpy(escape_values, &(cached_escape_values[i][0]), + BOARDMAX * sizeof(float)); + return 1; + } + + return 0; +} + + +void +store_escape_cache(int color, const char safe_stones[BOARDMAX], + const float escape_values[BOARDMAX]) +{ + int i; + Hash_data safe_stones_hash = goal_to_hashvalue(safe_stones); + + if (escape_cache_position_number != position_number) { + num_escape_cache_entries = 0; + escape_cache_position_number = position_number; } + + if (num_escape_cache_entries == ESCAPE_CACHE_SIZE) + return; + + for (i = 0; i < num_escape_cache_entries; i++) + if (hashdata_is_equal(escape_cache_hash[i], safe_stones_hash) + && escape_cache_color[i] == color) + return; + + memcpy(&(cached_escape_values[num_escape_cache_entries][0]), escape_values, + BOARDMAX * sizeof(float)); + memcpy(&safe_stones_hash, &(escape_cache_hash[i]), sizeof(Hash_data)); + escape_cache_color[i] = color; } @@ -1890,6 +1886,7 @@ static int territory_cache_position_numb static int territory_cache_influence_id = -1; static int territory_cache_color = -1; + /* We cache territory computations. This avoids unnecessary re-computations * when review_move_reasons is run a second time for the endgame patterns. * @@ -2197,30 +2194,18 @@ print_influence(const struct influence_d if (printmoyo & PRINTMOYO_STRENGTH) { /* Print the strength values. */ fprintf(stderr, "white strength:\n"); - if (q->is_territorial_influence) - print_numeric_influence(q, q->white_strength, "%5.1f", 5, 0, 0); - else - print_numeric_influence(q, q->white_strength, "%3.0f", 3, 0, 1); + print_numeric_influence(q, q->white_strength, "%5.1f", 5, 0, 0); fprintf(stderr, "black strength:\n"); - if (q->is_territorial_influence) - print_numeric_influence(q, q->black_strength, "%5.1f", 5, 0, 0); - else - print_numeric_influence(q, q->black_strength, "%3.0f", 3, 0, 1); + print_numeric_influence(q, q->black_strength, "%5.1f", 5, 0, 0); } if (printmoyo & PRINTMOYO_NUMERIC_INFLUENCE) { /* Print the white influence values. */ fprintf(stderr, "white influence (%s):\n", info_string); - if (q->is_territorial_influence) - print_numeric_influence(q, q->white_influence, "%5.1f", 5, 1, 0); - else - print_numeric_influence(q, q->white_influence, "%3.0f", 3, 1, 1); + print_numeric_influence(q, q->white_influence, "%5.1f", 5, 1, 0); /* Print the black influence values. */ fprintf(stderr, "black influence (%s):\n", info_string); - if (q->is_territorial_influence) - print_numeric_influence(q, q->black_influence, "%5.1f", 5, 1, 0); - else - print_numeric_influence(q, q->black_influence, "%3.0f", 3, 1, 1); + print_numeric_influence(q, q->black_influence, "%5.1f", 5, 1, 0); } if (printmoyo & PRINTMOYO_PRINT_INFLUENCE) { Index: engine/influence.h =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/influence.h,v retrieving revision 1.22 diff -u -p -r1.22 influence.h --- engine/influence.h 24 Jan 2004 04:04:56 -0000 1.22 +++ engine/influence.h 2 Sep 2004 15:18:50 -0000 @@ -35,22 +35,16 @@ * for territory evaluation. (initial_influence with dragons_known, * move_influence) */ -#define DEFAULT_ATTENUATION \ - (cosmic_importance * 2.7 + (1.0 - cosmic_importance) * 3.0) #define TERR_DEFAULT_ATTENUATION \ (cosmic_importance * 2.15 + (1.0 - cosmic_importance) * 2.4) /* Extra damping coefficient for spreading influence diagonally. */ -#define DIAGONAL_DAMPING \ - (cosmic_importance * 2.5 + (1.0 - cosmic_importance) * 2.0) #define TERR_DIAGONAL_DAMPING \ (cosmic_importance * 2.5 + (1.0 - cosmic_importance) * 1.7) - - /* Smallest amount of influence that we care about distributing. */ #define INFLUENCE_CUTOFF 0.02 @@ -113,6 +107,7 @@ struct influence_data int number_of_regions; int is_territorial_influence; /* 0 only if computing escape_influence.*/ + float default_attenuation; float territory_value[BOARDMAX]; int non_territory[BOARDMAX]; Index: engine/liberty.h =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/liberty.h,v retrieving revision 1.224 diff -u -p -r1.224 liberty.h --- engine/liberty.h 24 Aug 2004 15:24:30 -0000 1.224 +++ engine/liberty.h 2 Sep 2004 15:18:50 -0000 @@ -41,7 +41,6 @@ #define REVERSE_RESULT(result) (WIN - result) - void start_timer(int n); double time_report(int n, const char *occupation, int move, double mintime); void showstats(void); @@ -300,6 +299,7 @@ int vital_chain(int pos); int confirm_safety(int move, int color, int *defense_point, char safe_stones[BOARDMAX]); int dragon_weak(int pos); +float dragon_weakness(int pos, int ignore_dead_dragons); int size_of_biggest_critical_dragon(void); float blunder_size(int move, int color, int *defense_point, char safe_stones[BOARDMAX]); @@ -316,8 +316,10 @@ int does_secure(int color, int move, int void compute_new_dragons(int dragon_origins[BOARDMAX]); void join_dragons(int d1, int d2); -int dragon_escape(char goal[BOARDMAX], int color, char escape_value[BOARDMAX]); -void compute_refined_dragon_weaknesses(void); +float dragon_escape(const char goal[BOARDMAX], int color, + const float escape_value[BOARDMAX], + int careful); +void compute_refined_dragon_weaknesses(int dragon_status_known); struct eyevalue; void compute_dragon_genus(int d, struct eyevalue *genus, int eye_to_exclude); @@ -596,6 +598,7 @@ extern struct influence_data initial_bla extern struct influence_data initial_white_influence; extern struct influence_data move_influence; extern struct influence_data followup_influence; +extern struct influence_data escape_influence; #define INITIAL_INFLUENCE(color) ((color) == WHITE ? \ &initial_white_influence \ @@ -612,10 +615,17 @@ void compute_influence(int color, const void compute_followup_influence(const struct influence_data *base, struct influence_data *q, int move, const char *trace_message); + void compute_escape_influence(int color, const char safe_stones[BOARDMAX], - const char goal[BOARDMAX], const float strength[BOARDMAX], - char escape_value[BOARDMAX]); + struct influence_data *q); +void export_escape_values(const struct influence_data *q, int color, + float escape_values[BOARDMAX]); +void clear_escape_cache(void); +int retrieve_escape_cache(int color, const char safe_stones[BOARDMAX], + float escape_values[BOARDMAX]); +void store_escape_cache(int color, const char safe_stones[BOARDMAX], + const float escape_values[BOARDMAX]); float influence_delta_territory(const struct influence_data *base, const struct influence_data *q, int color, @@ -900,7 +910,7 @@ struct dragon_data2 { enum dragon_status safety; /* a more detailed status estimate */ float weakness; /* A new (3.4) continuous estimate of the dragon's safety */ float weakness_pre_owl; /* Dragon safety based on pre-owl computations */ - int escape_route; /* a measurement of likelihood of escape */ + float escape_route; /* a measurement of likelihood of escape */ struct eyevalue genus; /* the number of eyes (approximately) */ int heye; /* coordinates of a half eye */ int lunch; /* if lunch != 0 then lunch points to a boundary worm which */ @@ -1055,6 +1065,12 @@ int is_false_eye(struct half_eye_data he #endif /* _LIBERTY_H_ */ +float owl_escape; +float escape_coeff; +float escape_exp; +float max_escape_route; +float escape_default_attenuation; + /* * Local Variables: Index: engine/owl.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/owl.c,v retrieving revision 1.219 diff -u -p -r1.219 owl.c --- engine/owl.c 24 Aug 2004 14:39:53 -0000 1.219 +++ engine/owl.c 2 Sep 2004 15:18:52 -0000 @@ -77,7 +77,7 @@ struct local_owl_data { */ char neighbors[BOARDMAX]; - char escape_values[BOARDMAX]; + float escape_values[BOARDMAX]; int color; struct eye_data my_eye[BOARDMAX]; @@ -227,7 +227,7 @@ static void sniff_lunch(int lunch, int * static void eat_lunch_escape_bonus(int lunch, int *min, int *probable, int *max, struct local_owl_data *owl); static void compute_owl_escape_values(struct local_owl_data *owl); -static int owl_escape_route(struct local_owl_data *owl); +static float owl_escape_route(struct local_owl_data *owl, int careful); static void do_owl_analyze_semeai(int apos, int bpos, struct local_owl_data *owla, struct local_owl_data *owlb, @@ -320,6 +320,10 @@ static int prefer_ko; */ static int include_semeai_worms_in_eyespace = 0; +/* If a dragon achieves a higher escape value than ESCAPED, he will be + * considered alive. + */ +#define ESCAPED 3.51 static void @@ -804,7 +808,7 @@ do_owl_analyze_semeai(int apos, int bpos &live_reasona, 0, &probable_eyes_a, &eyemin_a, &eyemax_a)) I_look_alive = 1; - else if (stackp > 2 && owl_escape_route(owla) >= 5) { + else if (stackp > 2 && owl_escape_route(owla, 0) >= owl_escape) { live_reasona = "escaped"; I_look_alive = 1; } @@ -813,7 +817,7 @@ do_owl_analyze_semeai(int apos, int bpos &live_reasonb, 1, &probable_eyes_b, &eyemin_b, &eyemax_b)) you_look_alive = 1; - else if (stackp > 2 && owl_escape_route(owlb) >= 5) { + else if (stackp > 2 && owl_escape_route(owlb, 1) >= owl_escape) { live_reasonb = "escaped"; you_look_alive = 1; } @@ -1246,6 +1250,8 @@ semeai_trymove_and_recurse(int apos, int owl_update_goal(move, same_dragon, lunch, owlb, 1); owl_update_boundary_marks(move, owla); } + mark_goal_in_sgf(owla->goal); + mark_goal_in_sgf(owlb->goal); /* Do a recursive call to read the semeai after the move we just * tried. If dragon b was captured by the move, call @@ -1825,7 +1831,25 @@ do_owl_attack(int str, int *move, int *w current_owl_data = owl; memset(owl->safe_move_cache, 0, sizeof(owl->safe_move_cache)); - /* First see whether there is any chance to kill. */ +#if 0 + /* First test whether the dragon has escaped. */ + if (owl_escape_route(owl, 1) >= owl_escape) { + /* FIXME: We probably should make distinction in the returned + * result whether the dragon lives by making two eyes or by + * escaping. + */ + TRACE("%oVariation %d: ALIVE (escaped)\n", this_variation_number); + SGFTRACE(0, 0, "escaped"); + close_pattern_list(other, &shape_patterns); +#if USE_HASHTABLE_NG + READ_RETURN0_NG(OWL_ATTACK, str, depth - stackp); +#else + READ_RETURN0(read_result); +#endif + } +#endif + + /* Next see whether there is any chance to kill. */ if (owl_estimate_life(owl, NULL, vital_moves, &live_reason, 1, &probable_eyes, &eyemin, &eyemax)) { /* @@ -1984,24 +2008,6 @@ do_owl_attack(int str, int *move, int *w break; } /* switch (pass) */ - - /* FIXME: This block probably should reappear somewhere in this - * function. - */ -#if 0 - /* First test whether the dragon has escaped. */ - if (owl_escape_route(owl) >= 5) { - /* FIXME: We probably should make distinction in the returned - * result whether the dragon lives by making two eyes or by - * escaping. - */ - TRACE("%oVariation %d: ALIVE (escaped)\n", this_variation_number); - SGFTRACE(0, 0, "escaped"); - close_pattern_list(other, &shape_patterns); - READ_RETURN0(OWL_ATTACK, str, depth - stackp); - } -#endif - if (!moves) continue; @@ -2083,8 +2089,10 @@ do_owl_attack(int str, int *move, int *w } } + /* Test whether the move cut the goal dragon apart. */ if (moves[k].cuts[0] != NO_MOVE) owl_test_cuts(owl->goal, owl->color, moves[k].cuts); + mark_goal_in_sgf(owl->goal); if (origin == NO_MOVE) dcode = 0; @@ -2415,7 +2423,7 @@ do_owl_defend(int str, int *move, int *w int eyemin = -1; /* Lower bound on the number of eyes. */ int eyemax = -1; /* Upper bound on the number of eyes. */ struct eyevalue probable_eyes; /* Best guess of eyevalue. */ - int escape_route; + float escape_route; const char *live_reason; int move_cutoff; int xpos; @@ -2464,14 +2472,16 @@ do_owl_defend(int str, int *move, int *w * FIXME: Should introduce a new owl depth value rather than having * this hardwired value. */ - escape_route = owl_escape_route(owl); - if (stackp > 2 && escape_route >= 5) { + escape_route = owl_escape_route(owl, 0); + if (stackp > 2 && escape_route >= owl_escape) { /* FIXME: We probably should make distinction in the returned * result whether the dragon lives by making two eyes or by * escaping. */ - TRACE("%oVariation %d: ALIVE (escaped)\n", this_variation_number); - SGFTRACE(0, WIN, "escaped"); + char buf[100]; + gg_snprintf(buf, 100, "escaped: %f", escape_route); + TRACE("%oVariation %d: ALIVE (%s)\n", this_variation_number, buf); + SGFTRACE(0, WIN, buf); READ_RETURN(OWL_DEFEND, str, depth - stackp, move, 0, WIN); } @@ -2658,6 +2668,7 @@ do_owl_defend(int str, int *move, int *w * pattern explicitly asked for not doing this. */ owl_update_goal(mpos, moves[k].same_dragon, moves[k].lunch, owl, 0); + mark_goal_in_sgf(owl->goal); if (!ko_move) { int acode = do_owl_attack(str, NULL, &wid, owl, new_escape); @@ -4538,7 +4549,7 @@ owl_mark_boundary(struct local_owl_data * 2. We also add all stones belonging to the same generalized string * to the goal. If same_dragon is 1, we only add the stones if at * least one stone of the generalized string already was part of the - * goal. If same_dragon is 0, we don't add any stones at all. + * goal. If same_dragon is 0, we add stones only in very obvious cases. */ static void owl_update_goal(int pos, int same_dragon, int lunch, @@ -4550,7 +4561,6 @@ owl_update_goal(int pos, int same_dragon int do_add = 1; SGFTree *save_sgf_dumptree = sgf_dumptree; int save_count_variations = count_variations; - /* Turn off sgf output during find_superstring(). */ sgf_dumptree = NULL; @@ -4562,12 +4572,16 @@ owl_update_goal(int pos, int same_dragon find_superstring_conservative(pos, &num_stones, stones); else find_superstring(pos, &num_stones, stones); + if (0) { + gprintf("owl_update_goal: %d stones.\n", num_stones); + showboard(0); + } /* Turn sgf output back on. */ sgf_dumptree = save_sgf_dumptree; count_variations = save_count_variations; - /* If same_dragon field is 1, only add if the played stone + /* If same_dragon field is 0 or 1, only add if the played stone * clearly is in contact with the goal dragon. */ if (same_dragon <= 1) { @@ -4583,7 +4597,7 @@ owl_update_goal(int pos, int same_dragon for (k = 0; k < num_stones; k++) { if (owl->goal[stones[k]] == 0) { if (0) - TRACE("Added %1m to goal.\n", stones[k]); + gprintf("Added %1m to goal.\n", stones[k]); owl->goal[stones[k]] = 2; } } @@ -4789,6 +4803,7 @@ owl_test_cuts(char goal[BOARDMAX], int c showboard(0); componentdump(component2); } + free(conn_data); } sgf_dumptree = save_sgf_dumptree; count_variations = save_count_variations; @@ -5937,7 +5952,7 @@ owl_substantial(int str) if (trymove(libs[k], owl->color, NULL, 0)) { if (level >= 8) increase_depth_values(); - owl->goal[libs[k]] = 1; + owl_update_goal(libs[k], 2, NO_MOVE, owl, 0); } else { /* if we can't fill, try swapping with the next liberty */ @@ -5945,7 +5960,7 @@ owl_substantial(int str) && trymove(libs[k+1], owl->color, NULL, 0)) { if (level >= 8) increase_depth_values(); - owl->goal[libs[k+1]] = 1; + owl_update_goal(libs[k+1], 2, NO_MOVE, owl, 0); libs[k+1] = libs[k]; } else { @@ -6021,6 +6036,9 @@ sniff_lunch(int lunch, int *min, int *pr int other = OTHER_COLOR(board[lunch]); int libs[MAXLIBS]; int liberties; + float escape_value = 0.0; + int adjs[MAXCHAIN]; + int adj; int r; ASSERT1(IS_STONE(board[lunch]), lunch); @@ -6035,20 +6053,30 @@ sniff_lunch(int lunch, int *min, int *pr /* Do we believe this capture would help escaping? */ liberties = findlib(lunch, MAXLIBS, libs); for (r = 0; r < liberties; r++) { - if (owl->escape_values[libs[r]] > 0 - && !is_self_atari(libs[r], other)) { + if (!is_self_atari(libs[r], other)) { int k; for (k = 0; k < 8; k++) if (ON_BOARD(libs[r] + delta[k]) && owl->goal[libs[r] + delta[k]]) break; - if (k == 8) { - *min = 2; - *probable = 2; - *max = 2; - return; - } + if (k == 8) + escape_value += owl->escape_values[libs[r]]; } } + adj = chainlinks(lunch, adjs); + for (r = 0; r < adj; r++) + if (!owl->goal[adjs[r]]) { + int k; + escape_value += 0.1 * countstones(adjs[r]); + liberties = findlib(adjs[r], MAXLIBS, libs); + for (k = 0; k < liberties; k++) + escape_value += owl->escape_values[libs[k]]; + } + if (escape_value > 0.4) { + *min = 2; + *probable = 2; + *max = 2; + return; + } estimate_lunch_eye_value(lunch, min, probable, max, 1); @@ -6142,7 +6170,7 @@ eat_lunch_escape_bonus(int lunch, int *m if (adjoins) { int before, after; - before = dragon_escape(owl->goal, owl->color, owl->escape_values); + before = dragon_escape(owl->goal, owl->color, owl->escape_values, 1); /* if the escape route is already large enough to be considered * a WIN by the owl code, then no need for more */ if (before < 5) { @@ -6151,7 +6179,7 @@ eat_lunch_escape_bonus(int lunch, int *m for (n = 0; n < neighbors; n++) if (!owl->goal[adjacent[n]]) mark_string(adjacent[n], new_goal, 2); - after = dragon_escape(new_goal, owl->color, owl->escape_values); + after = dragon_escape(new_goal, owl->color, owl->escape_values, 1); /* Following is completely ad hoc. Another set of tests might * very well get better results. */ @@ -6206,6 +6234,38 @@ vital_chain(int pos) return max; } +static void +compute_owl_strength(int color, const char safe_stones[BOARDMAX], + float strength[BOARDMAX]) +{ + int pos; + int d; + float crude_weakness[MAX_DRAGONS]; + + /* The value of escaping towards a dragon is computed by its strength + * _without_ taking its own escape route into account. + */ + for (d = 0; d < number_of_dragons; d++) + crude_weakness[d] +#if 0 + = crude_dragon_weakness(dragon2[d].safety, &dragon2[d].genus, + dragon2[d].lunch != NO_MOVE, + dragon2[d].moyo_territorial_value, 0.0); +#else + = dragon2[d].weakness_pre_owl; +#endif + for (pos = BOARDMIN; pos < BOARDMAX; pos++) { + if (!ON_BOARD(pos)) + continue; + if (!safe_stones[pos] || dragon[pos].id == -1) + strength[pos] = 0.0; + else if (board[pos] == color) + strength[pos] = 100.0 * (1.0 - crude_weakness[dragon[pos].id]); + else + strength[pos] = 100.0; + } +} + static void compute_owl_escape_values(struct local_owl_data *owl) @@ -6213,46 +6273,46 @@ compute_owl_escape_values(struct local_o int pos; int m, n; char safe_stones[BOARDMAX]; - + float strength[BOARDMAX]; + get_lively_stones(OTHER_COLOR(owl->color), safe_stones); - compute_escape_influence(owl->color, safe_stones, NULL, NULL, - owl->escape_values); + for (pos = BOARDMIN; pos < BOARDMAX; pos++) { + if (!ON_BOARD(pos)) + continue; + if (board[pos] == owl->color + && owl->goal[pos]) + safe_stones[pos] = 0; + } + compute_owl_strength(owl->color, safe_stones, strength); + + /* Compute escape_value array. */ + if (!retrieve_escape_cache(owl->color, safe_stones, owl->escape_values)) { + compute_escape_influence(EMPTY, safe_stones, strength, &escape_influence); + export_escape_values(&escape_influence, owl->color, owl->escape_values); + store_escape_cache(owl->color, safe_stones, owl->escape_values); + } DEBUG(DEBUG_ESCAPE, "Owl escape values:\n"); for (m = 0; m < board_size; m++) { for (n = 0; n < board_size; n++) { pos = POS(m, n); - if (dragon[pos].color == owl->color && !owl->goal[pos]) { - if (dragon[pos].crude_status == ALIVE) - owl->escape_values[pos] = 6; - else if (dragon[pos].crude_status == UNKNOWN) { - if (DRAGON2(pos).moyo_size > 5) - owl->escape_values[pos] = 4; - else if (DRAGON2(pos).escape_route > 5) { - if (pos != DRAGON2(pos).origin) - owl->escape_values[pos] = owl->escape_values[DRAGON2(pos).origin]; - else { - int pos2; - char escape_values[BOARDMAX]; - char dragon[BOARDMAX]; - - compute_escape_influence(owl->color, safe_stones, owl->goal, NULL, - escape_values); - - for (pos2 = BOARDMIN; pos2 < BOARDMAX; pos2++) { - if (ON_BOARD(pos2)) - dragon[pos2] = is_same_dragon(pos2, pos); - } + if (board[pos] != owl->color + || owl->goal[pos] + || worm[pos].attack_codes[0] != 0) + continue; - if (dragon_escape(dragon, owl->color, escape_values) > 5) - owl->escape_values[pos] = 4; - } - } - } - } - DEBUG(DEBUG_ESCAPE, "%o%d", owl->escape_values[pos]); + if (dragon[pos].color == owl->color) { + if (dragon[pos].crude_status == ALIVE) + owl->escape_values[pos] = 2.0 * owl_escape; + else if (dragon[pos].crude_status == UNKNOWN + && pos == find_origin(pos)) + owl->escape_values[pos] = strength[pos] / 100.0; + } + else /* Newly placed stone. Give a little escape bonus. */ + owl->escape_values[pos] = 0.5; + /* DEBUG(DEBUG_ESCAPE, "%o%f ", owl->escape_values[pos]); */ } - DEBUG(DEBUG_ESCAPE, "%o\n"); + /* DEBUG(DEBUG_ESCAPE, "%o\n"); */ } } @@ -6261,20 +6321,14 @@ compute_owl_escape_values(struct local_o int owl_escape_value(int pos) { - /* FIXME: Should have a more robust mechanism to avoid - * escaping inwards. Returning a negative value is just a kludge. - */ - int k; ASSERT_ON_BOARD1(pos); if (current_owl_data->goal[pos]) - return -10; - - if (board[pos] == EMPTY) - for (k = 0; k < 8; k++) - if (ON_BOARD(pos + delta[k]) && current_owl_data->goal[pos + delta[k]]) - return -10; - - return current_owl_data->escape_values[pos]; + return 0; + if (board[pos] == current_owl_data->color) + return ((int) (current_owl_data->escape_values[pos] + 0.95)); + if (is_edge_vertex(pos)) + return ((int) (current_owl_data->escape_values[pos] + 0.4)); + return ((int) (current_owl_data->escape_values[pos] + 0.68)); } @@ -6437,10 +6491,10 @@ owl_strong_dragon(int pos) } -static int -owl_escape_route(struct local_owl_data *owl) +static float +owl_escape_route(struct local_owl_data *owl, int careful) { - return dragon_escape(owl->goal, owl->color, owl->escape_values); + return dragon_escape(owl->goal, owl->color, owl->escape_values, careful); } Index: engine/printutils.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/printutils.c,v retrieving revision 1.47 diff -u -p -r1.47 printutils.c --- engine/printutils.c 8 Jul 2004 15:32:25 -0000 1.47 +++ engine/printutils.c 2 Sep 2004 15:18:52 -0000 @@ -23,6 +23,7 @@ #include "board.h" #include "hash.h" #include "gg_utils.h" +#include "sgftree.h" #include #include @@ -509,6 +510,25 @@ simple_showboard(FILE *outfile) } +/* Adds square marks for each goal intersecion in the current sgf_dumptree. + * This function cannot be in sgf/ as it has to understand the 1-D board. + */ +void +mark_goal_in_sgf(char goal[BOARDMAX]) +{ + int pos; + SGFNode *node; + + if (!sgf_dumptree) + return; + node = sgftreeNodeCheck(sgf_dumptree); + + for (pos = BOARDMIN; pos < BOARDMAX; pos++) + if (ON_BOARD(pos) && goal[pos]) + sgfSquare(node, I(pos), J(pos)); +} + + /* * Local Variables: Index: engine/readconnect.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/readconnect.c,v retrieving revision 1.83 diff -u -p -r1.83 readconnect.c --- engine/readconnect.c 24 Aug 2004 14:39:53 -0000 1.83 +++ engine/readconnect.c 2 Sep 2004 15:18:53 -0000 @@ -1937,7 +1937,7 @@ static void clear_connection_data(struct static int trivial_connection(int str1, int str2, int *move); static int does_secure_through_ladder(int color, int move, int pos); static int ladder_capture(int str, int *move); -static int ladder_capturable(int pos, int color); +static int ladder_capturable(int pos, int color, int result_to_beat); static int no_escape_from_atari(int str); static int no_escape_from_ladder(int str); static int check_self_atari(int pos, int color_to_move); @@ -2508,7 +2508,7 @@ find_connection_moves(int str1, int str2 gprintf("%o%1M -0.1, disconnect move on edge\n", move); } - if (ladder_capturable(move, color_to_move)) { + if (ladder_capturable(move, color_to_move, WIN)) { distances[r] += 0.3; if (verbose > 0) gprintf("%o%1M +0.3, can be captured in a ladder\n", move); @@ -2699,7 +2699,7 @@ init_connection_data(int color, const ch clear_connection_data(conn); for (pos = BOARDMIN; pos < BOARDMAX; pos++) { - if (goal[pos]) { + if (ON_BOARD(pos) && goal[pos]) { if (board[pos] == color) { int origin = find_origin(pos); @@ -3285,7 +3285,7 @@ case_6_7_helper(struct connection_data * int apos = data->target; int other = OTHER_COLOR(color); - if (ladder_capturable(apos, other)) + if (ladder_capturable(apos, other, WIN)) ENQUEUE(conn, pos, apos, data->distance, 0.6, apos, NO_MOVE); else { float this_delta = 0.85 + 0.05 * gg_min(approxlib(apos, other, 5, NULL), 5); @@ -3409,16 +3409,12 @@ case_16_17_18_helper(struct connection_d * over the two diagonal links. * * (color) is the color for which we are computing connection distances, - * (target) the position we want to reach (can be set to NO_MOVE), - * (*conn) has to have the queue initialized with the positions - * from which we want to know the distances, - * (cutoff_distance) is the highest distance before we give up, - * (speculative) controls some special cases in the propagation rules - * below. + * (*conn) is the connection data that must have been initialized + * previously with a call to init_connection_data(). * * As an optimization, new points are either added directly via the ENQUEUE - * macro if the necessary test is an immediate (usually purely geometric) - * check, or if the decision is more expensive (usually depending on a + * macro, if the necessary test is an immediate (usually purely geometric) + * check, or, if the decision is more expensive (usually depending on a * ladder), it gets postponed and stored via push_connection_heap_entry() * for later evaluation. */ @@ -3511,6 +3507,8 @@ spread_connection_distances(int color, s /* Search for new vertices to propagate to. */ if (board[pos] == color) { for (k = 0; k < 4; k++) { + float dist; + /* List of relative coordinates. (pos) is marked by *. * * jef. @@ -3717,14 +3715,28 @@ spread_connection_distances(int color, s && board[apos] == EMPTY && conn->distances[bpos] > distance + 1.1 && does_secure(color, bpos, apos)) { - ENQUEUE(conn, pos, bpos, distance + 1.1, 1.0, apos, NO_MOVE); + if (conn->speculative) + ENQUEUE(conn, pos, bpos, distance + 1.1, 1.0, apos, NO_MOVE); + else { + dist = distance + 1.1; + if (board[gpos] == OTHER_COLOR(color)) + dist += 0.3; + ENQUEUE(conn, pos, bpos, dist, 1.0, apos, NO_MOVE); + } } if (board[bpos] == EMPTY && board[gpos] == EMPTY && conn->distances[bpos] > distance + 1.1 && does_secure(color, bpos, gpos)) { - ENQUEUE(conn, pos, bpos, distance + 1.1, 1.0, gpos, NO_MOVE); + if (conn->speculative) + ENQUEUE(conn, pos, bpos, distance + 1.1, 1.0, gpos, NO_MOVE); + else { + dist = distance + 1.1; + if (board[apos] == OTHER_COLOR(color)) + dist += 0.3; + ENQUEUE(conn, pos, bpos, dist, 1.0, apos, NO_MOVE); + } } /* Case 12. One-space jump to empty vertex "e" through empty @@ -3734,7 +3746,10 @@ spread_connection_distances(int color, s && board[epos] == EMPTY && conn->distances[epos] > distance + 1.1 && does_secure(color, epos, gpos)) { - ENQUEUE(conn, pos, epos, distance + 1.1, 1.0, gpos, NO_MOVE); + if (conn->speculative) + ENQUEUE(conn, pos, epos, distance + 1.1, 1.0, gpos, NO_MOVE); + else + ENQUEUE(conn, pos, epos, distance + 1.4, 1.0, gpos, NO_MOVE); } /* Case 13. One-space jump to empty vertex "e" through empty @@ -3746,8 +3761,11 @@ spread_connection_distances(int color, s && ((board[apos] == color && board[fpos] == color && board[bpos] == EMPTY) || (board[hpos] == color && board[jpos] == color - && board[ipos] == EMPTY))){ - ENQUEUE(conn, pos, epos, distance + 1.1, 1.0, gpos, NO_MOVE); + && board[ipos] == EMPTY))) { + if (conn->speculative) + ENQUEUE(conn, pos, epos, distance + 1.1, 1.0, gpos, NO_MOVE); + else + ENQUEUE(conn, pos, epos, distance + 1.4, 1.0, gpos, NO_MOVE); } /* Case 14. Diagonal connection to empty vertex "b" through @@ -3762,17 +3780,20 @@ spread_connection_distances(int color, s /* Case 15. Keima to "f" or "j" on edge. and one space jump on * first or second line. */ + dist = distance + 1.3; + if (!conn->speculative) + dist += 0.3; if (board[apos] == EMPTY && board[bpos] == EMPTY && board[gpos] == EMPTY && board[epos] == EMPTY && board[fpos] == EMPTY - && (conn->distances[fpos] > distance + 1.3 - || conn->distances[epos] > distance + 1.3) + && (conn->distances[fpos] > dist + || conn->distances[epos] > dist) && countlib(pos) >= 3 && (!ON_BOARD(cpos) || !ON_BOARD(hpos))) { - ENQUEUE(conn, pos, fpos, distance + 1.3, 1.0, NO_MOVE, NO_MOVE); - ENQUEUE(conn, pos, epos, distance + 1.3, 1.0, NO_MOVE, NO_MOVE); + ENQUEUE(conn, pos, fpos, dist, 1.0, NO_MOVE, NO_MOVE); + ENQUEUE(conn, pos, epos, dist, 1.0, NO_MOVE, NO_MOVE); } if (board[hpos] == EMPTY @@ -3780,12 +3801,12 @@ spread_connection_distances(int color, s && board[gpos] == EMPTY && board[epos] == EMPTY && board[jpos] == EMPTY - && (conn->distances[jpos] > distance + 1.3 - || conn->distances[epos] > distance + 1.3) + && (conn->distances[jpos] > dist + || conn->distances[epos] > dist) && countlib(pos) >= 3 && (!ON_BOARD(apos) || !ON_BOARD(kpos))) { - ENQUEUE(conn, pos, jpos, distance + 1.3, 1.0, NO_MOVE, NO_MOVE); - ENQUEUE(conn, pos, epos, distance + 1.3, 1.0, NO_MOVE, NO_MOVE); + ENQUEUE(conn, pos, jpos, dist, 1.0, NO_MOVE, NO_MOVE); + ENQUEUE(conn, pos, epos, dist, 1.0, NO_MOVE, NO_MOVE); } /* Case 16. Diagonal connection to empty vertex "b" through @@ -3799,10 +3820,13 @@ spread_connection_distances(int color, s * Case 18. Diagonal connection to empty vertex "b" through * empty vertex "a" or "g", with no particular properties. */ + dist = distance + 1.2; + if (!conn->speculative) + dist += 0.3; if (board[bpos] == EMPTY && (board[apos] == EMPTY || board[gpos] == EMPTY) - && conn->distances[bpos] > distance + 1.2) { - push_connection_heap_entry(conn, distance + 1.2, pos, bpos, + && conn->distances[bpos] > dist) { + push_connection_heap_entry(conn, dist, pos, bpos, case_16_17_18_helper); } @@ -3859,6 +3883,9 @@ spread_connection_distances(int color, s int bpos = pos + right + up; #if 0 int cpos = pos + 2 * right; +#endif + int dpos = pos + right - up; +#if 0 int epos = pos + 2*up; int fpos = pos + right + 2*up; #endif @@ -3877,6 +3904,13 @@ spread_connection_distances(int color, s else if (board[apos] == EMPTY) { float this_delta = 0.8 + 0.05 * gg_min(approxlib(apos, other, 6, NULL), 6); + if (!conn->speculative) { + if (ladder_capturable(apos, color, KO_B)) + this_delta += 0.2; + if (is_edge_vertex(apos) + || (board[bpos] == other && board[dpos] == other)) + this_delta += 0.1; + } ENQUEUE(conn, pos, apos, distance + this_delta, this_delta, NO_MOVE, NO_MOVE); } @@ -4112,7 +4146,7 @@ does_secure_through_ladder(int color, in int result = 0; if (trymove(move, color, NULL, NO_MOVE)) { - if (ladder_capturable(pos, OTHER_COLOR(color))) + if (ladder_capturable(pos, OTHER_COLOR(color), WIN)) result = 1; popgo(); } @@ -4154,15 +4188,15 @@ ladder_capture(int str, int *move) /* Test whether a move at pos by color can be captured in a ladder. */ static int -ladder_capturable(int pos, int color) +ladder_capturable(int pos, int color, int result_to_beat) { int result = 0; if (trymove(pos, color, NULL, NO_MOVE)) { int liberties = countlib(pos); - if (liberties == 1 && attack(pos, NULL) == WIN) + if (liberties == 1 && attack(pos, NULL) >= result_to_beat) result = 1; - else if (liberties == 2 && simple_ladder(pos, NULL) == WIN) + else if (liberties == 2 && simple_ladder(pos, NULL) >= result_to_beat) result = 1; popgo(); } @@ -4262,7 +4296,7 @@ check_self_atari(int pos, int color_to_m */ if (approxlib(pos, color_to_move, 1, &lib) >= 1 && approxlib(lib, OTHER_COLOR(color_to_move), 3, NULL) <= 2 - && ladder_capturable(lib, OTHER_COLOR(color_to_move))) + && ladder_capturable(lib, OTHER_COLOR(color_to_move), WIN)) return 1; #endif Index: engine/value_moves.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/value_moves.c,v retrieving revision 1.130 diff -u -p -r1.130 value_moves.c --- engine/value_moves.c 24 Aug 2004 15:24:30 -0000 1.130 +++ engine/value_moves.c 2 Sep 2004 15:18:55 -0000 @@ -1325,7 +1325,7 @@ examine_move_safety(int color) * rather where it's called. */ -static float +float dragon_weakness(int dr, int ignore_dead_dragons) { int dragon_safety = DRAGON2(dr).safety; @@ -2790,7 +2790,8 @@ estimate_strategical_value(int pos, int int found_one = 0; for (d = 0; d < DRAGON2(aa).neighbors; d++) - if (DRAGON(DRAGON2(aa).adjacent[d]).status == CRITICAL) + if (DRAGON(DRAGON2(aa).adjacent[d]).status == CRITICAL + && !owl_defense_move_reason_known) found_one = 1; if (found_one) break; Index: engine/worm.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/worm.c,v retrieving revision 1.63 diff -u -p -r1.63 worm.c --- engine/worm.c 31 May 2004 13:57:51 -0000 1.63 +++ engine/worm.c 2 Sep 2004 15:18:55 -0000 @@ -1710,8 +1710,6 @@ get_lively_stones(int color, char safe_s int ii; memset(safe_stones, 0, BOARDMAX * sizeof(*safe_stones)); for (ii = BOARDMIN; ii < BOARDMAX; ii++) - ASSERT1(safe_stones[ii] == 0, ii); - for (ii = BOARDMIN; ii < BOARDMAX; ii++) if (IS_STONE(board[ii]) && worm[ii].origin == ii) { if (worm[ii].attack_codes[0] == 0 Index: interface/main.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/interface/main.c,v retrieving revision 1.102 diff -u -p -r1.102 main.c --- interface/main.c 8 Jun 2004 05:06:14 -0000 1.102 +++ interface/main.c 2 Sep 2004 15:18:56 -0000 @@ -143,7 +143,12 @@ enum {OPT_BOARDSIZE = 127, OPT_MIRROR_LIMIT, OPT_METAMACHINE, OPT_RESIGN_ALLOWED, - OPT_NEVER_RESIGN + OPT_NEVER_RESIGN, + OPT_OWL_ESCAPE, + OPT_ESCAPE_EXP, + OPT_ESCAPE_COEFF, + OPT_MAX_ESCAPE, + OPT_ESC_ATT }; /* names of playing modes */ @@ -289,6 +294,11 @@ static struct gg_option const long_optio {"metamachine", no_argument, 0, OPT_METAMACHINE}, {"resign-allowed", no_argument, 0, OPT_RESIGN_ALLOWED}, {"never-resign", no_argument, 0, OPT_NEVER_RESIGN}, + {"owl-escape", required_argument, 0, OPT_OWL_ESCAPE}, + {"escape-exp", required_argument, 0, OPT_ESCAPE_EXP}, + {"escape-coeff", required_argument, 0, OPT_ESCAPE_COEFF}, + {"max-escape", required_argument, 0, OPT_MAX_ESCAPE}, + {"escape-att", required_argument, 0, OPT_ESC_ATT}, {NULL, 0, NULL, 0} }; @@ -977,7 +987,26 @@ main(int argc, char *argv[]) } return EXIT_SUCCESS; break; + + case OPT_OWL_ESCAPE: + owl_escape = atof(gg_optarg); + break; + case OPT_ESCAPE_COEFF: + escape_coeff = atof(gg_optarg); + break; + + case OPT_ESCAPE_EXP: + escape_exp = atof(gg_optarg); + break; + + case OPT_MAX_ESCAPE: + max_escape_route = atof(gg_optarg); + break; + + case OPT_ESC_ATT: + escape_default_attenuation = atof(gg_optarg); + break; /* NOTE: getopt returns '?' if an illegal option is supplied. */ case '?': Index: patterns/hoshi_keima.sgf =================================================================== RCS file: /cvsroot/gnugo/gnugo/patterns/hoshi_keima.sgf,v retrieving revision 1.7 diff -u -p -r1.7 hoshi_keima.sgf --- patterns/hoshi_keima.sgf 23 Apr 2004 01:58:54 -0000 1.7 +++ patterns/hoshi_keima.sgf 2 Sep 2004 15:18:56 -0000 @@ -39,7 +39,10 @@ GN[Hoshi+keima joseki database] ) (;B[qd]C[S]MA[ij];C[#b blocks below];W[pc]C[S]MA[mj] -(;B[od]C[S]MA[mi];W[rd]C[S]MA[mi];B[re]C[S]MA[mi];W[rc]C[S]MA[mi] +(;B[od]C[S +:-,reverse_followup(10),followup(15) +]MA[mi];W[rd]C[S] +MA[mi];B[re]C[S]MA[mi];W[rc]C[S]MA[mi] (;B[qe]C[S]MA[mi];W[nc]C[S]MA[mi];B[pf]C[S]MA[mj]) (;B[oc]C[0 @@ -351,17 +354,15 @@ W[qa]MA[mi]C[0];B[oa]MA[mi]C[U]) W[pb]MA[mi]C[0];B[oa]MA[li]C[U]) (;W[qc]MA[mi]C[0];B[rc]MA[mi]C[U];W[qa]MA[mi]C[0];B[oa]MA[mi]C[U]; -W[pb]MA[mi]C[0];B[oa]MA[mi]C[S];W[pa]MA[mi]C[0];B[tt];W[se] -MA[mi]C[0];B[tt];W[ma]MA[kb]C[0];B[lb]MA[jb]C[U];W[la]MA[jb] -C[0];B[kb]MA[jb]C[U]) +W[pb]MA[mi]C[0];B[oa]MA[mi]C[S];W[pa]MA[mi]C[0];B[tt];W[se]MA[mi]C[0]; +B[tt];W[ma]MA[kb]C[0];B[lb]MA[jb]C[U];W[la]MA[jb]C[0];B[kb]MA[jb]C[U]) (;W[se]MA[mi]C[0];B[qc]MA[mi]C[U];W[rc]MA[mi]C[0];B[sc]MA[mi]C[U] (;W[pb]MA[mi]C[0];B[ma]MA[mi]C[U];W[qa]MA[mi]C[0];B[oa]MA[mi]C[U]; W[na]MA[mi]C[0];B[oa]MA[mi]C[U]) (;W[qa]MA[mi]C[0];B[oa]MA[mi]C[U];W[pb]MA[mi]C[0];B[oa]MA[mi]C[U]; -W[pa]MA[mi]C[0];B[tt];W[ma]MA[ke]C[0];B[lb]MA[ke]C[U];W[la] -MA[jc]C[0] +W[pa]MA[mi]C[0];B[tt];W[ma]MA[ke]C[0];B[lb]MA[ke]C[U];W[la]MA[jc]C[0] (;B[kb]) (;B[ka]MA[jb]C[0]) Index: patterns/influence.db =================================================================== RCS file: /cvsroot/gnugo/gnugo/patterns/influence.db,v retrieving revision 1.13 diff -u -p -r1.13 influence.db --- patterns/influence.db 3 Mar 2004 22:14:12 -0000 1.13 +++ patterns/influence.db 2 Sep 2004 15:18:56 -0000 @@ -32,374 +32,15 @@ # , - point which influence can't pass through # # -# Two different classes of patterns are used here. +# One classe of patterns is used here: # -# E - Enhance influence. # I - Invasion points. attribute_map value_only goal_elements none -# callback_data is pattern class dependent for this database - - -######################## -# -# Enhancement patterns -# -######################## - -callback_data O - - -Pattern Enhance1 - -o..? -O.*. -O... -o.?? - -:8,E,value(30) - - -Pattern Enhance2 - -oO.... -oo..*. -oO.... - -:-,E,value(30) - - -Pattern Enhance3 - -oO..... -ooO..*. -oO..... - -:-,E,value(30) - - -Pattern Enhance4 - -ooO.... -ooo..*. -oO..... - -:8,E,value(20) - - -Pattern Enhance5 - -oO..... -oO...*. -oo..... -oO..... - -:8,E,value(30) - - -Pattern Enhance6 - -oO..... -O....*. -oo..... -oO..... - -:8,E,value(20) - - -Pattern Enhance7 - -oO...... -ooO...*. -oo...... -oO...... - -:8,E,value(20) - - -Pattern Enhance8 -# gf Corrected symmetry. (3.1.23) - -oO.... -o...*. -o..... -oO.... - -:8,E,value(15) - - -Pattern Enhance9 - -oO..... -oO...*. -o...... -O...... - -:8,E,value(20) - - -Pattern Enhance10 - -oO.... -O...*. -o..... -O..... - -:8,E,value(30) - - -Pattern Enhance11 - -oO.... -o...*. -O..... -O..... - -:8,E,value(30) - - -Pattern Enhance12 - -oO..... -o....*. -oO..... -O...... - -:8,E,value(30) - - -Pattern Enhance13 - -oO..... -oo...*. -ooO.... -O...... - -:8,E,value(30) - - -Pattern Enhance14 - -oO.... -o...*. -o..... -O..... - -:8,E,value(20) - - -Pattern Enhance15 - -??.....?? -oo.....oo -oo.O...oo -......*.. -......... ---------- - -:8,E,value(30) - -??.....?? -ac.....oo -bd.O...oo -......*.. -......... ---------- - -;o_somewhere(a,b,c,d) - - -Pattern Enhance16 - -oo..| -oO.*| -oo..| - -:-,E,value(20) - - -Pattern Enhance17 - -oo...| -oO.*.| -oo...| - -:-,E,value(20) - - -Pattern Enhance18 - -oo... -oO... -...*. -..... ------ - -:8,E,value(20) - - -Pattern Enhance19 - -oo... -oo.*. -oO... -..... -..... -..... ------ - -:8,E,value(20) - - -Pattern Enhance20 - -|..ooooo -|....... -|..*.... -|....... -|....... -|..O.oo. -|....oo. -|....... -|....... -+------- - -:8,E,value(50) - -|..ooooo -|....... -|..*.... -|....... -|....... -|..O.ac. -|....bd. -|....... -|....... -+------- - -;o_somewhere(a,b,c,d) - - -Pattern Enhance21 - -|.........o -|.........o -|..O.oo...o -|....oo.*.o -|.......... -|.......... -+---------- - -:8,E,value(50) - -|.........o -|.........o -|..O.ac...o -|....bd.*.o -|.......... -|.......... -+---------- - -;o_somewhere(a,b,c,d) - - -Pattern Enhance22 - -|..ooooo -|....... -|..*.... -|....... -|....... -|....oo. -|..O.oo. -|....... -|....... -+------- - -:8,E,value(40) - -|..ooooo -|....... -|..*.... -|....... -|....... -|....ac. -|..O.bd. -|....... -|....... -+------- - -;o_somewhere(a,b,c,d) - - -Pattern Enhance23 - -|.........o -|.........o -|....oo...o -|..O.oo.*.o -|.......... -|.......... -+---------- - -:8,E,value(40) - -|.........o -|.........o -|....ac...o -|..O.bd.*.o -|.......... -|.......... -+---------- - -;o_somewhere(a,b,c,d) - - -Pattern Enhance24 - -??.... -??O... -.O..*. -...... -...... ------- - -:8,E,value(30) - - -Pattern Enhance25 - -+------ -|...... -|...... -|.....o -|.o.X.o -|...... -|..O.*. -|...... -|..oo.. -|..oo.. - -:8,E,value(15) - - -Pattern Enhance26 - -+------ -|...... -|...... -|.....o -|.*.X.o -|...... -|..O.o. -|...... -|..oo.. -|..oo.. - -:8,E,value(15) +callback_data none ######################## @@ -408,7 +49,6 @@ Pattern Enhance26 # ######################## -callback_data none Pattern Invade1 @@ -448,7 +88,7 @@ Pattern Invade3 |..... |..O.. -:8,sIe,value(0.2) +:8,sI,value(0.2) Pattern Invade4 @@ -464,7 +104,7 @@ Pattern Invade4 |..... |..oO. -:8,sIe,value(0.2) +:8,sI,value(0.2) Pattern Invade4b @@ -480,7 +120,7 @@ Pattern Invade4b |..... |..ooO -:8,sIe,value(0.2) +:8,sI,value(0.2) Pattern Invade4c @@ -496,7 +136,7 @@ Pattern Invade4c |.....? |..oooO -:8,sIe,value(0.2) +:8,sI,value(0.2) Pattern Invade5 @@ -510,7 +150,7 @@ Pattern Invade5 |..... |..O.. -:8,Ie,value(0.2) +:8,I,value(0.2) Pattern Invade6 @@ -524,7 +164,7 @@ Pattern Invade6 |..... |..oO. -:8,Ie,value(0.2) +:8,I,value(0.2) Pattern Invade6b @@ -538,7 +178,7 @@ Pattern Invade6b |..... |..ooO -:8,Ie,value(0.2) +:8,I,value(0.2) Pattern Invade6c @@ -552,7 +192,7 @@ Pattern Invade6c |.....? |..oooO -:8,Ie,value(0.2) +:8,I,value(0.2) Pattern Invade7a @@ -565,7 +205,7 @@ O.....o ....... o.....o -:\,Ie,value(0.2) +:\,I,value(0.2) Pattern Invade7b @@ -578,7 +218,7 @@ Pattern Invade7b ....... ....... -:\,Ie,value(0.2) +:\,I,value(0.2) Pattern Invade7c @@ -592,7 +232,7 @@ Pattern Invade7c ....... ....... -:-,Ie,value(0.2) +:-,I,value(0.2) Pattern Invade7d @@ -605,7 +245,7 @@ O.....o ....... ------- -:8,Ie,value(0.2) +:8,I,value(0.2) Pattern Invade8 @@ -681,7 +321,7 @@ Pattern Invade13 |...O.. |...... -:8,Ie,value(0.2) +:8,I,value(0.2) Pattern Invade14 @@ -694,7 +334,7 @@ Pattern Invade14 |...O.. |...... -:8,Ie,value(0.2) +:8,I,value(0.2) Pattern Invade15 @@ -707,7 +347,7 @@ Pattern Invade15 |...... |...... -:8,Ie,value(0.4) +:8,I,value(0.4) Pattern Invade16 Index: patterns/mkpat.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/patterns/mkpat.c,v retrieving revision 1.137 diff -u -p -r1.137 mkpat.c --- patterns/mkpat.c 18 Aug 2004 14:10:37 -0000 1.137 +++ patterns/mkpat.c 2 Sep 2004 15:18:57 -0000 @@ -296,6 +296,7 @@ static struct autohelper_func autohelper {"does_attack", 2, 0, 1.00, "does_attack(%s, %s)"}, {"attack", 1, 0, 1.00, "ATTACK_MACRO(%s)"}, {"defend", 1, 0, 1.00, "DEFEND_MACRO(%s)"}, + {"weakness", 1, 0, 0.01, "dragon_weakness(%s, 0)"}, {"weak", 1, 0, 0.01, "dragon_weak(%s)"}, {"safe_xmove", 1, 0, 1.00, "safe_move(%s, OTHER_COLOR(color))"}, {"safe_omove", 1, 0, 1.00, "safe_move(%s, color)"}, @@ -1554,8 +1555,9 @@ parse_constraint_or_action(char *line, f break; } fprintf(stderr, - "%s(%d) : error : Syntax error in constraint or action, '(' expected (pattern %s).\n", - current_file, current_line_number, pattern_names[patno]); + "%s(%d) : error : Syntax error in constraint or action, '(' expected (pattern %s, autohelper %s).\n", + current_file, current_line_number, pattern_names[patno], + autohelper_functions[n].name); fatal_errors++; return; } Index: patterns/owl_defendpats.db =================================================================== RCS file: /cvsroot/gnugo/gnugo/patterns/owl_defendpats.db,v retrieving revision 1.120 diff -u -p -r1.120 owl_defendpats.db --- patterns/owl_defendpats.db 22 Aug 2004 20:48:36 -0000 1.120 +++ patterns/owl_defendpats.db 2 Sep 2004 15:18:59 -0000 @@ -6068,9 +6068,9 @@ o.*. obo? a..X o.*. -?x.O +?x.c -;owl_escape_value(a) + owl_escape_value(b) > 0 +;owl_goal_dragon(c) && owl_escape_value(a) + owl_escape_value(b) > 0 Pattern D1343b Index: patterns/patterns.db =================================================================== RCS file: /cvsroot/gnugo/gnugo/patterns/patterns.db,v retrieving revision 1.129 diff -u -p -r1.129 patterns.db --- patterns/patterns.db 22 Aug 2004 13:07:31 -0000 1.129 +++ patterns/patterns.db 2 Sep 2004 15:18:59 -0000 @@ -3329,7 +3329,7 @@ X.*.. ..... ----- -;!weak(a) +;weakness(a)<0.7 Pattern ES3b @@ -8003,7 +8003,7 @@ Pattern CB256 .*. ..X -:8,OXe +:8,OXea Pattern CB257 Index: regression/9x9.tst =================================================================== RCS file: /cvsroot/gnugo/gnugo/regression/9x9.tst,v retrieving revision 1.8 diff -u -p -r1.8 9x9.tst --- regression/9x9.tst 23 Apr 2004 16:11:43 -0000 1.8 +++ regression/9x9.tst 2 Sep 2004 15:19:00 -0000 @@ -1,6 +1,7 @@ +# Added E3. /ab loadsgf games/nngs/whitemouse-gnugo-3.5.2-200312052122.sgf 4 10 reg_genmove black -#? [E5|E6] +#? [E5|E6|E3] loadsgf games/nngs/whitemouse-gnugo-3.5.2-200312052122.sgf 6 20 reg_genmove black Index: regression/owl.tst =================================================================== RCS file: /cvsroot/gnugo/gnugo/regression/owl.tst,v retrieving revision 1.87 diff -u -p -r1.87 owl.tst --- regression/owl.tst 23 Apr 2004 16:11:58 -0000 1.87 +++ regression/owl.tst 2 Sep 2004 15:19:00 -0000 @@ -307,7 +307,7 @@ loadsgf games/incident114.sgf 82 # M5 is clearly inferior, as is L5. loadsgf games/incident291.sgf 86 79 owl_defend K4 -#? [1 (M6|L6)] +#? [1 (M6|L6|L7)] # incident 297 loadsgf games/incident297.sgf 95 @@ -916,7 +916,7 @@ loadsgf games/marginal_ko.sgf # See also strategy2:100. loadsgf games/strategy26.sgf 257 254 owl_defend O13 -#? [1 (M16|O14|N16|N17)] +#? [1 (M16|O14|N16|N17|N13)] loadsgf games/owl34.sgf 211 255 owl_defend D13 Index: regression/view.pike =================================================================== RCS file: /cvsroot/gnugo/gnugo/regression/view.pike,v retrieving revision 1.12 diff -u -p -r1.12 view.pike --- regression/view.pike 24 Aug 2004 14:39:53 -0000 1.12 +++ regression/view.pike 2 Sep 2004 15:19:00 -0000 @@ -508,7 +508,6 @@ class RegressionViewer GTK.Widget data_widget; GTK.ScrolledWindow scrolled_data_window; - array(GTK.Window) all_windows = ({}); GTK.Image gtk_image; GDK.Image gdk_image; GTK.Clist clist; @@ -517,6 +516,8 @@ class RegressionViewer mapping(string:array(string)) worms = ([]); mapping(string:array(string)) dragons = ([]); + int worms_initialized = 0; + int dragons_initialized = 0; string name; string result; @@ -541,13 +542,26 @@ class RegressionViewer int boardsize = (int) send_command("query_boardsize"); on_board_click_callback = callback; - foreach (send_command("worm_stones") / "\n", string worm) - worms[(worm / " ")[0]] = worm / " " - ({""}); + setup_board(boardsize); + + scrolled_data_window = GTK.ScrolledWindow(); + scrolled_data_window->set_policy(GTK.POLICY_AUTOMATIC, + GTK.POLICY_AUTOMATIC); + + clist = GTK.Clist(3); + scrolled_data_window->add(clist); +// thread_create(handle_testcase); + handle_testcase(); + } + + static void setup_board(int boardsize) + { goban = Goban(boardsize, 600); goban->add_stones("WHITE", send_command("list_stones white") / " "); goban->add_stones("BLACK", send_command("list_stones black") / " "); Image.Image im = goban->draw_board(); + gdk_image = GDK.Image(0)->set(im); gtk_image = GTK.Image(gdk_image); goban_widget = GTK.EventBox()->add(gtk_image); @@ -557,20 +571,33 @@ class RegressionViewer button_pressed_on_board); goban_widget->signal_connect_new("key_press_event", key_pressed_on_board); + } - scrolled_data_window = GTK.ScrolledWindow(); - scrolled_data_window->set_policy(GTK.POLICY_AUTOMATIC, - GTK.POLICY_AUTOMATIC); - all_windows = ({}); - - clist = GTK.Clist(3); - scrolled_data_window->add(clist); - -// thread_create(handle_testcase); - handle_testcase(); + void new_testcase(array(string) fulltest_, string testcase_command_) + { + werror("Loading new testcase.\n"); + worms_initialized = 0; + dragons_initialized = 0; + result = ""; + worms = ([]); + dragons = ([]); + + fulltest = fulltest_; + testcase_command = testcase_command_; + + load_testcase(); + werror("%s\n", send_command("showboard")); + int boardsize = (int) send_command("query_boardsize"); + + werror("Loaded new testcase.\n"); + goban = Goban(boardsize, 600); + goban->add_stones("WHITE", send_command("list_stones white") / " "); + goban->add_stones("BLACK", send_command("list_stones black") / " "); + redraw_board(); } + static void load_testcase() { foreach(fulltest, string testline) { @@ -580,7 +607,7 @@ class RegressionViewer } } - static void handle_testcase() + void handle_testcase() { traces = ({}); engine->trace_callback = collect_traces; @@ -600,6 +627,21 @@ class RegressionViewer traces += ({s}); } + void get_dragons() + { + foreach (send_command("dragon_stones") / "\n", string dragon) + dragons[(dragon / " ")[0]] = dragon / " " - ({""}); + dragons_initialized = 1; + } + + static void get_worms() + { + foreach (send_command("worm_stones") / "\n", string worm) + worms[(worm / " ")[0]] = worm / " " - ({""}); + worms_initialized = 1; + } + + void add_markup(int mode) { goban->clear_markup(); @@ -631,6 +673,8 @@ class RegressionViewer if (parent->dragon_status_button->get_active()) { + if (!dragons_initialized) + get_dragons(); foreach (dragons; string dragon; array(string) stones) { string status = get_worm_or_dragon_data("dragon", "status", @@ -642,6 +686,8 @@ class RegressionViewer } else if (parent->dragon_safety_button->get_active()) { + if (!dragons_initialized) + get_dragons(); foreach (dragons; string dragon; array(string) stones) { string safety = get_worm_or_dragon_data("dragon", "safety", @@ -653,6 +699,8 @@ class RegressionViewer } else if (parent->worm_status_button->get_active()) { + if (!worms_initialized) + get_worms(); foreach (worms; string worm; array(string) stones) { string attack = get_worm_or_dragon_data("worm", "attack_code", @@ -1097,9 +1145,7 @@ class RegressionViewer string send_command(string command) { string result; - //all_windows->set_cursor(GDK.Watch); result = engine->send_command(command)->text; - //all_windows->set_cursor(GDK.TopLeftArrow); return result; } @@ -1175,6 +1221,8 @@ class Controller GTK.Notebook data_notebook; GTK.Notebook selector_notebook; + GTK.Widget testcase_label; + GTK.RadioButton worm_data_button; GTK.RadioButton dragon_data1_button; GTK.RadioButton dragon_data2_button; @@ -1216,9 +1264,8 @@ class Controller GTK.CheckButton sgf_traces_button, sgf_viewer_button; GTK.Entry sgf_filename_entry, sgf_viewer_entry; GTK.Table sgf_stuff; - GTK.Button new_engine_button; - GTK.Entry engine_path_entry; - GTK.Entry engine_name_entry; + GTK.Button new_testcase_button, new_engine_button; + GTK.Entry new_testcase_entry, engine_path_entry, engine_name_entry; string delta_territory_move = "PASS"; string move_influence_move = "PASS"; @@ -1245,8 +1292,8 @@ class Controller } testcase_name = testcase; - GTK.Widget testcase_label = (GTK.Label(full_testcase * "\n") - ->set_justify(GTK.JUSTIFY_LEFT)); + testcase_label = (GTK.Label(full_testcase * "\n") + ->set_justify(GTK.JUSTIFY_LEFT)); main_window = GTK.Window(GTK.WindowToplevel); controller_notebook = GTK.Notebook(); @@ -1423,7 +1470,11 @@ class Controller ->attach_defaults(sgf_viewer_button, 0, 1, 1, 2) ->attach_defaults(sgf_viewer_entry, 1, 2, 1, 2); - + new_testcase_entry = GTK.Entry(); + new_testcase_entry->set_text("owl:1"); + new_testcase_entry->set_editable(1); + new_testcase_button = GTK.Button("Load new testcase"); + new_testcase_button->signal_connect_new("clicked", new_testcase); engine_path_entry = GTK.Entry(); engine_path_entry->set_text("../interface/gnugo"); engine_path_entry->set_editable(1); @@ -1508,7 +1559,9 @@ class Controller if (single_window_mode) engines_page->pack_start(engine_name_entry, 0, 0, 0); engines_page->pack_start(GTK.Alignment(1.0, 0.0, 0.0, 0.0) - ->add(new_engine_button), 0, 0, 0); + ->add(new_engine_button), 0, 0, 0) + ->pack_start(new_testcase_entry, 0, 0, 0) + ->pack_start(new_testcase_button, 0, 0, 0); controller_notebook->append_page(engines_page->set_border_width(12), GTK.Label("engines")); @@ -1758,8 +1811,7 @@ class Controller else { c1 = sprintf("analyze_semeai %s %s", - first_vertex, - vertex); + first_vertex, vertex); c2 = sprintf("analyze_semeai %s %s", vertex, first_vertex); // FIXME: We should use a semeai node counter rather @@ -1777,6 +1829,24 @@ class Controller } } + + static void new_testcase() + { + string new_testcase = new_testcase_entry->get_text(); + werror("Trying to load new testcase %s.", new_testcase); + if (!excerpt_testcase(new_testcase, viewers[0]->engine)) + { + werror("Failed to load testcase.\n"); + return; + } + testcase_name = new_testcase; + main_window->set_title(testcase_name); + testcase_label->set_text(full_testcase * "\n"); + viewers->new_testcase(full_testcase, testcase_command); + viewers->handle_testcase(); + } + + // The engine parameter is only needed to find out the color to // move when an sgf file is given. Since there can be multiple // viewers we shouldn't use this engine object for anything else