Optimisation, formatting changes
This commit is contained in:
parent
ab01fa2878
commit
3e2e69f1c3
13 changed files with 403 additions and 97 deletions
|
@ -1,3 +0,0 @@
|
|||
# Deadlock and Optimizations
|
||||
|
||||
Explain your optimizations if applicable
|
140
report.md
Normal file
140
report.md
Normal 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
BIN
sokoban
Binary file not shown.
|
@ -1 +0,0 @@
|
|||
rrRlllllL
|
39
src/ai/ai.c
39
src/ai/ai.c
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
BIN
src/ai/ai.o
BIN
src/ai/ai.o
Binary file not shown.
163
src/ai/utils.c
163
src/ai/utils.c
|
@ -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 *
|
||||
|
@ -340,8 +501,8 @@ bool winning_condition(sokoban_t *init_data, state_t *state) {
|
|||
void play_solution(sokoban_t init_data, char *solution ) {
|
||||
SCREEN *mainScreen = newterm(TERMINAL_TYPE, stdout, stdin);
|
||||
set_term(mainScreen);
|
||||
int cols = 1;
|
||||
|
||||
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]);
|
||||
|
|
|
@ -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);
|
||||
|
|
BIN
src/ai/utils.o
BIN
src/ai/utils.o
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue