276 lines
8.8 KiB
C
276 lines
8.8 KiB
C
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#define MAX_DIMENSION 1024
|
|
#define MAX_QUEUE 32*1024
|
|
#define abs(a) ((a) < 0 ? (-(a)) : (a))
|
|
|
|
typedef enum tile {
|
|
EMPTY,
|
|
WALL,
|
|
} tile_t;
|
|
|
|
typedef struct element {
|
|
int pos[2];
|
|
uint32_t cost;
|
|
uint8_t orientation;
|
|
uint8_t rotated;
|
|
} element_t;
|
|
|
|
|
|
int main() {
|
|
char c;
|
|
|
|
tile_t **map = calloc(MAX_DIMENSION, sizeof(map[0]));
|
|
int rows = 0, columns, i = 0;
|
|
map[0] = calloc(MAX_DIMENSION, sizeof(map[0][0]));
|
|
|
|
int start[2], end[2];
|
|
while ((c = getchar()) != EOF) {
|
|
switch(c) {
|
|
case '#':
|
|
map[rows][i] = WALL;
|
|
break;
|
|
case '.':
|
|
map[rows][i] = EMPTY;
|
|
break;
|
|
case 'S':
|
|
map[rows][i] = EMPTY;
|
|
start[0] = rows;
|
|
start[1] = i;
|
|
break;
|
|
case 'E':
|
|
map[rows][i] = EMPTY;
|
|
end[0] = rows;
|
|
end[1] = i;
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
if (c != '\n') {
|
|
continue;
|
|
}
|
|
|
|
columns = i - 1;
|
|
i = 0;
|
|
rows++;
|
|
map[rows] = calloc(MAX_DIMENSION, sizeof(map[0][0]));
|
|
}
|
|
|
|
uint32_t ***scanned = calloc(4, sizeof(scanned[0]));
|
|
for (int i = 0; i < 4; i++) {
|
|
scanned[i] = calloc(rows, sizeof(scanned[0][0]));
|
|
for (int j = 0; j < rows; j++) {
|
|
scanned[i][j] = calloc(columns, sizeof(scanned[0][0][0]));
|
|
memset(scanned[i][j], UINT32_MAX, columns * sizeof(scanned[0][0][0]));
|
|
}
|
|
}
|
|
|
|
element_t *queue = calloc(MAX_QUEUE, sizeof(queue[0]));
|
|
int queue_num = 1;
|
|
queue[0].pos[0] = start[0];
|
|
queue[0].pos[1] = start[1];
|
|
|
|
uint32_t best_cost = UINT32_MAX;
|
|
uint8_t last_orientation;
|
|
while (queue_num > 0) {
|
|
// Pop first
|
|
int pos[2];
|
|
pos[0] = queue[0].pos[0];
|
|
pos[1] = queue[0].pos[1];
|
|
uint32_t cost = queue[0].cost;
|
|
uint8_t orientation = queue[0].orientation;
|
|
uint8_t rotated = queue[0].rotated;
|
|
for (int i = 1; i < queue_num; i++) {
|
|
memcpy(&queue[i-1], &queue[i], sizeof(queue[0]));
|
|
}
|
|
queue_num--;
|
|
|
|
// Check end condition
|
|
if (pos[0] == end[0] && pos[1] == end[1]) {
|
|
printf("%u\n", cost);
|
|
best_cost = cost;
|
|
last_orientation = orientation;
|
|
break;
|
|
}
|
|
|
|
// Moving forward
|
|
int x, y;
|
|
switch (orientation) {
|
|
case 0: // RIGHT
|
|
x = pos[0];
|
|
y = pos[1] + 1;
|
|
break;
|
|
case 1: // DOWN
|
|
x = pos[0] + 1;
|
|
y = pos[1];
|
|
break;
|
|
case 2: // LEFT
|
|
x = pos[0];
|
|
y = pos[1] - 1;
|
|
break;
|
|
case 3: // UP
|
|
x = pos[0] - 1;
|
|
y = pos[1];
|
|
break;
|
|
}
|
|
// Not wall or out of map
|
|
if (x >= 0 && y >= 0 && x < rows && y < columns && map[x][y] == EMPTY) {
|
|
int visited_with_smaller_cost = 0;
|
|
for (int i = 0; i < queue_num; i++) {
|
|
if (scanned[orientation][x][y] <= (cost + 1)) {
|
|
visited_with_smaller_cost = 1;
|
|
}
|
|
}
|
|
|
|
if (!visited_with_smaller_cost) {
|
|
int insert_idx;
|
|
for (insert_idx = 0; insert_idx < queue_num; insert_idx++) {
|
|
if (queue[insert_idx].cost >= (cost + 1)) {
|
|
break;
|
|
}
|
|
}
|
|
if (insert_idx == (queue_num - 1) && queue[queue_num - 1].cost < (cost + 1)) {
|
|
insert_idx = queue_num;
|
|
}
|
|
// Shift
|
|
for (int i = queue_num; i > insert_idx; i--) {
|
|
memcpy(&queue[i], &queue[i-1], sizeof(queue[0]));
|
|
}
|
|
queue[insert_idx].cost = cost + 1;
|
|
queue[insert_idx].orientation = orientation;
|
|
queue[insert_idx].pos[0] = x;
|
|
queue[insert_idx].pos[1] = y;
|
|
queue[insert_idx].rotated = 0;
|
|
queue_num++;
|
|
scanned[orientation][x][y] = cost + 1;
|
|
}
|
|
}
|
|
|
|
if (!rotated) {
|
|
// Rotate clockwise
|
|
if (scanned[(orientation + 1) % 4][pos[0]][pos[1]] > cost + 1000) {
|
|
int insert_idx;
|
|
for (insert_idx = 0; insert_idx < queue_num; insert_idx++) {
|
|
if (queue[insert_idx].cost >= (cost + 1000)) {
|
|
break;
|
|
}
|
|
}
|
|
if (insert_idx == (queue_num - 1) && queue[queue_num - 1].cost < (cost + 1000)) {
|
|
insert_idx = queue_num;
|
|
}
|
|
// Shift
|
|
for (int i = queue_num; i > insert_idx; i--) {
|
|
memcpy(&queue[i], &queue[i-1], sizeof(queue[0]));
|
|
}
|
|
queue[insert_idx].cost = cost + 1000;
|
|
queue[insert_idx].orientation = (orientation + 1) % 4;
|
|
queue[insert_idx].pos[0] = pos[0];
|
|
queue[insert_idx].pos[1] = pos[1];
|
|
queue[insert_idx].rotated = 1;
|
|
queue_num++;
|
|
scanned[(orientation + 1) % 4][pos[0]][pos[1]] = cost + 1000;
|
|
}
|
|
|
|
// Rotate anticlockwise
|
|
uint8_t new_orientation = orientation == 0 ? 3 : orientation - 1;
|
|
if (scanned[new_orientation][pos[0]][pos[1]] > cost + 1000) {
|
|
int insert_idx;
|
|
for (insert_idx = 0; insert_idx < queue_num; insert_idx++) {
|
|
if (queue[insert_idx].cost >= (cost + 1000)) {
|
|
break;
|
|
}
|
|
}
|
|
if (insert_idx == (queue_num - 1) && queue[queue_num - 1].cost < (cost + 1000)) {
|
|
insert_idx = queue_num;
|
|
}
|
|
// Shift
|
|
for (int i = queue_num; i > insert_idx; i--) {
|
|
memcpy(&queue[i], &queue[i-1], sizeof(queue[0]));
|
|
}
|
|
queue[insert_idx].cost = cost + 1000;
|
|
queue[insert_idx].orientation = new_orientation;
|
|
queue[insert_idx].pos[0] = pos[0];
|
|
queue[insert_idx].pos[1] = pos[1];
|
|
queue[insert_idx].rotated = 1;
|
|
queue_num++;
|
|
scanned[new_orientation][pos[0]][pos[1]] = cost + 1000;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find all paths that costs exactly the best path cost
|
|
uint32_t **best_path_map = calloc(rows, sizeof(best_path_map[0]));
|
|
for (i = 0; i < rows; i++) {
|
|
best_path_map[i] = calloc(columns, sizeof(best_path_map[0][0]));
|
|
}
|
|
|
|
best_path_map[end[0]][end[1]] = best_cost;
|
|
int discovered = 1;
|
|
while (discovered) {
|
|
discovered = 0;
|
|
for (int i = 0; i < rows; i++) {
|
|
for (int j = 0; j < columns; j++) {
|
|
if (best_path_map[i][j]) {
|
|
for (int k = 0; k < 4; k++) {
|
|
int directions[4][2] = {{0,1}, {0,-1}, {1,0}, {-1,0}};
|
|
for (int l = 0; l < 4; l++) {
|
|
if (!best_path_map[i+directions[l][0]][j+directions[l][1]] && map[i+directions[l][0]][j+directions[l][1]] == EMPTY) {
|
|
uint32_t cost_here = scanned[k][i+directions[l][0]][j+directions[l][1]];
|
|
if (best_path_map[i][j] == cost_here + 1 || best_path_map[i][j] == cost_here + 1001 || best_path_map[i][j] + 999 == cost_here) {
|
|
discovered = 1;
|
|
best_path_map[i+directions[l][0]][j+directions[l][1]] = cost_here;
|
|
}
|
|
}
|
|
}
|
|
if (best_path_map[start[0]][start[1]]) {
|
|
discovered = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (discovered) {
|
|
break;
|
|
}
|
|
}
|
|
if (discovered) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
free(queue);
|
|
for (int i = 0; i < 4; i++) {
|
|
for (int j = 0; j < rows; j++) {
|
|
free(scanned[i][j]);
|
|
}
|
|
free(scanned[i]);
|
|
}
|
|
free(scanned);
|
|
|
|
|
|
int sum = 0;
|
|
for (i = 0; i < rows; i++) {
|
|
for (int j = 0; j < columns; j++) {
|
|
sum += best_path_map[i][j] > 0;
|
|
printf("%c", best_path_map[i][j] ? 'O' : (map[i][j] == EMPTY ? '.' : '.'));
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
printf("%i\n", sum);
|
|
|
|
for (i = 0; i < rows; i++) {
|
|
free(best_path_map[i]);
|
|
}
|
|
free(best_path_map);
|
|
|
|
for (i = 0; i < rows + 1; i++) {
|
|
free(map[i]);
|
|
}
|
|
free(map);
|
|
}
|