From 0cd460461fb868a94f311c59c1784de3d15f4d63 Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Sun, 11 Jun 2023 19:47:08 +0300 Subject: [PATCH] solve day 14 --- day14.c | 356 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.c | 1 + types.h | 2 + 3 files changed, 359 insertions(+) create mode 100644 day14.c diff --git a/day14.c b/day14.c new file mode 100644 index 0000000..c089208 --- /dev/null +++ b/day14.c @@ -0,0 +1,356 @@ +#include +#include +#include +#include +#include + +#include "types.h" +#include "vec2.h" +#include "aoc.h" + +typedef struct { + vec2_u32 *points; + size_t count; +} day14_line; + +typedef struct { + day14_line *lines; + size_t count; +} day14_input; + +typedef enum { + TILE_AIR, + TILE_SOLID, + TILE_SAND, +} map_tile; + +typedef struct { + map_tile *tiles; + size_t width; + size_t height; + vec2_u32 offset; +} day14_map; + +static void day14_parse_point(vec2_u32 *result, char *str) +{ + char *x = strtok(str, ","); + result->x = strtoul(x, NULL, 10); + + char *y = strtok(NULL, ","); + result->y = strtoul(y, NULL, 10); +} + +static void day14_parse_line(day14_line *result, char *line) +{ + result->count = 1; + for (int i = 0; i < strlen(line); i++) { + if (line[i] == '>') { + result->count++; + } + } + result->points = malloc(sizeof(vec2_u32)*result->count); + + int i = 0; + char *save; + char *token = strtok_r(line, " -> ", &save); + while(token) { + day14_parse_point(&result->points[i], token); + token = strtok_r(NULL, " -> ", &save); + i++; + } +} + +static void *day14_parse(char **lines, int line_count) +{ + day14_input *result = malloc(sizeof(day14_input)); + result->lines = malloc(sizeof(day14_line)*line_count); + result->count = line_count; + for (int i = 0; i < line_count; i++) { + day14_parse_line(&result->lines[i], lines[i]); + } + return result; +} + +static void printf_day14_line(day14_line *line) +{ + if (line->count > 0) { + for (int i = 0; i < line->count-1; i++) { + vec2_u32 point = line->points[i]; + printf("%d,%d -> ", point.x, point.y); + } + vec2_u32 last = line->points[line->count-1]; + printf("%d,%d", last.x, last.y); + } +} + +static void printf_day14_map(day14_map *map) +{ + char map_tile_chars[] = { + [TILE_AIR] = '.', + [TILE_SOLID] = '#', + [TILE_SAND] = 'O' + }; + + for (int y = 0; y < map->height; y++) { + for (int x = 0; x < map->width; x++) { + int idx = y * map->width + x; + char tile = map_tile_chars[map->tiles[idx]]; + printf("%c", tile); + } + printf("\n"); + } +} + +static void printf_day14_segment(vec2_u32 *A, vec2_u32 *B) +{ + printf("%d,%d -> %d,%d\n", A->x, A->y, B->x, B->y); +} + +static void day14_place_vertical(day14_map *map, u32 x, u32 y, i32 size) +{ + i32 from = size > 0 ? 0 : size+1; + i32 to = size > 0 ? size : 1; + + for (i32 i = from; i < to; i++) { + size_t idx = (y - map->offset.y + i) * map->width + (x - map->offset.x); + map->tiles[idx] = TILE_SOLID; + } +} + +static void day14_place_horizontal(day14_map *map, u32 x, u32 y, i32 size) +{ + i32 from = size > 0 ? 0 : size+1; + i32 to = size > 0 ? size : 1; + + for (i32 i = from; i < to; i++) { + size_t idx = (y - map->offset.y) * map->width + (x - map->offset.x + i); + map->tiles[idx] = TILE_SOLID; + } +} + +static day14_map *day14_create_map(day14_line *lines, size_t count, vec2_u32 *sand_spawner) +{ + day14_map *map = malloc(sizeof(day14_map)); + + vec2_u32 min_point = *sand_spawner; + vec2_u32 max_point = *sand_spawner; + for (int i = 0; i < count; i++) { + day14_line *line = &lines[i]; + for (int j = 0; j < line->count; j++) { + vec2_u32 *point = &line->points[j]; + min_point.x = MIN(min_point.x, point->x); + min_point.y = MIN(min_point.y, point->y); + max_point.x = MAX(max_point.x, point->x); + max_point.y = MAX(max_point.y, point->y); + } + } + + map->offset = min_point; + map->width = max_point.x - min_point.x + 1; + map->height = max_point.y - min_point.y + 1; + map->tiles = calloc(map->width*map->height, sizeof(map_tile)); + + for (int i = 0; i < count; i++) { + day14_line *line = &lines[i]; + for (int j = 1; j < line->count; j++) { + vec2_u32 *from = &line->points[j-1]; + vec2_u32 *to = &line->points[j]; + if (from->x == to->x) { + i32 size = to->y - from->y; + size += SIGN(size); + day14_place_vertical(map, from->x, from->y, size); + } else if (from->y == to->y) { + i32 size = to->x - from->x; + size += SIGN(size); + day14_place_horizontal(map, from->x, from->y, size); + } else { + abort(); + } + } + } + + return map; +} + +static bool day14_is_air(day14_map *map, u32 x, u32 y) +{ + if (x < map->offset.x || y < map->offset.y) return true; + + size_t oy = y - map->offset.y; + if (oy >= map->height) return true; + + size_t ox = x - map->offset.x; + if (ox >= map->width) return true; + + size_t idx = oy * map->width + ox; + return map->tiles[idx] == TILE_AIR; +} + +static bool day14_is_abyss(day14_map *map, u32 y) +{ + return y >= (map->offset.y + map->height); +} + +static bool day14_part1_spawn_sand(day14_map *map, u32 x, u32 y) +{ + while (true) { + if (day14_is_abyss(map, y)) { + return false; + } + + if (day14_is_air(map, x, y+1)) { + y++; + continue; + } + if (day14_is_air(map, x-1, y+1)) { + x--; + y++; + continue; + } + if (day14_is_air(map, x+1, y+1)) { + x++; + y++; + continue; + } + + size_t idx = (y - map->offset.y) * map->width + (x - map->offset.x); + map->tiles[idx] = TILE_SAND; + return true; + } +} + +static bool day14_is_in_bounds(day14_map *map, u32 x, u32 y) +{ + if (x < map->offset.x || y < map->offset.y) return false; + u32 ox = x - map->offset.x; + u32 oy = y - map->offset.y; + return ox < map->width && oy < map->height; +} + +static void day14_copy_tiles( + map_tile *from_map, u32 from_x, u32 from_y, size_t from_width, size_t from_height, + map_tile *to_map , u32 to_x , u32 to_y , size_t to_width , size_t to_height) +{ + for (u32 oy = 0; oy < from_height; oy++) { + for (u32 ox = 0; ox < from_width; ox++) { + u32 x = from_x + ox; + u32 y = from_y + oy; + u32 from_idx = oy * from_width + ox; + u32 to_idx = (y - to_y) * to_width + (x - to_x); + to_map[to_idx] = from_map[from_idx]; + } + } +} + +static void day14_ensure_includes(day14_map *map, u32 x, u32 y, bool floor) +{ + if (day14_is_in_bounds(map, x, y)) return; + + u32 new_offset_x = MIN(map->offset.x, x); + u32 new_offset_y = MIN(map->offset.y, y); + size_t new_width = MAX(map->offset.x + map->width - 1, x) - new_offset_x + 1; + size_t new_height = MAX(map->offset.y + map->height - 1, y) - new_offset_y + 1; + + map_tile *new_tiles = calloc(new_width*new_height, sizeof(map_tile)); + day14_copy_tiles( + map->tiles, map->offset.x, map->offset.y, map->width, map->height, + new_tiles, new_offset_x, new_offset_y, new_width, new_height + ); + + free(map->tiles); + map->tiles = new_tiles; + map->offset.x = new_offset_x; + map->offset.y = new_offset_y; + map->width = new_width; + map->height = new_height; + + if (floor) { + for (int ox = 0; ox < map->width; ox++) { + size_t idx = (map->height-1) * map->width + ox; + map->tiles[idx] = TILE_SOLID; + } + } +} + +static void day14_map_place_at(day14_map *map, u32 x, u32 y, map_tile tile) +{ + day14_ensure_includes(map, x, y, true); + + size_t idx = (y - map->offset.y) * map->width + (x - map->offset.x); + map->tiles[idx] = TILE_SAND; +} + +static bool day14_part2_spawn_sand(day14_map *map, u32 x, u32 y) +{ + if (!day14_is_air(map, x, y)) return false; + + while (true) { + if (y < map->offset.y + map->height-2) { + if (day14_is_air(map, x, y+1)) { + y++; + continue; + } + if (day14_is_air(map, x-1, y+1)) { + x--; + y++; + continue; + } + if (day14_is_air(map, x+1, y+1)) { + x++; + y++; + continue; + } + } + + day14_map_place_at(map, x, y, TILE_SAND); + return true; + } +} + +static void day14_add_floor(day14_map *map) +{ + size_t height = map->height; + map->height += 2; + map->tiles = realloc(map->tiles, map->width*map->height * sizeof(map_tile)); + + for (size_t ox = 0; ox < map->width; ox++) { + size_t idx = height * map->width + ox; + map->tiles[idx] = TILE_AIR; + } + + for (size_t ox = 0; ox < map->width; ox++) { + size_t idx = (height+1) * map->width + ox; + map->tiles[idx] = TILE_SOLID; + } +} + +static void day14_part1(void *p) +{ + day14_input *input = (day14_input*)p; + vec2_u32 sand_spawner = { .x = 500, .y = 0 }; + day14_map *map = day14_create_map(input->lines, input->count, &sand_spawner); + + size_t count = 0; + while (day14_part1_spawn_sand(map, sand_spawner.x, sand_spawner.y)) { + count++; + } + // printf_day14_map(map); + printf("Answer: %ld\n", count); +} + +static void day14_part2(void *p) +{ + day14_input *input = (day14_input*)p; + vec2_u32 sand_spawner = { .x = 500, .y = 0 }; + day14_map *map = day14_create_map(input->lines, input->count, &sand_spawner); + day14_add_floor(map); + + size_t count = 0; + while (day14_part2_spawn_sand(map, sand_spawner.x, sand_spawner.y)) { + count++; + } + // printf_day14_map(map); + printf("Answer: %ld\n", count); +} + +ADD_SOLUTION(14, day14_parse, day14_part1, day14_part2); diff --git a/main.c b/main.c index a7f837e..976938e 100644 --- a/main.c +++ b/main.c @@ -22,6 +22,7 @@ #include "day11.c" #include "day12.c" #include "day13.c" +#include "day14.c" Solution *find_solution(int day) { diff --git a/types.h b/types.h index e1eb896..fd4c64c 100644 --- a/types.h +++ b/types.h @@ -3,6 +3,8 @@ #include +#define SIGN(x) (x == 0 ? 0 : (x > 0 ? 1 : -1)) + typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32;