Index: ChangeLog =================================================================== RCS file: /cvsroot/gnugo/gnugo/ChangeLog,v retrieving revision 1.261 diff -u -r1.261 ChangeLog --- ChangeLog 7 Apr 2002 13:47:20 -0000 1.261 +++ ChangeLog 7 Apr 2002 14:47:22 -0000 @@ -2,6 +2,10 @@ -- ChangeLog ------------------------------------------------------------------------- +- major aftermath code revisions +- doing_scoring contingent revisions in influence_callback(), shapes(), + shapes_callback(), connection_value(), estimate_strategical_value(), + high_handicap_helper(), and reinforce_helper() - analyze_neighbor() in filllib.c revised - size of persistent owl cache increased from 100 to 150 entires - bugfix in owl_confirm_safety() Index: engine/aftermath.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/aftermath.c,v retrieving revision 1.23 diff -u -r1.23 aftermath.c --- engine/aftermath.c 7 Mar 2002 05:35:28 -0000 1.23 +++ engine/aftermath.c 7 Apr 2002 14:47:23 -0000 @@ -86,7 +86,6 @@ int under_control[BOARDMAX], int do_capture_dead_stones) { - int m, n; int k; int other = OTHER_COLOR(color); int distance[BOARDMAX]; @@ -107,105 +106,101 @@ reading_hotspots(reading_hotspot); /* As a preparation we compute a distance map to the invincible strings. */ - for (m = 0; m < board_size; m++) - for (n = 0; n < board_size; n++) { - pos = POS(m, n); - if (board[pos] == color && worm[pos].unconditional_status == ALIVE) - distance[pos] = 0; - else if (!do_capture_dead_stones - && board[pos] == other - && worm[pos].unconditional_status == DEAD) - distance[pos] = 0; - else - distance[pos] = -1; - } - + for (pos = BOARDMIN; pos < BOARDMAX; pos++) { + if (!ON_BOARD(pos)) + continue; + else if (board[pos] == color && worm[pos].unconditional_status == ALIVE) + distance[pos] = 0; + else if (!do_capture_dead_stones + && board[pos] == other + && worm[pos].unconditional_status == DEAD) + distance[pos] = 0; + else + distance[pos] = -1; + } + d = 0; do { something_found = 0; - for (m = 0; m < board_size; m++) - for (n = 0; n < board_size; n++) { - pos = POS(m, n); - if (distance[pos] == -1) { - for (k = 0; k < 4; k++) { - int pos2 = pos + delta[k]; - if (!ON_BOARD(pos2)) - continue; - if ((d == 0 || board[pos2] == EMPTY) - && distance[pos2] == d) { - if (d > 0 && board[pos] == other) { - distance[pos] = d + 1; - if (closest_opponent == NO_MOVE) - closest_opponent = pos; - } - else if (d > 0 && board[pos] == color) { - distance[pos] = d + 1; - if (closest_own == NO_MOVE) - closest_own = pos; - } - else if (board[pos] == EMPTY) { - distance[pos] = d + 1; - something_found = 1; - } - break; + for (pos = BOARDMIN; pos < BOARDMAX; pos++) { + if (ON_BOARD(pos) && distance[pos] == -1) { + for (k = 0; k < 4; k++) { + int pos2 = pos + delta[k]; + if (!ON_BOARD(pos2)) + continue; + if ((d == 0 || board[pos2] == EMPTY) + && distance[pos2] == d) { + if (d > 0 && board[pos] == other) { + distance[pos] = d + 1; + if (closest_opponent == NO_MOVE) + closest_opponent = pos; + } + else if (d > 0 && board[pos] == color) { + distance[pos] = d + 1; + if (closest_own == NO_MOVE) + closest_own = pos; + } + else if (board[pos] == EMPTY) { + distance[pos] = d + 1; + something_found = 1; } + break; } } } + } d++; } while (something_found); if (under_control) { - for (m = 0; m < board_size; m++) - for (n = 0; n < board_size; n++) { - pos = POS(m, n); - if (distance[pos] == -1) - under_control[pos] = 0; - else - under_control[pos] = 1; - } + for (pos = BOARDMIN; pos < BOARDMAX; pos++) { + if (!ON_BOARD(pos)) + continue; + else if (distance[pos] == -1) + under_control[pos] = 0; + else + under_control[pos] = 1; + } } if (debug & DEBUG_AFTERMATH) { - for (m = 0; m < board_size; m++) { - for (n = 0; n < board_size; n++) { - pos = POS(m, n); - if (distance[pos] > 0) - printf("%2d", distance[pos]); - else if (distance[pos] == 0) { - if (board[pos] == WHITE) - printf(" o"); - else if (board[pos] == BLACK) - printf(" x"); - else - printf(" ?"); - } - else { - if (board[pos] == WHITE) - printf(" O"); - else if (board[pos] == BLACK) - printf(" X"); - else - printf(" ."); - } + for (pos = BOARDMIN; pos < BOARDMAX; pos++) { + if (!ON_BOARD(pos)) + continue; + else if (distance[pos] > 0) + fprintf(stderr, "%2d", distance[pos]); + else if (distance[pos] == 0) { + if (board[pos] == WHITE) + gprintf(" o"); + else if (board[pos] == BLACK) + gprintf(" x"); + else + gprintf(" ?"); + } + else { + if (board[pos] == WHITE) + gprintf(" O"); + else if (board[pos] == BLACK) + gprintf(" X"); + else + gprintf(" ."); } - printf("\n"); } + gprintf("\n"); gprintf("Closest opponent %1m", closest_opponent); if (closest_opponent != NO_MOVE) - gprintf(", distance %d\n", - distance[closest_opponent]); + gprintf(", distance %d\n", distance[closest_opponent]); else gprintf("\n"); - + gprintf("Closest own %1m", closest_own); if (closest_own != NO_MOVE) gprintf(", distance %d\n", distance[closest_own]); else gprintf("\n"); } - + /* Case 0. This is a special measure to avoid a certain kind of * tactical reading inefficiency. * @@ -220,30 +215,32 @@ */ best_scoring_move = NO_MOVE; best_score = 5; - for (m = 0; m < board_size; m++) { - for (n = 0; n < board_size; n++) { - int libs; - pos = POS(m, n); - if (distance[pos] == 0 - || board[pos] != EMPTY) - continue; - - libs = approxlib(pos, color, 3, NULL); - if (libs < 3) - continue; - - for (k = 0; k < 4; k++) { - int dir = delta[k]; - if (!ON_BOARD(pos - dir) - && board[pos + dir] == color - && libs > countlib(pos + dir) - && (DRAGON2(pos + dir).safety == INVINCIBLE - || DRAGON2(pos + dir).safety == STRONGLY_ALIVE)) { - int score = 20 * (owl_hotspot[pos] + reading_hotspot[pos]); - if (score > best_score) { - best_score = score; - best_scoring_move = pos; - } + for (pos = BOARDMIN; pos < BOARDMAX; pos++) { + int libs; + if (board[pos] != EMPTY + || distance[pos] == 0) + continue; + + libs = approxlib(pos, color, 3, NULL); + if (libs < 3) + continue; + + if (is_self_atari(pos, other)) + continue; + + for (k = 0; k < 4; k++) { + int dir = delta[k]; + if (!ON_BOARD(pos - dir) + && board[pos + dir] == color + && (libs > countlib(pos + dir) + || (libs >= 4 + && libs == countlib(pos + dir))) + && (DRAGON2(pos + dir).safety == INVINCIBLE + || DRAGON2(pos + dir).safety == STRONGLY_ALIVE)) { + int score = 20 * (owl_hotspot[pos] + reading_hotspot[pos]); + if (score > best_score) { + best_score = score; + best_scoring_move = pos; } } } @@ -290,61 +287,59 @@ best_score = 0; best_scoring_move = move; - for (m = 0; m < board_size; m++) - for (n = 0; n < board_size; n++) { - int score = 0; - int move_ok = 0; - pos = POS(m, n); - if (distance[pos] != 1) + for (pos = BOARDMIN; pos < BOARDMAX; pos++) { + int score = 0; + int move_ok = 0; + if (!ON_BOARD(pos) || distance[pos] != 1) + continue; + mark++; + for (k = 0; k < 4; k++) { + int pos2 = pos + delta[k]; + if (!ON_BOARD(pos2)) continue; - mark++; - for (k = 0; k < 4; k++) { - int pos2 = pos + delta[k]; - if (!ON_BOARD(pos2)) - continue; - if (distance[pos2] < 1) - score--; - else if (board[pos2] == EMPTY) - score++; - else if (mx[pos2] == mark) - score--; + if (distance[pos2] < 1) + score--; + else if (board[pos2] == EMPTY) + score++; + else if (mx[pos2] == mark) + score--; + else { + if (board[pos2] == color) { + move_ok = 1; + score += 7; + if (countstones(pos2) > 2) + score++; + if (countstones(pos2) > 4) + score++; + if (countlib(pos2) < 4) + score++; + if (countlib(pos2) < 3) + score++; + } else { - if (board[pos2] == color) { - move_ok = 1; - score += 7; - if (countstones(pos2) > 2) - score++; - if (countstones(pos2) > 4) - score++; - if (countlib(pos2) < 4) - score++; - if (countlib(pos2) < 3) - score++; - } - else { - int deltalib = (approxlib(pos, other, MAXLIBS, NULL) - - countlib(pos2)); - move_ok = 1; + int deltalib = (approxlib(pos, other, MAXLIBS, NULL) + - countlib(pos2)); + move_ok = 1; + score++; + if (deltalib >= 0) + score++; + if (deltalib > 0) score++; - if (deltalib >= 0) - score++; - if (deltalib > 0) - score++; - } - mark_string(pos2, mx, mark); } + mark_string(pos2, mx, mark); } - if (is_suicide(pos, other)) - score -= 3; - - if (0) - gprintf("Score %1m = %d\n", pos, score); - - if (move_ok && score > best_score) { - best_score = score; - best_scoring_move = pos; - } } + if (is_suicide(pos, other)) + score -= 3; + + if (0) + gprintf("Score %1m = %d\n", pos, score); + + if (move_ok && score > best_score) { + best_score = score; + best_scoring_move = pos; + } + } move = best_scoring_move; } @@ -371,152 +366,169 @@ * and with at least one alive but not invincible stone adjacent or * diagonal. */ - for (m = 0; m < board_size; m++) - for (n = 0; n < board_size; n++) { - int eyespace_neighbors = 0; - int own_neighbors = 0; - int own_diagonals = 0; - int opponent_dragons = 0; - int own_worms = 0; - int safety = UNKNOWN; - int bonus = 0; - int mx[BOARDMAX]; - pos = POS(m, n); - score[pos] = 0; + for (pos = BOARDMIN; pos < BOARDMAX; pos++) { + int eyespace_neighbors = 0; + int own_neighbors = 0; + int own_diagonals = 0; + int opponent_dragons = 0; + int own_worms = 0; + int safety = UNKNOWN; + int bonus = 0; + int mx[BOARDMAX]; + score[pos] = 0; - if (board[pos] != EMPTY || distance[pos] != -1) - continue; + if (board[pos] != EMPTY || distance[pos] != -1) + continue; - memset(mx, 0, sizeof(mx)); - - for (k = 0; k < 8; k++) { - int pos2 = pos + delta[k]; - if (!ON_BOARD(pos2)) - continue; + memset(mx, 0, sizeof(mx)); + + for (k = 0; k < 8; k++) { + int pos2 = pos + delta[k]; + if (!ON_BOARD(pos2)) + continue; + + if (board[pos2] == EMPTY) { + if (k < 4) + eyespace_neighbors++; + continue; + } + + if (board[pos2] == other) { + int origin = dragon[pos2].origin; - if (board[pos2] == EMPTY) { - if (k < 4) + if (k < 4) { + if (dragon[pos2].matcher_status == ALIVE) { + safety = DEAD; + break; + } + else if (!mx[origin]) { eyespace_neighbors++; - continue; - } - - if (board[pos2] == other) { - int origin = dragon[pos2].origin; - - if (k < 4) { - if (dragon[pos2].matcher_status == ALIVE) { - safety = DEAD; - break; - } - else if (!mx[origin]) { - eyespace_neighbors++; - opponent_dragons++; - } + opponent_dragons++; } + } - if (!mx[origin] && dragon[pos2].matcher_status == DEAD) { + if (!mx[origin] && dragon[pos2].matcher_status == DEAD) { + bonus++; + if (k < 4 + && countlib(pos2) <= 2 + && countstones(pos2) >= 3) bonus++; - if (k < 4 - && countlib(pos2) <= 2 - && countstones(pos2) >= 3) - bonus++; - - if (k < 4 && countlib(pos2) == 1) - bonus += 3; - } - mx[origin] = 1; + + if (k < 4 && countlib(pos2) == 1) + bonus += 3; } - else if (board[pos2] == color) { - dragons[pos] = pos2; - - if (safety == UNKNOWN && dragon[pos2].matcher_status == ALIVE) - safety = ALIVE; - - if (DRAGON2(pos2).safety == INVINCIBLE) - safety = INVINCIBLE; + mx[origin] = 1; + } + else if (board[pos2] == color) { + dragons[pos] = pos2; + + if (safety == UNKNOWN && dragon[pos2].matcher_status == ALIVE) + safety = ALIVE; + + if (DRAGON2(pos2).safety == INVINCIBLE) + safety = INVINCIBLE; + + if (k < 4) { + int apos = worm[pos2].origin; - if (k < 4) { - int apos = worm[pos2].origin; - - if (!mx[apos]) { - own_worms++; - if (countstones(apos) == 1) - bonus += 2; - mx[apos] = 1; + if (!mx[apos]) { + own_worms++; + if (countstones(apos) == 1) + bonus += 2; + if (countlib(apos) < 6 + && approxlib(pos, color, 5, NULL) < countlib(apos)) + bonus -= 5; + mx[apos] = 1; + } + + if (countlib(apos) <= 2) { + int r; + int important = 0; + int safe_atari = 0; + for (r = 0; r < 4; r++) { + int d = delta[r]; + if (!ON_BOARD(apos+d)) + continue; + if (board[apos+d] == other + && dragon[apos+d].matcher_status == DEAD) + important = 1; + else if (board[apos+d] == EMPTY + && !is_self_atari(apos+d, other)) + safe_atari = 1; } - - if (countlib(apos) <= 2) { - int r; - int important = 0; - int safe_atari = 0; - for (r = 0; r < 4; r++) { - int d = delta[r]; - if (!ON_BOARD(apos+d)) - continue; - if (board[apos+d] == other - && dragon[apos+d].matcher_status == DEAD) - important = 1; - else if (board[apos+d] == EMPTY - && !is_self_atari(apos+d, other)) - safe_atari = 1; - } - if (approxlib(pos, color, 3, NULL) > 2) { - bonus++; - if (important) { + if (approxlib(pos, color, 3, NULL) > 2) { + bonus++; + if (important) { + bonus += 2; + if (safe_atari) bonus += 2; - if (safe_atari) - bonus += 2; - } } } - - own_neighbors++; } - else - own_diagonals++; + + own_neighbors++; } + else + own_diagonals++; } - if (safety == DEAD || safety == UNKNOWN - || eyespace_neighbors == 0 - || (own_neighbors + own_diagonals) == 0) - continue; - - score[pos] = 4 * eyespace_neighbors + bonus; - if (safety == INVINCIBLE) { - score[pos] += own_neighbors; - if (own_neighbors < 2) - score[pos] += own_diagonals; - if (own_worms > 1 && eyespace_neighbors >= 1) - score[pos] += 10 + 5 * (own_worms - 2); - } - else if (eyespace_neighbors > 2) + } + if (safety == DEAD || safety == UNKNOWN + || eyespace_neighbors == 0 + || (own_neighbors + own_diagonals) == 0) + continue; + + if (bonus < 0) + bonus = 0; + + score[pos] = 4 * eyespace_neighbors + bonus; + if (safety == INVINCIBLE) { + score[pos] += own_neighbors; + if (own_neighbors < 2) score[pos] += own_diagonals; - - /* Splitting bonus. */ - if (opponent_dragons > 1) - score[pos] += 10 * (opponent_dragons - 1); - - score[pos] += (int) (20.0 * owl_hotspot[pos]); - score[pos] += (int) (20.0 * reading_hotspot[pos]); - + if (own_worms > 1 && eyespace_neighbors >= 1) + score[pos] += 10 + 5 * (own_worms - 2); + } + else if (eyespace_neighbors > 2) + score[pos] += own_diagonals; + + /* Splitting bonus. */ + if (opponent_dragons > 1) + score[pos] += 10 * (opponent_dragons - 1); + + /* Hotspot bonus. */ + { + int owl_hotspot_bonus = (int) (20.0 * owl_hotspot[pos]); + int reading_hotspot_bonus = (int) (20.0 * reading_hotspot[pos]); + int hotspot_bonus = owl_hotspot_bonus + reading_hotspot_bonus; + + /* Don't allow the hotspot bonus to turn a positive score into + * a non-positive one. + */ + if (score[pos] > 0 && score[pos] + hotspot_bonus <= 0) + hotspot_bonus = 1 - score[pos]; + + score[pos] += hotspot_bonus; + if (1 && (debug & DEBUG_AFTERMATH)) gprintf("Score %1M = %d (hotspot bonus %d + %d)\n", pos, score[pos], - (int) (20.0 * owl_hotspot[pos]), - (int) (20.0 * reading_hotspot[pos])); + owl_hotspot_bonus, reading_hotspot_bonus); } - + + /* Avoid taking ko. */ + if (is_ko(pos, color, NULL)) + score[pos] = (score[pos] + 1) / 2; + } + while (1) { int bb; best_score = 0; move = NO_MOVE; - for (m = 0; m < board_size; m++) - for (n = 0; n < board_size; n++) { - pos = POS(m, n); - if (score[pos] > best_score) { - best_score = score[pos]; - move = pos; - } + for (pos = BOARDMIN; pos < BOARDMAX; pos++) { + if (ON_BOARD(pos) && score[pos] > best_score) { + best_score = score[pos]; + move = pos; } + } if (move == NO_MOVE) break; @@ -531,8 +543,55 @@ score[move] = 0; } else { - *aftermath_move = move; - return 1; + /* If we're getting short of liberties, we must be more careful. + * Check that no adjacent string or dragon gets more alive by + * the move. + */ + int libs = approxlib(move, color, 5, NULL); + int move_ok = 1; + if (libs < 5) { + for (k = 0; k < 4; k++) { + if (board[move + delta[k]] == color + && countlib(move + delta[k]) > libs) + break; + } + if (k < 4) { + if (trymove(move, color, "aftermath-B", move + delta[k], + EMPTY, NO_MOVE)) { + int adjs[MAXCHAIN]; + int neighbors; + int r; + neighbors = chainlinks(move, adjs); + for (r = 0; r < neighbors; r++) { + if (worm[adjs[r]].attack_codes[0] != 0 + && (find_defense(adjs[r], NULL) + > worm[adjs[r]].defend_codes[0])) { + DEBUG(DEBUG_AFTERMATH, + "Blunder: %1m becomes tactically safer after %1m\n", + adjs[r], move); + move_ok = 0; + } + } + popgo(); + for (r = 0; r < neighbors && move_ok; r++) { + if (dragon[adjs[r]].matcher_status == DEAD + && !owl_does_attack(move, adjs[r])) { + DEBUG(DEBUG_AFTERMATH, + "Blunder: %1m becomes more alive after %1m\n", + adjs[r], move); + move_ok = 0; + } + } + } + } + } + + if (!move_ok) + score[move] = 0; + else { + *aftermath_move = move; + return 1; + } } } @@ -540,115 +599,113 @@ * Finally we try to play on liberties of remaining DEAD opponent * dragons, carefully checking against mistakes. */ - for (m = 0; m < board_size; m++) - for (n = 0; n < board_size; n++) { - int bb; - int self_atari_ok = 0; - pos = POS(m, n); - if (board[pos] != EMPTY || distance[pos] != -1) + for (pos = BOARDMIN; pos < BOARDMAX; pos++) { + int bb; + int self_atari_ok = 0; + if (board[pos] != EMPTY || distance[pos] != -1) + continue; + move = NO_MOVE; + for (k = 0; k < 4; k++) { + int pos2 = pos + delta[k]; + if (!ON_BOARD(pos2)) continue; - move = NO_MOVE; - for (k = 0; k < 4; k++) { - int pos2 = pos + delta[k]; - if (!ON_BOARD(pos2)) - continue; - if (board[pos2] == other - && dragon[pos2].matcher_status != ALIVE - && (do_capture_dead_stones - || worm[pos2].unconditional_status != DEAD) - && DRAGON2(pos2).safety != INESSENTIAL) { - move = pos2; + if (board[pos2] == other + && dragon[pos2].matcher_status != ALIVE + && (do_capture_dead_stones + || worm[pos2].unconditional_status != DEAD) + && DRAGON2(pos2).safety != INESSENTIAL) { + move = pos2; + break; + } + } + if (move == NO_MOVE) + continue; + + /* At this point, (m, n) is a move that potentially may capture + * a dead opponent string at (move). + */ + + if (!trymove(pos, color, "aftermath-A", move, EMPTY, NO_MOVE)) + continue; + + /* It is frequently necessary to sacrifice own stones in order + * to force the opponent's stones to be removed from the board, + * e.g. by adding stones to fill up a nakade shape. However, we + * should only play into a self atari if the sacrificed stones + * are classified as INESSENTIAL. Thus it would be ok for O to + * try a self atari in this position: + * + * |OOOO + * |XXXO + * |..XO + * |OOXO + * +---- + * + * but not in this one: + * + * |XXX.. + * |OOXX. + * |.OOXX + * |XXOOX + * |.O.OX + * +----- + */ + + if ((board[SOUTH(pos)] == color + && DRAGON2(SOUTH(pos)).safety != INESSENTIAL) + || (board[WEST(pos)] == color + && DRAGON2(WEST(pos)).safety != INESSENTIAL) + || (board[NORTH(pos)] == color + && DRAGON2(NORTH(pos)).safety != INESSENTIAL) + || (board[EAST(pos)] == color + && DRAGON2(EAST(pos)).safety != INESSENTIAL)) + self_atari_ok = 0; + else + self_atari_ok = 1; + + /* Copy the potential move to (bb). */ + bb = pos; + + /* If the move is a self atari, but that isn't okay, try to + * recursively find a backfilling move which later makes the + * potential move possible. + */ + if (!self_atari_ok) { + while (countlib(pos) == 1) { + int lib; + findlib(pos, 1, &lib); + bb = lib; + if (!trymove(bb, color, "aftermath-B", move, EMPTY, NO_MOVE)) break; - } } - if (move == NO_MOVE) - continue; - - /* At this point, (m, n) is a move that potentially may capture - * a dead opponent string at (move). - */ - if (!trymove(pos, color, "aftermath-A", move, EMPTY, NO_MOVE)) - continue; - - /* It is frequently necessary to sacrifice own stones in order - * to force the opponent's stones to be removed from the board, - * e.g. by adding stones to fill up a nakade shape. However, we - * should only play into a self atari if the sacrificed stones - * are classified as INESSENTIAL. Thus it would be ok for O to - * try a self atari in this position: - * - * |OOOO - * |XXXO - * |..XO - * |OOXO - * +---- - * - * but not in this one: - * - * |XXX.. - * |OOXX. - * |.OOXX - * |XXOOX - * |.O.OX - * +----- - */ - - if ((board[SOUTH(pos)] == color - && DRAGON2(SOUTH(pos)).safety != INESSENTIAL) - || (board[WEST(pos)] == color - && DRAGON2(WEST(pos)).safety != INESSENTIAL) - || (board[NORTH(pos)] == color - && DRAGON2(NORTH(pos)).safety != INESSENTIAL) - || (board[EAST(pos)] == color - && DRAGON2(EAST(pos)).safety != INESSENTIAL)) - self_atari_ok = 0; - else - self_atari_ok = 1; - - /* Copy the potential move to (bb). */ - bb = pos; - - /* If the move is a self atari, but that isn't okay, try to - * recursively find a backfilling move which later makes the - * potential move possible. - */ - if (!self_atari_ok) { - while (countlib(pos) == 1) { - int lib; - findlib(pos, 1, &lib); - bb = lib; - if (!trymove(bb, color, "aftermath-B", move, EMPTY, NO_MOVE)) - break; - } - - if (countlib(pos) == 1) - bb = NO_MOVE; - } - - while (stackp > 0) - popgo(); + if (countlib(pos) == 1) + bb = NO_MOVE; + } - if (bb == NO_MOVE) - continue; + while (stackp > 0) + popgo(); + + if (bb == NO_MOVE) + continue; - /* Make sure that the potential move really isn't a self - * atari. In the case of a move found after backfilling this - * could happen (because the backfilling moves happened to - * capture some stones). - */ - if (!self_atari_ok && is_self_atari(bb, color)) - continue; - - /* Consult the owl code to determine whether the considered move - * really is effective. Blunders should be detected here. - */ - if (owl_does_attack(bb, move) == WIN) { - *aftermath_move = bb; - return 1; - } + /* Make sure that the potential move really isn't a self + * atari. In the case of a move found after backfilling this + * could happen (because the backfilling moves happened to + * capture some stones). + */ + if (!self_atari_ok && is_self_atari(bb, color)) + continue; + + /* Consult the owl code to determine whether the considered move + * really is effective. Blunders should be detected here. + */ + if (owl_does_attack(bb, move) == WIN) { + *aftermath_move = bb; + return 1; } - + } + /* Case 7. * In very rare cases it turns out we need yet another pass. An * example is this position: @@ -685,12 +742,96 @@ return -1; } +/* This is a substitute for genmove_conservative() which only does + * what is required when doing the aftermath. Notice though that this + * generates an "ordinary" move, in contrast to aftermath_genmove(). + * Usually this should turn up a pass, but when it doesn't it's + * important not to miss the move. + */ +static int +reduced_genmove(int *move, int color) +{ + float val; + int save_verbose; + + /* no move is found yet. */ + *move = NO_MOVE; + val = -1; + + /* Prepare pattern matcher and reading code. */ + reset_engine(); + + /* Find out information about the worms and dragons. */ + examine_position(color, EXAMINE_ALL); + + /* Make a score estimate. This can be used in later stages of the + * move generation. If we are ahead, we can play safely and if + * we are behind, we have to play more daringly. + */ + if (level >= 8) { + estimate_score(&lower_bound, &upper_bound); + if (verbose || showscore) { + if (lower_bound == upper_bound) + gprintf("\nScore estimate: %s %f\n", + lower_bound > 0 ? "W " : "B ", gg_abs(lower_bound)); + else + gprintf("\nScore estimate: %s %f to %s %f\n", + lower_bound > 0 ? "W " : "B ", gg_abs(lower_bound), + upper_bound > 0 ? "W " : "B ", gg_abs(upper_bound)); + fflush(stderr); + } + + /* The score will be used to determine when we are safely + * ahead. So we want the most conservative score. + */ + if (color == WHITE) + score = lower_bound; + else + score = upper_bound; + } + else + score = 0.0; + + gg_assert(stackp == 0); + + /* + * Ok, information gathering is complete. Now start to find some moves! + */ + + /* Pick up tactical moves. */ + worm_reasons(color); + + /* Pick up owl moves. */ + save_verbose = verbose; + if (verbose > 0) + verbose--; + owl_reasons(color); + verbose = save_verbose; + + /* Look for combination attacks and defenses against them. */ + combinations(color); + gg_assert(stackp == 0); + + /* Review the move reasons and estimate move values. */ + if (review_move_reasons(move, &val, color, 0.0, lower_bound)) + TRACE("Move generation likes %1m with value %f\n", *move, val); + gg_assert(stackp == 0); + + /* If no move is found then pass. */ + if (val < 0.0) { + TRACE("I pass.\n"); + *move = NO_MOVE; + } + else + TRACE("reduced_genmove() recommends %1m with value %f\n", *move, val); + + return val; +} /* Preliminary function for playing through the aftermath. */ static void do_play_aftermath(int color, struct aftermath_data *a) { - int i, j; int move; int pass = 0; int moves = 0; @@ -705,8 +846,7 @@ while (pass < 2 && moves < board_size * board_size) { int reading_nodes = get_reading_node_counter(); int owl_nodes = get_owl_node_counter(); - int move_val = genmove_conservative(&i, &j, color_to_play); - move = POS(i, j); + int move_val = reduced_genmove(&move, color_to_play); if (move_val < 0) move_val = aftermath_genmove(&move, color_to_play, (color_to_play == WHITE ? @@ -735,8 +875,7 @@ static void play_aftermath(int color) { - int m, n; - int ii; + int pos; struct board_state saved_board; struct aftermath_data *a = &aftermath; static int current_board[BOARDMAX]; @@ -749,15 +888,12 @@ cached_board = 0; } - for (m = 0; m < board_size; m++) - for (n = 0; n < board_size; n++) { - ii = POS(m, n); - - if (board[ii] != current_board[ii]) { - current_board[ii] = board[ii]; - cached_board = 0; - } + for (pos = BOARDMIN; pos < BOARDMAX; pos++) { + if (ON_BOARD(pos) && board[pos] != current_board[pos]) { + current_board[pos] = board[pos]; + cached_board = 0; } + } /* If this is exactly the same position as the one we analyzed the * last time, the content of the aftermath struct is up to date. @@ -778,50 +914,49 @@ do_play_aftermath(color, a); restore_board(&saved_board); - for (m = 0; m < board_size; m++) - for (n = 0; n < board_size; n++) { - ii = POS(m, n); - - if (a->black_control[ii]) { - a->black_area++; - if (board[ii] == WHITE) { - a->black_territory++; - a->white_prisoners++; - a->final_status[ii] = DEAD; - } - else if (board[ii] == EMPTY) { - a->black_territory++; - a->final_status[ii] = BLACK_TERRITORY; - } - else - a->final_status[ii] = ALIVE; + for (pos = BOARDMIN; pos < BOARDMAX; pos++) { + if (!ON_BOARD(pos)) + continue; + if (a->black_control[pos]) { + a->black_area++; + if (board[pos] == WHITE) { + a->black_territory++; + a->white_prisoners++; + a->final_status[pos] = DEAD; + } + else if (board[pos] == EMPTY) { + a->black_territory++; + a->final_status[pos] = BLACK_TERRITORY; } - else if (a->white_control[ii]) { - a->white_area++; - if (board[ii] == BLACK) { - a->white_territory++; - a->black_prisoners++; - a->final_status[ii] = DEAD; - } - else if (board[ii] == EMPTY) { - a->white_territory++; - a->final_status[ii] = WHITE_TERRITORY; - } - else - a->final_status[ii] = ALIVE; + else + a->final_status[pos] = ALIVE; + } + else if (a->white_control[pos]) { + a->white_area++; + if (board[pos] == BLACK) { + a->white_territory++; + a->black_prisoners++; + a->final_status[pos] = DEAD; + } + else if (board[pos] == EMPTY) { + a->white_territory++; + a->final_status[pos] = WHITE_TERRITORY; } + else + a->final_status[pos] = ALIVE; + } + else { + if (board[pos] == EMPTY) + a->final_status[pos] = DAME; else { - if (board[ii] == EMPTY) - a->final_status[ii] = DAME; - else { - a->final_status[ii] = ALIVE_IN_SEKI; - if (board[ii] == WHITE) - a->white_area++; - else - a->black_area++; - } + a->final_status[pos] = ALIVE_IN_SEKI; + if (board[pos] == WHITE) + a->white_area++; + else + a->black_area++; } } + } } float Index: engine/influence.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/influence.c,v retrieving revision 1.50 diff -u -r1.50 influence.c --- engine/influence.c 7 Apr 2002 13:23:04 -0000 1.50 +++ engine/influence.c 7 Apr 2002 14:47:23 -0000 @@ -657,6 +657,10 @@ && experimental_influence) return; + /* Don't use invasion (I) patterns when scoring. */ + if (doing_scoring && (pattern->class & CLASS_I)) + return; + /* Loop through pattern elements to see if an A or D pattern * can possibly have any effect. If not we can skip evaluating * constraint and/or helper. */ Index: engine/shapes.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/shapes.c,v retrieving revision 1.28 diff -u -r1.28 shapes.c --- engine/shapes.c 29 Mar 2002 23:03:27 -0000 1.28 +++ engine/shapes.c 7 Apr 2002 14:47:23 -0000 @@ -77,6 +77,10 @@ ||(!experimental_influence && (class & CLASS_F)))) return; + /* Don't accept fuseki marked patterns while scoring. */ + if (doing_scoring && (class & CLASS_F)) + return; + /* Don't need auxiliary data in this callback. */ UNUSED(data); @@ -484,7 +488,7 @@ if (josekidb) matchpat(shapes_callback, color, &joseki_db, NULL, NULL); - if (!disable_fuseki) + if (!disable_fuseki && !doing_scoring) matchpat(shapes_callback, color, &fusekipat_db, NULL, NULL); } Index: engine/value_moves.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/value_moves.c,v retrieving revision 1.25 diff -u -r1.25 value_moves.c --- engine/value_moves.c 7 Apr 2002 13:47:21 -0000 1.25 +++ engine/value_moves.c 7 Apr 2002 14:47:24 -0000 @@ -952,17 +952,19 @@ /* When scoring, we want to be restrictive with reinforcement moves - * inside own territory. Thus if both dragons are alive, strongly - * alive, or invincible, no bonus is awarded. + * inside own territory. Thus if both dragons are weakly_alive, + * alive, strongly alive, or invincible, no bonus is awarded. * * Notice that this requires that the territorial value is computed * before the strategical value. */ if (doing_scoring && move[tt].territorial_value < 0.0) { - if ((safety1 == ALIVE + if ((safety1 == WEAKLY_ALIVE + || safety1 == ALIVE || safety1 == STRONGLY_ALIVE || safety1 == INVINCIBLE) - && (safety2 == ALIVE + && (safety2 == WEAKLY_ALIVE + || safety2 == ALIVE || safety2 == STRONGLY_ALIVE || safety2 == INVINCIBLE)) return 0.0; @@ -1788,6 +1790,17 @@ * does not defend, no points. */ if (worm[bb].attack_codes[0] != 0 && !does_defend(pos, bb)) + this_value = 0.0; + + /* If we are doing scoring, are alive, and the move loses + * territory, no points. + */ + if (doing_scoring + && move[pos].territorial_value < 0.0 + && (DRAGON2(bb).safety == WEAKLY_ALIVE + || DRAGON2(bb).safety == ALIVE + || DRAGON2(bb).safety == STRONGLY_ALIVE + || DRAGON2(bb).safety == INVINCIBLE)) this_value = 0.0; if (this_value > dragon_value[d1]) { Index: patterns/helpers.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/patterns/helpers.c,v retrieving revision 1.34 diff -u -r1.34 helpers.c --- patterns/helpers.c 31 Mar 2002 20:46:47 -0000 1.34 +++ patterns/helpers.c 7 Apr 2002 14:47:24 -0000 @@ -145,7 +145,7 @@ { UNUSED(transformation); UNUSED(pattern); UNUSED(move); - return stones_on_board(OTHER_COLOR(color)) == 0; + return !doing_scoring && stones_on_board(OTHER_COLOR(color)) == 0; } @@ -160,7 +160,8 @@ { UNUSED(transformation); UNUSED(pattern); - return (!lively_dragon_exists(OTHER_COLOR(color)) + return (!doing_scoring + && !lively_dragon_exists(OTHER_COLOR(color)) && safe_move(move, color)); }