Index: engine/optics.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/optics.c,v retrieving revision 1.71 diff -u -r1.71 optics.c --- engine/optics.c 27 Mar 2003 10:33:12 -0000 1.71 +++ engine/optics.c 5 Apr 2003 22:07:18 -0000 @@ -1117,7 +1117,7 @@ if (eye_color == WHITE_BORDER) eye_color = WHITE; - if (eye[pos].esize-eye[pos].msize > 7) + if (eye[pos].esize-eye[pos].msize > 8) return 0; if (eye[pos].msize > MAXEYE) Index: engine/owl.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/owl.c,v retrieving revision 1.150 diff -u -r1.150 owl.c --- engine/owl.c 27 Mar 2003 10:33:12 -0000 1.150 +++ engine/owl.c 5 Apr 2003 22:07:29 -0000 @@ -196,11 +196,21 @@ struct local_owl_data *owlb, int komaster, int *resulta, int *resultb, int *move, int pass, int owl_phase); +static int semeai_trust_attack(int str); +static void semeai_review_owl_moves(struct owl_move_data owl_moves[MAX_MOVES], + struct local_owl_data *owla, + struct local_owl_data *owlb, int color, + int *safe_outside_liberty_found, + int *safe_common_liberty_found, + char mw[BOARDMAX], + struct owl_move_data semeai_moves[MAX_SEMEAI_MOVES], + int guess_same_dragon, int value_bonus); static int semeai_move_value(int move, struct local_owl_data *owla, struct local_owl_data *owlb, int raw_value); static int find_semeai_backfilling_move(int worm, int liberty); static int liberty_of_goal(int pos, struct local_owl_data *owl); +static int second_liberty_of_goal(int pos, struct local_owl_data *owl); static int matches_found; static char found_matches[BOARDMAX]; @@ -243,6 +253,7 @@ #define MAX_SEMEAI_WORMS 20 static int s_worms = 0; static int semeai_worms[MAX_SEMEAI_WORMS]; +static int critical_semeai_worms[MAX_SEMEAI_WORMS]; /* Called when (apos) and (bpos) point to adjacent dragons * of the opposite color, both with matcher_status DEAD or @@ -266,6 +277,7 @@ int w1, w2; int str; SGFTree *save_sgf_dumptree = sgf_dumptree; + int save_verbose = verbose; struct local_owl_data *owla; struct local_owl_data *owlb; @@ -293,12 +305,25 @@ } sgf_dumptree = NULL; + if (verbose > 0) + verbose--; for (str = BOARDMIN; str < BOARDMAX; str++) - if (ON_BOARD(str) && ms[str] && worm[str].origin == str) - if (owl_substantial(str)) { + if (ON_BOARD(str) && ms[str] && worm[str].origin == str) { + /* FIXME: Consider also strings neighboring the outside as + * critical semeai worms. + */ + if (countstones(str) > 6 && s_worms < MAX_SEMEAI_WORMS) { + critical_semeai_worms[s_worms] = 1; + semeai_worms[s_worms++] = str; + DEBUG(DEBUG_SEMEAI, "critical semeai worm: %1m\n", str); + } + else if (owl_substantial(str) && s_worms < MAX_SEMEAI_WORMS) { + critical_semeai_worms[s_worms] = 0; semeai_worms[s_worms++] = str; DEBUG(DEBUG_SEMEAI, "semeai worm: %1m\n", str); } + } + verbose = save_verbose; sgf_dumptree = save_sgf_dumptree; ASSERT1(board[apos] == OTHER_COLOR(board[bpos]), apos); @@ -365,16 +390,13 @@ struct owl_move_data moves[MAX_SEMEAI_MOVES]; struct owl_move_data outside_liberty; struct owl_move_data common_liberty; - struct owl_move_data backfilling_move; + struct owl_move_data backfill_outside_liberty; + struct owl_move_data backfill_common_liberty; char saved_goal[BOARDMAX]; int safe_outside_liberty_found = 0; - int unsafe_outside_liberty_found = 0; int safe_common_liberty_found = 0; - int unsafe_common_liberty_found = 0; - int backfilling_move_found = 0; char mw[BOARDMAX]; int k; - int same_dragon; SGFTree *save_sgf_dumptree = sgf_dumptree; int save_count_variations = count_variations; int move_value; @@ -386,9 +408,16 @@ int best_move_k = -1; Read_result *read_result; int this_variation_number = count_variations - 1; - + int opponent_looks_alive = 0; + int I_look_alive = 0; + int dummy_move; + int tested_moves; + SETUP_TRACE_INFO2("do_owl_analyze_semeai", apos, bpos); + if (!move) + move = &dummy_move; + shape_offensive_patterns.initialized = 0; shape_defensive_patterns.initialized = 0; @@ -402,7 +431,7 @@ && !pass && owl_phase) { if (get_read_result2(SEMEAI, EMPTY, 0, &apos, &bpos, &read_result)) { TRACE_CACHED_RESULT2(*read_result); - if (move && rr_get_result1(*read_result) != 0) + if (rr_get_result1(*read_result) != 0) *move = rr_get_move(*read_result); if (rr_get_result1(*read_result) == ALIVE) @@ -443,9 +472,10 @@ wormsb = catalog_goal(owlb, goal_wormsb); #endif - outside_liberty.pos = 0; - common_liberty.pos = 0; - backfilling_move.pos = 0; + outside_liberty.pos = NO_MOVE; + common_liberty.pos = NO_MOVE; + backfill_outside_liberty.pos = NO_MOVE; + backfill_common_liberty.pos = NO_MOVE; /* turn off the sgf file and variation counting */ sgf_dumptree = NULL; count_variations = 0; @@ -468,17 +498,24 @@ for (sworm = 0; sworm <= s_worms; sworm++) if (board[semeai_worms[sworm]] == other - && countlib(semeai_worms[sworm]) < 3 && attack(semeai_worms[sworm], &upos) == WIN) { - *resulta = ALIVE; - *resultb = DEAD; - if (move) *move = upos; - sgf_dumptree = save_sgf_dumptree; - count_variations = save_count_variations; - SGFTRACE_SEMEAI(upos, ALIVE, "tactical win found"); - close_pattern_list(color, &shape_defensive_patterns); - close_pattern_list(color, &shape_offensive_patterns); - READ_RETURN_SEMEAI(read_result, move, upos, ALIVE, DEAD); + if (semeai_trust_attack(semeai_worms[sworm]) + && critical_semeai_worms[sworm]) { + *resulta = ALIVE; + *resultb = DEAD; + *move = upos; + sgf_dumptree = save_sgf_dumptree; + count_variations = save_count_variations; + SGFTRACE_SEMEAI(upos, ALIVE, "tactical win found"); + close_pattern_list(color, &shape_defensive_patterns); + close_pattern_list(color, &shape_offensive_patterns); + READ_RETURN_SEMEAI(read_result, move, upos, ALIVE, DEAD); + } + else if (find_defense(semeai_worms[sworm], NULL)) { + owl_add_move(moves, upos, 160, "attack semeai worm", 1, 0, NO_MOVE, + MAX_SEMEAI_MOVES); + TRACE("Added %1m %d (-1)\n", upos, 160); + } } /* Look for a tactical rescue. If a semeai worm of owla is tactically * threatened, try to save it. @@ -486,14 +523,15 @@ for (sworm = 0; sworm <= s_worms; sworm++) if (board[semeai_worms[sworm]] == color - && countlib(semeai_worms[sworm]) < 3 && attack(semeai_worms[sworm], NULL) - && find_defense(semeai_worms[sworm], &upos)) + && find_defense(semeai_worms[sworm], &upos)) { owl_add_move(moves, upos, 160, "defend semeai worm", 1, 0, NO_MOVE, MAX_SEMEAI_MOVES); + TRACE("Added %1m %d (0)\n", upos, 160); + } } - /* - * We generate the candidate moves. During the early stages of + + /* We generate the candidate moves. During the early stages of * the semeai, there may be moves to expand or shrink the * eyespaces of the two dragons. During the later stages, the * picture is simplified and reading the semeai is a matter @@ -531,7 +569,7 @@ } #endif - owl_determine_life(owla, owlb, komaster, 1, vital_defensive_moves, + owl_determine_life(owla, owlb, komaster, 0, vital_defensive_moves, &probable_eyes_a, NULL, NULL); if (level >= 9) { current_owl_data = owla; @@ -561,13 +599,19 @@ probable_eyes_b.b = 0; } + if (verbose) { + gprintf("probable_eyes_a: %s\n", eyevalue_to_string(&probable_eyes_a)); + gprintf("probable_eyes_b: %s\n", eyevalue_to_string(&probable_eyes_b)); + } + /* Certain cases can be handled immediately. */ +#if 0 /* I live, you die, no move needed. */ if (min_eyes(&probable_eyes_a) >= 2 && max_eyes(&probable_eyes_b) < 2) { *resulta = ALIVE; *resultb = DEAD; - if (move) *move = PASS_MOVE; + *move = PASS_MOVE; sgf_dumptree = save_sgf_dumptree; count_variations = save_count_variations; if (max_eyes(&probable_eyes_b) == 0) { @@ -580,14 +624,17 @@ } READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, ALIVE, DEAD); } +#endif /* I am alive */ if (min_eyes(&probable_eyes_a) >= 2 || (stackp > 2 && owl_escape_route(owla) >= 5)) { + I_look_alive = 1; if (max_eyes(&probable_eyes_b) < 2) { +#if 0 /* you are already dead */ *resulta = ALIVE; *resultb = DEAD; - if (move) *move = PASS_MOVE; + *move = PASS_MOVE; sgf_dumptree = save_sgf_dumptree; count_variations = save_count_variations; if (max_eyes(&probable_eyes_b) == 0) { @@ -599,12 +646,13 @@ SGFTRACE_SEMEAI(PASS_MOVE, ALIVE, "Two eyes or escape versus one"); } READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, ALIVE, DEAD); +#endif } else if (min_eyes(&probable_eyes_b) >= 2) { /* both live */ *resulta = ALIVE; *resultb = ALIVE; - if (move) *move = PASS_MOVE; + *move = PASS_MOVE; sgf_dumptree = save_sgf_dumptree; count_variations = save_count_variations; TRACE("Both live\n"); @@ -612,10 +660,11 @@ READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, ALIVE, ALIVE); } else if (vital_offensive_moves[0].pos != NO_MOVE){ +#if 0 /* I can kill */ *resulta = ALIVE; *resultb = DEAD; - if (move) *move = vital_offensive_moves[0].pos; + *move = vital_offensive_moves[0].pos; sgf_dumptree = save_sgf_dumptree; count_variations = save_count_variations; if (min_eyes(&probable_eyes_b) == 0) { @@ -632,6 +681,7 @@ } READ_RETURN_SEMEAI(read_result, move, vital_offensive_moves[0].pos, ALIVE, DEAD); +#endif } /* If here, compute_eyes_pessimistic has returned min_eyes<2 * and max_eyes>=2 yet produced no defensive move. @@ -642,22 +692,25 @@ if (min_eyes(&probable_eyes_b) >= 2 || (stackp > 2 && owl_escape_route(owlb) >= 5)) { /* you are alive */ + opponent_looks_alive = 1; if (max_eyes(&probable_eyes_a) < 2) { +#if 0 /* I am dead */ *resulta = DEAD; *resultb = ALIVE; - if (move) *move = PASS_MOVE; + *move = PASS_MOVE; sgf_dumptree = save_sgf_dumptree; count_variations = save_count_variations; TRACE("You (%C) live, I die\n", other); SGFTRACE_SEMEAI(PASS_MOVE, DEAD, "You live, I die"); READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, DEAD, ALIVE); +#endif } else if (min_eyes(&probable_eyes_a) >= 2) { /* I am already alive */ *resulta = ALIVE; *resultb = ALIVE; - if (move) *move = PASS_MOVE; + *move = PASS_MOVE; sgf_dumptree = save_sgf_dumptree; count_variations = save_count_variations; TRACE("Both live\n"); @@ -668,7 +721,7 @@ /* I can live */ *resulta = ALIVE; *resultb = ALIVE; - if (move) *move = vital_defensive_moves[0].pos; + *move = vital_defensive_moves[0].pos; sgf_dumptree = save_sgf_dumptree; count_variations = save_count_variations; TRACE("Both live\n"); @@ -684,12 +737,14 @@ } /* Next the shape moves. */ - owl_shapes(&shape_defensive_patterns, shape_defensive_moves, color, owla, - &owl_defendpat_db); - for (k = 0; k < MAX_MOVES-1; k++) - if (!get_next_move_from_list(&shape_defensive_patterns, color, - shape_defensive_moves, 1)) - break; + if (!I_look_alive) { + owl_shapes(&shape_defensive_patterns, shape_defensive_moves, color, + owla, &owl_defendpat_db); + for (k = 0; k < MAX_MOVES-1; k++) + if (!get_next_move_from_list(&shape_defensive_patterns, color, + shape_defensive_moves, 1)) + break; + } owl_shapes(&shape_offensive_patterns, shape_offensive_moves, color, owlb, &owl_attackpat_db); for (k = 0; k < MAX_MOVES-1; k++) @@ -703,124 +758,36 @@ * have included a move that fills a liberty. If no such move is found, we * will have to add it later. */ - - for (k = 0; - k < MAX_MOVES-1 && vital_defensive_moves[k].pos != NO_MOVE; - k++) { - if (liberty_of_goal(vital_defensive_moves[k].pos, owlb)) { - if (!liberty_of_goal(vital_defensive_moves[k].pos, owla)) { - if (safe_move(vital_defensive_moves[k].pos, color)) - safe_outside_liberty_found = 1; - else - unsafe_outside_liberty_found = 1; - } - else { - if (safe_move(vital_defensive_moves[k].pos, color)) - safe_common_liberty_found = 1; - else - unsafe_common_liberty_found = 1; - } - } - mw[vital_defensive_moves[k].pos] = 1; - move_value = semeai_move_value(vital_defensive_moves[k].pos, - owla, owlb, - vital_defensive_moves[k].value) + 30; - owl_add_move(moves, vital_defensive_moves[k].pos, - move_value, "vital defensive move", - vital_defensive_moves[k].same_dragon, - vital_defensive_moves[k].escape, NO_MOVE, - MAX_SEMEAI_MOVES); - } - for (k = 0; - k < MAX_MOVES-1 && vital_offensive_moves[k].pos != NO_MOVE; - k++) { - if (liberty_of_goal(vital_offensive_moves[k].pos, owlb)) { - if (!liberty_of_goal(vital_offensive_moves[k].pos, owla)) { - if (safe_move(vital_offensive_moves[k].pos, color)) - safe_outside_liberty_found = 1; - else - unsafe_outside_liberty_found = 1; - } - else { - if (safe_move(vital_offensive_moves[k].pos, color)) - safe_common_liberty_found = 1; - else - unsafe_common_liberty_found = 1; - } - } - mw[vital_offensive_moves[k].pos] = 1; - if (liberty_of_goal(vital_offensive_moves[k].pos, owla)) - same_dragon = 2; - else - same_dragon = 0; - move_value = semeai_move_value(vital_offensive_moves[k].pos, - owla, owlb, - vital_offensive_moves[k].value) + 30; - owl_add_move(moves, vital_offensive_moves[k].pos, - move_value, vital_offensive_moves[k].name, - same_dragon, vital_offensive_moves[k].escape, - NO_MOVE, MAX_SEMEAI_MOVES); - } - for (k = 0; - k < MAX_MOVES-1 && shape_defensive_moves[k].pos != NO_MOVE; - k++) { - if (liberty_of_goal(shape_defensive_moves[k].pos, owlb)) { - if (!liberty_of_goal(shape_defensive_moves[k].pos, owla)) { - if (safe_move(shape_defensive_moves[k].pos, color)) - safe_outside_liberty_found = 1; - else - unsafe_outside_liberty_found = 1; - } - else { - if (safe_move(shape_defensive_moves[k].pos, color)) - safe_common_liberty_found = 1; - else - unsafe_common_liberty_found = 1; - } - } - mw[shape_defensive_moves[k].pos] = 1; - move_value = semeai_move_value(shape_defensive_moves[k].pos, - owla, owlb, - shape_defensive_moves[k].value); - owl_add_move(moves, shape_defensive_moves[k].pos, - move_value, - shape_defensive_moves[k].name, - shape_defensive_moves[k].same_dragon, - shape_defensive_moves[k].escape, NO_MOVE, MAX_SEMEAI_MOVES); - } - for (k = 0; - k < MAX_MOVES-1 && shape_offensive_moves[k].pos != NO_MOVE; - k++) { - if (liberty_of_goal(shape_offensive_moves[k].pos, owlb)) { - if (!liberty_of_goal(shape_offensive_moves[k].pos, owla)) { - if (safe_move(shape_offensive_moves[k].pos, color)) - safe_outside_liberty_found = 1; - else - unsafe_outside_liberty_found = 1; - } - else { - if (safe_move(shape_offensive_moves[k].pos, color)) - safe_common_liberty_found = 1; - else - unsafe_common_liberty_found = 1; - } - } - mw[shape_offensive_moves[k].pos] = 1; - if (liberty_of_goal(shape_offensive_moves[k].pos, owla)) - same_dragon = 2; - else - same_dragon = 0; - move_value = semeai_move_value(shape_offensive_moves[k].pos, - owla, owlb, - shape_offensive_moves[k].value); - owl_add_move(moves, shape_offensive_moves[k].pos, - move_value, shape_offensive_moves[k].name, - same_dragon, shape_offensive_moves[k].escape, - NO_MOVE, MAX_SEMEAI_MOVES); + + if (!I_look_alive) { + semeai_review_owl_moves(vital_defensive_moves, owla, owlb, color, + &safe_outside_liberty_found, + &safe_common_liberty_found, + mw, moves, 0, 30); + + semeai_review_owl_moves(shape_defensive_moves, owla, owlb, color, + &safe_outside_liberty_found, + &safe_common_liberty_found, + mw, moves, 0, 0); } + + if (!opponent_looks_alive) { + semeai_review_owl_moves(vital_offensive_moves, owla, owlb, color, + &safe_outside_liberty_found, + &safe_common_liberty_found, + mw, moves, 1, 30); + + semeai_review_owl_moves(shape_offensive_moves, owla, owlb, color, + &safe_outside_liberty_found, + &safe_common_liberty_found, + mw, moves, 1, 0); + } + +#if 0 /* If no owl moves were found, turn off the owl phase */ - if (moves[0].pos == 0) + if (moves[0].pos == NO_MOVE) owl_phase = 0; +#endif } /* now we look for a move to fill a liberty. */ @@ -830,7 +797,8 @@ goaldump(owla->goal); goaldump(owlb->goal); } - if (!safe_outside_liberty_found && moves[0].value < 100) { + if (!opponent_looks_alive + && !safe_outside_liberty_found && moves[0].value < 100) { int pos; for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (!ON_BOARD(pos)) @@ -845,11 +813,9 @@ outside_liberty.pos = pos; break; } - else if (!backfilling_move_found) { - backfilling_move.pos = find_semeai_backfilling_move(bpos, pos); - if (backfilling_move.pos != NO_MOVE) - backfilling_move_found = 1; - } + else if (backfill_outside_liberty.pos == NO_MOVE) + backfill_outside_liberty.pos = find_semeai_backfilling_move(bpos, + pos); } else { /* common liberty */ @@ -857,47 +823,66 @@ safe_common_liberty_found = 1; common_liberty.pos = pos; } - else - unsafe_common_liberty_found = 1; + else if (backfill_common_liberty.pos == NO_MOVE) + backfill_common_liberty.pos = find_semeai_backfilling_move(bpos, + pos); } } } } } - - if (safe_outside_liberty_found - && outside_liberty.pos != NO_MOVE) { - move_value = semeai_move_value(outside_liberty.pos, - owla, owlb, 50); - owl_add_move(moves, outside_liberty.pos, move_value, - "safe outside liberty", 0, 0, NO_MOVE, MAX_SEMEAI_MOVES); - } - else if (backfilling_move_found && backfilling_move.pos != NO_MOVE) { - move_value = semeai_move_value(backfilling_move.pos, - owla, owlb, 50); - owl_add_move(moves, backfilling_move.pos, move_value, - "backfilling move", 0, 0, NO_MOVE, MAX_SEMEAI_MOVES); - } - else if (safe_common_liberty_found - && common_liberty.pos != NO_MOVE) { - move_value = semeai_move_value(common_liberty.pos, - owla, owlb, 10); - owl_add_move(moves, common_liberty.pos, move_value, - "safe common liberty", 1, 0, NO_MOVE, MAX_SEMEAI_MOVES); + + if (!opponent_looks_alive) { + if (safe_outside_liberty_found + && outside_liberty.pos != NO_MOVE) { + move_value = semeai_move_value(outside_liberty.pos, + owla, owlb, 50); + owl_add_move(moves, outside_liberty.pos, move_value, + "safe outside liberty", 0, 0, NO_MOVE, MAX_SEMEAI_MOVES); + TRACE("Added %1m %d (5)\n", outside_liberty.pos, move_value); + } + else if (backfill_outside_liberty.pos != NO_MOVE) { + move_value = semeai_move_value(backfill_outside_liberty.pos, + owla, owlb, 50); + owl_add_move(moves, backfill_outside_liberty.pos, move_value, + "backfilling move", 0, 0, NO_MOVE, MAX_SEMEAI_MOVES); + TRACE("Added %1m %d (6)\n", backfill_outside_liberty.pos, move_value); + } + else if (safe_common_liberty_found + && common_liberty.pos != NO_MOVE) { + move_value = semeai_move_value(common_liberty.pos, + owla, owlb, 10); + owl_add_move(moves, common_liberty.pos, move_value, + "safe common liberty", 1, 0, NO_MOVE, MAX_SEMEAI_MOVES); + TRACE("Added %1m %d (7)\n", common_liberty.pos, move_value); + } + else if (backfill_common_liberty.pos != NO_MOVE) { + move_value = semeai_move_value(backfill_common_liberty.pos, + owla, owlb, 10); + owl_add_move(moves, backfill_common_liberty.pos, move_value, + "backfilling move", 0, 0, NO_MOVE, MAX_SEMEAI_MOVES); + TRACE("Added %1m %d (6)\n", backfill_common_liberty.pos, move_value); + } + } + + if (moves[0].pos == NO_MOVE) { + TRACE("No move found\n"); } + /* Now we are ready to try moves. Turn on the sgf output ... */ sgf_dumptree = save_sgf_dumptree; count_variations = save_count_variations; + tested_moves = 0; for (k = 0; k < MAX_SEMEAI_MOVES; k++) { int mpos = moves[k].pos; - if (k > 2 - || (stackp > 6 && k > 1) - || (stackp > SEMEAI_BRANCH_DEPTH && k > 0)) { + if (tested_moves > 2 + || (stackp > 6 && tested_moves > 1) + || (stackp > SEMEAI_BRANCH_DEPTH && tested_moves > 0)) { /* If allpats, try and pop to get the move in the sgf record */ - if (allpats && mpos!= NO_MOVE && - semeai_trymove(mpos, color, moves[k].name, apos, bpos, - owl_phase, moves[k].value)) + if (allpats && mpos!= NO_MOVE + && semeai_trymove(mpos, color, moves[k].name, apos, bpos, + owl_phase, moves[k].value)) popgo(); continue; } @@ -906,40 +891,51 @@ && stackp < MAX_SEMEAI_DEPTH && semeai_trymove(mpos, color, moves[k].name, apos, bpos, owl_phase, moves[k].value)) { + tested_moves++; TRACE("Trying %C %1m. Current stack: ", color, mpos); if (verbose) dump_stack(); + + if (owl_phase && stackp <= SEMEAI_BRANCH_DEPTH + 1) + push_owl(&owla, &owlb); + else + memcpy(saved_goal, owla->goal, sizeof(saved_goal)); + + owl_update_goal(mpos, moves[k].same_dragon, owla, 1); + owla->lunches_are_current = 0; + owl_update_boundary_marks(mpos, owla); + if (board[bpos] == EMPTY) { - this_resultb = DEAD; - this_resulta = ALIVE; + this_resulta = do_owl_attack(apos, NULL, NULL, + owla, EMPTY, NO_MOVE, 0); + this_resultb = REVERSE_RESULT(this_resulta); + if (this_resulta == 0) { + this_resultb = DEAD; + this_resulta = ALIVE; + } + else { + this_resultb = ALIVE; + this_resulta = DEAD; + } } else { - if (owl_phase && stackp <= SEMEAI_BRANCH_DEPTH + 1) - push_owl(&owla, &owlb); - else - memcpy(saved_goal, owla->goal, sizeof(saved_goal)); - - owl_update_goal(mpos, moves[k].same_dragon, owla, 1); - owla->lunches_are_current = 0; - owl_update_boundary_marks(mpos, owla); - do_owl_analyze_semeai(bpos, apos, owlb, owla, komaster, - &this_resultb, &this_resulta, NULL, 0, owl_phase); - - if (owl_phase && stackp <= SEMEAI_BRANCH_DEPTH + 1) { - pop_owl(&owlb); - pop_owl(&owla); - } - else - memcpy(owla->goal, saved_goal, sizeof(saved_goal)); + &this_resultb, &this_resulta, + NULL, 0, owl_phase); } + if (owl_phase && stackp <= SEMEAI_BRANCH_DEPTH + 1) { + pop_owl(&owlb); + pop_owl(&owla); + } + else + memcpy(owla->goal, saved_goal, sizeof(saved_goal)); popgo(); if (this_resultb == DEAD && this_resulta == ALIVE) { *resulta = ALIVE; *resultb = DEAD; - if (move) *move = mpos; + *move = mpos; TRACE("After %1m I (%C) am alive, you are dead\n", mpos, color); SGFTRACE_SEMEAI(mpos, ALIVE, moves[k].name); close_pattern_list(color, &shape_defensive_patterns); @@ -967,11 +963,23 @@ close_pattern_list(color, &shape_defensive_patterns); close_pattern_list(color, &shape_offensive_patterns); + + /* If we can't find a move and the opponent looks alive, we have + * lost. + */ + if (opponent_looks_alive) { + *resulta = DEAD; + *resultb = ALIVE; + *move = PASS_MOVE; + SGFTRACE2(PASS_MOVE, DEAD, "You live, I die"); + READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, DEAD, ALIVE); + } + /* If we can't find a move and opponent passed, it's seki */ if (best_resulta == UNKNOWN && pass == 1) { *resulta = ALIVE_IN_SEKI; *resultb = ALIVE_IN_SEKI; - if (move) *move = PASS_MOVE; + *move = PASS_MOVE; TRACE("Seki\n"); SGFTRACE_SEMEAI(PASS_MOVE, ALIVE_IN_SEKI, "Seki"); READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, @@ -983,7 +991,7 @@ resultb, resulta, NULL, 1, owl_phase); TRACE("No move found\n"); SGFTRACE_SEMEAI(PASS_MOVE, UNKNOWN, "No move found"); - if (move) *move = PASS_MOVE; + *move = PASS_MOVE; READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, *resulta, *resultb); } @@ -991,11 +999,92 @@ *resultb = best_resultb; if (best_resulta == DEAD) best_move = PASS_MOVE; - if (move) *move = best_move; + *move = best_move; SGFTRACE_SEMEAI(best_move, best_resulta, moves[best_move_k].name); READ_RETURN_SEMEAI(read_result, move, best_move, best_resulta, best_resultb); } - + + +/* In semeai situations tactical attacks often cannot be trusted. This + * in particular holds for strings with three or more liberties. Two + * liberties can usually be trusted, but if neither liberty can be + * played immediately, the need for backfilling moves gives an + * effective liberty count of more than two, again making the attack + * untrustworthy. + * + * This function decides whether an attack should be trusted. It does + * not check whether there actually is an attack, though. + */ +static int +semeai_trust_attack(int str) +{ + int liberties; + int libs[3]; + int other = OTHER_COLOR(board[str]); + + liberties = findlib(str, 3, libs); + if (liberties > 2) + return 0; + + if (liberties < 2) + return 1; + + if (!is_self_atari(libs[0], other) + || !is_self_atari(libs[1], other)) + return 1; + + return 0; +} + + +static void +semeai_review_owl_moves(struct owl_move_data owl_moves[MAX_MOVES], + struct local_owl_data *owla, + struct local_owl_data *owlb, int color, + int *safe_outside_liberty_found, + int *safe_common_liberty_found, + char mw[BOARDMAX], + struct owl_move_data semeai_moves[MAX_SEMEAI_MOVES], + int guess_same_dragon, int value_bonus) +{ + int upos; + int move_value; + int same_dragon; + int k; + + for (k = 0; k < MAX_MOVES-1; k++) { + upos = owl_moves[k].pos; + if (upos == NO_MOVE) + break; + + if (liberty_of_goal(upos, owlb) + && safe_move(upos, color)) { + if (!liberty_of_goal(upos, owla)) + *safe_outside_liberty_found = 1; + else + *safe_common_liberty_found = 1; + } + + if (guess_same_dragon) { + if (liberty_of_goal(upos, owla) + || second_liberty_of_goal(upos, owla)) + same_dragon = 2; + else + same_dragon = 0; + } + else + same_dragon = owl_moves[k].same_dragon; + + mw[upos] = 1; + move_value = (semeai_move_value(upos, owla, owlb, owl_moves[k].value) + + value_bonus); + owl_add_move(semeai_moves, upos, move_value, owl_moves[k].name, + same_dragon, owl_moves[k].escape, + NO_MOVE, MAX_SEMEAI_MOVES); + TRACE("Added %1m %d\n", upos, move_value); + } +} + /* Returns the number of liberties gained by the first goal minus the * number of liberties lost by the second goal when a move is played * at move (if positive, zero otherwise). Used for sorting the moves. @@ -1015,12 +1104,8 @@ verbose = 0; if (safe_move(move, color)) { for (pos = BOARDMIN; pos < BOARDMAX; pos++) { - if (ON_BOARD(pos) - && board[pos] != EMPTY) { - int origin = find_origin(pos); - - if (origin != pos) - continue; + if (IS_STONE(board[pos]) + && pos == find_origin(pos)) { if (owla->goal[pos]) net -= 75*countlib(pos); if (owlb->goal[pos]) @@ -1032,12 +1117,8 @@ return 0; } for (pos = BOARDMIN; pos < BOARDMAX; pos++) { - if (ON_BOARD(pos) - && board[pos] != EMPTY) { - int origin = find_origin(pos); - - if (origin != pos) - continue; + if (IS_STONE(board[pos]) + && pos == find_origin(pos)) { if (owla->goal[pos] || (pos == move && liberty_of_goal(move, owla))) net += 75*countlib(pos); @@ -1052,11 +1133,9 @@ if (net < 0) net = 0; -#if 0 - if (semeai_focus != EMPTY - && move_focus == semeai_focus) - return raw_value + net + 50; -#endif + + net /= 25; + return raw_value + net; } @@ -1077,6 +1156,17 @@ return 0; } +static int +second_liberty_of_goal(int pos, struct local_owl_data *owl) +{ + int k; + for (k = 0; k < 4; k++) + if (board[pos + delta[k]] == EMPTY && liberty_of_goal(pos + delta[k], owl)) + return 1; + + return 0; +} + /* 'liberty' is a liberty of 'worm' which we would like to fill. * However it is not safe to play there, so we look for a @@ -1103,9 +1193,6 @@ int other = OTHER_COLOR(color); int result = NO_MOVE; - if (stackp > backfill_depth) - return NO_MOVE; - if (safe_move(liberty, other)) return liberty; if (is_self_atari(liberty, other)) { @@ -3192,7 +3279,7 @@ /* * NOTICE : In order to stabilize the regression test results, * arbitrary parameters like pattern memory address and move position - * have been included in the sorting algorythm. + * have been included in the sorting algorithm. */ float top_val = list->pattern_list[top].pattern->value; struct pattern *top_pattern = list->pattern_list[top].pattern; @@ -3761,8 +3848,8 @@ /* Look through the neighbors of the victim for dragons of * our color. If we find at least one being thought alive * everything is ok. Otherwise we keep track of the - * largest one for further examination. - */ + * largest one for further examination. + */ int largest = 0; int k; int bpos = NO_MOVE; @@ -3788,9 +3875,9 @@ if (bpos == NO_MOVE) safe = 1; - /* If not yet thought safe, ask the owl code whether the - * owl attack defends the (largest) attacker. - */ + /* If not yet thought safe, ask the owl code whether the + * owl attack defends the (largest) attacker. + */ if (!safe && owl_does_defend(move, bpos, &kworm) != WIN) { DEBUG(DEBUG_OWL, "owl: %1m attacks %1m at move %d, but the attacker dies.\n", @@ -3803,8 +3890,8 @@ /* If we've reached this far, the attack is okay. */ if (DRAGON2(pos).owl_attack_code == GAIN) { add_gain_move(move, pos, DRAGON2(pos).owl_attack_kworm ); - DEBUG(DEBUG_OWL, "owl: %1m attacks %1m with gain at move %d\n", move, pos, - movenum+1); + DEBUG(DEBUG_OWL, "owl: %1m attacks %1m with gain at move %d\n", + move, pos, movenum+1); } else { add_owl_attack_move(move, pos, DRAGON2(pos).owl_attack_code); @@ -4606,7 +4693,7 @@ * other dragon and can't be saved is not lively. */ if (other_owl_data) { - if (other_owl_data->goal[pos]) + if (other_owl_data->goal[pos] && !semeai_trust_attack(pos)) return 1; for (lunch = 0; lunch < MAX_LUNCHES; lunch++) if (other_owl_data->lunch[lunch] == origin Index: patterns/eyes.db =================================================================== RCS file: /cvsroot/gnugo/gnugo/patterns/eyes.db,v retrieving revision 1.34 diff -u -r1.34 eyes.db --- patterns/eyes.db 6 Feb 2003 13:34:59 -0000 1.34 +++ patterns/eyes.db 5 Apr 2003 22:07:32 -0000 @@ -4603,13 +4603,24 @@ Pattern 74500 +|. +|X +|XX. +|.X ++-- + +:1111 + + +Pattern 74510 + .. .X*.! :1122 -Pattern 74501 +Pattern 74511 xx .*x.! @@ -4617,7 +4628,7 @@ :1122 -Pattern 74502 +Pattern 74512 Xx .Xx.! @@ -4625,7 +4636,7 @@ :1111 -Pattern 74503 +Pattern 74513 xx !.x.! @@ -5039,6 +5050,37 @@ :1122 +Pattern 78520 + +|. +|X. +|XX +|.X ++-- + +:1111 + + +Pattern 78521 + +|X. +|XX +|.X. ++--- + +:1111 + + +Pattern 78522 + +|X +|XX. +|.X. ++--- + +:1111 + + Pattern 78550 x.x @@ -5060,6 +5102,16 @@ # ... Pattern 79000 + +|X. +|XX. +|.X ++-- + +:1111 + + +Pattern 79010 # Big nakade. x .X. @@ -5068,7 +5120,7 @@ :1122 -Pattern 79001 +Pattern 79011 # Big nakade. . .X. @@ -5077,7 +5129,7 @@ :1122 -Pattern 79002 +Pattern 79012 # Big nakade. x @@ -5087,7 +5139,7 @@ :1122 -Pattern 79003 +Pattern 79013 # X here may give ko. . @@ -5097,7 +5149,7 @@ :1122 -Pattern 79004 +Pattern 79014 # O here gives ko. x @@ -5107,7 +5159,7 @@ :1122 -Pattern 79005 +Pattern 79015 # Big nakade. x @@ -5117,7 +5169,7 @@ :1122 -Pattern 79006 +Pattern 79016 # Big nakade. x @@ -5127,7 +5179,7 @@ :1122 -Pattern 79007 +Pattern 79017 # Big nakade. x @@ -5137,7 +5189,7 @@ :1122 -Pattern 79008 +Pattern 79018 # Big nakade. X @@ -5147,7 +5199,7 @@ :1122 -Pattern 79009 +Pattern 79019 # Big nakade. x @@ -5157,7 +5209,7 @@ :1111 -Pattern 79010 +Pattern 79020 # Big nakade. . @@ -5167,7 +5219,7 @@ :1111 -Pattern 79011 +Pattern 79021 # Big nakade. x @@ -5177,7 +5229,7 @@ :1111 -Pattern 79012 +Pattern 79022 # Big nakade. x @@ -5187,7 +5239,7 @@ :1111 -Pattern 79013 +Pattern 79023 # Big nakade. x @@ -5383,9 +5435,9 @@ :1111 -# . . -#Topology: . . -# .... +# . . +# Topology: . . +# .... Pattern 80800 # tm New Pattern (3.1.20) (see viking:9) @@ -5395,6 +5447,66 @@ .X.. :1122 + + +# . +# Topology: ... +# .... + +Pattern 80900 +# gf New pattern. (3.3.18) + + . +|XX. +|.XX. ++---- + +:1111 + + +# . +# Topology: ... +# .... + +Pattern 81000 +# gf New pattern. (3.3.18) + +|. +|XX. +|.XX. ++---- + +:1111 + + +# .. +# Topology: .. +# .... + +Pattern 81100 +# gf New pattern. (3.3.18) + +|.. +|XX +|.XX. ++---- + +:1111 + + +# .. +# Topology: ... +# ... + +Pattern 81200 +# gf New pattern. (3.3.18) + +|.. +|XX. +|.XX ++--- + +:1111 ############################### Index: patterns/owl_attackpats.db =================================================================== RCS file: /cvsroot/gnugo/gnugo/patterns/owl_attackpats.db,v retrieving revision 1.77 diff -u -r1.77 owl_attackpats.db --- patterns/owl_attackpats.db 30 Mar 2003 12:26:11 -0000 1.77 +++ patterns/owl_attackpats.db 5 Apr 2003 22:07:36 -0000 @@ -4121,6 +4121,34 @@ :8,-,value(65) +Pattern A1131 +# gf New pattern. (3.3.18) + +XO.Y +?*.? +---- + +:8,-,value(90) + +AO.B +?*.? +---- + +;owl_strong_dragon(A) && !xplay_disconnect(*,A,B) + + +Pattern A1132 +# gf New pattern. (3.3.18) + +|.O?? +|OYX? +|*OXX +|..OX ++---- + +:8,-,value(70) + + ######################################################### # # # Kill or threaten a worm of the dragon # Index: patterns/owl_defendpats.db =================================================================== RCS file: /cvsroot/gnugo/gnugo/patterns/owl_defendpats.db,v retrieving revision 1.87 diff -u -r1.87 owl_defendpats.db --- patterns/owl_defendpats.db 30 Mar 2003 12:26:11 -0000 1.87 +++ patterns/owl_defendpats.db 5 Apr 2003 22:07:41 -0000 @@ -1611,6 +1611,7 @@ Pattern D504 # Don't apply this pattern on the edge. See patterns D505 and D506 +# gf Revised constraint. (3.3.18) ?*.O hanging connection to defend and make eye ?.OX @@ -1620,7 +1621,8 @@ ?*ba ?.cX -; lib(a)>1 && safe_xmove(b) && oplay_attack(*,b,b) && does_attack(b,c) +; lib(a)>1 && safe_xmove(b) && !obvious_false_oeye(b) +; && oplay_attack(*,b,b) && does_attack(b,c) Pattern D505 @@ -6256,6 +6258,7 @@ Pattern D1378 # tm reduced value (3.1.17) +# gf Revised constraint. (3.3.18) X*X breakout & attack ?O? @@ -6266,7 +6269,7 @@ ?O? ; vital_chain(A) && vital_chain(B) -; && oplay_attack_either(*,A,B) +; && oplay_attack_either(*,A,B) && !oplay_connect(*,A,B) Pattern D1379 @@ -6466,6 +6469,23 @@ ;(owl_escape_value(b)>0 || owl_escape_value(c)>0 || owl_escape_value(d)>0) ;&& !oplay_attack(*,a) + + +Pattern D1389 +# gf New pattern. (3.3.18) +# See also D1340. + +OX.O +?*.? +---- + +:8,E,value(84) + +aX.b +?*.? +---- + +;(owl_strong_dragon(a) || owl_strong_dragon(b)) && !oplay_disconnect(*,a,b) #########################################################