2022 day 17
This commit is contained in:
parent
0f12151a23
commit
756cc390e1
4 changed files with 303 additions and 0 deletions
1
2022/src/day17/input
Normal file
1
2022/src/day17/input
Normal file
File diff suppressed because one or more lines are too long
75
2022/src/day17/part1.rs
Normal file
75
2022/src/day17/part1.rs
Normal file
|
@ -0,0 +1,75 @@
|
|||
mod shape;
|
||||
|
||||
use shape::*;
|
||||
|
||||
use std::fs;
|
||||
|
||||
|
||||
fn main() {
|
||||
//let input = String::from(">>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>");
|
||||
let input = String::from_utf8_lossy(&fs::read("input").unwrap()).into_owned();
|
||||
let mut grid = vec![[false;FIELD_WIDTH];4];
|
||||
|
||||
let mut chars = input.trim().chars().cycle();
|
||||
let mut top = 0;
|
||||
for (i,shape) in SHAPES.iter().cycle().enumerate() {
|
||||
if i >= 2022 { break; }
|
||||
let mut x = 2;
|
||||
let mut y = top + 2 + shape.height();
|
||||
if grid.len() <= y {
|
||||
grid.resize(y + 1, [false;FIELD_WIDTH]);
|
||||
}
|
||||
while let Some(c) = chars.next() {
|
||||
// for (y2,line) in grid.iter().enumerate().rev() {
|
||||
// if y2 > y && *line == [false;FIELD_WIDTH] {continue;}
|
||||
// print!("|");
|
||||
// for (x2,tile) in line.iter().enumerate() {
|
||||
// if *tile {
|
||||
// print!("#");
|
||||
// } else if x2 >= x && y >= y2 && shape.mask().any(|[xo,yo]| *xo == x2 - x && *yo == y - y2) {
|
||||
// print!("@");
|
||||
// } else {
|
||||
// print!(".");
|
||||
// }
|
||||
// }
|
||||
// println!("|");
|
||||
// }
|
||||
// println!("+-------+\n");
|
||||
|
||||
|
||||
match c {
|
||||
'<' => {if !shape.collide_left(x, y, &grid) {
|
||||
x -= 1;
|
||||
}},
|
||||
'>' => {if !shape.collide_right(x, y, &grid) {
|
||||
x += 1;
|
||||
}},
|
||||
_ => { panic!("Bad character '{:?}' in input", c); }
|
||||
}
|
||||
if shape.collide_down(x, y, &grid) {
|
||||
shape.place(x, y, &mut grid);
|
||||
if y >= top {
|
||||
top = y + 1;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
y -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// for line in grid.iter().rev() {
|
||||
// if *line == [false;FIELD_WIDTH] {continue;}
|
||||
// print!("|");
|
||||
// for tile in line {
|
||||
// if *tile {
|
||||
// print!("#");
|
||||
// } else {
|
||||
// print!(".");
|
||||
// }
|
||||
// }
|
||||
// println!("|");
|
||||
// }
|
||||
// println!("+-------+\n{top}\n");
|
||||
}
|
||||
println!("{top}");
|
||||
}
|
131
2022/src/day17/part2.rs
Normal file
131
2022/src/day17/part2.rs
Normal file
|
@ -0,0 +1,131 @@
|
|||
mod shape;
|
||||
|
||||
use shape::*;
|
||||
|
||||
use std::fs;
|
||||
|
||||
|
||||
const CYCLE_ATTEMPTS: usize = 10;
|
||||
|
||||
fn main() {
|
||||
let input = String::from_utf8_lossy(&fs::read("input").unwrap()).into_owned();
|
||||
let cycle = find_cycle_length(&input);
|
||||
println!("{cycle:?}");
|
||||
|
||||
for i in 1..=10 {
|
||||
let target = 1000 * i;
|
||||
assert_eq!(find_height_after(&input, target) as u64, calc_height_after(&cycle, &input, target as u64));
|
||||
}
|
||||
|
||||
println!("{}", calc_height_after(&cycle, &input, 1000000000000));
|
||||
}
|
||||
|
||||
fn calc_height_after(cycle: &Cycle, input: &String, n_shapes: u64) -> u64 {
|
||||
if n_shapes < cycle.cycle_start_shapes {
|
||||
return find_height_after(input, n_shapes as usize) as u64;
|
||||
}
|
||||
let n_minus_start = n_shapes - cycle.cycle_start_shapes;
|
||||
let n_cycles = n_minus_start / cycle.cycle_shapes;
|
||||
let offset = n_minus_start % cycle.cycle_shapes;
|
||||
n_cycles * cycle.cycle_height + find_height_after(input, (offset + cycle.cycle_start_shapes) as usize) as u64
|
||||
}
|
||||
|
||||
fn find_height_after(input: &String, n_shapes: usize) -> usize {
|
||||
let mut chars = input.trim().chars().cycle();
|
||||
let mut grid = vec![[false;FIELD_WIDTH];4];
|
||||
let mut top = 0;
|
||||
for (i,shape) in SHAPES.iter().cycle().enumerate() {
|
||||
if i >= n_shapes { break; }
|
||||
let mut x = 2;
|
||||
let mut y = top + 2 + shape.height();
|
||||
if grid.len() <= y {
|
||||
grid.resize(y + 1, [false;FIELD_WIDTH]);
|
||||
}
|
||||
while let Some(c) = chars.next() {
|
||||
match c {
|
||||
'<' => {if !shape.collide_left(x, y, &grid) {
|
||||
x -= 1;
|
||||
}},
|
||||
'>' => {if !shape.collide_right(x, y, &grid) {
|
||||
x += 1;
|
||||
}},
|
||||
_ => { panic!("Bad character '{:?}' in input", c); }
|
||||
}
|
||||
if shape.collide_down(x, y, &grid) {
|
||||
shape.place(x, y, &mut grid);
|
||||
if y >= top {
|
||||
top = y + 1;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
y -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
top
|
||||
}
|
||||
|
||||
fn find_cycle_length(input: &String) -> Cycle {
|
||||
let limit = CYCLE_ATTEMPTS * input.trim().len();
|
||||
let mut chars = input.trim().chars().cycle().enumerate();
|
||||
let mut grid = vec![[false;FIELD_WIDTH];4];
|
||||
let mut top = 0;
|
||||
let mut last_i = 0;
|
||||
let mut last_i2 = 0;
|
||||
let mut last_top = 0;
|
||||
let mut last_top2 = 0;
|
||||
for (i,shape) in SHAPES.iter().cycle().enumerate() {
|
||||
let mut x = 2;
|
||||
let mut y = top + 2 + shape.height();
|
||||
if grid.len() <= y {
|
||||
grid.resize(y + 1, [false;FIELD_WIDTH]);
|
||||
}
|
||||
while let Some((j,c)) = chars.next() {
|
||||
if j > limit {
|
||||
panic!("Couldn't find a cycle length after {} cycles.", CYCLE_ATTEMPTS);
|
||||
}
|
||||
if i != 0 && j % input.trim().len() == 0 {
|
||||
let cycle_shapes = i - last_i;
|
||||
let cycle_height = top - last_top;
|
||||
// println!("{i:5} {:4} {top:5} {:4}", i - last_i, top - last_top);
|
||||
if cycle_shapes == last_i - last_i2 && cycle_height == last_top - last_top2 {
|
||||
return Cycle {
|
||||
cycle_start_shapes: last_i2 as u64,
|
||||
cycle_shapes: cycle_shapes as u64,
|
||||
cycle_height: cycle_height as u64
|
||||
};
|
||||
}
|
||||
last_i2 = last_i;
|
||||
last_i = i;
|
||||
last_top2 = last_top;
|
||||
last_top = top;
|
||||
}
|
||||
match c {
|
||||
'<' => {if !shape.collide_left(x, y, &grid) {
|
||||
x -= 1;
|
||||
}},
|
||||
'>' => {if !shape.collide_right(x, y, &grid) {
|
||||
x += 1;
|
||||
}},
|
||||
_ => { panic!("Bad character '{}' in input", c); }
|
||||
}
|
||||
if shape.collide_down(x, y, &grid) {
|
||||
shape.place(x, y, &mut grid);
|
||||
if y >= top {
|
||||
top = y + 1;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
y -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("Cycle loop ended unexpectedly");
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Cycle {
|
||||
cycle_start_shapes: u64,
|
||||
cycle_shapes: u64,
|
||||
cycle_height: u64
|
||||
}
|
96
2022/src/day17/shape.rs
Normal file
96
2022/src/day17/shape.rs
Normal file
|
@ -0,0 +1,96 @@
|
|||
use Shape::*;
|
||||
|
||||
|
||||
pub const FIELD_WIDTH: usize = 7;
|
||||
pub const SHAPES: [Shape;5] = [Line, Cross, L, Column, Square];
|
||||
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Shape {
|
||||
// ####
|
||||
Line,
|
||||
// .#.
|
||||
// ###
|
||||
// .#.
|
||||
Cross,
|
||||
// ..#
|
||||
// ..#
|
||||
// ###
|
||||
L,
|
||||
// #
|
||||
// #
|
||||
// #
|
||||
// #
|
||||
Column,
|
||||
// ##
|
||||
// ##
|
||||
Square
|
||||
}
|
||||
|
||||
impl Shape {
|
||||
pub fn collide_down(&self, x: usize, y: usize, grid: &Vec<[bool;FIELD_WIDTH]>) -> bool {
|
||||
y < self.height() || match *self {
|
||||
Line => grid[y-1][x ] || grid[y-1][x+1] || grid[y-1][x+2] || grid[y-1][x+3],
|
||||
Cross => grid[y-3][x+1] || grid[y-2][x ] || grid[y-2][x+2],
|
||||
L => grid[y-3][x ] || grid[y-3][x+1] || grid[y-3][x+2],
|
||||
Column => grid[y-4][x ],
|
||||
Square => grid[y-2][x ] || grid[y-2][x+1]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collide_left(&self, x: usize, y: usize, grid: &Vec<[bool;FIELD_WIDTH]>) -> bool {
|
||||
x == 0 || match *self {
|
||||
Line => grid[y][x-1],
|
||||
Cross => grid[y-2][x ] || grid[y-1][x-1] || grid[y ][x ],
|
||||
L => grid[y-2][x-1] || grid[y-1][x+1] || grid[y ][x+1],
|
||||
Column => grid[y-3][x-1] || grid[y-2][x-1] || grid[y-1][x-1] || grid[y ][x-1],
|
||||
Square => grid[y-1][x-1] || grid[y ][x-1]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collide_right(&self, x: usize, y: usize, grid: &Vec<[bool;FIELD_WIDTH]>) -> bool {
|
||||
x + self.width() >= FIELD_WIDTH || match *self {
|
||||
Line => grid[y ][x+4],
|
||||
Cross => grid[y-2][x+2] || grid[y-1][x+3] || grid[y ][x+2],
|
||||
L => grid[y-2][x+3] || grid[y-1][x+3] || grid[y ][x+3],
|
||||
Column => grid[y-3][x+1] || grid[y-2][x+1] || grid[y-1][x+1] || grid[y ][x+1],
|
||||
Square => grid[y-1][x+2] || grid[y ][x+2]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn place(&self, x: usize, y: usize, grid: &mut Vec<[bool;FIELD_WIDTH]>) {
|
||||
for [xo,yo] in self.mask() {
|
||||
grid[y-yo][x+xo] = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mask(&self) -> std::slice::Iter<[usize;2]> {
|
||||
match *self {
|
||||
Line => [[0,0],[1,0],[2,0],[3,0]].iter(),
|
||||
Cross => [[1,0],[0,1],[1,1],[2,1],[1,2]].iter(),
|
||||
L => [[2,0],[2,1],[0,2],[1,2],[2,2]].iter(),
|
||||
Column => [[0,0],[0,1],[0,2],[0,3]].iter(),
|
||||
Square => [[0,0],[1,0],[0,1],[1,1]].iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn height(&self) -> usize {
|
||||
match *self {
|
||||
Line => 1,
|
||||
Cross => 3,
|
||||
L => 3,
|
||||
Column => 4,
|
||||
Square => 2
|
||||
}
|
||||
}
|
||||
|
||||
pub fn width(&self) -> usize {
|
||||
match *self {
|
||||
Line => 4,
|
||||
Cross => 3,
|
||||
L => 3,
|
||||
Column => 1,
|
||||
Square => 2
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue