solve day 17
This commit is contained in:
parent
b94258260d
commit
13525fd481
328
day17.c
Normal file
328
day17.c
Normal file
@ -0,0 +1,328 @@
|
||||
#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);
|
1
main.c
1
main.c
@ -25,6 +25,7 @@
|
||||
#include "day14.c"
|
||||
#include "day15.c"
|
||||
#include "day16.c"
|
||||
#include "day17.c"
|
||||
|
||||
Solution *find_solution(int day)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user