| 2311 | | int |
| 2312 | | owl_threaten_attack(int target, int *attack1, int *attack2) |
| 2313 | | { |
| 2314 | | struct owl_move_data moves[MAX_MOVES]; |
| 2315 | | int k; |
| 2316 | | int other = OTHER_COLOR(board[target]); |
| 2317 | | struct local_owl_data *owl; |
| 2318 | | int result = 0; |
| 2319 | | int reading_nodes_when_called = get_reading_node_counter(); |
| 2320 | | signed char saved_boundary[BOARDMAX]; |
| 2321 | | double start = 0.0; |
| 2322 | | int tactical_nodes; |
| 2323 | | int move = 0; |
| 2324 | | int move2 = 0; |
| 2325 | | struct matched_patterns_list_data shape_patterns; |
| 2326 | | |
| 2327 | | shape_patterns.initialized = 0; |
| 2328 | | result_certain = 1; |
| 2329 | | if (search_persistent_owl_cache(OWL_THREATEN_ATTACK, target, 0, 0, |
| 2330 | | &result, attack1, attack2, NULL)) |
| 2331 | | return result; |
| 2332 | | |
| 2333 | | if (debug & DEBUG_OWL_PERFORMANCE) |
| 2334 | | start = gg_cputime(); |
| 2335 | | |
| 2336 | | gg_assert(stackp == 0); |
| 2337 | | TRACE("owl_threaten_attack %1m\n", target); |
| 2338 | | init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); |
| 2339 | | memcpy(saved_boundary, owl->boundary, sizeof(saved_boundary)); |
| 2340 | | owl_make_domains(owl, NULL); |
| 2341 | | owl_shapes(&shape_patterns, moves, other, owl, &owl_attackpat_db); |
| 2342 | | for (k = 0; k < MAX_MOVES; k++) { |
| 2343 | | current_owl_data = owl; |
| 2344 | | if (!get_next_move_from_list(&shape_patterns, other, moves, 1, owl)) |
| 2345 | | break; |
| 2346 | | else { |
| 2347 | | int mpos = moves[k].pos; |
| 2348 | | |
| 2349 | | if (mpos != NO_MOVE && moves[k].value > 0) |
| 2350 | | if (trymove(mpos, other, moves[k].name, target)) { |
| 2351 | | int pos; |
| 2352 | | int origin = NO_MOVE; |
| 2353 | | owl->lunches_are_current = 0; |
| 2354 | | owl_update_boundary_marks(mpos, owl); |
| 2355 | | |
| 2356 | | /* If the origin of the dragon has been captured, we look |
| 2357 | | * for another string which was part of the original dragon, |
| 2358 | | * marked when stackp==0, which has not been captured. If no |
| 2359 | | * such string is found, owl_attack declares victory. |
| 2360 | | */ |
| 2361 | | |
| 2362 | | if (board[target] == EMPTY) { |
| 2363 | | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { |
| 2364 | | if (IS_STONE(board[pos]) && owl->goal[pos] == 1) { |
| 2365 | | origin = find_origin(pos); |
| 2366 | | break; |
| 2367 | | } |
| 2368 | | } |
| 2369 | | |
| 2370 | | if (origin == NO_MOVE |
| 2371 | | || do_owl_attack(origin, NULL, NULL, owl, 0)) { |
| 2372 | | /* probably this can't happen */ |
| 2373 | | popgo(); |
| 2374 | | gg_assert(stackp == 0); |
| 2375 | | result = 1; |
| 2376 | | break; |
| 2377 | | } |
| 2378 | | } |
| 2379 | | else if (do_owl_attack(target, &move2, NULL, owl, 0) == WIN) { |
| 2380 | | move = moves[k].pos; |
| 2381 | | popgo(); |
| 2382 | | gg_assert(stackp == 0); |
| 2383 | | result = 1; |
| 2384 | | break; |
| 2385 | | } |
| 2386 | | popgo(); |
| 2387 | | memcpy(owl->boundary, saved_boundary, sizeof(saved_boundary)); |
| 2388 | | } |
| 2389 | | } |
| 2390 | | } |
| 2391 | | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; |
| 2392 | | gg_assert(stackp == 0); |
| 2393 | | |
| 2394 | | DEBUG(DEBUG_OWL_PERFORMANCE, |
| 2395 | | "owl_threaten_attack %1m %1m %1m, result %d (%d, %d nodes, %f seconds)\n", |
| 2396 | | target, move, move2, result, local_owl_node_counter, |
| 2397 | | tactical_nodes, gg_cputime() - start); |
| 2398 | | |
| 2399 | | store_persistent_owl_cache(OWL_THREATEN_ATTACK, target, 0, 0, |
| 2400 | | result, move, move2, 0, |
| 2401 | | tactical_nodes, owl->goal, board[target]); |
| 2402 | | |
| 2403 | | if (attack1) |
| 2404 | | *attack1 = move; |
| 2405 | | if (attack2) |
| 2406 | | *attack2 = move2; |
| 2407 | | |
| 2408 | | close_pattern_list(other, &shape_patterns); |
| 2409 | | return result; |
| 2410 | | } |
| 2411 | | |
| 2412 | | |
| 2413 | | /* Returns true if a move can be found to defend the dragon |
| 2414 | | * at (target), in which case (*defense_point) is the recommended move. |
| 2415 | | * (defense_point) can be a null pointer if the result is not needed. |
| 2416 | | * |
| 2417 | | * The array goal marks the extent of the dragon. This must |
| 2418 | | * be maintained during reading. Call this function only when |
| 2419 | | * stackp==0; otherwise you can call do_owl_attack but you must |
| 2420 | | * set up the goal and boundary arrays by hand first. |
| 2421 | | * |
| 2422 | | * Returns KO_A or KO_B if the position is ko: |
| 2423 | | * |
| 2424 | | * - Returns KO_A if the defendse succeeds provided the defender is willing to |
| 2425 | | * ignore any ko threat (the defender makes the first ko capture). |
| 2426 | | * - Returns KO_B if the defense succeeds provided the defender has a ko threat |
| 2427 | | * which must be answered (the attacker makes the first ko capture). |
| 2428 | | * |
| 2429 | | * If GNU Go is compiled with `configure --enable-experimental-owl-ext' |
| 2430 | | * then a return codes of GAIN is also possible. |
| 2431 | | * |
| 2432 | | * - Returns LOSS if the defense succeeds but another worm of the |
| 2433 | | * defender's is captured in during the defense. The location |
| 2434 | | * of the killed worm is returned through the *kworm field. |
| 2435 | | * |
| 2436 | | * The array goal marks the extent of the dragon. This must |
| 2437 | | * be maintained during reading. |
| 2438 | | */ |
| 2439 | | |
| 2440 | | int |
| 2441 | | owl_defend(int target, int *defense_point, int *certain, int *kworm) |
| 2442 | | { |
| 2443 | | int result; |
| 2444 | | static struct local_owl_data *owl; |
| 2445 | | int reading_nodes_when_called = get_reading_node_counter(); |
| 2446 | | double start = 0.0; |
| 2447 | | int tactical_nodes; |
| 2448 | | int move = NO_MOVE; |
| 2449 | | int wpos = NO_MOVE; |
| 2450 | | int wid = MAX_GOAL_WORMS; |
| 2451 | | |
| 2452 | | result_certain = 1; |
| 2453 | | if (worm[target].unconditional_status == DEAD) |
| 2454 | | return 0; |
| 2455 | | |
| 2456 | | if (search_persistent_owl_cache(OWL_DEFEND, target, 0, 0, &result, |
| 2457 | | defense_point, kworm, certain)) |
| 2458 | | return result; |
| 2459 | | |
| 2460 | | if (debug & DEBUG_OWL_PERFORMANCE) |
| 2461 | | start = gg_cputime(); |
| 2462 | | |
| 2463 | | TRACE("owl_defend %1m\n", target); |
| 2464 | | init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); |
| 2465 | | owl_make_domains(owl, NULL); |
| 2466 | | prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, |
| 2467 | | kworm, 1); |
| 2468 | | result = do_owl_defend(target, &move, &wid, owl, 0); |
| 2469 | | finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); |
| 2470 | | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; |
| 2471 | | |
| 2472 | | DEBUG(DEBUG_OWL_PERFORMANCE, |
| 2473 | | "owl_defend %1m, result %d %1m (%d, %d nodes, %f seconds)\n", |
| 2474 | | target, result, move, local_owl_node_counter, |
| 2475 | | tactical_nodes, gg_cputime() - start); |
| 2476 | | |
| 2477 | | store_persistent_owl_cache(OWL_DEFEND, target, 0, 0, result, move, wpos, |
| 2478 | | result_certain, tactical_nodes, owl->goal, |
| 2479 | | board[target]); |
| 2480 | | |
| 2481 | | if (defense_point) |
| 2482 | | *defense_point = move; |
| 2483 | | if (kworm) |
| 2484 | | *kworm = wpos; |
| 2485 | | if (certain) |
| 2486 | | *certain = result_certain; |
| 2487 | | |
| 2488 | | return result; |
| 2489 | | } |
| 2490 | | |
| 2491 | | |
| 2492 | | /* Static function containing the main recursive code for owl_defend. |
| 2493 | | */ |
| 2494 | | |
| | 2601 | /* Returns true if a move can be found to attack the dragon |
| | 2602 | * at (target), in which case (*attack_point) is the recommended move. |
| | 2603 | * (attack_point) can be a null pointer if only the result is needed. |
| | 2604 | * |
| | 2605 | * The array goal marks the extent of the dragon. This must |
| | 2606 | * be maintained during reading. Call this function only when |
| | 2607 | * stackp==0; otherwise you can call do_owl_attack but you must |
| | 2608 | * set up the goal and boundary arrays by hand first. |
| | 2609 | * |
| | 2610 | * Returns KO_A or KO_B if the position is ko: |
| | 2611 | * |
| | 2612 | * - Returns KO_A if the attack prevails provided attacker is willing to |
| | 2613 | * ignore any ko threat (the attacker makes the first ko capture). |
| | 2614 | * |
| | 2615 | * - Returns KO_B if attack succeeds provided attacker has a ko threat |
| | 2616 | * which must be answered (the defender makes the first ko capture). |
| | 2617 | * |
| | 2618 | * If GNU Go is compiled with `configure --enable-experimental-owl-ext' |
| | 2619 | * then a return codes of GAIN is also possible. |
| | 2620 | * |
| | 2621 | * - Returns GAIN if the attack fails but another worm of the |
| | 2622 | * opponent's is captured in during the failed attack. The location |
| | 2623 | * of the killed worm is returned through the *kworm field. |
| | 2624 | * |
| | 2625 | */ |
| | 2626 | |
| | 2627 | int |
| | 2628 | owl_attack(int target, int *attack_point, int *certain, int *kworm) |
| | 2629 | { |
| | 2630 | int result; |
| | 2631 | struct local_owl_data *owl; |
| | 2632 | int reading_nodes_when_called = get_reading_node_counter(); |
| | 2633 | double start = 0.0; |
| | 2634 | int tactical_nodes; |
| | 2635 | int move = NO_MOVE; |
| | 2636 | int wpos = NO_MOVE; |
| | 2637 | int wid = MAX_GOAL_WORMS; |
| | 2638 | |
| | 2639 | result_certain = 1; |
| | 2640 | if (worm[target].unconditional_status == DEAD) { |
| | 2641 | if (attack_point) |
| | 2642 | *attack_point = NO_MOVE; |
| | 2643 | if (kworm) |
| | 2644 | *kworm = NO_MOVE; |
| | 2645 | if (certain) |
| | 2646 | *certain = 1; |
| | 2647 | return 1; |
| | 2648 | } |
| | 2649 | |
| | 2650 | if (search_persistent_owl_cache(OWL_ATTACK, target, 0, 0, &result, |
| | 2651 | attack_point, kworm, certain)) |
| | 2652 | return result; |
| | 2653 | |
| | 2654 | if (debug & DEBUG_OWL_PERFORMANCE) |
| | 2655 | start = gg_cputime(); |
| | 2656 | |
| | 2657 | TRACE("owl_attack %1m\n", target); |
| | 2658 | init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); |
| | 2659 | owl_make_domains(owl, NULL); |
| | 2660 | prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, |
| | 2661 | kworm, 1); |
| | 2662 | result = do_owl_attack(owl, &move, &wid); |
| | 2663 | finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); |
| | 2664 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; |
| | 2665 | |
| | 2666 | DEBUG(DEBUG_OWL_PERFORMANCE, |
| | 2667 | "owl_attack %1m, result %d %1m (%d, %d nodes, %f seconds)\n", |
| | 2668 | target, result, move, local_owl_node_counter, |
| | 2669 | tactical_nodes, gg_cputime() - start); |
| | 2670 | |
| | 2671 | store_persistent_owl_cache(OWL_ATTACK, target, 0, 0, |
| | 2672 | result, move, wpos, |
| | 2673 | result_certain, tactical_nodes, |
| | 2674 | owl->goal, board[target]); |
| | 2675 | if (attack_point) |
| | 2676 | *attack_point = move; |
| | 2677 | if (kworm) |
| | 2678 | *kworm = wpos; |
| | 2679 | if (certain) |
| | 2680 | *certain = result_certain; |
| | 2681 | |
| | 2682 | return result; |
| | 2683 | } |
| | 2684 | |
| | 2685 | |
| | 2686 | /* Returns true if a move can be found to defend the dragon |
| | 2687 | * at (target), in which case (*defense_point) is the recommended move. |
| | 2688 | * (defense_point) can be a null pointer if the result is not needed. |
| | 2689 | * |
| | 2690 | * The array goal marks the extent of the dragon. This must |
| | 2691 | * be maintained during reading. Call this function only when |
| | 2692 | * stackp==0; otherwise you can call do_owl_attack but you must |
| | 2693 | * set up the goal and boundary arrays by hand first. |
| | 2694 | * |
| | 2695 | * Returns KO_A or KO_B if the position is ko: |
| | 2696 | * |
| | 2697 | * - Returns KO_A if the defendse succeeds provided the defender is willing to |
| | 2698 | * ignore any ko threat (the defender makes the first ko capture). |
| | 2699 | * - Returns KO_B if the defense succeeds provided the defender has a ko threat |
| | 2700 | * which must be answered (the attacker makes the first ko capture). |
| | 2701 | * |
| | 2702 | * If GNU Go is compiled with `configure --enable-experimental-owl-ext' |
| | 2703 | * then a return codes of GAIN is also possible. |
| | 2704 | * |
| | 2705 | * - Returns LOSS if the defense succeeds but another worm of the |
| | 2706 | * defender's is captured in during the defense. The location |
| | 2707 | * of the killed worm is returned through the *kworm field. |
| | 2708 | * |
| | 2709 | * The array goal marks the extent of the dragon. This must |
| | 2710 | * be maintained during reading. |
| | 2711 | */ |
| | 2712 | |
| | 2713 | int |
| | 2714 | owl_defend(int target, int *defense_point, int *certain, int *kworm) |
| | 2715 | { |
| | 2716 | int result; |
| | 2717 | static struct local_owl_data *owl; |
| | 2718 | int reading_nodes_when_called = get_reading_node_counter(); |
| | 2719 | double start = 0.0; |
| | 2720 | int tactical_nodes; |
| | 2721 | int move = NO_MOVE; |
| | 2722 | int wpos = NO_MOVE; |
| | 2723 | int wid = MAX_GOAL_WORMS; |
| | 2724 | |
| | 2725 | result_certain = 1; |
| | 2726 | if (worm[target].unconditional_status == DEAD) |
| | 2727 | return 0; |
| | 2728 | |
| | 2729 | if (search_persistent_owl_cache(OWL_DEFEND, target, 0, 0, &result, |
| | 2730 | defense_point, kworm, certain)) |
| | 2731 | return result; |
| | 2732 | |
| | 2733 | if (debug & DEBUG_OWL_PERFORMANCE) |
| | 2734 | start = gg_cputime(); |
| | 2735 | |
| | 2736 | TRACE("owl_defend %1m\n", target); |
| | 2737 | init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); |
| | 2738 | owl_make_domains(owl, NULL); |
| | 2739 | prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, |
| | 2740 | kworm, 1); |
| | 2741 | result = do_owl_defend(owl, &move, &wid); |
| | 2742 | finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); |
| | 2743 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; |
| | 2744 | |
| | 2745 | DEBUG(DEBUG_OWL_PERFORMANCE, |
| | 2746 | "owl_defend %1m, result %d %1m (%d, %d nodes, %f seconds)\n", |
| | 2747 | target, result, move, local_owl_node_counter, |
| | 2748 | tactical_nodes, gg_cputime() - start); |
| | 2749 | |
| | 2750 | store_persistent_owl_cache(OWL_DEFEND, target, 0, 0, result, move, wpos, |
| | 2751 | result_certain, tactical_nodes, owl->goal, |
| | 2752 | board[target]); |
| | 2753 | |
| | 2754 | if (defense_point) |
| | 2755 | *defense_point = move; |
| | 2756 | if (kworm) |
| | 2757 | *kworm = wpos; |
| | 2758 | if (certain) |
| | 2759 | *certain = result_certain; |
| | 2760 | |
| | 2761 | return result; |
| | 2762 | } |
| | 2763 | |
| | 2764 | |
| | 2765 | /* Returns true if the dragon at (target) can be captured given |
| | 2766 | * two moves in a row. The first two moves to capture the |
| | 2767 | * dragon are given as (*attack1) and (*attack2). |
| | 2768 | */ |
| | 2769 | |
| | 2770 | int |
| | 2771 | owl_threaten_attack(int target, int *attack1, int *attack2) |
| | 2772 | { |
| | 2773 | struct owl_move_data moves[MAX_MOVES]; |
| | 2774 | int k; |
| | 2775 | int other = OTHER_COLOR(board[target]); |
| | 2776 | struct local_owl_data *owl; |
| | 2777 | int result = 0; |
| | 2778 | int reading_nodes_when_called = get_reading_node_counter(); |
| | 2779 | signed char saved_boundary[BOARDMAX]; |
| | 2780 | double start = 0.0; |
| | 2781 | int tactical_nodes; |
| | 2782 | int move = 0; |
| | 2783 | int move2 = 0; |
| | 2784 | struct matched_patterns_list_data shape_patterns; |
| | 2785 | |
| | 2786 | shape_patterns.initialized = 0; |
| | 2787 | result_certain = 1; |
| | 2788 | if (search_persistent_owl_cache(OWL_THREATEN_ATTACK, target, 0, 0, |
| | 2789 | &result, attack1, attack2, NULL)) |
| | 2790 | return result; |
| | 2791 | |
| | 2792 | if (debug & DEBUG_OWL_PERFORMANCE) |
| | 2793 | start = gg_cputime(); |
| | 2794 | |
| | 2795 | gg_assert(stackp == 0); |
| | 2796 | TRACE("owl_threaten_attack %1m\n", target); |
| | 2797 | init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); |
| | 2798 | memcpy(saved_boundary, owl->boundary, sizeof(saved_boundary)); |
| | 2799 | owl_make_domains(owl, NULL); |
| | 2800 | owl_shapes(&shape_patterns, moves, other, owl, &owl_attackpat_db); |
| | 2801 | for (k = 0; k < MAX_MOVES; k++) { |
| | 2802 | current_owl_data = owl; |
| | 2803 | if (!get_next_move_from_list(&shape_patterns, other, moves, 1, owl)) |
| | 2804 | break; |
| | 2805 | else { |
| | 2806 | int mpos = moves[k].pos; |
| | 2807 | |
| | 2808 | if (mpos != NO_MOVE && moves[k].value > 0) |
| | 2809 | if (trymove(mpos, other, moves[k].name, target)) { |
| | 2810 | int pos; |
| | 2811 | int origin = NO_MOVE; |
| | 2812 | owl->lunches_are_current = 0; |
| | 2813 | owl_update_boundary_marks(mpos, owl); |
| | 2814 | |
| | 2815 | /* If the origin of the dragon has been captured, we look |
| | 2816 | * for another string which was part of the original dragon, |
| | 2817 | * marked when stackp==0, which has not been captured. If no |
| | 2818 | * such string is found, owl_attack declares victory. |
| | 2819 | */ |
| | 2820 | |
| | 2821 | if (board[target] == EMPTY) { |
| | 2822 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { |
| | 2823 | if (IS_STONE(board[pos]) && owl->goal[pos] == 1) { |
| | 2824 | origin = find_origin(pos); |
| | 2825 | break; |
| | 2826 | } |
| | 2827 | } |
| | 2828 | owl->origin = origin; |
| | 2829 | |
| | 2830 | if (origin == NO_MOVE || do_owl_attack(owl, NULL, NULL)) { |
| | 2831 | /* probably this can't happen */ |
| | 2832 | popgo(); |
| | 2833 | gg_assert(stackp == 0); |
| | 2834 | result = 1; |
| | 2835 | break; |
| | 2836 | } |
| | 2837 | } |
| | 2838 | else if (do_owl_attack(owl, &move2, NULL) == WIN) { |
| | 2839 | move = moves[k].pos; |
| | 2840 | popgo(); |
| | 2841 | gg_assert(stackp == 0); |
| | 2842 | result = 1; |
| | 2843 | break; |
| | 2844 | } |
| | 2845 | popgo(); |
| | 2846 | memcpy(owl->boundary, saved_boundary, sizeof(saved_boundary)); |
| | 2847 | } |
| | 2848 | } |
| | 2849 | } |
| | 2850 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; |
| | 2851 | gg_assert(stackp == 0); |
| | 2852 | |
| | 2853 | DEBUG(DEBUG_OWL_PERFORMANCE, |
| | 2854 | "owl_threaten_attack %1m %1m %1m, result %d (%d, %d nodes, %f seconds)\n", |
| | 2855 | target, move, move2, result, local_owl_node_counter, |
| | 2856 | tactical_nodes, gg_cputime() - start); |
| | 2857 | |
| | 2858 | store_persistent_owl_cache(OWL_THREATEN_ATTACK, target, 0, 0, |
| | 2859 | result, move, move2, 0, |
| | 2860 | tactical_nodes, owl->goal, board[target]); |
| | 2861 | |
| | 2862 | if (attack1) |
| | 2863 | *attack1 = move; |
| | 2864 | if (attack2) |
| | 2865 | *attack2 = move2; |
| | 2866 | |
| | 2867 | close_pattern_list(other, &shape_patterns); |
| | 2868 | return result; |
| | 2869 | } |
| | 2870 | |
| | 2871 | |
| | 2964 | int |
| | 2965 | owl_does_attack(int move, int target, int *kworm) |
| | 2966 | { |
| | 2967 | int color = board[target]; |
| | 2968 | int other = OTHER_COLOR(color); |
| | 2969 | int result = 0; |
| | 2970 | struct local_owl_data *owl; |
| | 2971 | int reading_nodes_when_called = get_reading_node_counter(); |
| | 2972 | int tactical_nodes; |
| | 2973 | int origin; |
| | 2974 | int dcode; |
| | 2975 | int wpos = NO_MOVE; |
| | 2976 | int wid = MAX_GOAL_WORMS; |
| | 2977 | double start = 0.0; |
| | 2978 | |
| | 2979 | if (debug & DEBUG_OWL_PERFORMANCE) |
| | 2980 | start = gg_cputime(); |
| | 2981 | |
| | 2982 | if (worm[target].unconditional_status == ALIVE) |
| | 2983 | return 0; |
| | 2984 | |
| | 2985 | origin = dragon[target].origin; |
| | 2986 | TRACE("owl_does_attack %1m %1m(%1m)\n", move, target, origin); |
| | 2987 | |
| | 2988 | if (search_persistent_owl_cache(OWL_DOES_ATTACK, move, target, 0, |
| | 2989 | &result, kworm, NULL, NULL)) |
| | 2990 | return result; |
| | 2991 | |
| | 2992 | /* FIXME: We want to do this after the trymove(), but currently |
| | 2993 | * owl_mark_dragon() may crash if the trymove() happens to remove |
| | 2994 | * some stones of the goal dragon from the board. |
| | 2995 | */ |
| | 2996 | #if 1 |
| | 2997 | init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); |
| | 2998 | #endif |
| | 2999 | |
| | 3000 | if (trymove(move, other, "owl_does_attack", target)) { |
| | 3001 | /* Check if a compatible owl_defend() is cached. */ |
| | 3002 | if (search_persistent_owl_cache(OWL_DEFEND, origin, 0, 0, |
| | 3003 | &result, NULL, kworm, NULL)) { |
| | 3004 | popgo(); |
| | 3005 | return REVERSE_RESULT(result); |
| | 3006 | } |
| | 3007 | |
| | 3008 | #if 0 |
| | 3009 | local_owl_node_counter = 0; |
| | 3010 | owl->lunches_are_current = 0; |
| | 3011 | owl_mark_dragon(target, NO_MOVE, owl); |
| | 3012 | #endif |
| | 3013 | owl_update_boundary_marks(move, owl); |
| | 3014 | #if 0 |
| | 3015 | compute_owl_escape_values(owl); |
| | 3016 | #endif |
| | 3017 | /* FIXME: Should also check if part of the dragon was captured, |
| | 3018 | * like do_owl_attack() does. |
| | 3019 | */ |
| | 3020 | if (board[target] == EMPTY) |
| | 3021 | dcode = 0; |
| | 3022 | else { |
| | 3023 | prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, |
| | 3024 | kworm, 0); |
| | 3025 | dcode = do_owl_defend(owl, NULL, &wid); |
| | 3026 | finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); |
| | 3027 | } |
| | 3028 | result = REVERSE_RESULT(dcode); |
| | 3029 | owl->lunches_are_current = 0; |
| | 3030 | popgo(); |
| | 3031 | } |
| | 3032 | else |
| | 3033 | return 0; /* Don't cache anything in this case. */ |
| | 3034 | |
| | 3035 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; |
| | 3036 | |
| | 3037 | DEBUG(DEBUG_OWL_PERFORMANCE, |
| | 3038 | "owl_does_attack %1m %1m(%1m), result %d (%d, %d nodes, %f seconds)\n", |
| | 3039 | move, target, origin, result, local_owl_node_counter, |
| | 3040 | tactical_nodes, gg_cputime() - start); |
| | 3041 | |
| | 3042 | store_persistent_owl_cache(OWL_DOES_ATTACK, move, target, 0, |
| | 3043 | result, wpos, 0, 0, |
| | 3044 | tactical_nodes, owl->goal, board[target]); |
| | 3045 | |
| | 3046 | if (kworm) |
| | 3047 | *kworm = wpos; |
| | 3048 | return result; |
| | 3049 | } |
| | 3050 | |
| | 3051 | |
| | 3052 | /* Use the owl code to determine whether the move at (move) makes |
| | 3053 | * the dragon at (target) owl safe. This is used to test whether |
| | 3054 | * tactical defenses are strategically viable and whether a vital eye |
| | 3055 | * point does kill an owl critical dragon. |
| | 3056 | * |
| | 3057 | * Should be called only when stackp==0. |
| | 3058 | */ |
| | 3059 | |
| | 3060 | int |
| | 3061 | owl_does_defend(int move, int target, int *kworm) |
| | 3062 | { |
| | 3063 | int color = board[target]; |
| | 3064 | int result = 0; |
| | 3065 | struct local_owl_data *owl; |
| | 3066 | int reading_nodes_when_called = get_reading_node_counter(); |
| | 3067 | int tactical_nodes; |
| | 3068 | int origin; |
| | 3069 | int acode; |
| | 3070 | int wpos = NO_MOVE; |
| | 3071 | int wid = MAX_GOAL_WORMS; |
| | 3072 | double start = 0.0; |
| | 3073 | |
| | 3074 | if (debug & DEBUG_OWL_PERFORMANCE) |
| | 3075 | start = gg_cputime(); |
| | 3076 | |
| | 3077 | if (worm[target].unconditional_status == DEAD) |
| | 3078 | return 0; |
| | 3079 | |
| | 3080 | origin = dragon[target].origin; |
| | 3081 | TRACE("owl_does_defend %1m %1m(%1m)\n", move, target, origin); |
| | 3082 | |
| | 3083 | if (search_persistent_owl_cache(OWL_DOES_DEFEND, move, target, 0, |
| | 3084 | &result, kworm, NULL, NULL)) |
| | 3085 | return result; |
| | 3086 | |
| | 3087 | if (trymove(move, color, "owl_does_defend", target)) { |
| | 3088 | /* Check if a compatible owl_attack() is cached. */ |
| | 3089 | if (search_persistent_owl_cache(OWL_ATTACK, origin, 0, 0, |
| | 3090 | &result, NULL, kworm, NULL)) { |
| | 3091 | popgo(); |
| | 3092 | return REVERSE_RESULT(result); |
| | 3093 | } |
| | 3094 | |
| | 3095 | /* |
| | 3096 | * FIXME: (move) will be added to the goal dragon although we |
| | 3097 | * do not know whether it is really connected. |
| | 3098 | */ |
| | 3099 | init_owl(&owl, target, NO_MOVE, move, 1, NULL); |
| | 3100 | prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, |
| | 3101 | kworm, 0); |
| | 3102 | acode = do_owl_attack(owl, NULL, &wid); |
| | 3103 | finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); |
| | 3104 | result = REVERSE_RESULT(acode); |
| | 3105 | popgo(); |
| | 3106 | } |
| | 3107 | else |
| | 3108 | return 0; /* Don't cache anything in this case. */ |
| | 3109 | |
| | 3110 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; |
| | 3111 | |
| | 3112 | DEBUG(DEBUG_OWL_PERFORMANCE, |
| | 3113 | "owl_does_defend %1m %1m(%1m), result %d (%d, %d nodes, %f seconds)\n", |
| | 3114 | move, target, origin, result, local_owl_node_counter, |
| | 3115 | tactical_nodes, gg_cputime() - start); |
| | 3116 | |
| | 3117 | store_persistent_owl_cache(OWL_DOES_DEFEND, move, target, 0, |
| | 3118 | result, wpos, 0, 0, |
| | 3119 | tactical_nodes, owl->goal, board[target]); |
| | 3120 | |
| | 3121 | if (kworm) |
| | 3122 | *kworm = wpos; |
| | 3123 | return result; |
| | 3124 | } |
| | 3125 | |
| | 3126 | |
| | 3127 | /* Use the owl code to determine whether the dragon at (target) is owl |
| | 3128 | * safe after an own move at (move). This is used to detect |
| | 3129 | * blunders. In case the dragon is not safe, it also tries to find a |
| | 3130 | * defense point making (target) safe in a later move. |
| | 3131 | * |
| | 3132 | * Should be called only when stackp==0. |
| | 3133 | */ |
| | 3134 | |
| | 3135 | int |
| | 3136 | owl_confirm_safety(int move, int target, int *defense_point, int *kworm) |
| | 3137 | { |
| | 3138 | int color = board[target]; |
| | 3139 | int result = 0; |
| | 3140 | struct local_owl_data *owl; |
| | 3141 | int reading_nodes_when_called = get_reading_node_counter(); |
| | 3142 | int tactical_nodes; |
| | 3143 | int origin; |
| | 3144 | int defense = 0; |
| | 3145 | double start = 0.0; |
| | 3146 | int acode; |
| | 3147 | int wpos = NO_MOVE; |
| | 3148 | int wid = MAX_GOAL_WORMS; |
| | 3149 | |
| | 3150 | if (debug & DEBUG_OWL_PERFORMANCE) |
| | 3151 | start = gg_cputime(); |
| | 3152 | |
| | 3153 | if (worm[target].unconditional_status == DEAD) |
| | 3154 | return 0; |
| | 3155 | |
| | 3156 | origin = dragon[target].origin; |
| | 3157 | TRACE("owl_confirm_safety %1m %1m(%1m)\n", move, target, origin); |
| | 3158 | |
| | 3159 | if (search_persistent_owl_cache(OWL_CONFIRM_SAFETY, move, target, 0, |
| | 3160 | &result, defense_point, kworm, NULL)) |
| | 3161 | return result; |
| | 3162 | |
| | 3163 | if (trymove(move, color, "owl_confirm_safety", target)) { |
| | 3164 | /* Check if a compatible owl_attack() is cached. */ |
| | 3165 | if (search_persistent_owl_cache(OWL_ATTACK, origin, 0, 0, |
| | 3166 | &result, defense_point, kworm, NULL)) { |
| | 3167 | popgo(); |
| | 3168 | if (result == 0) |
| | 3169 | return WIN; |
| | 3170 | else if (result == GAIN) |
| | 3171 | return LOSS; |
| | 3172 | else |
| | 3173 | return 0; |
| | 3174 | } |
| | 3175 | |
| | 3176 | init_owl(&owl, target, NO_MOVE, move, 1, NULL); |
| | 3177 | prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, |
| | 3178 | kworm, 0); |
| | 3179 | acode = do_owl_attack(owl, &defense, &wid); |
| | 3180 | finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); |
| | 3181 | if (acode == 0) |
| | 3182 | result = WIN; |
| | 3183 | else if (acode == GAIN) |
| | 3184 | result = LOSS; |
| | 3185 | popgo(); |
| | 3186 | } |
| | 3187 | else |
| | 3188 | return 0; /* Don't cache anything in this case. */ |
| | 3189 | |
| | 3190 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; |
| | 3191 | |
| | 3192 | DEBUG(DEBUG_OWL_PERFORMANCE, |
| | 3193 | "owl_confirm_safety %1m %1m(%1m), result %d %1m (%d, %d nodes, %f seconds)\n", |
| | 3194 | move, target, origin, result, defense, |
| | 3195 | local_owl_node_counter, tactical_nodes, |
| | 3196 | gg_cputime() - start); |
| | 3197 | |
| | 3198 | store_persistent_owl_cache(OWL_CONFIRM_SAFETY, move, target, 0, |
| | 3199 | result, defense, wpos, 0, |
| | 3200 | tactical_nodes, owl->goal, board[target]); |
| | 3201 | |
| | 3202 | if (defense_point) |
| | 3203 | *defense_point = defense; |
| | 3204 | if (kworm) |
| | 3205 | *kworm = wpos; |
| | 3206 | |
| | 3207 | return result; |
| | 3208 | } |
| | 3209 | |
| | 3210 | |
| | 3211 | /* Use the owl code to determine whether connecting the two dragons |
| | 3212 | * (target1) and (target2) by playing at (move) results in a living |
| | 3213 | * dragon. Should be called only when stackp==0. |
| | 3214 | */ |
| | 3215 | |
| | 3216 | int |
| | 3217 | owl_connection_defends(int move, int target1, int target2) |
| | 3218 | { |
| | 3219 | int color = board[target1]; |
| | 3220 | int result = 0; |
| | 3221 | int reading_nodes_when_called = get_reading_node_counter(); |
| | 3222 | int tactical_nodes; |
| | 3223 | double start = 0.0; |
| | 3224 | struct local_owl_data *owl; |
| | 3225 | |
| | 3226 | if (debug & DEBUG_OWL_PERFORMANCE) |
| | 3227 | start = gg_cputime(); |
| | 3228 | |
| | 3229 | ASSERT1(board[target2] == color, target2); |
| | 3230 | TRACE("owl_connection_defends %1m %1m %1m\n", move, target1, target2); |
| | 3231 | |
| | 3232 | if (worm[target1].unconditional_status == DEAD) |
| | 3233 | return 0; |
| | 3234 | if (worm[target2].unconditional_status == DEAD) |
| | 3235 | return 0; |
| | 3236 | |
| | 3237 | if (search_persistent_owl_cache(OWL_CONNECTION_DEFENDS, move, target1, |
| | 3238 | target2, &result, NULL, NULL, NULL)) |
| | 3239 | return result; |
| | 3240 | |
| | 3241 | init_owl(&owl, target1, target2, NO_MOVE, 1, NULL); |
| | 3242 | |
| | 3243 | if (trymove(move, color, "owl_connection_defends", target1)) { |
| | 3244 | owl_update_goal(move, SAME_DRAGON_MAYBE_CONNECTED, NO_MOVE, owl, 0, NULL); |
| | 3245 | owl->origin = move; |
| | 3246 | if (!do_owl_attack(owl, NULL, NULL)) |
| | 3247 | result = WIN; |
| | 3248 | owl->lunches_are_current = 0; |
| | 3249 | popgo(); |
| | 3250 | } |
| | 3251 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; |
| | 3252 | |
| | 3253 | DEBUG(DEBUG_OWL_PERFORMANCE, |
| | 3254 | "owl_conn_defends %1m %1m %1m, result %d (%d, %d nodes, %f seconds)\n", |
| | 3255 | move, target1, target2, result, local_owl_node_counter, |
| | 3256 | tactical_nodes, gg_cputime() - start); |
| | 3257 | |
| | 3258 | store_persistent_owl_cache(OWL_CONNECTION_DEFENDS, move, target1, target2, |
| | 3259 | result, 0, 0, 0, tactical_nodes, |
| | 3260 | owl->goal, color); |
| | 3261 | |
| | 3262 | return result; |
| | 3263 | } |
| | 3264 | |
| | 3265 | |
| | 3266 | /* This function, called when stackp==0, returns true if capturing |
| | 3267 | * the string at (str) results in a live group. |
| | 3268 | */ |
| | 3269 | |
| | 3270 | #define MAX_SUBSTANTIAL_LIBS 10 |
| | 3271 | |
| | 3272 | int |
| | 3273 | owl_substantial(int str) |
| | 3274 | { |
| | 3275 | int k; |
| | 3276 | int libs[MAX_SUBSTANTIAL_LIBS + 1]; |
| | 3277 | int liberties = findlib(str, MAX_SUBSTANTIAL_LIBS+1, libs); |
| | 3278 | int reading_nodes_when_called = get_reading_node_counter(); |
| | 3279 | int tactical_nodes; |
| | 3280 | int result; |
| | 3281 | double start = 0.0; |
| | 3282 | struct local_owl_data *owl; |
| | 3283 | int num_moves = 0; |
| | 3284 | |
| | 3285 | if (debug & DEBUG_OWL_PERFORMANCE) |
| | 3286 | start = gg_cputime(); |
| | 3287 | |
| | 3288 | /* FIXME: We want to use the full init_owl here too (cf. similar |
| | 3289 | * remark below). |
| | 3290 | */ |
| | 3291 | reduced_init_owl(&owl, 1); |
| | 3292 | |
| | 3293 | owl->color = OTHER_COLOR(board[str]); |
| | 3294 | |
| | 3295 | /* Big strings are always substantial since the biggest nakade is |
| | 3296 | * six stones. (There are probably rare exceptions to this |
| | 3297 | * rule, but they are unlikely to come up in a game.) |
| | 3298 | */ |
| | 3299 | if (countstones(str) > 6) |
| | 3300 | return 1; |
| | 3301 | |
| | 3302 | if (liberties > MAX_SUBSTANTIAL_LIBS) |
| | 3303 | return 0; |
| | 3304 | |
| | 3305 | memset(owl->goal, 0, sizeof(owl->goal)); |
| | 3306 | /* Mark the neighbors of the string. If one is found which is alive, return |
| | 3307 | * true. */ |
| | 3308 | { |
| | 3309 | int adjs[MAXCHAIN]; |
| | 3310 | int adj; |
| | 3311 | |
| | 3312 | adj = chainlinks(str, adjs); |
| | 3313 | for (k = 0; k < adj; k++) { |
| | 3314 | if (dragon[adjs[k]].status == ALIVE) |
| | 3315 | return 1; |
| | 3316 | mark_dragon(adjs[k], owl->goal, 1); |
| | 3317 | } |
| | 3318 | } |
| | 3319 | |
| | 3320 | /* We must check the cache while stackp == 0, but we wait until the |
| | 3321 | * trivial tests have been done. |
| | 3322 | */ |
| | 3323 | if (search_persistent_owl_cache(OWL_SUBSTANTIAL, str, 0, 0, |
| | 3324 | &result, NULL, NULL, NULL)) |
| | 3325 | return result; |
| | 3326 | |
| | 3327 | /* fill all the liberties */ |
| | 3328 | for (k = 0; k < liberties; k++) { |
| | 3329 | if (trymove(libs[k], owl->color, NULL, 0)) { |
| | 3330 | if (get_level() >= 8) |
| | 3331 | increase_depth_values(); |
| | 3332 | owl->goal[libs[k]] = 1; |
| | 3333 | num_moves++; |
| | 3334 | } |
| | 3335 | else { |
| | 3336 | /* if we can't fill, try swapping with the next liberty */ |
| | 3337 | if (k < liberties-1 |
| | 3338 | && trymove(libs[k+1], owl->color, NULL, 0)) { |
| | 3339 | if (get_level() >= 8) |
| | 3340 | increase_depth_values(); |
| | 3341 | owl->goal[libs[k+1]] = 1; |
| | 3342 | libs[k+1] = libs[k]; |
| | 3343 | num_moves++; |
| | 3344 | } |
| | 3345 | else { |
| | 3346 | /* Can't fill the liberties. Give up! */ |
| | 3347 | while (num_moves-- > 0) { |
| | 3348 | if (get_level() >= 8) |
| | 3349 | decrease_depth_values(); |
| | 3350 | popgo(); |
| | 3351 | } |
| | 3352 | return 0; |
| | 3353 | } |
| | 3354 | } |
| | 3355 | } |
| | 3356 | /* FIXME: We would want to use init_owl() here too, but it doesn't |
| | 3357 | * fit very well with the construction of the goal array above. |
| | 3358 | */ |
| | 3359 | memcpy(owl->cumulative_goal, owl->goal, BOARDMAX); |
| | 3360 | compute_owl_escape_values(owl); |
| | 3361 | owl_mark_boundary(owl); |
| | 3362 | owl->lunches_are_current = 0; |
| | 3363 | owl->escape_moves = 0; |
| | 3364 | owl->origin = libs[0]; |
| | 3365 | |
| | 3366 | if (do_owl_attack(owl, NULL, NULL)) |
| | 3367 | result = 0; |
| | 3368 | else |
| | 3369 | result = 1; |
| | 3370 | while (num_moves-- > 0) { |
| | 3371 | if (get_level() >= 8) |
| | 3372 | decrease_depth_values(); |
| | 3373 | popgo(); |
| | 3374 | } |
| | 3375 | |
| | 3376 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; |
| | 3377 | DEBUG(DEBUG_OWL_PERFORMANCE, |
| | 3378 | "owl_substantial %1m, result %d (%d, %d nodes, %f seconds)\n", |
| | 3379 | str, result, local_owl_node_counter, |
| | 3380 | tactical_nodes, gg_cputime() - start); |
| | 3381 | |
| | 3382 | store_persistent_owl_cache(OWL_SUBSTANTIAL, str, 0, 0, result, 0, 0, 0, |
| | 3383 | tactical_nodes, owl->goal, owl->color); |
| | 3384 | |
| | 3385 | return result; |
| | 3386 | } |
| | 3387 | |
| | 3388 | |
| | 3389 | |
| 5322 | | /* Use the owl code to determine whether the move at (move) makes |
| 5323 | | * the dragon at (target) owl safe. This is used to test whether |
| 5324 | | * tactical defenses are strategically viable and whether a vital eye |
| 5325 | | * point does kill an owl critical dragon. |
| 5326 | | * |
| 5327 | | * Should be called only when stackp==0. |
| 5328 | | */ |
| 5329 | | |
| 5330 | | int |
| 5331 | | owl_does_defend(int move, int target, int *kworm) |
| 5332 | | { |
| 5333 | | int color = board[target]; |
| 5334 | | int result = 0; |
| 5335 | | struct local_owl_data *owl; |
| 5336 | | int reading_nodes_when_called = get_reading_node_counter(); |
| 5337 | | int tactical_nodes; |
| 5338 | | int origin; |
| 5339 | | int acode; |
| 5340 | | int wpos = NO_MOVE; |
| 5341 | | int wid = MAX_GOAL_WORMS; |
| 5342 | | double start = 0.0; |
| 5343 | | |
| 5344 | | if (debug & DEBUG_OWL_PERFORMANCE) |
| 5345 | | start = gg_cputime(); |
| 5346 | | |
| 5347 | | if (worm[target].unconditional_status == DEAD) |
| 5348 | | return 0; |
| 5349 | | |
| 5350 | | origin = dragon[target].origin; |
| 5351 | | TRACE("owl_does_defend %1m %1m(%1m)\n", move, target, origin); |
| 5352 | | |
| 5353 | | if (search_persistent_owl_cache(OWL_DOES_DEFEND, move, target, 0, |
| 5354 | | &result, kworm, NULL, NULL)) |
| 5355 | | return result; |
| 5356 | | |
| 5357 | | if (trymove(move, color, "owl_does_defend", target)) { |
| 5358 | | /* Check if a compatible owl_attack() is cached. */ |
| 5359 | | if (search_persistent_owl_cache(OWL_ATTACK, origin, 0, 0, |
| 5360 | | &result, NULL, kworm, NULL)) { |
| 5361 | | popgo(); |
| 5362 | | return REVERSE_RESULT(result); |
| 5363 | | } |
| 5364 | | |
| 5365 | | /* |
| 5366 | | * FIXME: (move) will be added to the goal dragon although we |
| 5367 | | * do not know whether it is really connected. |
| 5368 | | */ |
| 5369 | | init_owl(&owl, target, NO_MOVE, move, 1, NULL); |
| 5370 | | prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, |
| 5371 | | kworm, 0); |
| 5372 | | acode = do_owl_attack(target, NULL, &wid, owl, 0); |
| 5373 | | finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); |
| 5374 | | result = REVERSE_RESULT(acode); |
| 5375 | | popgo(); |
| 5376 | | } |
| 5377 | | else |
| 5378 | | return 0; /* Don't cache anything in this case. */ |
| 5379 | | |
| 5380 | | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; |
| 5381 | | |
| 5382 | | DEBUG(DEBUG_OWL_PERFORMANCE, |
| 5383 | | "owl_does_defend %1m %1m(%1m), result %d (%d, %d nodes, %f seconds)\n", |
| 5384 | | move, target, origin, result, local_owl_node_counter, |
| 5385 | | tactical_nodes, gg_cputime() - start); |
| 5386 | | |
| 5387 | | store_persistent_owl_cache(OWL_DOES_DEFEND, move, target, 0, |
| 5388 | | result, wpos, 0, 0, |
| 5389 | | tactical_nodes, owl->goal, board[target]); |
| 5390 | | |
| 5391 | | if (kworm) |
| 5392 | | *kworm = wpos; |
| 5393 | | return result; |
| 5394 | | } |
| 5395 | | |
| 5396 | | |
| 5397 | | /* Use the owl code to determine whether the dragon at (target) is owl |
| 5398 | | * safe after an own move at (move). This is used to detect |
| 5399 | | * blunders. In case the dragon is not safe, it also tries to find a |
| 5400 | | * defense point making (target) safe in a later move. |
| 5401 | | * |
| 5402 | | * Should be called only when stackp==0. |
| 5403 | | */ |
| 5404 | | |
| 5405 | | int |
| 5406 | | owl_confirm_safety(int move, int target, int *defense_point, int *kworm) |
| 5407 | | { |
| 5408 | | int color = board[target]; |
| 5409 | | int result = 0; |
| 5410 | | struct local_owl_data *owl; |
| 5411 | | int reading_nodes_when_called = get_reading_node_counter(); |
| 5412 | | int tactical_nodes; |
| 5413 | | int origin; |
| 5414 | | int defense = 0; |
| 5415 | | double start = 0.0; |
| 5416 | | int acode; |
| 5417 | | int wpos = NO_MOVE; |
| 5418 | | int wid = MAX_GOAL_WORMS; |
| 5419 | | |
| 5420 | | if (debug & DEBUG_OWL_PERFORMANCE) |
| 5421 | | start = gg_cputime(); |
| 5422 | | |
| 5423 | | if (worm[target].unconditional_status == DEAD) |
| 5424 | | return 0; |
| 5425 | | |
| 5426 | | origin = dragon[target].origin; |
| 5427 | | TRACE("owl_confirm_safety %1m %1m(%1m)\n", move, target, origin); |
| 5428 | | |
| 5429 | | if (search_persistent_owl_cache(OWL_CONFIRM_SAFETY, move, target, 0, |
| 5430 | | &result, defense_point, kworm, NULL)) |
| 5431 | | return result; |
| 5432 | | |
| 5433 | | if (trymove(move, color, "owl_confirm_safety", target)) { |
| 5434 | | /* Check if a compatible owl_attack() is cached. */ |
| 5435 | | if (search_persistent_owl_cache(OWL_ATTACK, origin, 0, 0, |
| 5436 | | &result, defense_point, kworm, NULL)) { |
| 5437 | | popgo(); |
| 5438 | | if (result == 0) |
| 5439 | | return WIN; |
| 5440 | | else if (result == GAIN) |
| 5441 | | return LOSS; |
| 5442 | | else |
| 5443 | | return 0; |
| 5444 | | } |
| 5445 | | |
| 5446 | | init_owl(&owl, target, NO_MOVE, move, 1, NULL); |
| 5447 | | prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, |
| 5448 | | kworm, 0); |
| 5449 | | acode = do_owl_attack(target, &defense, &wid, owl, 0); |
| 5450 | | finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); |
| 5451 | | if (acode == 0) |
| 5452 | | result = WIN; |
| 5453 | | else if (acode == GAIN) |
| 5454 | | result = LOSS; |
| 5455 | | popgo(); |
| 5456 | | } |
| 5457 | | else |
| 5458 | | return 0; /* Don't cache anything in this case. */ |
| 5459 | | |
| 5460 | | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; |
| 5461 | | |
| 5462 | | DEBUG(DEBUG_OWL_PERFORMANCE, |
| 5463 | | "owl_confirm_safety %1m %1m(%1m), result %d %1m (%d, %d nodes, %f seconds)\n", |
| 5464 | | move, target, origin, result, defense, |
| 5465 | | local_owl_node_counter, tactical_nodes, |
| 5466 | | gg_cputime() - start); |
| 5467 | | |
| 5468 | | store_persistent_owl_cache(OWL_CONFIRM_SAFETY, move, target, 0, |
| 5469 | | result, defense, wpos, 0, |
| 5470 | | tactical_nodes, owl->goal, board[target]); |
| 5471 | | |
| 5472 | | if (defense_point) |
| 5473 | | *defense_point = defense; |
| 5474 | | if (kworm) |
| 5475 | | *kworm = wpos; |
| 5476 | | |
| 5477 | | return result; |
| 5478 | | } |
| 5479 | | |
| 5480 | | |
| 5481 | | /* Use the owl code to determine whether the attack move at (move) of |
| 5482 | | * the dragon (target) is effective, i.e. whether it kills the stones. |
| 5483 | | * |
| 5484 | | * Should be called only when stackp==0. |
| 5485 | | */ |
| 5486 | | |
| 5487 | | int |
| 5488 | | owl_does_attack(int move, int target, int *kworm) |
| 5489 | | { |
| 5490 | | int color = board[target]; |
| 5491 | | int other = OTHER_COLOR(color); |
| 5492 | | int result = 0; |
| 5493 | | struct local_owl_data *owl; |
| 5494 | | int reading_nodes_when_called = get_reading_node_counter(); |
| 5495 | | int tactical_nodes; |
| 5496 | | int origin; |
| 5497 | | int dcode; |
| 5498 | | int wpos = NO_MOVE; |
| 5499 | | int wid = MAX_GOAL_WORMS; |
| 5500 | | double start = 0.0; |
| 5501 | | |
| 5502 | | if (debug & DEBUG_OWL_PERFORMANCE) |
| 5503 | | start = gg_cputime(); |
| 5504 | | |
| 5505 | | if (worm[target].unconditional_status == ALIVE) |
| 5506 | | return 0; |
| 5507 | | |
| 5508 | | origin = dragon[target].origin; |
| 5509 | | TRACE("owl_does_attack %1m %1m(%1m)\n", move, target, origin); |
| 5510 | | |
| 5511 | | if (search_persistent_owl_cache(OWL_DOES_ATTACK, move, target, 0, |
| 5512 | | &result, kworm, NULL, NULL)) |
| 5513 | | return result; |
| 5514 | | |
| 5515 | | /* FIXME: We want to do this after the trymove(), but currently |
| 5516 | | * owl_mark_dragon() may crash if the trymove() happens to remove |
| 5517 | | * some stones of the goal dragon from the board. |
| 5518 | | */ |
| 5519 | | #if 1 |
| 5520 | | init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); |
| 5521 | | #endif |
| 5522 | | |
| 5523 | | if (trymove(move, other, "owl_does_attack", target)) { |
| 5524 | | /* Check if a compatible owl_defend() is cached. */ |
| 5525 | | if (search_persistent_owl_cache(OWL_DEFEND, origin, 0, 0, |
| 5526 | | &result, NULL, kworm, NULL)) { |
| 5527 | | popgo(); |
| 5528 | | return REVERSE_RESULT(result); |
| 5529 | | } |
| 5530 | | |
| 5531 | | #if 0 |
| 5532 | | local_owl_node_counter = 0; |
| 5533 | | owl->lunches_are_current = 0; |
| 5534 | | owl_mark_dragon(target, NO_MOVE, owl); |
| 5535 | | #endif |
| 5536 | | owl_update_boundary_marks(move, owl); |
| 5537 | | #if 0 |
| 5538 | | compute_owl_escape_values(owl); |
| 5539 | | #endif |
| 5540 | | /* FIXME: Should also check if part of the dragon was captured, |
| 5541 | | * like do_owl_attack() does. |
| 5542 | | */ |
| 5543 | | if (board[target] == EMPTY) |
| 5544 | | dcode = 0; |
| 5545 | | else { |
| 5546 | | prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, |
| 5547 | | kworm, 0); |
| 5548 | | dcode = do_owl_defend(target, NULL, &wid, owl, 0); |
| 5549 | | finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); |
| 5550 | | } |
| 5551 | | result = REVERSE_RESULT(dcode); |
| 5552 | | owl->lunches_are_current = 0; |
| 5553 | | popgo(); |
| 5554 | | } |
| 5555 | | else |
| 5556 | | return 0; /* Don't cache anything in this case. */ |
| 5557 | | |
| 5558 | | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; |
| 5559 | | |
| 5560 | | DEBUG(DEBUG_OWL_PERFORMANCE, |
| 5561 | | "owl_does_attack %1m %1m(%1m), result %d (%d, %d nodes, %f seconds)\n", |
| 5562 | | move, target, origin, result, local_owl_node_counter, |
| 5563 | | tactical_nodes, gg_cputime() - start); |
| 5564 | | |
| 5565 | | store_persistent_owl_cache(OWL_DOES_ATTACK, move, target, 0, |
| 5566 | | result, wpos, 0, 0, |
| 5567 | | tactical_nodes, owl->goal, board[target]); |
| 5568 | | |
| 5569 | | if (kworm) |
| 5570 | | *kworm = wpos; |
| 5571 | | return result; |
| 5572 | | } |
| 5573 | | |
| 5574 | | |
| 5575 | | /* Use the owl code to determine whether connecting the two dragons |
| 5576 | | * (target1) and (target2) by playing at (move) results in a living |
| 5577 | | * dragon. Should be called only when stackp==0. |
| 5578 | | */ |
| 5579 | | |
| 5580 | | int |
| 5581 | | owl_connection_defends(int move, int target1, int target2) |
| 5582 | | { |
| 5583 | | int color = board[target1]; |
| 5584 | | int result = 0; |
| 5585 | | int reading_nodes_when_called = get_reading_node_counter(); |
| 5586 | | int tactical_nodes; |
| 5587 | | double start = 0.0; |
| 5588 | | struct local_owl_data *owl; |
| 5589 | | |
| 5590 | | if (debug & DEBUG_OWL_PERFORMANCE) |
| 5591 | | start = gg_cputime(); |
| 5592 | | |
| 5593 | | ASSERT1(board[target2] == color, target2); |
| 5594 | | TRACE("owl_connection_defends %1m %1m %1m\n", move, target1, target2); |
| 5595 | | |
| 5596 | | if (worm[target1].unconditional_status == DEAD) |
| 5597 | | return 0; |
| 5598 | | if (worm[target2].unconditional_status == DEAD) |
| 5599 | | return 0; |
| 5600 | | |
| 5601 | | if (search_persistent_owl_cache(OWL_CONNECTION_DEFENDS, move, target1, |
| 5602 | | target2, &result, NULL, NULL, NULL)) |
| 5603 | | return result; |
| 5604 | | |
| 5605 | | init_owl(&owl, target1, target2, NO_MOVE, 1, NULL); |
| 5606 | | |
| 5607 | | if (trymove(move, color, "owl_connection_defends", target1)) { |
| 5608 | | owl_update_goal(move, SAME_DRAGON_MAYBE_CONNECTED, NO_MOVE, owl, 0, NULL); |
| 5609 | | if (!do_owl_attack(move, NULL, NULL, owl, 0)) |
| 5610 | | result = WIN; |
| 5611 | | owl->lunches_are_current = 0; |
| 5612 | | popgo(); |
| 5613 | | } |
| 5614 | | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; |
| 5615 | | |
| 5616 | | DEBUG(DEBUG_OWL_PERFORMANCE, |
| 5617 | | "owl_conn_defends %1m %1m %1m, result %d (%d, %d nodes, %f seconds)\n", |
| 5618 | | move, target1, target2, result, local_owl_node_counter, |
| 5619 | | tactical_nodes, gg_cputime() - start); |
| 5620 | | |
| 5621 | | store_persistent_owl_cache(OWL_CONNECTION_DEFENDS, move, target1, target2, |
| 5622 | | result, 0, 0, 0, tactical_nodes, |
| 5623 | | owl->goal, color); |
| 5624 | | |
| 5625 | | return result; |
| 5626 | | } |
| 5627 | | |
| 5628 | | |
| 6107 | | /* This function, called when stackp==0, returns true if capturing |
| 6108 | | * the string at (str) results in a live group. |
| 6109 | | */ |
| 6110 | | |
| 6111 | | #define MAX_SUBSTANTIAL_LIBS 10 |
| 6112 | | |
| 6113 | | int |
| 6114 | | owl_substantial(int str) |
| 6115 | | { |
| 6116 | | int k; |
| 6117 | | int libs[MAX_SUBSTANTIAL_LIBS + 1]; |
| 6118 | | int liberties = findlib(str, MAX_SUBSTANTIAL_LIBS+1, libs); |
| 6119 | | int reading_nodes_when_called = get_reading_node_counter(); |
| 6120 | | int tactical_nodes; |
| 6121 | | int result; |
| 6122 | | double start = 0.0; |
| 6123 | | struct local_owl_data *owl; |
| 6124 | | int num_moves = 0; |
| 6125 | | |
| 6126 | | if (debug & DEBUG_OWL_PERFORMANCE) |
| 6127 | | start = gg_cputime(); |
| 6128 | | |
| 6129 | | /* FIXME: We want to use the full init_owl here too (cf. similar |
| 6130 | | * remark below). |
| 6131 | | */ |
| 6132 | | reduced_init_owl(&owl, 1); |
| 6133 | | |
| 6134 | | owl->color = OTHER_COLOR(board[str]); |
| 6135 | | local_owl_node_counter = 0; |
| 6136 | | |
| 6137 | | /* Big strings are always substantial since the biggest nakade is |
| 6138 | | * six stones. (There are probably rare exceptions to this |
| 6139 | | * rule, but they are unlikely to come up in a game.) |
| 6140 | | */ |
| 6141 | | if (countstones(str) > 6) |
| 6142 | | return 1; |
| 6143 | | |
| 6144 | | if (liberties > MAX_SUBSTANTIAL_LIBS) |
| 6145 | | return 0; |
| 6146 | | |
| 6147 | | memset(owl->goal, 0, sizeof(owl->goal)); |
| 6148 | | /* Mark the neighbors of the string. If one is found which is alive, return |
| 6149 | | * true. */ |
| 6150 | | { |
| 6151 | | int adjs[MAXCHAIN]; |
| 6152 | | int adj; |
| 6153 | | |
| 6154 | | adj = chainlinks(str, adjs); |
| 6155 | | for (k = 0; k < adj; k++) { |
| 6156 | | if (dragon[adjs[k]].status == ALIVE) |
| 6157 | | return 1; |
| 6158 | | mark_dragon(adjs[k], owl->goal, 1); |
| 6159 | | } |
| 6160 | | } |
| 6161 | | |
| 6162 | | /* We must check the cache while stackp == 0, but we wait until the |
| 6163 | | * trivial tests have been done. |
| 6164 | | */ |
| 6165 | | if (search_persistent_owl_cache(OWL_SUBSTANTIAL, str, 0, 0, |
| 6166 | | &result, NULL, NULL, NULL)) |
| 6167 | | return result; |
| 6168 | | |
| 6169 | | /* fill all the liberties */ |
| 6170 | | for (k = 0; k < liberties; k++) { |
| 6171 | | if (trymove(libs[k], owl->color, NULL, 0)) { |
| 6172 | | if (get_level() >= 8) |
| 6173 | | increase_depth_values(); |
| 6174 | | owl->goal[libs[k]] = 1; |
| 6175 | | num_moves++; |
| 6176 | | } |
| 6177 | | else { |
| 6178 | | /* if we can't fill, try swapping with the next liberty */ |
| 6179 | | if (k < liberties-1 |
| 6180 | | && trymove(libs[k+1], owl->color, NULL, 0)) { |
| 6181 | | if (get_level() >= 8) |
| 6182 | | increase_depth_values(); |
| 6183 | | owl->goal[libs[k+1]] = 1; |
| 6184 | | libs[k+1] = libs[k]; |
| 6185 | | num_moves++; |
| 6186 | | } |
| 6187 | | else { |
| 6188 | | /* Can't fill the liberties. Give up! */ |
| 6189 | | while (num_moves-- > 0) { |
| 6190 | | if (get_level() >= 8) |
| 6191 | | decrease_depth_values(); |
| 6192 | | popgo(); |
| 6193 | | } |
| 6194 | | return 0; |
| 6195 | | } |
| 6196 | | } |
| 6197 | | } |
| 6198 | | /* FIXME: We would want to use init_owl() here too, but it doesn't |
| 6199 | | * fit very well with the construction of the goal array above. |
| 6200 | | */ |
| 6201 | | memcpy(owl->cumulative_goal, owl->goal, BOARDMAX); |
| 6202 | | compute_owl_escape_values(owl); |
| 6203 | | owl_mark_boundary(owl); |
| 6204 | | owl->lunches_are_current = 0; |
| 6205 | | |
| 6206 | | if (do_owl_attack(libs[0], NULL, NULL, owl, 0)) |
| 6207 | | result = 0; |
| 6208 | | else |
| 6209 | | result = 1; |
| 6210 | | while (num_moves-- > 0) { |
| 6211 | | if (get_level() >= 8) |
| 6212 | | decrease_depth_values(); |
| 6213 | | popgo(); |
| 6214 | | } |
| 6215 | | |
| 6216 | | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; |
| 6217 | | DEBUG(DEBUG_OWL_PERFORMANCE, |
| 6218 | | "owl_substantial %1m, result %d (%d, %d nodes, %f seconds)\n", |
| 6219 | | str, result, local_owl_node_counter, |
| 6220 | | tactical_nodes, gg_cputime() - start); |
| 6221 | | |
| 6222 | | store_persistent_owl_cache(OWL_SUBSTANTIAL, str, 0, 0, result, 0, 0, 0, |
| 6223 | | tactical_nodes, owl->goal, owl->color); |
| 6224 | | |
| 6225 | | return result; |
| 6226 | | } |
| 6227 | | |
| 6228 | | |
| 6229 | | |