/** Removes the first edge e = (s,s') with s' in passed from the linked list associated to s in succ_to_visit if there is one, the first edge of the list otherwise Returns that edge Note that t belongs to P_O Note that the successors list is supposed to be non empty (a test must be done before) */ static safety_game_edge* get_first_successor_passed_non_losing(tuple *t, GHashTable *succ_to_visit, GHashTable *passed, antichain *losing_PO, antichain *losing_PI) { hash_table_key* key = new_hash_table_key(t, -1); // Try to retrieve the successors to visit list from succ_to_visit GList *succ_list = g_hash_table_lookup(succ_to_visit, key); safety_game_edge* first_succ_passed; GList *curlink = succ_list; char found = FALSE; int i=0; while(curlink != NULL) { if(is_passed(((safety_game_edge*)curlink->data)->to, passed) == TRUE && is_losing(((safety_game_edge*)curlink->data)->to, losing_PO, losing_PI) == FALSE) { // is passed? first_succ_passed = curlink->data; succ_list = g_list_delete_link(succ_list, curlink); g_hash_table_insert(succ_to_visit, (gconstpointer*)key, (gconstpointer*)succ_list); found = TRUE; break; } i++; curlink = curlink->next; } if(found == FALSE) { // no succ passed? first_succ_passed = succ_list->data; succ_list = remove_first(succ_list); g_hash_table_insert(succ_to_visit, (gconstpointer*)key, (gconstpointer*)succ_list); } return first_succ_passed; }
static int full_new_depth(int depth, int move, board_t * board, bool single_reply, bool in_pv, int height, bool extended, bool * cap_extended, int ThreadId) { int new_depth; ASSERT(depth_is_ok(depth)); ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(single_reply==true||single_reply==false); ASSERT(in_pv==true||in_pv==false); ASSERT(depth>0); new_depth = depth - 1; *cap_extended = false; if (in_pv && board->square[MOVE_TO(move)] != Empty && !PIECE_IS_PAWN(board->square[MOVE_TO(move)])){ if ((board->piece_size[White] + board->piece_size[Black]) == 3){ return new_depth+1; } else if ((board->piece_size[White] == 3 && board->piece_size[Black] == 2) || (board->piece_size[White] == 2 && board->piece_size[Black] == 3)) return new_depth+1; } if ((single_reply && ExtendSingleReply) //|| (in_pv && MOVE_TO(move) == board->cap_sq // recapture // && see_move(move,board) >= -100 /*|| ABS(VALUE_PIECE(board->square[MOVE_TO(move)])-VALUE_PIECE(board->square[MOVE_FROM(move)])) <= 250 )*/) //|| (in_pv && PIECE_IS_PAWN(MOVE_PIECE(move,board)) // && (PAWN_RANK(MOVE_TO(move),board->turn) == Rank7 || PAWN_RANK(MOVE_TO(move),board->turn) == Rank6)) //|| (in_pv && board->square[MOVE_TO(move)] != PieceNone256 && SearchCurrent[ThreadId]->act_iteration-height >= 6 && see_move(move,board) >= -100) //|| (in_pv && board->square[MOVE_TO(move)] != PieceNone256 && !extended && see_move(move,board) >= -100) //|| (in_pv && mate_threat == true) || move_is_check(move,board) && (in_pv || see_move(move,board) >= -100)) { // @tried no check extension in endgame (~ -5 elo) // @tried no bad SEE check extension in PV (~ -5 elo) return new_depth+1; } if (in_pv && PIECE_IS_PAWN(MOVE_PIECE(move,board))){ if (is_passed(board,MOVE_TO(move))) return new_depth+1; } if (in_pv && board->square[MOVE_TO(move)] != PieceNone256 && !extended && see_move(move,board) >= -100){ *cap_extended = true; return new_depth+1; } ASSERT(new_depth>=0&&new_depth<=depth); return new_depth; }
static bool move_is_dangerous(int move, const board_t * board) { int piece; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(!move_is_tactical(move,board)); piece = MOVE_PIECE(move,board); if (PIECE_IS_PAWN(piece) && is_passed(board,MOVE_TO(move)) /*PAWN_RANK(MOVE_TO(move),board->turn) >= Rank7*/) { return true; } return false; }
/** Part of the winning positions computation: adds t to the antichain of winning positions If t belongs to P_O, find a successor non losing passed and add update the winning positions with it If t belongs to P_I, computes all successors and update the winning positions with each of them Optimization (maximum successors for P_I): if t belongs to P_I, only deal the maximum successors of t **/ static void update_winning_positions(tuple *t, alphabet_info *alphabet, safety_game *winning_positions, GHashTable *mapping_table, antichain* losing_PO, antichain* losing_PI, GHashTable* passed) { hash_table_key* key = new_hash_table_key(t, -1); // Insert t in the mapping_table char *value = (char*)malloc(sizeof(char)); value[0] = TRUE; g_hash_table_insert(mapping_table, (gconstpointer*)key, (gconstpointer*)value); int nb_outgoing_edges, sigma_size; antichain *max_succ; if(t->cf->player == P_O) { nb_outgoing_edges = 1; sigma_size = alphabet->sigma_output_size; add_element_to_antichain(winning_positions->positions_O, t, (void*)compare_tuples); } else { nb_outgoing_edges = alphabet->sigma_input_size; sigma_size = alphabet->sigma_input_size; add_element_to_antichain(winning_positions->positions_I, t, (void*)compare_tuples); max_succ = maximal_tuple_succ(t, alphabet); } // Compute successors of t tuple *cur_succ, *good_succ; int i; char *cur_value; hash_table_key *cur_key; char found = FALSE; for(i=0; i<sigma_size; i++) { cur_succ = tuple_succ(t, i, alphabet); if(t->cf->player == P_O) { //t belongs to P_O if((is_losing(cur_succ, losing_PO, losing_PI) == FALSE) && (is_passed(cur_succ, passed) == TRUE)) { // Not losing and passed? cur_key = new_hash_table_key(cur_succ, -1); // Check whether cur_succ already belongs to the solution or not cur_value = (char*)g_hash_table_lookup(mapping_table, cur_key); if(cur_value == NULL) { // new state in solution good_succ = cur_succ; } else { // existing state found = TRUE; free_tuple_full(cur_succ); break; } free(cur_key); } else { free_tuple_full(cur_succ); } } else { //t belongs to P_I //if(is_losing(cur_succ, losing_PO, losing_PI) == FALSE) { // cur_succ is not losing? GList *curlink = max_succ->incomparable_elements; found = FALSE; while(curlink != NULL) { // Among the maximal successors of t, find one which is greater than cur_succ if(compare_tuples(cur_succ, curlink->data) == TRUE) { cur_key = new_hash_table_key(curlink->data, -1); // Check whether cur_succ already belongs to the solution or not cur_value = (char*)g_hash_table_lookup(mapping_table, cur_key); if(cur_value == NULL) { // new state in solution update_winning_positions(clone_tuple(curlink->data), alphabet, winning_positions, mapping_table, losing_PO, losing_PI, passed); } free(cur_key); found = TRUE; //found a state to reach when P_I plays the i-th letter of its alphabet break; } curlink = curlink->next; } free_tuple_full(cur_succ); if(found == FALSE) { // Impossible? printf("add_state_to_solution: no succ for P_I in maximal succ\n"); exit(0); } /*} else { // Impossible? printf("add_state_to_solution: succ losing for P_I\n"); exit(0); }*/ } } if(t->cf->player == P_I) { free_antichain_full(max_succ, (void*)free_tuple_full); } if(t->cf->player == P_O && found == FALSE) { // Impossible? update_winning_positions(good_succ, alphabet, winning_positions, mapping_table, losing_PO, losing_PI, passed); } }
/** OTFUR algorithm **/ otfur_result* otfur(antichain *safety_game_PO, antichain *safety_game_PI, GNode* cfinfo, alphabet_info *alphabet, char starting_player, int dimension, int* max_credit) { clock_t start_time = clock(); // Structures initialization GHashTable *succ_to_visit = g_hash_table_new_full(hash_key, compare_keys, free, NULL); GHashTable *passed = g_hash_table_new_full(hash_key, compare_keys, (GDestroyNotify)free_hash_table_key, free); GHashTable *depend = g_hash_table_new_full(hash_key, compare_keys, free, NULL); //GList *trash = NULL; GList *waiting = NULL; antichain *losing_PO = new_antichain(); antichain *losing_PI = new_antichain(); // Build initial tuple tuple *s_ini = build_initial_tuple(cfinfo, dimension, max_credit); // if s_ini belongs to the system and all its successors are unsafe, s_ini is losing if((starting_player == P_O && has_successor_in_safety_game(s_ini, alphabet, safety_game_PI) == FALSE)/* || (starting_player == P_I && has_successor_not_in_safety_game(s_ini, alphabet, safety_game_PO) == TRUE)*/) { // To avoid k differences between forward and backward algorithms add_to_losing(s_ini, losing_PO, losing_PI); } // else, explore its successors else { waiting = add_to_waiting(s_ini, alphabet, safety_game_PO, safety_game_PI, losing_PO, losing_PI, waiting, succ_to_visit, passed); add_to_passed(s_ini, passed); } safety_game_edge *cur_edge; char cur_losing; int nb_cf_passed = 1; int nb_iter = 0; antichain* cur_safety_game; while(waiting != NULL && is_losing(s_ini, losing_PO, losing_PI) == FALSE) { nb_iter++; GList *last_link = g_list_last(waiting); cur_edge = (safety_game_edge*)last_link->data; waiting = remove_last(waiting); if(is_losing(cur_edge->from, losing_PO, losing_PI) == FALSE) { // If s is not losing if(is_passed(cur_edge->to, passed) == FALSE) { // If s' is not passed add_to_passed(cur_edge->to, passed); nb_cf_passed++; if(is_losing(cur_edge->to, losing_PO, losing_PI) == TRUE) { // If s' is losing -> add e for reevaluation //waiting = g_list_append(waiting, clone_safety_game_edge(cur_edge)); waiting = g_list_append(waiting, cur_edge); } // Basic case: if s' belongs to P_O and has no successor safe non losing, add it to losing // Put the same test for P_I to avoid k differences between forward and backward algorithms else if(cur_edge->to->cf->player == P_O && has_successor_non_losing_in_safety_game(cur_edge->to, alphabet, losing_PO, losing_PI, safety_game_PI) == FALSE) { //waiting = g_list_append(waiting, clone_safety_game_edge(cur_edge)); waiting = g_list_append(waiting, cur_edge); add_to_losing(cur_edge->to, losing_PO, losing_PI); } else { // else, explore its successors add_to_depend(cur_edge->to, cur_edge, depend); waiting = add_to_waiting(cur_edge->to, alphabet, safety_game_PO, safety_game_PI, losing_PO, losing_PI, waiting, succ_to_visit, passed); } } else { // s' is passed if(cur_edge->from->cf->player == P_O) { // s belongs to P_O if(is_losing(cur_edge->to, losing_PO, losing_PI) == FALSE) { // if s' is not losing, add e to the dependencies of s' add_to_depend(cur_edge->to, cur_edge, depend); } else { // if s' is losing, and if there is no more successor of s non losing to visit, s is losing -> add its dependencies to waiting if(has_one_succ_to_visit_non_losing(cur_edge->from, succ_to_visit, losing_PO, losing_PI) == FALSE) { add_to_losing(cur_edge->from, losing_PO, losing_PI); waiting = g_list_concat(waiting, get_dependencies(cur_edge->from, depend)); } else { // if there is still at least a successor non passed non losing, add it to waiting waiting = g_list_append(waiting, get_first_successor_passed_non_losing(cur_edge->from, succ_to_visit, passed, losing_PO, losing_PI)); } } } else { // s belongs to P_I // reevaluation if(is_losing(cur_edge->to, losing_PO, losing_PI) == TRUE) { cur_losing = TRUE; } else { cur_losing = reevaluation(cur_edge->from, alphabet, losing_PO, losing_PI); } // if s is losing, add its dependencies to waiting if(cur_losing == TRUE) { add_to_losing(cur_edge->from, losing_PO, losing_PI); waiting = g_list_concat(waiting, get_dependencies(cur_edge->from, depend)); } // if s' is not losing, add e to its dependencies list if(is_losing(cur_edge->to, losing_PO, losing_PI) == FALSE) { add_to_depend(cur_edge->to, cur_edge, depend); } } //trash = g_list_append(trash, cur_edge->to); } } else { // if s is losing, add its dependencies to waiting //free_tuple_full(cur_edge->to); waiting = g_list_concat(waiting, get_dependencies(cur_edge->from, depend)); } } float otfur_time = (clock() - start_time) * 1e-6; // Extract the solution from passed and losing start_time = clock(); safety_game* sg = compute_winning_positions(losing_PO, losing_PI, passed, build_initial_tuple(cfinfo, dimension, max_credit), alphabet, nb_cf_passed); float winning_positions_computation_time = (clock() - start_time) * 1e-6; // Free memory GList *curlink = waiting; //while(curlink != NULL) { // free_safety_game_edge(curlink->data); // curlink = curlink->next; //} g_list_free(waiting); /*curlink = trash; while(curlink != NULL) { free_tuple_full(curlink->data); curlink = curlink->next; } g_list_free(trash);*/ //g_hash_table_destroy(passed); // this one is not correct ! g_hash_table_foreach(depend, free_depend, NULL); g_hash_table_destroy(depend); g_hash_table_foreach(succ_to_visit, free_succ_to_visit, NULL); g_hash_table_destroy(succ_to_visit); // Build result otfur_result *res = (otfur_result*)malloc(sizeof(otfur_result)); res->winning_positions = sg; res->otfur_time = otfur_time; res->winning_positions_computation_time = winning_positions_computation_time; res->nb_cf_passed = nb_cf_passed; res->nb_iter = nb_iter; return res; }