advent2021

Advent of Code 2021 Solutions
git clone git://bsandro.tech/advent2021
Log | Files | Refs | README | LICENSE

puzzle.c (5301B)


      1 #define _DEFAULT_SOURCE
      2 
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <errno.h>
      6 #include <string.h>
      7 #include <strings.h>
      8 #include <stdbool.h>
      9 #include <assert.h>
     10 #include <time.h>
     11 #include <inttypes.h>
     12 #include <ctype.h>
     13 
     14 #include "util.h"
     15 
     16 #define STR_LEN 1024
     17 #define MAP_WIDTH 500
     18 #define MAP_HEIGHT 500
     19 
     20 struct tile_t {
     21 	int x, y;
     22 	int g, h, f;
     23 	int p_x, p_y;
     24 };
     25 
     26 struct map_t {
     27 	int8_t map[MAP_HEIGHT][MAP_WIDTH];
     28 	int base_width, base_height;
     29 };
     30 
     31 int astar(struct map_t *map, struct tile_t start, struct tile_t end);
     32 void array_push(struct array_t *array, struct tile_t tile);
     33 struct tile_t * array_get(struct array_t *array, int x, int y);
     34 bool array_has(struct array_t *array, int x, int y);
     35 void array_delete(struct array_t *array, struct tile_t tile);
     36 void array_print(struct array_t *array);
     37 int get_risk(struct map_t *map, int x, int y);
     38 
     39 void puzzle(const char *filename, long long *result1, long long *result2) {
     40 	FILE *infile = fopen(filename, "r");
     41 	if (infile == NULL) {
     42 		fprintf(stderr, "fopen() error: %s\n", strerror(errno));
     43 		return;
     44 	}
     45 
     46 	char buf[STR_LEN] = {0};
     47 	int line_num = 0;
     48 	int line_width = 0;
     49 	struct map_t map = {0};
     50 
     51 	while (fgets(buf, STR_LEN, infile) != NULL) {
     52 		assert(line_num < MAP_HEIGHT);
     53 		size_t len = strlen(buf);
     54 		line_width = len - 1;
     55 		assert(len > 0 && line_width < MAP_WIDTH);
     56 		for (int i = 0; i < line_width; ++i) {
     57 			if (isdigit((int)buf[i])) {
     58 				map.map[line_num][i] = buf[i] - '0';
     59 			}
     60 		}
     61 
     62 		++line_num;
     63 		bzero(buf, STR_LEN);
     64 	}
     65 
     66 	map.base_width = line_width;
     67 	map.base_height = line_num;
     68 
     69 	struct tile_t start = {0};
     70 	struct tile_t end1 = {
     71 		.x = map.base_width - 1,
     72 		.y = map.base_height - 1
     73 	};
     74 	struct tile_t end2 = {
     75 		.x = map.base_width*5 - 1,
     76 		.y = map.base_height*5 - 1
     77 	};
     78 
     79 	*result1 = astar(&map, start, end1);
     80 	*result2 = astar(&map, start, end2);
     81 
     82 	// mutiny! ignoring feof/ferror.
     83 	fclose(infile);
     84 }
     85 
     86 int astar(struct map_t *map, struct tile_t start, struct tile_t end) {
     87 	struct array_t visited = { .data = NULL };
     88 	array_init(&visited, sizeof(struct tile_t), 10);
     89 	struct array_t upcoming = { .data = NULL };
     90 	array_init(&upcoming, sizeof(struct tile_t), 10);
     91 
     92 	array_push(&upcoming, start);
     93 
     94 	static const int dirs[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
     95 
     96 	while (upcoming.count > 0) {
     97 		// choosing upcoming node with the lowest f
     98 		struct tile_t *upcoming_data = upcoming.data;
     99 		struct tile_t current = upcoming_data[0];
    100 		for (size_t i = 0; i < upcoming.count; ++i) {
    101 			if (upcoming_data[i].f < current.f) {
    102 				current = upcoming_data[i];
    103 			}
    104 		}
    105 
    106 		if (current.x == end.x && current.y == end.y) {
    107 			free(upcoming.data);
    108 			free(visited.data);
    109 			return current.f;
    110 		}
    111 
    112 		array_push(&visited, current);
    113 		array_delete(&upcoming, current);
    114 
    115 		// iterating through adjacent nodes
    116 		for (int i = 0; i < 4; ++i) {
    117 			int x1 = current.x + dirs[i][0];
    118 			int y1 = current.y + dirs[i][1];
    119 			struct tile_t child = {.x=x1, .y=y1, .p_x = current.x, .p_y = current.y};
    120 			// end point is always bottom right
    121 			if (x1 > end.x || x1 < 0 || y1 > end.y || y1 < 0) {
    122 				continue;
    123 			}
    124 			if (array_has(&visited, child.x, child.y)) {
    125 				continue;
    126 			}
    127 			child.g = current.g + get_risk(map, x1, y1);
    128 			child.h = (end.y - y1) + (end.x - x1);
    129 			child.f = child.g + child.h;
    130 
    131 			struct tile_t *s = array_get(&upcoming, child.x, child.y);
    132 			if (s == NULL) {
    133 				array_push(&upcoming, child);
    134 			} else if (s->g <= child.g) {
    135 				continue;
    136 			}
    137 		}
    138 	}
    139 
    140 	printf("error\n");
    141 	free(upcoming.data);
    142 	free(visited.data);
    143 	return 0;
    144 }
    145 
    146 int get_risk(struct map_t *map, int x, int y) {
    147 	assert(map != NULL);
    148 	if (x < map->base_width && y < map->base_height) {
    149 		return map->map[y][x];
    150 	}
    151 
    152 	// quadrants and stuff
    153 	static const int quadrants[5][5] = {
    154 		{0, 1, 2, 3, 4},
    155 		{1, 2, 3, 4, 5},
    156 		{2, 3, 4, 5, 6},
    157 		{3, 4, 5, 6, 7},
    158 		{4, 5, 6, 7, 8}
    159 	};
    160 	int qx = x / map->base_width;
    161 	int qy = y / map->base_height;
    162 	int x1 = x % map->base_width;
    163 	int y1 = y % map->base_height;
    164 	assert(qx < 5 && qy < 5);
    165 	int m = quadrants[qy][qx];
    166 
    167 	int risk = map->map[y1][x1] + m;
    168 	if (risk > 9) {
    169 		risk -= 9;
    170 	}
    171 
    172 	return risk;
    173 }
    174 
    175 void array_print(struct array_t *array) {
    176 	struct tile_t *data = array->data;
    177 	for (size_t i = 0; i < array->count; ++i) {
    178 		printf("[%zu] %d,%d (f:%d,g:%d)\n", i, data[i].x, data[i].y, data[i].f, data[i].g);
    179 	}
    180 }
    181 
    182 void array_push(struct array_t *array, struct tile_t tile) {
    183 	if (array->count >= array->cap) {
    184 		array_expand(array);
    185 	}
    186 	struct tile_t *data = array->data;
    187 	data[array->count++] = tile;
    188 }
    189 
    190 struct tile_t * array_get(struct array_t *array, int x, int y) {
    191 	struct tile_t *data = array->data;
    192 	for (size_t i = 0; i < array->count; ++i) {
    193 		if (data[i].x == x && data[i].y == y) {
    194 			return &data[i];
    195 		}
    196 	}
    197 	return NULL;
    198 }
    199 
    200 bool array_has(struct array_t *array, int x, int y) {
    201 	return array_get(array, x, y) != NULL;
    202 }
    203 
    204 void array_delete(struct array_t *array, struct tile_t tile) {
    205 	assert(array != NULL);
    206 	if (!array_has(array, tile.x, tile.y)) return;
    207 
    208 	struct tile_t *data_new = calloc(array->cap, array->elem_size);
    209 	struct tile_t *data = array->data;
    210 	size_t count_new = 0;
    211 	for (size_t i = 0; i < array->count; ++i) {
    212 		if (data[i].x != tile.x || data[i].y != tile.y) {
    213 			data_new[count_new++] = data[i];
    214 		}
    215 	}
    216 	free(array->data);
    217 	array->data = data_new;
    218 	array->count = count_new;
    219 }