day(17): add solution
Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
parent
f72e98c586
commit
f93a8db8f9
2 changed files with 207 additions and 0 deletions
1
samples/day17.txt
Normal file
1
samples/day17.txt
Normal file
|
@ -0,0 +1 @@
|
|||
>>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>
|
206
src/bin/day17.rs
Normal file
206
src/bin/day17.rs
Normal file
|
@ -0,0 +1,206 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use aoc_2022::*;
|
||||
|
||||
type Input = Vec<char>;
|
||||
type Output = usize;
|
||||
|
||||
type Position = Vector2D<isize>;
|
||||
|
||||
struct InfiniteIndex {
|
||||
size: usize,
|
||||
i: usize,
|
||||
}
|
||||
|
||||
impl InfiniteIndex {
|
||||
fn new(size: usize) -> InfiniteIndex {
|
||||
InfiniteIndex { size, i: size - 1 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for InfiniteIndex {
|
||||
type Item = usize;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.i = (self.i + 1) % self.size;
|
||||
Some(self.i)
|
||||
}
|
||||
}
|
||||
|
||||
struct Tunnel {
|
||||
buffer: usize,
|
||||
jets: Vec<char>,
|
||||
rocks: Vec<Vec<char>>,
|
||||
flushed: usize,
|
||||
next_jet: InfiniteIndex,
|
||||
next_shape: InfiniteIndex,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref SHAPES: Vec<Vec<Vec<char>>> = vec![
|
||||
vec!["####"],
|
||||
vec![" # ", "###", " # "],
|
||||
vec![" #", " #", "###"],
|
||||
vec!["#", "#", "#", "#"],
|
||||
vec!["##", "##"],
|
||||
]
|
||||
.iter()
|
||||
.map(|shape| shape.iter().map(|l| l.chars().collect()).collect())
|
||||
.collect_vec();
|
||||
}
|
||||
|
||||
fn below(position: Vector2D<isize>) -> Vector2D<isize> {
|
||||
position + Vector2D::new(0, 1)
|
||||
}
|
||||
|
||||
fn left(position: Vector2D<isize>) -> Vector2D<isize> {
|
||||
position + Vector2D::new(-1, 0)
|
||||
}
|
||||
|
||||
fn right(position: Vector2D<isize>) -> Vector2D<isize> {
|
||||
position + Vector2D::new(1, 0)
|
||||
}
|
||||
|
||||
fn occupied(shape: &[Vec<char>]) -> impl Iterator<Item = Position> + '_ {
|
||||
shape.iter().enumerate().flat_map(|(y, row)| {
|
||||
row.iter().enumerate().filter_map(move |(x, c)| {
|
||||
if c == &'#' {
|
||||
Some(Vector2D::new(x as isize, y as isize))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
impl Tunnel {
|
||||
fn new(buffer: usize, jets: Vec<char>) -> Tunnel {
|
||||
let jet_count = jets.len();
|
||||
|
||||
Tunnel {
|
||||
buffer,
|
||||
jets,
|
||||
rocks: vec!["#########".chars().collect_vec()],
|
||||
flushed: 0,
|
||||
next_jet: InfiniteIndex::new(jet_count),
|
||||
next_shape: InfiniteIndex::new(SHAPES.len()),
|
||||
}
|
||||
}
|
||||
|
||||
fn hit(&self, shape: &[Vec<char>], position: Position) -> bool {
|
||||
occupied(shape).any(|rock| self.rocks[position + rock] != ' ')
|
||||
}
|
||||
|
||||
fn emplace(&mut self, shape: &[Vec<char>], position: Position) {
|
||||
for rock in occupied(shape) {
|
||||
self.rocks[position + rock] = '#';
|
||||
}
|
||||
|
||||
while !self.rocks.first().unwrap().contains(&'#') {
|
||||
self.rocks.remove(0);
|
||||
}
|
||||
|
||||
while self.rocks.len() > self.buffer {
|
||||
self.rocks.pop();
|
||||
self.flushed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn add_one(&mut self) {
|
||||
let shape = &SHAPES[self.next_shape.next().unwrap()];
|
||||
|
||||
for _ in 0..shape.len() + 3 {
|
||||
self.rocks.insert(0, "| |".chars().collect_vec());
|
||||
}
|
||||
|
||||
let mut position = Vector2D::new(3, 0);
|
||||
loop {
|
||||
let jet = self.jets[self.next_jet.next().unwrap()];
|
||||
|
||||
if jet == '>' && !self.hit(shape, right(position)) {
|
||||
position = right(position);
|
||||
} else if jet == '<' && !self.hit(shape, left(position)) {
|
||||
position = left(position);
|
||||
}
|
||||
|
||||
if self.hit(shape, below(position)) {
|
||||
break;
|
||||
}
|
||||
|
||||
position = below(position);
|
||||
}
|
||||
|
||||
self.emplace(shape, position);
|
||||
}
|
||||
|
||||
fn add(&mut self, mut count: usize) -> &Tunnel {
|
||||
let mut seen: HashMap<String, (usize, usize)> = HashMap::new();
|
||||
|
||||
while count > 0 {
|
||||
let hash = self.rocks.iter().map(|l| l.iter().join("")).join("");
|
||||
|
||||
match seen.get(&hash) {
|
||||
Some((to_add, height)) => {
|
||||
let p_height = self.height() - height;
|
||||
let p_length = to_add - count;
|
||||
|
||||
self.flushed += (count / p_length) * p_height;
|
||||
count %= p_length;
|
||||
break;
|
||||
}
|
||||
None => {
|
||||
seen.insert(hash, (count, self.height()));
|
||||
self.add_one();
|
||||
count -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _ in 0..count {
|
||||
self.add_one();
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn height(&self) -> usize {
|
||||
self.rocks.len() + self.flushed - 1
|
||||
}
|
||||
}
|
||||
|
||||
struct Day17;
|
||||
impl Solution<Input, Output> for Day17 {
|
||||
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
|
||||
file_to_string(pathname).trim_end().chars().collect()
|
||||
}
|
||||
|
||||
fn part_1(input: &Input) -> Output {
|
||||
Tunnel::new(100, input.clone()).add(2022).height()
|
||||
}
|
||||
|
||||
fn part_2(input: &Input) -> Output {
|
||||
Tunnel::new(100, input.clone()).add(1000000000000).height()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Day17::run("sample")
|
||||
Day17::main()
|
||||
}
|
||||
|
||||
test_sample!(day_17, Day17, 3068, 1514285714288);
|
||||
|
||||
#[cfg(test)]
|
||||
mod day_17_extended {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_infinite_index() {
|
||||
let index = InfiniteIndex::new(3);
|
||||
|
||||
assert_eq!(
|
||||
index.take(10).collect::<Vec<usize>>(),
|
||||
vec![0, 1, 2, 0, 1, 2, 0, 1, 2, 0]
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue