Ticket #215: gunnar_9_1.6.diff

File gunnar_9_1.6.diff, 10.1 KB (added by gunnar, 2 years ago)

First implementation of unconditional safety search

  • engine/liberty.h

    diff --git a/engine/liberty.h b/engine/liberty.h
    index 05edaf3..6249af2 100644
    a b void find_unconditionally_meaningless_moves(int unconditional_territory[BOARDMAX 
    447447int unconditionally_meaningless_move(int pos, int color, 
    448448                                     int *replacement_move); 
    449449void unconditional_move_reasons(int color); 
     450int static_safety(int d); 
    450451 
    451452void find_superstring(int str, int *num_stones, int *stones); 
    452453void find_superstring_conservative(int str, int *num_stones, int *stones); 
  • engine/unconditional.c

    diff --git a/engine/unconditional.c b/engine/unconditional.c
    index 3f35cd1..2f05393 100644
    a b unconditional_move_reasons(int color) 
    697697    } 
    698698} 
    699699 
     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. */ 
     710struct 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. */ 
     720struct 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. */ 
     730static struct uss_tree tree; 
     731 
     732/* Find an existing node or make a new one. */ 
     733/* FIXME: Transposition detection would go here. */ 
     734static int 
     735uss_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 */ 
     798static void 
     799do_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 */ 
     888static int 
     889safety_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 */ 
     906int 
     907static_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 
    700985/* 
    701986 * Local Variables: 
    702987 * tab-width: 8 
  • interface/play_gtp.c

    diff --git a/interface/play_gtp.c b/interface/play_gtp.c
    index 8e33963..3705ead 100644
    a b DECLARE(gtp_set_search_diamond); 
    165165DECLARE(gtp_set_search_limit); 
    166166DECLARE(gtp_showboard); 
    167167DECLARE(gtp_start_sgftrace); 
     168DECLARE(gtp_static_safety); 
    168169DECLARE(gtp_surround_map); 
    169170DECLARE(gtp_tactical_analyze_semeai); 
    170171DECLARE(gtp_test_eyeshape); 
    static struct gtp_command commands[] = { 
    305306  {"set_search_limit",        gtp_set_search_limit}, 
    306307  {"showboard",               gtp_showboard}, 
    307308  {"start_sgftrace",          gtp_start_sgftrace}, 
     309  {"static_safety",           gtp_static_safety}, 
    308310  {"surround_map",            gtp_surround_map}, 
    309311  {"tactical_analyze_semeai", gtp_tactical_analyze_semeai}, 
    310312  {"test_eyeshape",           gtp_test_eyeshape}, 
    gtp_unconditional_status(char *s) 
    23742376} 
    23752377 
    23762378 
     2379/************************ 
     2380 * Static safety        * 
     2381 ************************/ 
     2382 
     2383/* Function:  Determine the static safety of a dragon 
     2384 * Arguments: vertex 
     2385 * Fails:     invalid vertex, empty vertex 
     2386 * Returns:   1 if dragon is statically safe and 0 if not. 
     2387 */ 
     2388 
     2389static int 
     2390gtp_static_safety(char *s) 
     2391{ 
     2392  int i, j; 
     2393  int safe; 
     2394 
     2395  if (!gtp_decode_coord(s, &i, &j)) 
     2396    return gtp_failure("invalid coordinate"); 
     2397 
     2398  if (board[POS(i, j)] == EMPTY) 
     2399    return gtp_failure("empty coordinate"); 
     2400 
     2401  silent_examine_position(EXAMINE_DRAGONS); 
     2402  safe = static_safety(POS(i, j)); 
     2403 
     2404  return gtp_success("%d", safe); 
     2405} 
     2406 
     2407 
    23772408/*********************** 
    23782409 * combination attacks * 
    23792410 ***********************/