diff --git a/README.md b/README.md deleted file mode 100644 index a23b0c5..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Deadlock and Optimizations - -Explain your optimizations if applicable \ No newline at end of file diff --git a/report.md b/report.md new file mode 100644 index 0000000..0f6dd9f --- /dev/null +++ b/report.md @@ -0,0 +1,140 @@ +# Deadlock and Optimizations + +## Deadlock detections +### Simple corner deadlock detection +The simple corner deadlock that was already implemented aims to prevent a box +from being pushed into a corner. If it is pushed into a corner, it is +inaccessible and immovable, thus rendering that state unsolvable. + +### Freeze deadlock detection +Described +[here](http://sokobano.de/wiki/index.php?title=Deadlocks#Freeze_deadlocks), +freeze detection is about preventing two boxes from being adjacent against a +wall when there is no goal in that location. This deadlock detection was chosen +as there are many scenarios (such as in test_maps/test_map2 and +test_maps/test_map3) where there are multiple boxes and a complex map layout. +This deadlock detection saves a small amount time and reduces the number of +nodes that are expanded. + +The code for this detection is contained in `utils.c`, from lines 321 - 480. +The function `freeze_deadlock()` is used to check if there is a box adjacent to +the player, and if there is, then the function `adjacent_box_check()` is called +to check for the freeze deadlock scenario. + +## Optimisation results +These results show the impact of implementing the freeze deadlock detection. + +Hardware used: +- CPU: AMD Ryzen 4800U +- GPU: AMD Radeon RX Vega 8 +- Memory: 16GB + +### Summary of optimisation +The following table summarises some of the key statistics prior to implementing +freeze deadlock detection, and after implementing freeze deadlock detection. + +Test: test_map1 + +| Statistic | Before implementation | After implementation | % improvement | +|----------------|-----------------------|----------------------|---------------| +| Expanded nodes | 13 | 13 | 0.00 | +| Time (s) | 0.050106 | 0.047723 | 4.76 | + + +Test: test_map2 + +| Statistic | Before implementation | After implementation | % improvement | +|----------------|-----------------------|----------------------|---------------| +| Expanded nodes | 978745 | 944140 | 3.54 | +| Time | 3.456764 | 3.227685 | 6.63 | + +Test: test_map3 + +| Statistic | Before implementation | After implementation | % improvement | +|----------------|-----------------------|----------------------|---------------| +| Expanded nodes | 230041 | 215253 | 6.43 | +| Time | 1.828733 | 1.680901 | 8.08 | + +### Full output (before optimisation) +``` +$ ./sokoban -s test_maps/test_map1 + +SOLUTION: +rrRRRR + +STATS: + Expanded nodes: 13 + Generated nodes: 48 + Duplicated nodes: 8 + Solution Length: 6 + Expanded/seconds: 259 + Time (seconds): 0.050106 + +$ ./sokoban -s test_maps/test_map2 + +SOLUTION: +rrrrrrdrdLLLLLLLLullluRRRRRRRururRRRRRRRRRR + +STATS: + Expanded nodes: 978745 + Generated nodes: 3914976 + Duplicated nodes: 2288345 + Solution Length: 43 + Expanded/seconds: 283139 + Time (seconds): 3.456764 + +$ ./sokoban -s test_maps/test_map3 + +SOLUTION: +drdrdrdrrruuuuuulllllddrdrdrDulululldRdRdrdRRllululuurdrdrDululldRuuluurrrrrdddddrddlUUUUUUruLLLLLulDDdrdddrdRRlluluurdrDulldruluulldRuuurrrrrdddddrddlUUUUUUruLLLLLulDDdrdrdddRRlluurDldRuulululldRdRluuuurrrrrdddddrddlUUUUUUruLLLLLulDDdrdrdrddRluulululldRdRluuuurrrrrdddddrddlUUUUUUruLLLLLulDD + +STATS: + Expanded nodes: 230041 + Generated nodes: 920160 + Duplicated nodes: 331571 + Solution Length: 292 + Expanded/seconds: 125792 + Time (seconds): 1.828733 +``` + +### Full output (after optimisation) +``` +$ ./sokoban -s test_maps/test_map1 + +SOLUTION: +rrRRRR + +STATS: + Expanded nodes: 13 + Generated nodes: 48 + Duplicated nodes: 8 + Solution Length: 6 + Expanded/seconds: 272 + Time (seconds): 0.047723 + +$ ./sokoban -s test_maps/test_map2 + +SOLUTION: +rrrrrrdrdLLLLLLLLullluRRRRRRRurruRRRRRRRRRR + +STATS: + Expanded nodes: 944140 + Generated nodes: 3776556 + Duplicated nodes: 2199712 + Solution Length: 43 + Expanded/seconds: 292513 + Time (seconds): 3.227685 + +$ ./sokoban -s test_maps/test_map3 + +SOLUTION: +drdrdrdrrruuuuuulllllddrdrdrDulululldRdRdrdRRllululuurdrdrDululldRuuluurrrrrdddddrddlUUUUUUruLLLLLulDDdrdddrdRRlluluurdrDulldruululldRuuurrrrrdddddrddlUUUUUUruLLLLLulDDdrdrdddRRlluurDldRluuululldRdRluuuurrrrrdddddrddlUUUUUUruLLLLLulDDdrdrdrddRluulululldRdRluuuurrrrrdddddrddlUUUUUUruLLLLLulDD + +STATS: + Expanded nodes: 215253 + Generated nodes: 861008 + Duplicated nodes: 309654 + Solution Length: 292 + Expanded/seconds: 128058 + Time (seconds): 1.680901 +``` diff --git a/sokoban b/sokoban index 74045ad..a128b8a 100755 Binary files a/sokoban and b/sokoban differ diff --git a/solution.txt b/solution.txt deleted file mode 100644 index 60103d9..0000000 --- a/solution.txt +++ /dev/null @@ -1 +0,0 @@ -rrRlllllL diff --git a/src/ai/ai.c b/src/ai/ai.c index c3e2191..8f5258a 100644 --- a/src/ai/ai.c +++ b/src/ai/ai.c @@ -13,37 +13,37 @@ */ char *save_solution(node_t *solution_node) { node_t *n = solution_node; - char *solution_string = malloc(sizeof(char) * solution_node->depth+1); + char *solution_string = malloc(sizeof(char) * solution_node->depth + 1); solution_string[n->depth] = '\0'; while (n->parent != NULL) { switch (n->move) { case up: if (n->parent->state.map[n->state.player_y][n->state.player_x] == '$') - solution_string[n->depth-1] = 'U'; + solution_string[n->depth - 1] = 'U'; else - solution_string[n->depth-1] = 'u'; + solution_string[n->depth - 1] = 'u'; break; case down: if (n->parent->state.map[n->state.player_y][n->state.player_x] == '$') - solution_string[n->depth-1] = 'D'; + solution_string[n->depth - 1] = 'D'; else - solution_string[n->depth-1] = 'd'; + solution_string[n->depth - 1] = 'd'; break; case left: if (n->parent->state.map[n->state.player_y][n->state.player_x] == '$') - solution_string[n->depth-1] = 'L'; + solution_string[n->depth - 1] = 'L'; else - solution_string[n->depth-1] = 'l'; + solution_string[n->depth - 1] = 'l'; break; case right: if (n->parent->state.map[n->state.player_y][n->state.player_x] == '$') - solution_string[n->depth-1] = 'R'; + solution_string[n->depth - 1] = 'R'; else - solution_string[n->depth-1] = 'r'; + solution_string[n->depth - 1] = 'r'; break; } @@ -73,7 +73,7 @@ void copy_state(sokoban_t *init_data, state_t *dst, state_t *src) { * Create the initial node */ node_t *create_init_node(sokoban_t *init_data) { - node_t *new_n = (node_t *) malloc(sizeof(node_t)); + node_t *new_n = malloc(sizeof(node_t)); new_n->parent = NULL; new_n->priority = 0; @@ -98,7 +98,7 @@ node_t *create_init_node(sokoban_t *init_data) { * Create the a node from a parent node */ node_t *create_node(sokoban_t *init_data, node_t *parent) { - node_t *new_n = (node_t *) malloc(sizeof(node_t)); + node_t *new_n = malloc(sizeof(*new_n)); new_n->parent = parent; new_n->depth = parent->depth + 1; copy_state(init_data, &(new_n->state), &(parent->state)); @@ -138,7 +138,7 @@ void update_explore_table(node_t *n, unsigned expanded_nodes) { sizeof(node_t*) * expanded_nodes_table_size); } - expanded_nodes_table[ expanded_nodes ] = n; + expanded_nodes_table[expanded_nodes] = n; } void free_memory(unsigned expanded_nodes, int lines) { @@ -189,7 +189,8 @@ void find_solution(sokoban_t *init_data, bool show_solution) { // Choose initial capacity of PRIME NUMBER // Specify the size of the keys and values you want to store once // The Hash Table only accept a 1D key and value. - ht_setup(&hashTable, sizeof(int8_t) * init_data->num_chars_map, sizeof(int8_t) * init_data->num_chars_map, 16769023); + ht_setup(&hashTable, sizeof(int8_t) * init_data->num_chars_map, \ + sizeof(int8_t) * init_data->num_chars_map, 16769023); // Data structure to create a 1D representation of the map // Needed to interact with the hash table @@ -200,8 +201,8 @@ void find_solution(sokoban_t *init_data, bool show_solution) { // Initialize expanded nodes table. // This table will be used to free your memory once a solution is found - expanded_nodes_table = (node_t **) malloc(sizeof(node_t *) * expanded_nodes_table_size); - + expanded_nodes_table = malloc(sizeof(*expanded_nodes_table) * \ + expanded_nodes_table_size); // Add the initial node node_t *n = create_init_node(init_data); @@ -221,13 +222,17 @@ void find_solution(sokoban_t *init_data, bool show_solution) { } for (move_t action = left; action <= down; action++) { - node_t *new = malloc(sizeof(*new)); + node_t *new; bool player_moved = applyAction(init_data, n, &new, action); generated_nodes += 1; - if (!player_moved || simple_corner_deadlock(init_data, &(new->state))) { + + if (!player_moved || freeze_deadlock(init_data, &(new->state)) || \ + simple_corner_deadlock(init_data, &(new->state))) { + for (int i = 0; i < init_data->lines; i++) { free(new->state.map[i]); } + free(new->state.map); free(new); continue; @@ -256,17 +261,8 @@ void find_solution(sokoban_t *init_data, bool show_solution) { free_memory(expanded_nodes, init_data->lines); free(flat_map); - // Free initial data - for (int i = 0; i < init_data->lines; i++) { - free((init_data->map)[i]); - free((init_data->map_save)[i]); - } - free(init_data->map); - free(init_data->map_save); - free(init_data->buffer); - // Free priority queue - for (int i = pq.count; i > 0; i--) { + for (int i = 0; i < pq.count; i++) { for (int j = 0; j < init_data->lines; j++) { if (pq.heaparr[i]->state.map[j]) { free(pq.heaparr[i]->state.map[j]); @@ -326,4 +322,13 @@ void solve(char const *path, bool show_solution) { sokoban = find_player(sokoban); find_solution(&sokoban, show_solution); + + // Free initial data + for (int i = 0; i < sokoban.lines; i++) { + free((sokoban.map)[i]); + free((sokoban.map_save)[i]); + } + free(sokoban.map); + free(sokoban.map_save); + free(sokoban.buffer); } diff --git a/src/ai/ai.o b/src/ai/ai.o index fda4a3f..4cb0547 100644 Binary files a/src/ai/ai.o and b/src/ai/ai.o differ diff --git a/src/ai/utils.c b/src/ai/utils.c index af54f94..64498ca 100644 --- a/src/ai/utils.c +++ b/src/ai/utils.c @@ -21,19 +21,19 @@ bool is_goal_loc(int y, int x, sokoban_t *init_data) { } bool push_box_left(sokoban_t *init_data, state_t *state) { - if (state->map[state->player_y][state->player_x-2] == '$' || \ - state->map[state->player_y][state->player_x-2] == '*') { + if (state->map[state->player_y][state->player_x - 2] == '$' || \ + state->map[state->player_y][state->player_x - 2] == '*') { return false; - } else if (state->map[state->player_y][state->player_x-2] == '#') { + } else if (state->map[state->player_y][state->player_x - 2] == '#') { return false; } else { - state->map[state->player_y][state->player_x-1] = '@'; + state->map[state->player_y][state->player_x - 1] = '@'; - if (state->map[state->player_y][state->player_x-2] == '.') { - state->map[state->player_y][state->player_x-2] = '*'; + if (state->map[state->player_y][state->player_x - 2] == '.') { + state->map[state->player_y][state->player_x - 2] = '*'; } else { - state->map[state->player_y][state->player_x-2] = '$'; + state->map[state->player_y][state->player_x - 2] = '$'; } state->map[state->player_y][state->player_x] = ' '; @@ -51,19 +51,19 @@ bool push_box_left(sokoban_t *init_data, state_t *state) { } bool push_box_right(sokoban_t *init_data, state_t *state) { - if (state->map[state->player_y][state->player_x+2] == '$' || \ - state->map[state->player_y][state->player_x+2] == '*') { + if (state->map[state->player_y][state->player_x + 2] == '$' || \ + state->map[state->player_y][state->player_x + 2] == '*') { return false; - } else if (state->map[state->player_y][state->player_x+2] == '#') { + } else if (state->map[state->player_y][state->player_x + 2] == '#') { return false; } else { - state->map[state->player_y][state->player_x+1] = '@'; + state->map[state->player_y][state->player_x + 1] = '@'; - if (state->map[state->player_y][state->player_x+2] == '.') { - state->map[state->player_y][state->player_x+2] = '*'; + if (state->map[state->player_y][state->player_x + 2] == '.') { + state->map[state->player_y][state->player_x + 2] = '*'; } else { - state->map[state->player_y][state->player_x+2] = '$'; + state->map[state->player_y][state->player_x + 2] = '$'; } state->map[state->player_y][state->player_x] = ' '; @@ -81,19 +81,19 @@ bool push_box_right(sokoban_t *init_data, state_t *state) { } bool push_box_up(sokoban_t *init_data, state_t *state) { - if (state->map[state->player_y-2][state->player_x] == '$' || \ - state->map[state->player_y-2][state->player_x] == '*') { + if (state->map[state->player_y - 2][state->player_x] == '$' || \ + state->map[state->player_y - 2][state->player_x] == '*') { return false; - } else if (state->map[state->player_y-2][state->player_x] == '#') { + } else if (state->map[state->player_y - 2][state->player_x] == '#') { return false; } else { - state->map[state->player_y-1][state->player_x] = '@'; + state->map[state->player_y - 1][state->player_x] = '@'; - if (state->map[state->player_y-2][state->player_x] == '.') { - state->map[state->player_y-2][state->player_x] = '*'; + if (state->map[state->player_y - 2][state->player_x] == '.') { + state->map[state->player_y - 2][state->player_x] = '*'; } else { - state->map[state->player_y-2][state->player_x] = '$'; + state->map[state->player_y - 2][state->player_x] = '$'; } state->map[state->player_y][state->player_x] = ' '; @@ -111,19 +111,19 @@ bool push_box_up(sokoban_t *init_data, state_t *state) { } bool push_box_down(sokoban_t *init_data, state_t *state) { - if (state->map[state->player_y+2][state->player_x] == '$' || \ - state->map[state->player_y+2][state->player_x] == '*') { + if (state->map[state->player_y + 2][state->player_x] == '$' || \ + state->map[state->player_y + 2][state->player_x] == '*') { return false; - } else if (state->map[state->player_y+2][state->player_x] == '#') { + } else if (state->map[state->player_y + 2][state->player_x] == '#') { return false; } else { - state->map[state->player_y+1][state->player_x] = '@'; + state->map[state->player_y + 1][state->player_x] = '@'; - if (state->map[state->player_y+2][state->player_x] == '.') { - state->map[state->player_y+2][state->player_x] = '*'; + if (state->map[state->player_y + 2][state->player_x] == '.') { + state->map[state->player_y + 2][state->player_x] = '*'; } else { - state->map[state->player_y+2][state->player_x] = '$'; + state->map[state->player_y + 2][state->player_x] = '$'; } state->map[state->player_y][state->player_x] = ' '; @@ -145,13 +145,13 @@ bool push_box_down(sokoban_t *init_data, state_t *state) { ***************************************/ bool move_left_player(sokoban_t *init_data, state_t *state) { - if (state->map[state->player_y][state->player_x-1] != '#') { - if (state->map[state->player_y][state->player_x-1] == '$' || \ - state->map[state->player_y][state->player_x-1] == '*') { + if (state->map[state->player_y][state->player_x - 1] != '#') { + if (state->map[state->player_y][state->player_x - 1] == '$' || \ + state->map[state->player_y][state->player_x - 1] == '*') { return push_box_left(init_data, state); } else { - state->map[state->player_y][state->player_x-1] = '@'; + state->map[state->player_y][state->player_x - 1] = '@'; state->map[state->player_y][state->player_x] = ' '; if (is_goal_loc(state->player_y, state->player_x, init_data) && \ @@ -170,13 +170,13 @@ bool move_left_player(sokoban_t *init_data, state_t *state) { } bool move_right_player(sokoban_t *init_data, state_t *state) { - if (state->map[state->player_y][state->player_x+1] != '#') { - if (state->map[state->player_y][state->player_x+1] == '$' || \ - state->map[state->player_y][state->player_x+1] == '*') { + if (state->map[state->player_y][state->player_x + 1] != '#') { + if (state->map[state->player_y][state->player_x + 1] == '$' || \ + state->map[state->player_y][state->player_x + 1] == '*') { return push_box_right(init_data, state); } else { - state->map[state->player_y][state->player_x+1] = '@'; + state->map[state->player_y][state->player_x + 1] = '@'; state->map[state->player_y][state->player_x] = ' '; if (is_goal_loc(state->player_y, state->player_x, init_data) && \ @@ -195,13 +195,13 @@ bool move_right_player(sokoban_t *init_data, state_t *state) { } bool move_up_player(sokoban_t *init_data, state_t *state) { - if (state->map[state->player_y-1][state->player_x] != '#') { - if (state->map[state->player_y-1][state->player_x] == '$' || \ - state->map[state->player_y-1][state->player_x] == '*') { + if (state->map[state->player_y - 1][state->player_x] != '#') { + if (state->map[state->player_y - 1][state->player_x] == '$' || \ + state->map[state->player_y - 1][state->player_x] == '*') { return push_box_up(init_data, state); } else { - state->map[state->player_y-1][state->player_x] = '@'; + state->map[state->player_y - 1][state->player_x] = '@'; state->map[state->player_y][state->player_x] = ' '; if (is_goal_loc(state->player_y, state->player_x, init_data) && \ @@ -220,13 +220,13 @@ bool move_up_player(sokoban_t *init_data, state_t *state) { } bool move_down_player(sokoban_t *init_data, state_t *state) { - if (state->map[state->player_y+1][state->player_x] != '#') { - if (state->map[state->player_y+1][state->player_x] == '$' || \ - state->map[state->player_y+1][state->player_x] == '*') { + if (state->map[state->player_y + 1][state->player_x] != '#') { + if (state->map[state->player_y + 1][state->player_x] == '$' || \ + state->map[state->player_y + 1][state->player_x] == '*') { return push_box_down(init_data, state); } else { - state->map[state->player_y+1][state->player_x] = '@'; + state->map[state->player_y + 1][state->player_x] = '@'; state->map[state->player_y][state->player_x] = ' '; if (is_goal_loc(state->player_y, state->player_x, init_data) && \ @@ -279,10 +279,10 @@ bool execute_move_t(sokoban_t *init_data, state_t *state, move_t move) { bool corner_check(int x, int y, sokoban_t *init_data, state_t *state) { // Check if corner - if (((state->map[y][x+1] == '#' && state->map[y+1][x] == '#') || - (state->map[y+1][x] == '#' && state->map[y][x-1] == '#') || - (state->map[y][x-1] == '#' && state->map[y-1][x] == '#') || - (state->map[y-1][x] == '#' && state->map[y][x+1] == '#')) && + if (((state->map[y][x + 1] == '#' && state->map[y + 1][x] == '#') || + (state->map[y + 1][x] == '#' && state->map[y][x - 1] == '#') || + (state->map[y][x - 1] == '#' && state->map[y - 1][x] == '#') || + (state->map[y - 1][x] == '#' && state->map[y][x + 1] == '#')) && !is_goal_loc(state->player_y, state->player_x, init_data)) { return true; @@ -300,17 +300,17 @@ bool simple_corner_deadlock(sokoban_t *init_data, state_t *state) { deadlock = corner_check(x, y, init_data, state); } - if (state->map[state->player_y-1][state->player_x] == '$') { + if (state->map[state->player_y - 1][state->player_x] == '$') { y = state->player_y - 1; deadlock = corner_check(x, y, init_data, state); } - if (state->map[state->player_y][state->player_x+1] == '$') { + if (state->map[state->player_y][state->player_x + 1] == '$') { x = state->player_x + 1; deadlock = corner_check(x, y, init_data, state); } - if (state->map[state->player_y][state->player_x-1] == '$') { + if (state->map[state->player_y][state->player_x - 1] == '$') { x = state->player_x - 1; deadlock = corner_check(x, y, init_data, state); } @@ -318,6 +318,167 @@ bool simple_corner_deadlock(sokoban_t *init_data, state_t *state) { return deadlock; } +bool adjacent_box_check(int x, int y, sokoban_t *init_data, state_t *state) { + /* The outside if statement for each example checks to see if the indices + * are within the map to prevent out-of-bounds errors. + * The inside if statement then checks if the current state matches the + * example. + */ + + /* Example 1: + * # # # # # + * # $ + * # $ + * # @ + * # + */ + if ((y - 2 >= 0) && (x < (int) strlen((state->map)[y - 2])) && \ + (x - 1 < (int) strlen((state->map)[y - 1]))) { + if (state->map[y - 1][x - 1] == '$' && state->map[y - 2][x] == '#' && \ + state->map[y - 2][x - 1] == '#' && \ + !is_goal_loc(state->player_y, state->player_x, init_data)) { + return true; + } + } + + /* Example 2: + * # # # # # + * # $ + * # $ + * # @ + * # + */ + if ((y - 2 >= 0) && (x + 1 < (int) strlen((state->map)[y - 2])) && \ + (x + 1 < (int) strlen((state->map)[y - 1]))) { + if (state->map[y - 1][x + 1] == '$' && state->map[y - 2][x] == '#' && \ + state->map[y - 2][x + 1] == '#' && \ + !is_goal_loc(state->player_y, state->player_x, init_data)) { + return true; + } + } + + /* Example 3: + * # # # # # + * # + * # $ + * # $ @ + * # + */ + if ((y < init_data->lines) && (x - 1 < (int) strlen(state->map[y - 1]))) { + if (state->map[y - 1][x - 1] == '$' && state->map[y][x - 2] == '#' && \ + state->map[y - 1][x - 2] == '#' && \ + !is_goal_loc(state->player_y, state->player_x, init_data)) { + return true; + } + } + + /* Example 4: + * # # # # # + * # + * # $ @ + * # $ + * # + */ + if ((y + 1 < init_data->lines) && (x - 1 < (int) strlen(state->map[y + 1]))) { + if (state->map[y + 1][x - 1] == '$' && state->map[y][x - 2] == '#' && \ + state->map[y + 1][x - 2] == '#' && \ + !is_goal_loc(state->player_y, state->player_x, init_data)) { + return true; + } + } + + /* Example 5: + * # + * # + * $ # + * @ $ # + * # + * # # # # # + */ + if ((y < init_data->lines) && (x + 2 < (int) strlen(state->map[y - 1])) && \ + (x + 2 < (int) strlen(state->map[y]))) { + if (state->map[y - 1][x + 1] == '$' && state->map[y][x + 2] == '#' && \ + state->map[y - 1][x + 2] == '#' && \ + !is_goal_loc(state->player_y, state->player_x, init_data)) { + return true; + } + } + /* Example 6: + * # + * @ $ # + * $ # + * # + * # # # # # + */ + if ((y - 1 < init_data->lines) && (x + 2 < (int) strlen(state->map[y - 1])) && \ + (x + 2 < (int) strlen(state->map[y]))) { + if (state->map[y - 1][x + 1] == '$' && state->map[y][x + 2] == '#' && \ + state->map[y - 1][x + 2] == '#' && \ + !is_goal_loc(state->player_y, state->player_x, init_data)) { + return true; + } + } + + /* Example 7: + * # + * @ # + * $ # + * $ # + * # # # # # + */ + if ((y + 2 < init_data->lines) && (x < (int) strlen((state->map)[y + 2])) && \ + (x - 1 < (int) strlen((state->map)[y + 1]))) { + if (state->map[y + 1][x - 1] == '$' && state->map[y + 2][x] == '#' && \ + state->map[y + 2][x - 1] == '#' && \ + !is_goal_loc(state->player_y, state->player_x, init_data)) { + return true; + } + } + + /* Example 8: + * # + * @ # + * $ # + * $ # + * # # # # # + */ + if ((y + 2 < init_data->lines) && (x + 1 < (int) strlen((state->map)[y + 2])) && \ + (x + 1 < (int) strlen((state->map)[y + 1]))) { + if (state->map[y + 1][x + 1] == '$' && state->map[y + 2][x] == '#' && \ + state->map[y + 2][x + 1] == '#' && \ + !is_goal_loc(state->player_y, state->player_x, init_data)) { + return true; + } + } + + return false; +} + +bool freeze_deadlock(sokoban_t *init_data, state_t *state) { + bool deadlock = false; + + int x = state->player_x; + int y = state->player_y; + + if (state->map[y - 1][x] == '$') { + deadlock = adjacent_box_check(x, y - 1, init_data, state); + } + + if (state->map[y + 1][x] == '$') { + deadlock = adjacent_box_check(x, y + 1, init_data, state); + } + + if (state->map[y][x - 1] == '$') { + deadlock = adjacent_box_check(x - 1, y, init_data, state); + } + + if (state->map[y][x + 1] == '$') { + deadlock = adjacent_box_check(x + 1, y, init_data, state); + } + + return deadlock; +} + /***************************************************************************** * Function: winning_condition * * Parameters: sokoban_t *init_data, state_t *state * @@ -337,13 +498,13 @@ bool winning_condition(sokoban_t *init_data, state_t *state) { return true; } -void play_solution(sokoban_t init_data, char *solution) { +void play_solution(sokoban_t init_data, char *solution ) { SCREEN *mainScreen = newterm(TERMINAL_TYPE, stdout, stdin); set_term(mainScreen); - int cols = 1; - for (int i = 0; i < init_data.lines; i++) { - if (strlen(init_data.map[i]) > (size_t) cols) { + int cols = 1; + for(int i = 0; i < init_data.lines; i++){ + if(strlen(init_data.map[i]) > (size_t) cols){ cols = strlen(init_data.map[i]); } } @@ -368,15 +529,15 @@ void play_solution(sokoban_t init_data, char *solution) { int key_pressed = 0; - if (solution[i] == 'u' || solution[i] == 'U') { + if(solution[i] == 'u' || solution[i] == 'U') { key_pressed = KEY_UP; - } else if (solution[i] == 'd' || solution[i] == 'D') { + } else if(solution[i] == 'd' || solution[i] == 'D') { key_pressed = KEY_DOWN; - } else if (solution[i] == 'l' || solution[i] == 'L') { + } else if(solution[i] == 'l' || solution[i] == 'L') { key_pressed = KEY_LEFT; - } else if (solution[i] == 'r' || solution[i] == 'R') { + } else if(solution[i] == 'r' || solution[i] == 'R') { key_pressed = KEY_RIGHT; - } + } init_data = key_check(init_data, key_pressed); init_data = check_zone_reset(init_data); diff --git a/src/ai/utils.h b/src/ai/utils.h index b769885..b094699 100644 --- a/src/ai/utils.h +++ b/src/ai/utils.h @@ -44,6 +44,10 @@ bool execute_move_t(sokoban_t *init_data, state_t *state, move_t move); bool simple_corner_deadlock(sokoban_t *init_data, state_t *state); +bool adjacent_box_check(int x, int y, sokoban_t *init_data, state_t *state); + +bool freeze_deadlock(sokoban_t *init_data, state_t *state); + bool winning_condition(sokoban_t *init_data, state_t *state); void play_solution(sokoban_t init_data, char* solution); diff --git a/src/ai/utils.o b/src/ai/utils.o index bcf4c54..689eace 100644 Binary files a/src/ai/utils.o and b/src/ai/utils.o differ diff --git a/src/loose_check.c b/src/loose_check.c index 44a10c2..1508e24 100644 --- a/src/loose_check.c +++ b/src/loose_check.c @@ -33,10 +33,10 @@ void loose_check(sokoban_t sokoban) { void storage_loose_check(int y, int x, sokoban_t sokoban) { if (sokoban.map[y][x] == '$') { - if (((sokoban.map[y][x+1] == '#' && sokoban.map[y+1][x] == '#') || - (sokoban.map[y+1][x] == '#' && sokoban.map[y][x-1] == '#') || - (sokoban.map[y][x-1] == '#' && sokoban.map[y-1][x] == '#') || - (sokoban.map[y-1][x] == '#' && sokoban.map[y][x+1] == '#')) && + if (((sokoban.map[y][x + 1] == '#' && sokoban.map[y + 1][x] == '#') || + (sokoban.map[y + 1][x] == '#' && sokoban.map[y][x - 1] == '#') || + (sokoban.map[y][x - 1] == '#' && sokoban.map[y - 1][x] == '#') || + (sokoban.map[y - 1][x] == '#' && sokoban.map[y][x + 1] == '#')) && !is_goal_cell(y, x, sokoban ) ) { endwin(); diff --git a/src/loose_check.o b/src/loose_check.o index 7d04ef8..16266d6 100644 Binary files a/src/loose_check.o and b/src/loose_check.o differ diff --git a/src/map_reading.c b/src/map_reading.c index 5f4c331..8e3582a 100644 --- a/src/map_reading.c +++ b/src/map_reading.c @@ -82,8 +82,8 @@ sokoban_t make_map(char const *path, sokoban_t sokoban) { for (int i = 0; i < columns; i++) { sokoban.map[j][i] = sokoban.buffer[k]; sokoban.map_save[j][i] = sokoban.buffer[k]; - sokoban.map[j][i+1] = '\0'; - sokoban.map_save[j][i+1] = '\0'; + sokoban.map[j][i + 1] = '\0'; + sokoban.map_save[j][i + 1] = '\0'; k++; } diff --git a/src/map_reading.o b/src/map_reading.o index a38c8ff..008e6c1 100644 Binary files a/src/map_reading.o and b/src/map_reading.o differ