221 lines
6.0 KiB
C
221 lines
6.0 KiB
C
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
|
|
#define MAX_DIMENSION 1024
|
|
#define QUEUE_MAX 32*1024
|
|
#define abs(a) ((a) < 0 ? (-(a)) : (a))
|
|
|
|
const int directions[][2] = {
|
|
{0, 1},
|
|
{1, 0},
|
|
{0, -1},
|
|
{-1, 0}
|
|
};
|
|
|
|
#define directions_len (sizeof(directions)/sizeof(directions[0]))
|
|
|
|
typedef enum tile {
|
|
EMPTY,
|
|
WALL,
|
|
} tile_t;
|
|
|
|
int ospf(tile_t **map, int rows, int colums, int start[2], int end[2]);
|
|
|
|
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]));
|
|
}
|
|
|
|
int without_cheats = 0;
|
|
|
|
// Shortest path search
|
|
uint32_t **costs = calloc(rows, sizeof(costs[0]));
|
|
for (int i = 0; i < rows; i++) {
|
|
costs[i] = calloc(columns, sizeof(costs[0][0]));
|
|
memset(costs[i], 255, columns * sizeof(costs[0][0]));
|
|
}
|
|
|
|
int (*queue)[2] = calloc(QUEUE_MAX, sizeof(queue[0]));
|
|
int queue_num = 1;
|
|
// Start at 0,0
|
|
queue[0][0] = start[0];
|
|
queue[0][1] = start[1];
|
|
costs[start[0]][start[1]] = 0;
|
|
|
|
while (queue_num > 0) {
|
|
// Pop first
|
|
int x = queue[0][0];
|
|
int y = queue[0][1];
|
|
uint32_t cost = costs[x][y];
|
|
|
|
if (x == end[0] && y == end[1]) {
|
|
without_cheats = cost;
|
|
break;
|
|
}
|
|
|
|
for (int i = 0; i < queue_num - 1; i++) {
|
|
queue[i][0] = queue[i+1][0];
|
|
queue[i][1] = queue[i+1][1];
|
|
}
|
|
queue_num--;
|
|
|
|
// Check each direction
|
|
for (int i = 0; i < directions_len; i++) {
|
|
int dx = directions[i][0];
|
|
int dy = directions[i][1];
|
|
|
|
int next_x = x + dx;
|
|
int next_y = y + dy;
|
|
|
|
// Outside of map, into wall, or better path exists
|
|
if (next_x < 0 || next_y < 0 || next_x >= rows || next_y >= columns || map[next_x][next_y] || costs[next_x][next_y] <= cost + 1) {
|
|
continue;
|
|
}
|
|
|
|
// Insert into queue
|
|
uint32_t next_cost = cost + 1;
|
|
costs[next_x][next_y] = next_cost;
|
|
int insert_idx;
|
|
for (insert_idx = 0; insert_idx < queue_num; insert_idx++) {
|
|
int qx = queue[insert_idx][0];
|
|
int qy = queue[insert_idx][1];
|
|
if (costs[qx][qy] > next_cost) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (int j = queue_num; j > insert_idx; j--) {
|
|
queue[j][0] = queue[j-1][0];
|
|
queue[j][1] = queue[j-1][1];
|
|
}
|
|
queue[insert_idx][0] = next_x;
|
|
queue[insert_idx][1] = next_y;
|
|
queue_num++;
|
|
}
|
|
}
|
|
|
|
int good_cheats = 0;
|
|
int good_cheat_limit = 100;
|
|
|
|
// Find where to cheat
|
|
for (i = 0; i < rows; i++) {
|
|
for (int j = 0; j < columns; j++) {
|
|
if (map[i][j] != EMPTY) {
|
|
continue;
|
|
}
|
|
// Try each direction and check if there's a wall
|
|
for (int k = 0; k < directions_len; k++) {
|
|
int dx = directions[k][0];
|
|
int dy = directions[k][1];
|
|
int x = i + dx;
|
|
int y = j + dy;
|
|
|
|
if (x < 0 || y < 0 || x >= rows || y >= columns || map[x][y] != WALL) {
|
|
continue;
|
|
}
|
|
|
|
// Now check whether we can get back to the track to shorten out time
|
|
for (int l = 0; l < directions_len; l++) {
|
|
int dx = directions[l][0];
|
|
int dy = directions[l][1];
|
|
int xx = x + dx;
|
|
int yy = y + dy;
|
|
|
|
if (xx < 0 || yy < 0 || xx >= rows || yy >= columns || map[xx][yy] == WALL) {
|
|
continue;
|
|
}
|
|
|
|
// Not good cheat enough
|
|
if (costs[xx][yy] < costs[i][j] + good_cheat_limit + 2) {
|
|
continue;
|
|
}
|
|
|
|
good_cheats++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int good_cheats2 = 0;
|
|
int good_cheat_limit2 = 100;
|
|
|
|
// Find where to cheat
|
|
for (i = 0; i < rows; i++) {
|
|
for (int j = 0; j < columns; j++) {
|
|
if (map[i][j] != EMPTY) {
|
|
continue;
|
|
}
|
|
|
|
// Check submap, we can get to anywhere inside a 20 radius "circle" by activating the cheat here
|
|
for (int k = i-20; k <= i+20; k++) {
|
|
for (int l = j-(20-abs(k-i)); l <= j+(20-abs(k-i)); l++) {
|
|
if (k < 0 || l < 0 || k >= rows || l >= columns) {
|
|
continue;
|
|
}
|
|
|
|
// We don't want to get into a wall
|
|
if (map[k][l] == WALL) {
|
|
continue;
|
|
}
|
|
|
|
// Not good cheat enough
|
|
if (costs[k][l] < costs[i][j] + good_cheat_limit2 + abs(k-i) + abs(l-j)) {
|
|
continue;
|
|
}
|
|
|
|
good_cheats2++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
printf("%i\n", good_cheats);
|
|
printf("%i\n", good_cheats2);
|
|
|
|
for (i = 0; i < rows; i++) {
|
|
free(costs[i]);
|
|
}
|
|
free(costs);
|
|
for (i = 0; i < rows + 1; i++) {
|
|
free(map[i]);
|
|
}
|
|
free(map);
|
|
}
|