/** * @brief Recursive function used to traverse the game tree. * * @param [in] result a reference to the exact solution data structure * @param [in] gp the game position to traverse * @return a pointer to a new serch node structure */ static SearchNode * game_position_solve_impl ( ExactSolution * const result, const GamePosition * const gp_old) { SearchNode *node; SearchNode *node2; node = NULL; node2 = NULL; result->node_count++; NodeInfo * const node_info = &stack->nodes[++stack->fill_point]; //NodeInfo * const next_node_info = &stack->nodes[stack->fill_point]; LegalMoveList * const moves = &node_info->moves; const SquareSet move_set = game_position_legal_moves(gp_old); legal_move_list_from_set(move_set, moves); //GamePositionX * const gp = &node_info->gp; //GamePositionX * const next_gp = &next_node_info->gp; /* GamePosition gp; uint64 hash; */ if (move_set == empty_square_set) { GamePosition *flipped_players = game_position_pass(gp_old); const int previous_move_count = stack->nodes[stack->fill_point - 1].moves.move_count; const SquareSet empties = board_empties(gp_old->board); if (empties != empty_square_set && previous_move_count != 0) { node = search_node_negated(game_position_solve_impl(result, flipped_players)); } else { result->leaf_count++; node = search_node_new((Square) -1, game_position_final_value(gp_old)); } flipped_players = game_position_free(flipped_players); } else { node = search_node_new((Square) -1, -65); for (int i = 0; i < moves->move_count; i++) { const Square move = moves->squares[i]; GamePosition *gp2 = game_position_make_move(gp_old, move); node2 = search_node_negated(game_position_solve_impl(result, gp2)); gp2 = game_position_free(gp2); if (node2->value > node->value) { search_node_free(node); node = node2; node->move = move; node2 = NULL; } else { node2 = search_node_free(node2); } } } stack->fill_point--; return node; }
static void game_position_make_move_test (GamePositionDbFixture *fixture, gconstpointer test_data) { GamePositionDb *db = fixture->db; GamePosition *after_make_move; GamePosition *initial = get_gp_from_db(db, "initial"); after_make_move = game_position_make_move(initial, D3); GamePosition *first_move_d3 = get_gp_from_db(db, "first-move-d3"); g_assert(0 == game_position_compare(first_move_d3, after_make_move)); game_position_free(after_make_move); GamePosition *early_game_b_9_moves = get_gp_from_db(db, "early-game-b-9-moves"); after_make_move = game_position_make_move(early_game_b_9_moves, C3); GamePosition *early_game_bc3_10_moves = get_gp_from_db(db, "early-game-bc3-10-moves"); g_assert(0 == game_position_compare(early_game_bc3_10_moves, after_make_move)); game_position_free(after_make_move); after_make_move = game_position_make_move(early_game_b_9_moves, C6); GamePosition *early_game_bc6_10_moves = get_gp_from_db(db, "early-game-bc6-10-moves"); g_assert(0 == game_position_compare(early_game_bc6_10_moves, after_make_move)); game_position_free(after_make_move); GamePosition *before; GamePosition *expected; before = get_gp_from_db(db, "make-move-test-case-a-before"); after_make_move = game_position_make_move(before, D4); expected = get_gp_from_db(db, "make-move-test-case-a-after"); g_assert(0 == game_position_compare(expected, after_make_move)); game_position_free(after_make_move); before = get_gp_from_db(db, "make-move-test-case-b-before"); after_make_move = game_position_make_move(before, D4); expected = get_gp_from_db(db, "make-move-test-case-b-after"); g_assert(0 == game_position_compare(expected, after_make_move)); game_position_free(after_make_move); before = get_gp_from_db(db, "make-move-test-case-c-before"); after_make_move = game_position_make_move(before, D4); expected = get_gp_from_db(db, "make-move-test-case-c-after"); g_assert(0 == game_position_compare(expected, after_make_move)); game_position_free(after_make_move); before = get_gp_from_db(db, "make-move-test-case-d-before"); after_make_move = game_position_make_move(before, B4); expected = get_gp_from_db(db, "make-move-test-case-d-after"); g_assert(0 == game_position_compare(expected, after_make_move)); game_position_free(after_make_move); }
/* * Sorts moves in ascending order of mobility. */ static void sort_moves_by_mobility_count (MoveList *move_list, const GamePosition *const gp) { MoveListElement *curr = NULL; int move_index = 0; const SquareSet moves = game_position_legal_moves(gp); SquareSet moves_to_search = moves; for (int i = 0; i < legal_moves_priority_cluster_count; i++) { moves_to_search = legal_moves_priority_mask[i] & moves; while (moves_to_search) { curr = &move_list->elements[move_index]; const Square move = bit_works_bitscanLS1B_64(moves_to_search); moves_to_search &= ~(1ULL << move); GamePosition *next_gp = game_position_make_move(gp, move); const SquareSet next_moves = game_position_legal_moves(next_gp); game_position_free(next_gp); const int next_move_count = bit_works_bitcount_64(next_moves); curr->sq = move; curr->mobility = next_move_count; for (MoveListElement *element = move_list->head.succ; element != NULL; element = element->succ) { if (curr->mobility < element->mobility) { /* Insert current before element. */ MoveListElement *left = element->pred; MoveListElement *right = element; curr->pred = left; curr->succ = right; left->succ = curr; right->pred = curr; goto out; } } out: move_index++; } } return; }
/* * Main recursive search function. */ static SearchNode * game_position_solve_impl (ExactSolution *const result, const GamePosition *const gp, const int achievable, const int cutoff, PVCell ***pve_parent_line_p) { result->node_count++; SearchNode *node = NULL; SearchNode *node2 = NULL; PVCell **pve_line = NULL; if (log_env->log_is_on) { call_count++; gp_hash_stack_fill_point++; LogDataH log_data; log_data.sub_run_id = 0; log_data.call_id = call_count; log_data.hash = game_position_hash(gp); gp_hash_stack[gp_hash_stack_fill_point] = log_data.hash; log_data.parent_hash = gp_hash_stack[gp_hash_stack_fill_point - 1]; log_data.blacks = (gp->board)->blacks; log_data.whites = (gp->board)->whites; log_data.player = gp->player; gchar *json_doc = game_tree_log_data_h_json_doc(gp_hash_stack_fill_point, gp); log_data.json_doc = json_doc; log_data.json_doc_len = strlen(json_doc); game_tree_log_write_h(log_env, &log_data); g_free(json_doc); } const SquareSet moves = game_position_legal_moves(gp); if (0ULL == moves) { pve_line = pve_line_create(pve); GamePosition *flipped_players = game_position_pass(gp); if (game_position_has_any_legal_move(flipped_players)) { node = search_node_negated(game_position_solve_impl(result, flipped_players, -cutoff, -achievable, &pve_line)); } else { result->leaf_count++; node = search_node_new(pass_move, game_position_final_value(gp)); } pve_line_add_move(pve, pve_line, pass_move, flipped_players); pve_line_delete(pve, *pve_parent_line_p); *pve_parent_line_p = pve_line; game_position_free(flipped_players); } else { MoveList move_list; bool branch_is_active = false; move_list_init(&move_list); sort_moves_by_mobility_count(&move_list, gp); for (MoveListElement *element = move_list.head.succ; element != &move_list.tail; element = element->succ) { const Square move = element->sq; if (!node) node = search_node_new(move, (pv_full_recording) ? achievable - 1 : achievable); GamePosition *gp2 = game_position_make_move(gp, move); pve_line = pve_line_create(pve); node2 = search_node_negated(game_position_solve_impl(result, gp2, -cutoff, -node->value, &pve_line)); if (node2->value > node->value || (!branch_is_active && node2->value == node->value)) { branch_is_active = true; search_node_free(node); node = node2; node->move = move; node2 = NULL; pve_line_add_move(pve, pve_line, move, gp2); game_position_free(gp2); pve_line_delete(pve, *pve_parent_line_p); *pve_parent_line_p = pve_line; if (node->value > cutoff) goto out; if (!pv_full_recording && node->value == cutoff) goto out; } else { if (pv_full_recording && node2->value == node->value) { pve_line_add_move(pve, pve_line, move, gp2); pve_line_add_variant(pve, *pve_parent_line_p, pve_line); } else { pve_line_delete(pve, pve_line); } search_node_free(node2); game_position_free(gp2); } } } out: if (log_env->log_is_on) { gp_hash_stack_fill_point--; } return node; }