HISTORY_LEN :: 1_000_000;
AREA_WIDTH :: 7;
AREA_HEIGHT :: 4 * HISTORY_LEN;
MAX_REPEAT :: 1_000_000_000_000;
solve_day17 :: (test: bool) {
instructions := read_entire_file(ifx test then "inputs/day17_test.txt" else "inputs/day17.txt");
current_height := 0;
current_shape := 0;
current_instruction := 0;
part2_part := 0;
part1 := 0;
part2 := 0;
last_y := 0;
area: []u8 = NewArray(AREA_HEIGHT, u8);
repeat_length := -1;
start_repeat := 0;
shape := 0;
history := NewArray(HISTORY_LEN, History_Item);
while true {
defer shape += 1;
drop := Shape_Drop.{
shape = *SHAPES[current_shape],
x = 2,
y = current_height + 4,
};
while can_drop_shape(drop, area) {
drop.y -= 1;
if can_shift(drop, area, instructions[current_instruction]) {
drop.x += ifx instructions[current_instruction] == #char "<" then -1 else 1;
}
current_instruction = (current_instruction + 1) % instructions.count;
}
for y: 0..drop.shape.height - 1 {
area[drop.y + y] |= drop.shape.data[y] << drop.x;
}
current_height = max(current_height, drop.y + drop.shape.height);
current_shape = (current_shape + 1) % SHAPES.count;
history[shape] = .{
x = drop.x,
y_diff = drop.y - last_y,
height = current_height,
};
last_y = drop.y;
if repeat_length == -1 && shape > 101 {
repeat_length = check_for_repeat(history, shape);
if repeat_length != -1 {
start_repeat = shape;
f := (MAX_REPEAT - shape) / repeat_length;
missing := (MAX_REPEAT - shape) % f;
missing_height := history[shape - repeat_length + missing].height - history[shape - repeat_length].height;
height_diff := history[shape].height - history[shape - repeat_length].height;
part2 = height_diff * f + history[shape].height + missing_height - 1;
}
}
if shape == 2021 part1 = current_height;
if shape > 2021 && repeat_length != -1 break;
}
print("Part 1: %\n", part1);
print("Part 2: %\n", part2);
}
check_for_repeat :: (history: []History_Item, newest: s64) -> s64 {
i := newest;
while true {
i -= SHAPES.count;
if i < 0 break;
offset := 0;
while i - offset > 0 && history[newest - offset].y_diff == history[i - offset].y_diff && history[newest - offset].x == history[i - offset].x {
if newest - offset == i {
return newest - i;
}
offset += 1;
}
}
return - 1;
}
can_drop_shape :: (drop: Shape_Drop, area: []u8) -> bool {
if drop.y - 1 < 0 return false;
for y: 0..drop.shape.height - 1 {
if area[drop.y + y - 1] & (drop.shape.data[y] << drop.x) return false;
}
return true;
}
can_shift :: (drop: Shape_Drop, area: []u8, instruction: u8) -> bool {
way_to_shift := ifx instruction == #char "<" then -1 else 1;
if drop.shape.width + drop.x + way_to_shift > AREA_WIDTH || drop.x + way_to_shift < 0 return false;
for y: 0..drop.shape.height - 1 {
if area[drop.y + y] & (drop.shape.data[y] << (drop.x + way_to_shift)) return false;
}
return true;
}
print_area :: (area: []u8, height: s64) {
for < y: height..0 {
print("|");
for 0..AREA_WIDTH - 1 {
if area[y] & ((cast(u8)1) << it) != 0 print("#");
else print(".");
}
print("|\n");
}
print("+-------+\n\n");
}
SHAPES :: Shape.[.{ 4, 1, u8.[0b1111] }, .{ 3, 3, u8.[0b010, 0b111, 0b010] }, .{3, 3, u8.[0b111, 0b100, 0b100] }, .{ 1, 4, u8.[0b1, 0b1, 0b1, 0b1]}, .{ 2, 2, u8.[0b11, 0b11] }];
Shape_Drop :: struct {
shape: *Shape;
x: s64 = 2;
y: s64 = 0;
}
Shape :: struct {
width: s64;
height: s64;
data: []u8;
}
History_Item :: struct {
x: s64;
y_diff: s64;
height: s64;
}