Index: engine/board.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/board.c,v retrieving revision 1.71 diff -u -r1.71 board.c --- engine/board.c 1 Apr 2003 18:54:24 -0000 1.71 +++ engine/board.c 8 Apr 2003 19:24:31 -0000 @@ -557,52 +557,6 @@ /* - * Special version for semeai reading. - */ - -int -semeai_trymove(int pos, int color, const char *message, int str1, int str2, - int owl_phase, int value) -{ - /* Do the real work elsewhere. */ - if (!do_trymove(pos, color, 0)) - return 0; - - if (message == NULL) - message = "UNKNOWN"; - - /* Store the move in an sgf tree if one is available. */ - if (sgf_dumptree) { - char buf[100]; - char sbuf1[5], sbuf2[5]; - /* Simply calling location_to_string three times doesn't work - * because the internal buffer of that function gets overwritten - * too soon. So we allocate our own char buffers. - */ - location_to_buffer(str1, sbuf1); - location_to_buffer(str2, sbuf2); - - if (owl_phase) - gg_snprintf(buf, 100, - "%s in semeai %s-%s (variation %d, value %d, owl_phase)", - message, sbuf1, sbuf2, count_variations, value); - else - gg_snprintf(buf, 100, - "%s in semeai %s-%s (variation %d, value %d)", - message, sbuf1, sbuf2, count_variations, value); - sgftreeAddPlayLast(sgf_dumptree, color, I(pos), J(pos)); - sgftreeAddComment(sgf_dumptree, buf); - } - - if (count_variations) - count_variations++; - stats.nodes++; - - return 1; -} - - -/* * tryko pushes the position onto the stack, and makes a move * at (pos) of (color). The move is allowed even if it is an * illegal ko capture. It is to be imagined that (color) has Index: engine/cache.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/cache.c,v retrieving revision 1.21 diff -u -r1.21 cache.c --- engine/cache.c 5 Mar 2003 18:52:56 -0000 1.21 +++ engine/cache.c 8 Apr 2003 19:24:32 -0000 @@ -806,8 +806,8 @@ sgftreeAddComment(sgf_dumptree, buf); } -/* Write two group reading (connection or semeai) trace data to an SGF - * file. Normally called through the macro SGFTRACE2 in cache.h. +/* Write two group reading (connection) trace data to an SGF file. + * Normally called through the macro SGFTRACE2 in cache.h. */ void @@ -828,6 +828,38 @@ sprintf(buf + strlen(buf), "%s PASS", result); else sprintf(buf + strlen(buf), "%s [%d]", result, move); + + if (message) + sprintf(buf + strlen(buf), " (%s)", message); + + sgftreeAddComment(sgf_dumptree, buf); +} + +/* Write semeai reading trace data to an SGF file. Normally called + * through the macro SGFTRACE_SEMEAI in cache.h. + */ + +void +sgf_trace_semeai(const char *func, int str1, int str2, int move, + int result1, int result2, const char *message) +{ + char buf[100]; + + sprintf(buf, "%s %c%d %c%d: ", func, + J(str1) + 'A' + (J(str1) >= 8), board_size - I(str1), + J(str2) + 'A' + (J(str2) >= 8), board_size - I(str2)); + + if (ON_BOARD(move)) + sprintf(buf + strlen(buf), "%s %s %c%d", + result_to_string(result1), result_to_string(result2), + J(move) + 'A' + (J(move) >= 8), board_size - I(move)); + else if (is_pass(move)) + sprintf(buf + strlen(buf), "%s %s PASS", + result_to_string(result1), result_to_string(result2)); + else + sprintf(buf + strlen(buf), "%s %s [%d]", + result_to_string(result1), result_to_string(result2), + move); if (message) sprintf(buf + strlen(buf), " (%s)", message); Index: engine/cache.h =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/cache.h,v retrieving revision 1.26 diff -u -r1.26 cache.h --- engine/cache.h 5 Mar 2003 18:52:56 -0000 1.26 +++ engine/cache.h 8 Apr 2003 19:24:33 -0000 @@ -214,9 +214,12 @@ /* Trace messages in decidestring/decidedragon sgf file. */ void sgf_trace(const char *func, int str, int move, int result, const char *message); -/* Trace messages in decideconnection/decidesemeai sgf file. */ +/* Trace messages in decideconnection sgf file. */ void sgf_trace2(const char *func, int str1, int str2, int move, const char* result, const char *message); +/* Trace messages in decidesemeai sgf file. */ +void sgf_trace_semeai(const char *func, int str1, int str2, int move, + int result1, int result2, const char *message); /* Macro to hide the call to sgf_trace(). Notice that a little black * magic is going on here. Before using this macro, SETUP_TRACE_INFO @@ -236,10 +239,10 @@ sgf_trace2(read_function_name, q1, q2, move, \ result_to_string(result), message) -#define SGFTRACE_SEMEAI(move, result, message) \ +#define SGFTRACE_SEMEAI(move, result1, result2, message) \ if (sgf_dumptree) \ - sgf_trace2(read_function_name, q1, q2, move, \ - safety_to_string(result), message) + sgf_trace_semeai(read_function_name, q1, q2, move, \ + result1, result2, message) int get_read_result(int routine, int komaster, int kom_pos, Index: engine/gnugo.h =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/gnugo.h,v retrieving revision 1.91 diff -u -r1.91 gnugo.h --- engine/gnugo.h 22 Feb 2003 10:54:24 -0000 1.91 +++ engine/gnugo.h 8 Apr 2003 19:24:35 -0000 @@ -437,8 +437,6 @@ int is_illegal_ko_capture(int pos, int color); int trymove(int pos, int color, const char *message, int str, int komaster, int kom_pos); -int semeai_trymove(int pos, int color, const char *message, - int str1, int str2, int owl_phase, int value); int tryko(int pos, int color, const char *message, int komaster, int kom_pos); void popgo(void); 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 8 Apr 2003 19:24:39 -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 8 Apr 2003 19:24:49 -0000 @@ -163,6 +163,7 @@ struct eyevalue *probable_eyes, int *eyemin, int *eyemax); static int owl_estimate_life(struct local_owl_data *owl, + struct local_owl_data *second_owl, struct owl_move_data vital_moves[MAX_MOVES], const char **live_reason, int komaster, int does_attack, @@ -192,15 +193,28 @@ static void compute_owl_escape_values(struct local_owl_data *owl); static int owl_escape_route(struct local_owl_data *owl); static void do_owl_analyze_semeai(int apos, int bpos, - struct local_owl_data *owla, - struct local_owl_data *owlb, int komaster, - int *resulta, int *resultb, - int *move, int pass, int owl_phase); + struct local_owl_data *owla, + struct local_owl_data *owlb, + int komaster, int kom_pos, + int *resulta, int *resultb, + int *move, int pass, int owl_phase); +static void semeai_add_sgf_comment(int value, int owl_phase); +static int semeai_trust_tactical_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, + int *critical_semeai_worms); static int semeai_move_value(int move, struct local_owl_data *owla, - struct local_owl_data *owlb, - int raw_value); + struct local_owl_data *owlb, int raw_value, + int *critical_semeai_worms); 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 +257,7 @@ #define MAX_SEMEAI_WORMS 20 static int s_worms = 0; static int semeai_worms[MAX_SEMEAI_WORMS]; +static int important_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 +281,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 +309,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) { + important_semeai_worms[s_worms] = 1; + semeai_worms[s_worms++] = str; + DEBUG(DEBUG_SEMEAI, "important semeai worm: %1m\n", str); + } + else if (owl_substantial(str) && s_worms < MAX_SEMEAI_WORMS) { + important_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); @@ -325,8 +354,21 @@ else semeai_focus = NO_MOVE; - do_owl_analyze_semeai(apos, bpos, owla, owlb, EMPTY, + do_owl_analyze_semeai(apos, bpos, owla, owlb, EMPTY, NO_MOVE, resulta, resultb, move, 0, owl); + if (resulta) { + if (*resulta != 0) + *resulta = ALIVE; + else + *resulta = DEAD; + } + + if (resultb) { + if (*resultb != 0) + *resultb = DEAD; + else + *resultb = ALIVE; + } } /* It is assumed that the 'a' player moves first, and @@ -346,7 +388,8 @@ static void do_owl_analyze_semeai(int apos, int bpos, struct local_owl_data *owla, - struct local_owl_data *owlb, int komaster, + struct local_owl_data *owlb, + int komaster, int kom_pos, int *resulta, int *resultb, int *move, int pass, int owl_phase) { @@ -365,30 +408,38 @@ 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; - int best_resulta = UNKNOWN; - int best_resultb = UNKNOWN; + int best_resulta = 0; + int best_resultb = 0; int best_move = 0; - int this_resulta = UNKNOWN; - int this_resultb = UNKNOWN; - int best_move_k = -1; + const char *best_move_name = NULL; + int this_resulta = -1; + int this_resultb = -1; + int new_komaster = EMPTY; + int new_kom_pos = NO_MOVE; + int ko_move = 0; Read_result *read_result; int this_variation_number = count_variations - 1; + int you_look_alive = 0; + int I_look_alive = 0; + int dummy_move; + int tested_moves; + int critical_semeai_worms[MAX_SEMEAI_WORMS]; 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 +453,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) @@ -429,7 +480,7 @@ this_variation_number, bpos); SGFTRACE_SEMEAI(rr_get_move(*read_result), rr_get_result1(*read_result), - "cached"); + rr_get_result2(*read_result), "cached"); *resulta = rr_get_result1(*read_result); *resultb = rr_get_result2(*read_result); return; @@ -443,9 +494,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; @@ -466,34 +518,46 @@ int upos; int sworm; - for (sworm = 0; sworm <= s_worms; sworm++) + for (sworm = 0; sworm <= s_worms; sworm++) { + critical_semeai_worms[sworm] = 0; 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_tactical_attack(semeai_worms[sworm]) + && important_semeai_worms[sworm]) { + *resulta = WIN; + *resultb = WIN; + *move = upos; + sgf_dumptree = save_sgf_dumptree; + count_variations = save_count_variations; + SGFTRACE_SEMEAI(upos, WIN, WIN, "tactical win found"); + close_pattern_list(color, &shape_defensive_patterns); + close_pattern_list(color, &shape_offensive_patterns); + READ_RETURN_SEMEAI(read_result, move, upos, WIN, WIN); + } + else if (find_defense(semeai_worms[sworm], NULL)) { + critical_semeai_worms[sworm] = 1; + owl_add_move(moves, upos, 95, "attack semeai worm", 1, 0, NO_MOVE, + MAX_SEMEAI_MOVES); + TRACE("Added %1m %d (-1)\n", upos, 95); + } } + } /* Look for a tactical rescue. If a semeai worm of owla is tactically * threatened, try to save it. */ 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)) - owl_add_move(moves, upos, 160, "defend semeai worm", 1, 0, NO_MOVE, + && find_defense(semeai_worms[sworm], &upos)) { + critical_semeai_worms[sworm] = 1; + owl_add_move(moves, upos, 85, "defend semeai worm", 1, 0, NO_MOVE, MAX_SEMEAI_MOVES); + TRACE("Added %1m %d (0)\n", upos, 85); + } } - /* - * 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 @@ -507,6 +571,11 @@ */ struct eyevalue probable_eyes_a; struct eyevalue probable_eyes_b; + int eyemin_a; + int eyemin_b; + int eyemax_a; + int eyemax_b; + const char *live_reason; /* We do not wish for any string of the 'b' dragon to be * counted as a lunch of the 'a' dragon since owl_determine_life @@ -531,165 +600,53 @@ } #endif - owl_determine_life(owla, owlb, komaster, 1, vital_defensive_moves, - &probable_eyes_a, NULL, NULL); - if (level >= 9) { - current_owl_data = owla; - matches_found = 0; - memset(found_matches, 0, sizeof(found_matches)); - matchpat(owl_shapes_callback, other, - &owl_vital_apat_db, vital_defensive_moves, owla->goal); - /* FIXME: This is kind of quick and dirty. */ - if (probable_eyes_a.b > matches_found) - probable_eyes_a.b -= matches_found; - else - probable_eyes_a.b = 0; - } - - owl_determine_life(owlb, owla, komaster, 1, vital_offensive_moves, - &probable_eyes_b, NULL, NULL); - if (level >= 9) { - current_owl_data = owlb; - matches_found = 0; - memset(found_matches, 0, sizeof(found_matches)); - matchpat(owl_shapes_callback, color, - &owl_vital_apat_db, vital_offensive_moves, owlb->goal); - /* FIXME: This is kind of quick and dirty. */ - if (probable_eyes_b.b > matches_found) - probable_eyes_b.b -= matches_found; - else - probable_eyes_b.b = 0; - } - /* Certain cases can be handled immediately. */ - /* 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; + if (owl_estimate_life(owla, owlb, vital_defensive_moves, + &live_reason, komaster, 0, &probable_eyes_a, + &eyemin_a, &eyemax_a) + || (stackp > 2 && owl_escape_route(owla) >= 5)) + I_look_alive = 1; + + if (owl_estimate_life(owlb, owla, vital_offensive_moves, + &live_reason, komaster, 1, &probable_eyes_b, + &eyemin_b, &eyemax_b) + || (stackp > 2 && owl_escape_route(owlb) >= 5)) + you_look_alive = 1; + + if (verbose) { + gprintf("probable_eyes_a: %s eyemin: %d eyemax: %d", + eyevalue_to_string(&probable_eyes_a), eyemin_a, eyemax_a); + if (I_look_alive) + gprintf("%o I look alive"); + gprintf("%o\n"); + gprintf("probable_eyes_b: %s eyemin: %d eyemax: %d", + eyevalue_to_string(&probable_eyes_b), eyemin_b, eyemax_b); + if (you_look_alive) + gprintf("%o you look alive"); + gprintf("%o\n"); + } + + /* Stop here if both look certain to live. */ + if (I_look_alive && you_look_alive) { + *resulta = WIN; + *resultb = 0; + *move = PASS_MOVE; sgf_dumptree = save_sgf_dumptree; count_variations = save_count_variations; - if (max_eyes(&probable_eyes_b) == 0) { - TRACE("Two eyes versus none\n"); - SGFTRACE_SEMEAI(PASS_MOVE, ALIVE, "Two eyes versus none"); - } - else { - TRACE("Two eyes versus one\n"); - SGFTRACE_SEMEAI(PASS_MOVE, ALIVE, "Two eyes versus one"); - } - READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, ALIVE, DEAD); - } - /* I am alive */ - if (min_eyes(&probable_eyes_a) >= 2 - || (stackp > 2 && owl_escape_route(owla) >= 5)) { - if (max_eyes(&probable_eyes_b) < 2) { - /* you are already dead */ - *resulta = ALIVE; - *resultb = DEAD; - if (move) *move = PASS_MOVE; - sgf_dumptree = save_sgf_dumptree; - count_variations = save_count_variations; - if (max_eyes(&probable_eyes_b) == 0) { - TRACE("Two eyes or escape versus none\n"); - SGFTRACE_SEMEAI(PASS_MOVE, ALIVE, "Two eyes or escape versus none"); - } - else { - TRACE("Two eyes or escape versus one\n"); - SGFTRACE_SEMEAI(PASS_MOVE, ALIVE, "Two eyes or escape versus one"); - } - READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, ALIVE, DEAD); - } - else if (min_eyes(&probable_eyes_b) >= 2) { - /* both live */ - *resulta = ALIVE; - *resultb = ALIVE; - if (move) *move = PASS_MOVE; - sgf_dumptree = save_sgf_dumptree; - count_variations = save_count_variations; - TRACE("Both live\n"); - SGFTRACE_SEMEAI(PASS_MOVE, ALIVE, "Both live"); - READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, ALIVE, ALIVE); - } - else if (vital_offensive_moves[0].pos != NO_MOVE){ - /* I can kill */ - *resulta = ALIVE; - *resultb = DEAD; - if (move) *move = vital_offensive_moves[0].pos; - sgf_dumptree = save_sgf_dumptree; - count_variations = save_count_variations; - if (min_eyes(&probable_eyes_b) == 0) { - TRACE("Two eyes or escape versus none, %1m kills certainly\n", - vital_offensive_moves[0].pos); - SGFTRACE_SEMEAI(vital_offensive_moves[0].pos, - ALIVE, "Two eyes or escape versus none"); - } - else { - TRACE("Two eyes or escape versus one, %1m kills certainly\n", - vital_offensive_moves[0].pos); - SGFTRACE_SEMEAI(vital_offensive_moves[0].pos, - ALIVE, "Two eyes or escape versus one"); - } - READ_RETURN_SEMEAI(read_result, move, vital_offensive_moves[0].pos, - ALIVE, DEAD); - } - /* If here, compute_eyes_pessimistic has returned min_eyes<2 - * and max_eyes>=2 yet produced no defensive move. - */ - else - DEBUG(DEBUG_SEMEAI, "inconsistent eyevalue\n"); - } - if (min_eyes(&probable_eyes_b) >= 2 - || (stackp > 2 && owl_escape_route(owlb) >= 5)) { - /* you are alive */ - if (max_eyes(&probable_eyes_a) < 2) { - /* I am dead */ - *resulta = DEAD; - *resultb = ALIVE; - if (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); - } - else if (min_eyes(&probable_eyes_a) >= 2) { - /* I am already alive */ - *resulta = ALIVE; - *resultb = ALIVE; - if (move) *move = PASS_MOVE; - sgf_dumptree = save_sgf_dumptree; - count_variations = save_count_variations; - TRACE("Both live\n"); - SGFTRACE_SEMEAI(PASS_MOVE, ALIVE, "Both live"); - READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, ALIVE, ALIVE); - } - else if (vital_defensive_moves[0].pos != NO_MOVE) { - /* I can live */ - *resulta = ALIVE; - *resultb = ALIVE; - if (move) *move = vital_defensive_moves[0].pos; - sgf_dumptree = save_sgf_dumptree; - count_variations = save_count_variations; - TRACE("Both live\n"); - SGFTRACE_SEMEAI(vital_defensive_moves[0].pos, ALIVE, "Both live"); - READ_RETURN_SEMEAI(read_result, move, vital_defensive_moves[0].pos, - ALIVE, ALIVE); - } - /* If here, compute_eyes_pessimistic has returned min_eyes<2 - * and max_eyes>=2 yet produced no defensive move. - */ - else - DEBUG(DEBUG_SEMEAI, "inconsistent eyevalue\n"); + TRACE("Both live\n"); + SGFTRACE_SEMEAI(PASS_MOVE, WIN, 0, "Both live"); + READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, WIN, 0); } /* 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,134 +660,51 @@ * 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, + critical_semeai_worms); + + semeai_review_owl_moves(shape_defensive_moves, owla, owlb, color, + &safe_outside_liberty_found, + &safe_common_liberty_found, + mw, moves, 0, 0, + critical_semeai_worms); } + + if (!you_look_alive) { + semeai_review_owl_moves(vital_offensive_moves, owla, owlb, color, + &safe_outside_liberty_found, + &safe_common_liberty_found, + mw, moves, 1, 30, + critical_semeai_worms); + + semeai_review_owl_moves(shape_offensive_moves, owla, owlb, color, + &safe_outside_liberty_found, + &safe_common_liberty_found, + mw, moves, 1, 0, + critical_semeai_worms); + } + +#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. */ - if (0) { + if (1 && verbose) { showboard(0); goaldump(owla->goal); goaldump(owlb->goal); } - if (!safe_outside_liberty_found && moves[0].value < 100) { + if (!you_look_alive + && !safe_outside_liberty_found && moves[0].value < 100) { int pos; for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (!ON_BOARD(pos)) @@ -845,11 +719,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,145 +729,303 @@ 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 (!you_look_alive) { + if (safe_outside_liberty_found + && outside_liberty.pos != NO_MOVE) { + move_value = semeai_move_value(outside_liberty.pos, + owla, owlb, 50, + critical_semeai_worms); + 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, + critical_semeai_worms); + 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, + critical_semeai_worms); + 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, + critical_semeai_worms); + 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 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 (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 + && trymove(mpos, color, moves[k].name, apos, komaster, kom_pos)) { + semeai_add_sgf_comment(moves[k].value, owl_phase); popgo(); + } continue; } if (mpos != NO_MOVE && count_variations < semeai_variations && stackp < MAX_SEMEAI_DEPTH - && semeai_trymove(mpos, color, moves[k].name, apos, bpos, - owl_phase, moves[k].value)) { + && komaster_trymove(mpos, color, moves[k].name, apos, + komaster, kom_pos, + &new_komaster, &new_kom_pos, &ko_move, + best_resulta == 0 || best_resultb == 0)) { + semeai_add_sgf_comment(moves[k].value, owl_phase); + tested_moves++; TRACE("Trying %C %1m. Current stack: ", color, mpos); if (verbose) dump_stack(); + TRACE("%s, value %d, same_dragon %d\n", moves[k].name, moves[k].value, + moves[k].same_dragon); + + 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; + owlb->lunches_are_current = 0; + owl_update_boundary_marks(mpos, owlb); + if (board[bpos] == EMPTY) { - this_resultb = DEAD; - this_resulta = ALIVE; + this_resulta = REVERSE_RESULT(do_owl_attack(apos, NULL, NULL, owla, + new_komaster, new_kom_pos, + 0)); + this_resultb = this_resulta; } 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)); + do_owl_analyze_semeai(bpos, apos, owlb, owla, + new_komaster, new_kom_pos, + &this_resultb, &this_resulta, + NULL, 0, owl_phase); + this_resulta = REVERSE_RESULT(this_resulta); + this_resultb = REVERSE_RESULT(this_resultb); } + 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 (ko_move) { + if (this_resulta != 0) + this_resulta = KO_B; + if (this_resultb != 0) + this_resultb = KO_B; + } - if (this_resultb == DEAD && this_resulta == ALIVE) { - *resulta = ALIVE; - *resultb = DEAD; - if (move) *move = mpos; + if (this_resultb == WIN && this_resulta == WIN) { + *resulta = WIN; + *resultb = WIN; + *move = mpos; TRACE("After %1m I (%C) am alive, you are dead\n", mpos, color); - SGFTRACE_SEMEAI(mpos, ALIVE, moves[k].name); + SGFTRACE_SEMEAI(mpos, WIN, WIN, moves[k].name); close_pattern_list(color, &shape_defensive_patterns); close_pattern_list(color, &shape_offensive_patterns); - READ_RETURN_SEMEAI(read_result, move, mpos, ALIVE, DEAD); - } - if (this_resulta == ALIVE_IN_SEKI - && this_resultb == ALIVE_IN_SEKI - && best_resulta != ALIVE) { - best_resulta = ALIVE_IN_SEKI; - best_resultb = ALIVE_IN_SEKI; - best_move = mpos; - best_move_k = k; + READ_RETURN_SEMEAI(read_result, move, mpos, WIN, WIN); } - if (this_resulta == DEAD - && this_resultb == ALIVE - && best_resulta == UNKNOWN) { - best_resulta = DEAD; - best_resultb = ALIVE; + else if (this_resulta > best_resulta + || (this_resulta == best_resulta + && this_resultb > best_resultb)) { + best_resulta = this_resulta; + best_resultb = this_resultb; best_move = mpos; - best_move_k = k; + best_move_name = moves[k].name; } } } close_pattern_list(color, &shape_defensive_patterns); close_pattern_list(color, &shape_offensive_patterns); + + if (tested_moves <= 1 && count_variations >= semeai_variations) { + *resulta = 0; + *resultb = 0; + *move = PASS_MOVE; + SGFTRACE_SEMEAI(PASS_MOVE, 0, 0, "Out of nodes"); + READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, 0, 0); + } + + /* If we can't find a move and the opponent looks alive, we have + * lost. + */ + if (tested_moves == 0 && you_look_alive) { + *resulta = 0; + *resultb = 0; + *move = PASS_MOVE; + SGFTRACE_SEMEAI(PASS_MOVE, 0, 0, "You live, I die"); + READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, 0, 0); + } + /* 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; + if (tested_moves == 0 && pass == 1) { + *resulta = WIN; + *resultb = 0; + *move = PASS_MOVE; TRACE("Seki\n"); - SGFTRACE_SEMEAI(PASS_MOVE, ALIVE_IN_SEKI, "Seki"); - READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, - ALIVE_IN_SEKI, ALIVE_IN_SEKI); + SGFTRACE_SEMEAI(PASS_MOVE, WIN, 0, "Seki"); + READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, WIN, 0); } + /* If no move was found, then pass */ - if (best_resulta == UNKNOWN) { - do_owl_analyze_semeai(bpos, apos, owlb, owla, komaster, + if (tested_moves == 0) { + do_owl_analyze_semeai(bpos, apos, owlb, owla, komaster, kom_pos, resultb, resulta, NULL, 1, owl_phase); + *resulta = REVERSE_RESULT(*resulta); + *resultb = REVERSE_RESULT(*resultb); TRACE("No move found\n"); - SGFTRACE_SEMEAI(PASS_MOVE, UNKNOWN, "No move found"); - if (move) *move = PASS_MOVE; + SGFTRACE_SEMEAI(PASS_MOVE, *resulta, *resultb, "No move found"); + *move = PASS_MOVE; READ_RETURN_SEMEAI(read_result, move, PASS_MOVE, *resulta, *resultb); } *resulta = best_resulta; *resultb = best_resultb; - if (best_resulta == DEAD) + if (best_resulta == 0) best_move = PASS_MOVE; - if (move) *move = best_move; - SGFTRACE_SEMEAI(best_move, best_resulta, moves[best_move_k].name); + *move = best_move; + SGFTRACE_SEMEAI(best_move, best_resulta, best_resultb, best_move_name); READ_RETURN_SEMEAI(read_result, move, best_move, best_resulta, best_resultb); } - + + +/* Add details in sgf file about move value and whether owl_phase is active. */ +static void +semeai_add_sgf_comment(int value, int owl_phase) +{ + char buf[100]; + + if (!sgf_dumptree) + return; + + if (owl_phase) + gg_snprintf(buf, 100, "value %d, owl_phase", value); + else + gg_snprintf(buf, 100, "value %d", value); + sgftreeAddComment(sgf_dumptree, buf); +} + + +/* 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_tactical_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 *critical_semeai_worms) +{ + 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 = 1; + 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, + critical_semeai_worms) + + 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. @@ -1004,23 +1034,21 @@ static int semeai_move_value(int move, struct local_owl_data *owla, struct local_owl_data *owlb, - int raw_value) + int raw_value, int *critical_semeai_worms) { int pos; int net = 0; int color = owla->color; int save_verbose = verbose; + int k; + int bonus = 0; ASSERT1(board[move] == EMPTY, move); 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 +1060,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); @@ -1045,6 +1069,20 @@ net -= 100*countlib(pos); } } + + increase_depth_values(); + for (k = 0; k <= s_worms; k++) { + if (!critical_semeai_worms[k]) + continue; + if (board[semeai_worms[k]] == color + && !attack(semeai_worms[k], NULL)) + bonus += 50; + else if (board[semeai_worms[k]] == OTHER_COLOR(color) + && !find_defense(semeai_worms[k], NULL)) + bonus += 50; + } + decrease_depth_values(); + popgo(); } @@ -1052,12 +1090,11 @@ if (net < 0) net = 0; -#if 0 - if (semeai_focus != EMPTY - && move_focus == semeai_focus) - return raw_value + net + 50; -#endif - return raw_value + net; + + net /= 25; + net *= 3; + + return raw_value + net + bonus; } @@ -1077,6 +1114,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 +1151,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)) { @@ -1332,7 +1377,7 @@ memset(owl->safe_move_cache, 0, sizeof(owl->safe_move_cache)); /* First see whether there is any chance to kill. */ - if (owl_estimate_life(owl, vital_moves, &live_reason, komaster, 1, + if (owl_estimate_life(owl, NULL, vital_moves, &live_reason, komaster, 1, &probable_eyes, &eyemin, &eyemax)) { /* * We need to check here if there's a worm under atari. If yes, @@ -1647,9 +1692,10 @@ > worm[owl_goal_worm[saveworm]].size) saveworm = wid; } - /* the conditions here are set so that this code doesn't get - triggered when the capture is immediate (the tactical reading - code should take care of these) */ + /* The conditions here are set so that this code doesn't get + * triggered when the capture is immediate (the tactical + * reading code should take care of these). + */ else if (experimental_owl_ext && goal_worms_computed #if 0 && stackp>1 @@ -2008,7 +2054,7 @@ /* First see whether we might already be alife. */ if (escape < MAX_ESCAPE) { - if (owl_estimate_life(owl, vital_moves, &live_reason, komaster, 0, + if (owl_estimate_life(owl, NULL, vital_moves, &live_reason, komaster, 0, &probable_eyes, &eyemin, &eyemax)) { SGFTRACE(0, WIN, live_reason); TRACE("%oVariation %d: ALIVE (%s)\n", @@ -2343,6 +2389,7 @@ */ static int owl_estimate_life(struct local_owl_data *owl, + struct local_owl_data *second_owl, struct owl_move_data vital_moves[MAX_MOVES], const char **live_reason, int komaster, int does_attack, struct eyevalue *probable_eyes, int *eyemin, int *eyemax) @@ -2355,7 +2402,7 @@ sgf_dumptree = NULL; count_variations = 0; - owl_determine_life(owl, NULL, komaster, does_attack, vital_moves, + owl_determine_life(owl, second_owl, komaster, does_attack, vital_moves, probable_eyes, eyemin, eyemax); matches_found = 0; @@ -2793,16 +2840,18 @@ eyevalue_list[num_eyes++] = e; } - TRACE("save lunch at %1m with %1m, score %d\n", - owl->lunch[lunch], defense_point, value); + TRACE("save lunch at %1m with %1m, score %d, probable eye %d, max eye %d\n", + owl->lunch[lunch], defense_point, value, + lunch_probable, lunch_max); owl_add_move(moves, defense_point, value, "save lunch", 1, 0, NO_MOVE, MAX_MOVES); } else { attack_point = improve_lunch_attack(owl->lunch[lunch], owl->lunch_attack_point[lunch]); - TRACE("eat lunch at %1m with %1m, score %d\n", - owl->lunch[lunch], attack_point, value); + TRACE("eat lunch at %1m with %1m, score %d, probable eye %d, max eye %d\n", + owl->lunch[lunch], attack_point, value, + lunch_probable, lunch_max); owl_add_move(moves, attack_point, value, "eat lunch", 1, 0, NO_MOVE, MAX_MOVES); num_lunch++; @@ -3192,7 +3241,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 +3810,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 +3837,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 +3852,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 +4655,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_tactical_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 8 Apr 2003 19:25:02 -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 8 Apr 2003 19:25:06 -0000 @@ -566,6 +566,18 @@ :8,-,value(35) +Pattern A208c +# gf New pattern. (3.3.18) + +?Xx +O.X +.*Y +.oo +--- + +:8,-,value(56) + + Pattern A209 ?Ooo @@ -4119,6 +4131,34 @@ ...o? :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?? save one lunch and make one a false eye +|OYX? +|*OXX +|..OX ++---- + +:8,-,value(70) ######################################################### Index: patterns/owl_defendpats.db =================================================================== RCS file: /cvsroot/gnugo/gnugo/patterns/owl_defendpats.db,v retrieving revision 1.88 diff -u -r1.88 owl_defendpats.db --- patterns/owl_defendpats.db 5 Apr 2003 17:43:25 -0000 1.88 +++ patterns/owl_defendpats.db 8 Apr 2003 19:25:11 -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 @@ -6266,6 +6268,7 @@ Pattern D1378 # tm reduced value (3.1.17) +# gf Revised constraint. (3.3.18) X*X breakout & attack ?O? @@ -6276,7 +6279,7 @@ ?O? ; vital_chain(A) && vital_chain(B) -; && oplay_attack_either(*,A,B) +; && oplay_attack_either(*,A,B) && !oplay_connect(*,A,B) Pattern D1379 @@ -6476,6 +6479,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) ######################################################### Index: regression/nngs3.tst =================================================================== RCS file: /cvsroot/gnugo/gnugo/regression/nngs3.tst,v retrieving revision 1.39 diff -u -r1.39 nngs3.tst --- regression/nngs3.tst 7 Apr 2003 03:47:40 -0000 1.39 +++ regression/nngs3.tst 8 Apr 2003 19:25:13 -0000 @@ -354,6 +354,10 @@ #? [E17]* +# FIXME: Test cases 790-860 all need revision to take the semeai in +# the upper left corner into account. Restricted genmove would be +# appropriate in most cases. (gf 3.3.18) + loadsgf games/nngs/Lazarus-gnugo-3.2-200205011927.sgf 80 790 gg_genmove black #? [D5] Index: regression/reading.tst =================================================================== RCS file: /cvsroot/gnugo/gnugo/regression/reading.tst,v retrieving revision 1.51 diff -u -r1.51 reading.tst --- regression/reading.tst 3 Apr 2003 00:55:37 -0000 1.51 +++ regression/reading.tst 8 Apr 2003 19:25:14 -0000 @@ -745,6 +745,11 @@ 180 defend P1 #? [2 Q3]* +# See also trevora:480. F5 doesn't work. +loadsgf games/trevor/auto/a031.sgf 40 +181 defend E3 +#? [1 E4] + # Report number of nodes visited by the tactical reading 10000 get_reading_node_counter #? [0]& Index: regression/semeai.tst =================================================================== RCS file: /cvsroot/gnugo/gnugo/regression/semeai.tst,v retrieving revision 1.31 diff -u -r1.31 semeai.tst --- regression/semeai.tst 7 Apr 2003 03:47:40 -0000 1.31 +++ regression/semeai.tst 8 Apr 2003 19:25:14 -0000 @@ -86,32 +86,32 @@ loadsgf games/semeai/semeai6.sgf 1 owl_analyze_semeai C1 E1 -#? [ALIVE_IN_SEKI ALIVE_IN_SEKI (F1|F2|F3|F4|F5|PASS)] +#? [ALIVE ALIVE (F1|F2|F3|F4|F5|PASS)] 2 owl_analyze_semeai E1 C1 -#? [ALIVE_IN_SEKI ALIVE_IN_SEKI (B5|B4|B3|PASS)] +#? [ALIVE ALIVE (B5|B4|B3|PASS)] 3 owl_analyze_semeai L1 N1 -#? [ALIVE_IN_SEKI ALIVE_IN_SEKI (O5|O4|O3|P3|Q2|Q1|PASS)] +#? [ALIVE ALIVE (O5|O4|O3|P3|Q2|Q1|PASS)] 4 owl_analyze_semeai N1 L1 -#? [ALIVE_IN_SEKI ALIVE_IN_SEKI (H2|J3|K3|PASS)] +#? [ALIVE ALIVE (H2|J3|K3|PASS)] 5 owl_analyze_semeai A14 A12 #? [ALIVE DEAD (A13|B13|C13|D13)] 6 owl_analyze_semeai A12 A14 -#? [ALIVE DEAD (A15|B15|C15|D15)] +#? [ALIVE DEAD (A15|B15|C15|D15|E15)] loadsgf games/semeai/semeai7.sgf 7 owl_analyze_semeai C1 E1 -#? [ALIVE_IN_SEKI ALIVE_IN_SEKI (F1|F2|F3|F4|F5|PASS)] +#? [ALIVE ALIVE (F1|F2|F3|F4|F5|PASS)] 8 owl_analyze_semeai E1 C1 #? [ALIVE DEAD (B2|B3)] 9 owl_analyze_semeai L1 N1 -#? [ALIVE_IN_SEKI ALIVE_IN_SEKI (O5|O4|O3|P3|Q2|Q1)] +#? [ALIVE ALIVE (O5|O4|O3|P3|Q2|Q1)] 10 owl_analyze_semeai N1 L1 #? [ALIVE DEAD (H2|K3)] @@ -139,7 +139,7 @@ #? [DEAD ALIVE PASS] 18 owl_analyze_semeai A12 A14 -#? [ALIVE DEAD (A15|B15|D15|PASS)] +#? [ALIVE DEAD (A15|B15|D15|E15|PASS)] loadsgf games/semeai/semeai9.sgf @@ -153,7 +153,7 @@ #? [ALIVE DEAD (T17|T19)] 22 owl_analyze_semeai O19 N19 -#? [ALIVE DEAD (L15|L17|L18)] +#? [ALIVE DEAD (L16|L17|L18|M19)] loadsgf golois/Goemate990902-1.sgf @@ -191,7 +191,7 @@ loadsgf games/nicklas/nicklas14.sgf 55 32 owl_analyze_semeai B8 D9 -#? [ALIVE_IN_SEKI ALIVE_IN_SEKI B6] +#? [ALIVE ALIVE B6] loadsgf games/nicklas/nicklas14.sgf 55 33 owl_analyze_semeai D9 B8 @@ -205,7 +205,7 @@ # ab added (3.1.22) loadsgf games/mertin13x13/gnugo-goliath2.W+38.sgf 61 35 owl_analyze_semeai M13 M11 -#? [ALIVE DEAD PASS]* +#? [ALIVE DEAD (PASS|N13|N10|H11)]* # See also reading:166 loadsgf games/nngs/Lazarus-gnugo-3.1.19-200201092246.sgf 66 @@ -218,10 +218,11 @@ loadsgf games/semeai/semeai11.sgf 38 owl_analyze_semeai B9 B8 -#? [ALIVE DEAD PASS]* +#? [ALIVE DEAD (PASS|B1|A9)]* # See also global:3. loadsgf golois/Aya991113-13.sgf +# Correct answer is ko dependent (2 2 H4). 39 owl_analyze_semeai R12 H9 #? [!ALIVE DEAD J4]* @@ -237,7 +238,6 @@ #? [ALIVE DEAD (A15|A11|B11|C14|C13|D12|D11|D10|D9|B9|A9)]* 44 owl_analyze_semeai A12 A16 #? [ALIVE DEAD (A17|B17|C16|D16|E15|E14|F13|F12|F11|F10|F9|F8|B9|A9)] - ########### end of semeai tests ################# Index: regression/semeai_rot.tst =================================================================== RCS file: /cvsroot/gnugo/gnugo/regression/semeai_rot.tst,v retrieving revision 1.1 diff -u -r1.1 semeai_rot.tst --- regression/semeai_rot.tst 28 Dec 2002 01:39:44 -0000 1.1 +++ regression/semeai_rot.tst 8 Apr 2003 19:25:14 -0000 @@ -18,5 +18,5 @@ orientation 1 loadsgf games/nicklas/nicklas14.sgf 55 32 owl_analyze_semeai B8 D9 -#? [ALIVE_IN_SEKI ALIVE_IN_SEKI B6]* +#? [ALIVE ALIVE B6]* Index: regression/trevora.tst =================================================================== RCS file: /cvsroot/gnugo/gnugo/regression/trevora.tst,v retrieving revision 1.28 diff -u -r1.28 trevora.tst --- regression/trevora.tst 3 Mar 2003 04:30:50 -0000 1.28 +++ regression/trevora.tst 8 Apr 2003 19:25:14 -0000 @@ -308,6 +308,7 @@ #Sacrificing these two for the rest seems best. +# See also reading:181. loadsgf games/trevor/auto/a031.sgf 40 480 gg_genmove white #? [E4]