329 lines
7.3 KiB
C
329 lines
7.3 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/param.h>
|
|
|
|
#include "aoc.h"
|
|
#include "types.h"
|
|
#include "vec2.h"
|
|
|
|
#define DAY17_BOARD_WIDTH 7
|
|
|
|
typedef enum {
|
|
DIR_LEFT,
|
|
DIR_RIGHT,
|
|
} day17_direction;
|
|
|
|
typedef struct {
|
|
day17_direction *dirs;
|
|
size_t count;
|
|
} day17_gusts;
|
|
|
|
typedef struct {
|
|
vec2 blocks[5];
|
|
size_t count;
|
|
size_t width;
|
|
size_t height;
|
|
} day17_piece;
|
|
|
|
day17_piece g_day17_pieces[] = {
|
|
// ####
|
|
{
|
|
.blocks = {
|
|
{ .x = 0, .y = 0 },
|
|
{ .x = 1, .y = 0 },
|
|
{ .x = 2, .y = 0 },
|
|
{ .x = 3, .y = 0 },
|
|
},
|
|
.count = 4,
|
|
.width = 4, .height = 1,
|
|
},
|
|
|
|
// .#.
|
|
// ###
|
|
// .#.
|
|
{
|
|
.blocks = {
|
|
{ .x = 1, .y = 0 },
|
|
{ .x = 0, .y = 1 },
|
|
{ .x = 1, .y = 1 },
|
|
{ .x = 2, .y = 1 },
|
|
{ .x = 1, .y = 2 },
|
|
},
|
|
.count = 5,
|
|
.width = 3, .height = 3,
|
|
},
|
|
|
|
// ..#
|
|
// ..#
|
|
// ###
|
|
{
|
|
.blocks = {
|
|
{ .x = 0, .y = 0 },
|
|
{ .x = 1, .y = 0 },
|
|
{ .x = 2, .y = 0 },
|
|
{ .x = 2, .y = 1 },
|
|
{ .x = 2, .y = 2 },
|
|
},
|
|
.count = 5,
|
|
.width = 3, .height = 3,
|
|
},
|
|
|
|
// #
|
|
// #
|
|
// #
|
|
// #
|
|
{
|
|
.blocks = {
|
|
{ .x = 0, .y = 0 },
|
|
{ .x = 0, .y = 1 },
|
|
{ .x = 0, .y = 2 },
|
|
{ .x = 0, .y = 3 },
|
|
},
|
|
.count = 4,
|
|
.width = 1, .height = 4,
|
|
},
|
|
|
|
// ##
|
|
// ##
|
|
{
|
|
.blocks = {
|
|
{ .x = 0, .y = 0 },
|
|
{ .x = 1, .y = 0 },
|
|
{ .x = 0, .y = 1 },
|
|
{ .x = 1, .y = 1 },
|
|
},
|
|
.count = 4,
|
|
.width = 2, .height = 2,
|
|
},
|
|
};
|
|
|
|
static void* day17_parse(char** lines, int line_count)
|
|
{
|
|
day17_gusts *data = malloc(sizeof(day17_gusts));
|
|
data->count = strlen(lines[0]);
|
|
data->dirs = malloc(sizeof(day17_direction) * data->count);
|
|
for (int i = 0; i < data->count; i++) {
|
|
data->dirs[i] = lines[0][i] == '>' ? DIR_RIGHT : DIR_LEFT;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
static void day17_print_board(bool board[][DAY17_BOARD_WIDTH], u32 height, day17_piece *falling_piece, vec2 *falling_pos)
|
|
{
|
|
for (int i = height; i >= 0; i--) {
|
|
printf("|");
|
|
for (int j = 0; j < DAY17_BOARD_WIDTH; j++) {
|
|
bool is_falling_piece = false;
|
|
if (falling_piece) {
|
|
for (int k = 0; k < falling_piece->count; k++) {
|
|
vec2 *block = &falling_piece->blocks[k];
|
|
if (block->x + falling_pos->x == j && block->y + falling_pos->y == i) {
|
|
is_falling_piece = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (is_falling_piece) {
|
|
printf("@");
|
|
} else {
|
|
printf(board[i][j] ? "#" : ".");
|
|
}
|
|
}
|
|
printf("|\n");
|
|
}
|
|
printf("+-------+\n");
|
|
}
|
|
|
|
static bool day17_is_solid(bool board[][DAY17_BOARD_WIDTH], i32 x, i32 y)
|
|
{
|
|
if (y < 0 || x < 0 || x > DAY17_BOARD_WIDTH-1) return true;
|
|
return board[y][x];
|
|
}
|
|
|
|
static bool day17_can_move_down(bool board[][DAY17_BOARD_WIDTH], day17_piece *piece, vec2 *pos)
|
|
{
|
|
for (int i = 0; i < piece->count; i++) {
|
|
i32 x = piece->blocks[i].x + pos->x;
|
|
i32 y = piece->blocks[i].y + pos->y;
|
|
if (day17_is_solid(board, x, y-1)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool day17_can_move_left(bool board[][DAY17_BOARD_WIDTH], day17_piece *piece, vec2 *pos)
|
|
{
|
|
if (pos->x == 0) return false;
|
|
for (int i = 0; i < piece->count; i++) {
|
|
i32 x = piece->blocks[i].x + pos->x;
|
|
i32 y = piece->blocks[i].y + pos->y;
|
|
if (day17_is_solid(board, x-1, y)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool day17_can_move_right(bool board[][DAY17_BOARD_WIDTH], day17_piece *piece, vec2 *pos)
|
|
{
|
|
if (pos->x >= DAY17_BOARD_WIDTH - piece->width) return false;
|
|
|
|
for (int i = 0; i < piece->count; i++) {
|
|
i32 x = piece->blocks[i].x + pos->x;
|
|
i32 y = piece->blocks[i].y + pos->y;
|
|
if (day17_is_solid(board, x+1, y)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static u32 get_max_piece_height()
|
|
{
|
|
u32 max_piece_height = 0;
|
|
for (int i = 0; i < ARRAY_LEN(g_day17_pieces); i++) {
|
|
max_piece_height = MAX(max_piece_height, g_day17_pieces[i].height);
|
|
}
|
|
return max_piece_height;
|
|
}
|
|
|
|
static void day17_simulate_rock(bool board[][DAY17_BOARD_WIDTH], u32 rock_idx, u32 *tower_height, day17_gusts *gusts, u32 *current_gust)
|
|
{
|
|
vec2 piece_pos = { .x = 2, .y = *tower_height+3 };
|
|
day17_piece *piece = &g_day17_pieces[rock_idx % ARRAY_LEN(g_day17_pieces)];
|
|
|
|
while (true) {
|
|
if (gusts->dirs[*current_gust] == DIR_LEFT) {
|
|
if (day17_can_move_left(board, piece, &piece_pos)) {
|
|
piece_pos.x -= 1;
|
|
}
|
|
} else if (gusts->dirs[*current_gust] == DIR_RIGHT) {
|
|
if (day17_can_move_right(board, piece, &piece_pos)) {
|
|
piece_pos.x += 1;
|
|
}
|
|
}
|
|
*current_gust = (*current_gust + 1) % gusts->count;
|
|
|
|
if (!day17_can_move_down(board, piece, &piece_pos)) {
|
|
break;
|
|
}
|
|
|
|
piece_pos.y -= 1;
|
|
}
|
|
|
|
for (int i = 0; i < piece->count; i++) {
|
|
u32 x = piece->blocks[i].x + piece_pos.x;
|
|
u32 y = piece->blocks[i].y + piece_pos.y;
|
|
board[y][x] = true;
|
|
*tower_height = MAX(*tower_height, y+1);
|
|
}
|
|
}
|
|
|
|
static void day17_part1(void *p)
|
|
{
|
|
day17_gusts *data = (day17_gusts*)p;
|
|
|
|
u32 rock_amount = 2022;
|
|
|
|
u32 max_piece_height = get_max_piece_height();
|
|
u32 max_board_height = (rock_amount+2) * max_piece_height + 3;
|
|
bool board[max_board_height][DAY17_BOARD_WIDTH];
|
|
memset(board, false, max_board_height*DAY17_BOARD_WIDTH*sizeof(bool));
|
|
|
|
u32 tower_height = 0;
|
|
u32 current_gust = 0;
|
|
for (size_t rock_idx = 0; rock_idx < rock_amount; rock_idx++) {
|
|
day17_simulate_rock(board, rock_idx, &tower_height, data, ¤t_gust);
|
|
}
|
|
|
|
printf("%d\n", tower_height);
|
|
}
|
|
|
|
static i32 day17_find_cycle_start(u32 *height_differences, u32 heights_count)
|
|
{
|
|
for (u32 cycle_size = heights_count/2; cycle_size > 5; cycle_size--) {
|
|
u32 cycle_start = heights_count - 2*cycle_size;
|
|
|
|
bool found_cycle = true;
|
|
for (int i = 0; i < cycle_size; i++) {
|
|
if (height_differences[cycle_start + i] != height_differences[cycle_start + cycle_size + i]) {
|
|
found_cycle = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found_cycle) {
|
|
return cycle_start;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void day17_part2(void *p)
|
|
{
|
|
day17_gusts *data = (day17_gusts*)p;
|
|
|
|
u64 target_rocks = 1000000000000;
|
|
|
|
u32 rock_amount = 4000;
|
|
u32 max_piece_height = get_max_piece_height();
|
|
u32 max_board_height = (rock_amount+2) * max_piece_height + 3;
|
|
bool board[max_board_height][DAY17_BOARD_WIDTH];
|
|
memset(board, false, max_board_height*DAY17_BOARD_WIDTH*sizeof(bool));
|
|
|
|
u32 height_differences[rock_amount];
|
|
memset(height_differences, 0, sizeof(u32) * rock_amount);
|
|
|
|
|
|
i32 cycle_start = -1;
|
|
u32 cycle_length = 0;
|
|
|
|
u32 tower_height = 0;
|
|
u32 current_gust = 0;
|
|
for (size_t rock_idx = 0; rock_idx < rock_amount; rock_idx++) {
|
|
u32 old_height = tower_height;
|
|
day17_simulate_rock(board, rock_idx, &tower_height, data, ¤t_gust);
|
|
height_differences[rock_idx] = tower_height - old_height;
|
|
|
|
cycle_start = day17_find_cycle_start(height_differences, rock_idx+1);
|
|
if (cycle_start != -1) {
|
|
cycle_length = (rock_idx-cycle_start)/2 + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cycle_start == -1) {
|
|
printf("Failed to find cycle, try increasing `rock_amount` for searching longer\n");
|
|
return;
|
|
}
|
|
|
|
u64 answer = 0;
|
|
|
|
// Add height up to the first cycle
|
|
for (int i = 0; i < cycle_start; i++) {
|
|
answer += height_differences[i];
|
|
}
|
|
|
|
u64 cycle_height = 0;
|
|
for (int i = 0; i < cycle_length; i++) {
|
|
cycle_height += height_differences[cycle_start + i];
|
|
}
|
|
|
|
// Calculate how many cycles would occur and extrapolate height
|
|
answer += (target_rocks - cycle_start) / cycle_length * cycle_height;
|
|
|
|
// Add remaining rocks at the end which have not completed a cycle
|
|
u32 remainder_rocks = (target_rocks - cycle_start) % cycle_length;
|
|
for (int i = 0; i < remainder_rocks; i++) {
|
|
answer += height_differences[cycle_start + i];
|
|
}
|
|
|
|
printf("%ld\n", answer);
|
|
|
|
}
|
|
|
|
ADD_SOLUTION(17, day17_parse, day17_part1, day17_part2);
|