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 }