| | 700 | |
| | 701 | /************ Unconditional Safety Search ****************/ |
| | 702 | |
| | 703 | /* FIXME: Move this code to a file of its own. */ |
| | 704 | |
| | 705 | /* FIXME: Dynamic size/allocation of unconditional safety search tree. */ |
| | 706 | #define MAX_USS_NODES 10000 |
| | 707 | |
| | 708 | /* Node struct for unconditional safety search (uss). */ |
| | 709 | /* FIXME: More efficient storage of children. */ |
| | 710 | struct uss_node { |
| | 711 | int solved; /* Node is solved. */ |
| | 712 | int value; /* Safe or unsafe when solved. */ |
| | 713 | short child_moves[MAX_BOARD * MAX_BOARD]; |
| | 714 | int child_nodes[MAX_BOARD * MAX_BOARD]; |
| | 715 | int num_children; |
| | 716 | }; |
| | 717 | |
| | 718 | /* Tree struct for unconditional safety search (uss). */ |
| | 719 | /* FIXME: Add hashtable to detect and join transpositions. */ |
| | 720 | struct uss_tree { |
| | 721 | struct uss_node nodes[MAX_USS_NODES]; |
| | 722 | short moves_of_interest[MAX_BOARD * MAX_BOARD]; |
| | 723 | int num_moves_of_interest; |
| | 724 | int next_node; |
| | 725 | int color; |
| | 726 | int target; |
| | 727 | }; |
| | 728 | |
| | 729 | /* Global search tree. */ |
| | 730 | static struct uss_tree tree; |
| | 731 | |
| | 732 | /* Find an existing node or make a new one. */ |
| | 733 | /* FIXME: Transposition detection would go here. */ |
| | 734 | static int |
| | 735 | uss_find_node(struct uss_tree *tree, int color) |
| | 736 | { |
| | 737 | int k; |
| | 738 | int unconditional_territory[BOARDMAX]; |
| | 739 | int move; |
| | 740 | int node = tree->next_node; |
| | 741 | SGFTree *save_sgf_dumptree = sgf_dumptree; |
| | 742 | int save_count_variations = count_variations; |
| | 743 | tree->next_node++; |
| | 744 | |
| | 745 | /* Not solved yet. */ |
| | 746 | tree->nodes[node].solved = 0; |
| | 747 | tree->nodes[node].value = 0; |
| | 748 | |
| | 749 | if (board[tree->target] == EMPTY) { |
| | 750 | /* The dragon origin has been captured, give up any hope that the |
| | 751 | * dragon is safe. |
| | 752 | */ |
| | 753 | tree->nodes[node].solved = 1; |
| | 754 | tree->nodes[node].value = 0; |
| | 755 | } |
| | 756 | else { |
| | 757 | /* Check whether the dragon has become unconditionally alive. Turn |
| | 758 | * off sgf traces so that sgf output for the unconditional safety |
| | 759 | * search becomes sane. |
| | 760 | */ |
| | 761 | sgf_dumptree = NULL; |
| | 762 | count_variations = 0; |
| | 763 | unconditional_life(unconditional_territory, tree->color); |
| | 764 | sgf_dumptree = save_sgf_dumptree; |
| | 765 | count_variations = save_count_variations; |
| | 766 | |
| | 767 | if (unconditional_territory[tree->target] > 0) { |
| | 768 | /* Dragon is safe. */ |
| | 769 | tree->nodes[node].solved = 1; |
| | 770 | tree->nodes[node].value = 1; |
| | 771 | } |
| | 772 | } |
| | 773 | |
| | 774 | /* Find child moves. */ |
| | 775 | tree->nodes[node].num_children = 0; |
| | 776 | for (k = 0; k < tree->num_moves_of_interest; k++) { |
| | 777 | move = tree->moves_of_interest[k]; |
| | 778 | if (board[move] == EMPTY |
| | 779 | && is_legal(move, color)) { |
| | 780 | tree->nodes[node].child_moves[tree->nodes[node].num_children] = move; |
| | 781 | tree->nodes[node].child_nodes[tree->nodes[node].num_children] = 0; |
| | 782 | tree->nodes[node].num_children++; |
| | 783 | } |
| | 784 | } |
| | 785 | |
| | 786 | /* No legal moves, the opponent wins. */ |
| | 787 | if (tree->nodes[node].num_children == 0) { |
| | 788 | tree->nodes[node].solved = 1; |
| | 789 | tree->nodes[node].value = (color != tree->color); |
| | 790 | } |
| | 791 | |
| | 792 | return node; |
| | 793 | } |
| | 794 | |
| | 795 | /* Recursive function to search one line to the bottom. I.e. dragon |
| | 796 | * becomes unconditionally alive, is captured, or no more moves. |
| | 797 | */ |
| | 798 | static void |
| | 799 | do_safety_search(struct uss_tree *tree, int node, int color) |
| | 800 | { |
| | 801 | int k; |
| | 802 | int best_child = -1; |
| | 803 | int success; |
| | 804 | int move; |
| | 805 | int unsolved_child_found = 0; |
| | 806 | int safe_child_found = 0; |
| | 807 | int unsafe_child_found = 0; |
| | 808 | |
| | 809 | struct uss_node *n = &tree->nodes[node]; |
| | 810 | if (n->solved) |
| | 811 | return; |
| | 812 | |
| | 813 | /* Choose a child to explore. First take any unexplored child, if |
| | 814 | * none repeat search of a non-solved one. |
| | 815 | */ |
| | 816 | /* FIXME: Add intelligent move ordering! */ |
| | 817 | for (k = 0; k < n->num_children; k++) { |
| | 818 | if (n->child_nodes[k] == 0) { |
| | 819 | best_child = k; |
| | 820 | break; |
| | 821 | } |
| | 822 | |
| | 823 | if (best_child == -1 && !tree->nodes[n->child_nodes[k]].solved) |
| | 824 | best_child = k; |
| | 825 | } |
| | 826 | |
| | 827 | /* There has to be a move, otherwise the node would already have |
| | 828 | * been solved. |
| | 829 | */ |
| | 830 | gg_assert(best_child >= 0); |
| | 831 | |
| | 832 | /* Make the move. */ |
| | 833 | /* FIXME: The attacker should be allowed to break ko rules, but not |
| | 834 | * the defender. |
| | 835 | */ |
| | 836 | move = n->child_moves[best_child]; |
| | 837 | success = trymove(move, color, "do_safety_search", move); |
| | 838 | gg_assert(success); |
| | 839 | |
| | 840 | /* If not previously explored, find and record the new node. */ |
| | 841 | if (!n->child_nodes[best_child]) { |
| | 842 | n->child_nodes[best_child] = uss_find_node(tree, OTHER_COLOR(color)); |
| | 843 | } |
| | 844 | |
| | 845 | /* If we're not out of nodes, recurse. */ |
| | 846 | if (tree->next_node < MAX_USS_NODES) |
| | 847 | do_safety_search(tree, n->child_nodes[best_child], OTHER_COLOR(color)); |
| | 848 | popgo(); |
| | 849 | |
| | 850 | /* Examine status of child nodes. */ |
| | 851 | for (k = 0; k < n->num_children; k++) { |
| | 852 | if (n->child_nodes[k] == 0) |
| | 853 | unsolved_child_found = 1; |
| | 854 | else if (!tree->nodes[n->child_nodes[k]].solved) |
| | 855 | unsolved_child_found = 1; |
| | 856 | else if (tree->nodes[n->child_nodes[k]].value == 1) |
| | 857 | safe_child_found = 1; |
| | 858 | else |
| | 859 | unsafe_child_found = 1; |
| | 860 | } |
| | 861 | |
| | 862 | /* Update solvedness status of this node. */ |
| | 863 | if (color == tree->color) { |
| | 864 | if (safe_child_found) { |
| | 865 | n->solved = 1; |
| | 866 | n->value = 1; |
| | 867 | } |
| | 868 | else if (!unsolved_child_found) { |
| | 869 | n->solved = 1; |
| | 870 | n->value = 0; |
| | 871 | } |
| | 872 | } |
| | 873 | else { |
| | 874 | if (unsafe_child_found) { |
| | 875 | n->solved = 1; |
| | 876 | n->value = 0; |
| | 877 | } |
| | 878 | else if (!unsolved_child_found) { |
| | 879 | n->solved = 1; |
| | 880 | n->value = 1; |
| | 881 | } |
| | 882 | } |
| | 883 | } |
| | 884 | |
| | 885 | /* Top level call for unconditional safety search. Node 0 is the base |
| | 886 | * position. |
| | 887 | */ |
| | 888 | static int |
| | 889 | safety_search(struct uss_tree *tree, int color) |
| | 890 | { |
| | 891 | uss_find_node(tree, color); |
| | 892 | while (tree->next_node < MAX_USS_NODES && !tree->nodes[0].solved) { |
| | 893 | do_safety_search(tree, 0, color); |
| | 894 | } |
| | 895 | |
| | 896 | gprintf("Static safety search used %d nodes.\n", tree->next_node); |
| | 897 | |
| | 898 | return tree->nodes[0].value; |
| | 899 | } |
| | 900 | |
| | 901 | /* Determine static safety of a dragon. This is a rather conservative |
| | 902 | * estimation of safety but should be quite reliable when safety is |
| | 903 | * declared. Use unconditional safety search to determine the safety |
| | 904 | * of the dragon. |
| | 905 | */ |
| | 906 | int |
| | 907 | static_safety(int d) |
| | 908 | { |
| | 909 | int color = board[d]; |
| | 910 | int eyespaces[BOARDMAX]; |
| | 911 | int pos; |
| | 912 | int dr; |
| | 913 | |
| | 914 | memset(eyespaces, 0, sizeof(eyespaces)); |
| | 915 | |
| | 916 | d = dragon[d].origin; |
| | 917 | |
| | 918 | /* Set up unconditional search tree. */ |
| | 919 | tree.next_node = 0; |
| | 920 | tree.num_moves_of_interest = 0; |
| | 921 | tree.target = d; |
| | 922 | tree.color = color; |
| | 923 | |
| | 924 | /* Find search area as surrounding eyes and the stones of the dragon. */ |
| | 925 | /* FIXME: This probably isn't quite enough, e.g. lunches should also |
| | 926 | * be included and likely outer liberties. |
| | 927 | */ |
| | 928 | if (color == BLACK) { |
| | 929 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { |
| | 930 | if (!ON_BOARD(pos)) |
| | 931 | continue; |
| | 932 | |
| | 933 | if (black_eye[pos].color == BLACK |
| | 934 | && black_eye[pos].origin == pos |
| | 935 | && find_eye_dragons(pos, black_eye, BLACK, &dr, 1) == 1 |
| | 936 | && is_same_dragon(dr, d)) { |
| | 937 | eyespaces[pos] = 1; |
| | 938 | } |
| | 939 | } |
| | 940 | } |
| | 941 | else { |
| | 942 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { |
| | 943 | if (!ON_BOARD(pos)) |
| | 944 | continue; |
| | 945 | |
| | 946 | if (white_eye[pos].color == WHITE |
| | 947 | && white_eye[pos].origin == pos |
| | 948 | && find_eye_dragons(pos, white_eye, WHITE, &dr, 1) == 1 |
| | 949 | && is_same_dragon(dr, d)) { |
| | 950 | eyespaces[pos] = 1; |
| | 951 | } |
| | 952 | } |
| | 953 | } |
| | 954 | |
| | 955 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { |
| | 956 | if (!ON_BOARD(pos)) |
| | 957 | continue; |
| | 958 | if ((color == BLACK |
| | 959 | && black_eye[pos].color == BLACK |
| | 960 | && eyespaces[black_eye[pos].origin]) |
| | 961 | || (color == WHITE |
| | 962 | && white_eye[pos].color == WHITE |
| | 963 | && eyespaces[white_eye[pos].origin])) { |
| | 964 | tree.moves_of_interest[tree.num_moves_of_interest++] = pos; |
| | 965 | } |
| | 966 | if (board[pos] == color |
| | 967 | && dragon[pos].origin == d) { |
| | 968 | tree.moves_of_interest[tree.num_moves_of_interest++] = pos; |
| | 969 | } |
| | 970 | } |
| | 971 | |
| | 972 | if (1) { |
| | 973 | int k; |
| | 974 | gprintf("Moves of interest: "); |
| | 975 | for (k = 0; k < tree.num_moves_of_interest; k++) |
| | 976 | gprintf("%1m ", tree.moves_of_interest[k]); |
| | 977 | gprintf("\n"); |
| | 978 | } |
| | 979 | |
| | 980 | /* Perform the unconditional safety search. */ |
| | 981 | return safety_search(&tree, OTHER_COLOR(color)); |
| | 982 | } |
| | 983 | |
| | 984 | |