Optimisation, formatting changes

This commit is contained in:
Rory Healy 2021-10-22 15:30:16 +11:00
parent ab01fa2878
commit 3e2e69f1c3
13 changed files with 403 additions and 97 deletions

View file

@ -1,3 +0,0 @@
# Deadlock and Optimizations
Explain your optimizations if applicable

140
report.md Normal file
View file

@ -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
```

BIN
sokoban

Binary file not shown.

View file

@ -1 +0,0 @@
rrRlllllL

View file

@ -73,7 +73,7 @@ void copy_state(sokoban_t *init_data, state_t *dst, state_t *src) {
* Create the initial node * Create the initial node
*/ */
node_t *create_init_node(sokoban_t *init_data) { 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->parent = NULL;
new_n->priority = 0; 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 * Create the a node from a parent node
*/ */
node_t *create_node(sokoban_t *init_data, node_t *parent) { 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->parent = parent;
new_n->depth = parent->depth + 1; new_n->depth = parent->depth + 1;
copy_state(init_data, &(new_n->state), &(parent->state)); copy_state(init_data, &(new_n->state), &(parent->state));
@ -189,7 +189,8 @@ void find_solution(sokoban_t *init_data, bool show_solution) {
// Choose initial capacity of PRIME NUMBER // Choose initial capacity of PRIME NUMBER
// Specify the size of the keys and values you want to store once // Specify the size of the keys and values you want to store once
// The Hash Table only accept a 1D key and value. // 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 // Data structure to create a 1D representation of the map
// Needed to interact with the hash table // 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. // Initialize expanded nodes table.
// This table will be used to free your memory once a solution is found // 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 // Add the initial node
node_t *n = create_init_node(init_data); 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++) { 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); bool player_moved = applyAction(init_data, n, &new, action);
generated_nodes += 1; 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++) { for (int i = 0; i < init_data->lines; i++) {
free(new->state.map[i]); free(new->state.map[i]);
} }
free(new->state.map); free(new->state.map);
free(new); free(new);
continue; continue;
@ -256,17 +261,8 @@ void find_solution(sokoban_t *init_data, bool show_solution) {
free_memory(expanded_nodes, init_data->lines); free_memory(expanded_nodes, init_data->lines);
free(flat_map); 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 // 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++) { for (int j = 0; j < init_data->lines; j++) {
if (pq.heaparr[i]->state.map[j]) { if (pq.heaparr[i]->state.map[j]) {
free(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); sokoban = find_player(sokoban);
find_solution(&sokoban, show_solution); 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);
} }

Binary file not shown.

View file

@ -318,6 +318,167 @@ bool simple_corner_deadlock(sokoban_t *init_data, state_t *state) {
return deadlock; 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 * * Function: winning_condition *
* Parameters: sokoban_t *init_data, state_t *state * * Parameters: sokoban_t *init_data, state_t *state *
@ -340,8 +501,8 @@ bool winning_condition(sokoban_t *init_data, state_t *state) {
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); SCREEN *mainScreen = newterm(TERMINAL_TYPE, stdout, stdin);
set_term(mainScreen); set_term(mainScreen);
int cols = 1;
int cols = 1;
for(int i = 0; i < init_data.lines; i++){ for(int i = 0; i < init_data.lines; i++){
if(strlen(init_data.map[i]) > (size_t) cols){ if(strlen(init_data.map[i]) > (size_t) cols){
cols = strlen(init_data.map[i]); cols = strlen(init_data.map[i]);

View file

@ -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 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); bool winning_condition(sokoban_t *init_data, state_t *state);
void play_solution(sokoban_t init_data, char* solution); void play_solution(sokoban_t init_data, char* solution);

Binary file not shown.

Binary file not shown.

Binary file not shown.