Skip to content

Commit c0cfa23

Browse files
committed
AoC 2022 Day 17 - rust
1 parent 179a273 commit c0cfa23

File tree

5 files changed

+386
-9
lines changed

5 files changed

+386
-9
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
| bash | [](src/main/bash/AoC2022_01.sh) | [](src/main/bash/AoC2022_02.sh) | [](src/main/bash/AoC2022_03.sh) | [](src/main/bash/AoC2022_04.sh) | | [](src/main/bash/AoC2022_06.sh) | [](src/main/bash/AoC2022_07.sh) | | | [](src/main/bash/AoC2022_10.sh) | | | | | | | | | | | | | | | [](src/main/bash/AoC2022_25.sh) |
3131
| c++ | [](src/main/cpp/2022/01/AoC2022_01.cpp) | [](src/main/cpp/2022/02/AoC2022_02.cpp) | [](src/main/cpp/2022/03/AoC2022_03.cpp) | [](src/main/cpp/2022/04/AoC2022_04.cpp) | [](src/main/cpp/2022/05/AoC2022_05.cpp) | [](src/main/cpp/2022/06/AoC2022_06.cpp) | | | [](src/main/cpp/2022/09/AoC2022_09.cpp) | [](src/main/cpp/2022/10/AoC2022_10.cpp) | | | | [](src/main/cpp/2022/14/AoC2022_14.cpp) | | | | | | | | | [](src/main/cpp/2022/23/AoC2022_23.cpp) | [](src/main/cpp/2022/24/AoC2022_24.cpp) | [](src/main/cpp/2022/25/AoC2022_25.cpp) |
3232
| julia | [](src/main/julia/AoC2022_01.jl) | [](src/main/julia/AoC2022_02.jl) | [](src/main/julia/AoC2022_03.jl) | [](src/main/julia/AoC2022_04.jl) | | [](src/main/julia/AoC2022_06.jl) | | | | [](src/main/julia/AoC2022_10.jl) | [](src/main/julia/AoC2022_11.jl) | | | | | | | | | | | | | | |
33-
| rust | [](src/main/rust/AoC2022_01/src/main.rs) | [](src/main/rust/AoC2022_02/src/main.rs) | [](src/main/rust/AoC2022_03/src/main.rs) | [](src/main/rust/AoC2022_04/src/main.rs) | [](src/main/rust/AoC2022_05/src/main.rs) | [](src/main/rust/AoC2022_06/src/main.rs) | [](src/main/rust/AoC2022_07/src/main.rs) | [](src/main/rust/AoC2022_08/src/main.rs) | [](src/main/rust/AoC2022_09/src/main.rs) | [](src/main/rust/AoC2022_10/src/main.rs) | [](src/main/rust/AoC2022_11/src/main.rs) | [](src/main/rust/AoC2022_12/src/main.rs) | [](src/main/rust/AoC2022_13/src/main.rs) | [](src/main/rust/AoC2022_14/src/main.rs) | | [](src/main/rust/AoC2022_16/src/main.rs) | | [](src/main/rust/AoC2022_18/src/main.rs) | [](src/main/rust/AoC2022_19/src/main.rs) | [](src/main/rust/AoC2022_20/src/main.rs) | | | | | [](src/main/rust/AoC2022_25/src/main.rs) |
33+
| rust | [](src/main/rust/AoC2022_01/src/main.rs) | [](src/main/rust/AoC2022_02/src/main.rs) | [](src/main/rust/AoC2022_03/src/main.rs) | [](src/main/rust/AoC2022_04/src/main.rs) | [](src/main/rust/AoC2022_05/src/main.rs) | [](src/main/rust/AoC2022_06/src/main.rs) | [](src/main/rust/AoC2022_07/src/main.rs) | [](src/main/rust/AoC2022_08/src/main.rs) | [](src/main/rust/AoC2022_09/src/main.rs) | [](src/main/rust/AoC2022_10/src/main.rs) | [](src/main/rust/AoC2022_11/src/main.rs) | [](src/main/rust/AoC2022_12/src/main.rs) | [](src/main/rust/AoC2022_13/src/main.rs) | [](src/main/rust/AoC2022_14/src/main.rs) | | [](src/main/rust/AoC2022_16/src/main.rs) | [](src/main/rust/AoC2022_17/src/main.rs) | [](src/main/rust/AoC2022_18/src/main.rs) | [](src/main/rust/AoC2022_19/src/main.rs) | [](src/main/rust/AoC2022_20/src/main.rs) | | | | | [](src/main/rust/AoC2022_25/src/main.rs) |
3434
<!-- @END:ImplementationsTable:2022@ -->
3535

3636
## 2021

src/main/rust/AoC2022_17/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "AoC2022_17"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
aoc = { path = "../aoc" }
8+
lazy_static = "1.4"

src/main/rust/AoC2022_17/src/main.rs

Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
#![allow(non_snake_case)]
2+
3+
use aoc::geometry::{Direction, Translate, XY};
4+
use aoc::Puzzle;
5+
use lazy_static::lazy_static;
6+
use std::{
7+
collections::{HashMap, HashSet},
8+
str::FromStr,
9+
};
10+
11+
const WIDTH: usize = 7;
12+
const OFFSET_X: i32 = 2;
13+
const OFFSET_Y: i32 = 3;
14+
const KEEP_ROWS: usize = 55;
15+
const LOOP_TRESHOLD: usize = 3_000;
16+
17+
lazy_static! {
18+
static ref FLOOR: HashSet<XY> =
19+
(0..WIDTH).map(|x| XY::of(x as i32, -1)).collect();
20+
static ref SHAPES: Vec<Vec<XY>> =
21+
vec![
22+
// 🔲🔲🔲🔲
23+
vec![XY::of(0, 0), XY::of(1, 0), XY::of(2, 0), XY::of(3, 0)],
24+
// 🔲
25+
// 🔲🔲🔲
26+
// 🔲
27+
vec![
28+
XY::of(0, 1),
29+
XY::of(1, 0),
30+
XY::of(1, 1),
31+
XY::of(1, 2),
32+
XY::of(2, 1),
33+
],
34+
// 🔲
35+
// 🔲
36+
// 🔲🔲🔲
37+
vec![
38+
XY::of(0, 0),
39+
XY::of(1, 0),
40+
XY::of(2, 0),
41+
XY::of(2, 1),
42+
XY::of(2, 2),
43+
],
44+
// 🔲
45+
// 🔲
46+
// 🔲
47+
// 🔲
48+
vec![XY::of(0, 0), XY::of(0, 1), XY::of(0, 2), XY::of(0, 3)],
49+
// 🔲🔲
50+
// 🔲🔲
51+
vec![XY::of(0, 0), XY::of(0, 1), XY::of(1, 0), XY::of(1, 1)],
52+
];
53+
}
54+
55+
#[derive(Clone, Copy)]
56+
struct Cycle {
57+
cycle: usize,
58+
top: i32,
59+
}
60+
61+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
62+
struct State {
63+
shape: usize,
64+
tops: [i32; WIDTH],
65+
jet: Direction,
66+
}
67+
68+
struct Shape {
69+
idx: usize,
70+
shape: Vec<XY>,
71+
}
72+
73+
#[derive(Debug)]
74+
struct Rock {
75+
idx: usize,
76+
shape: HashSet<XY>,
77+
}
78+
79+
struct ShapesIterator {
80+
idx: usize,
81+
}
82+
83+
struct JetIterator {
84+
idx: usize,
85+
jets: Vec<Direction>,
86+
}
87+
88+
#[derive(Debug)]
89+
struct Stack {
90+
positions: HashSet<XY>,
91+
tops: HashMap<usize, i32>,
92+
top: i32,
93+
}
94+
95+
struct AoC2022_17;
96+
97+
impl Rock {
98+
fn move_(&self, vector: &XY) -> Self {
99+
let new_shape = self
100+
.shape
101+
.iter()
102+
.map(|s| s.translate(vector, 1))
103+
.collect::<HashSet<_>>();
104+
Rock {
105+
idx: self.idx,
106+
shape: new_shape,
107+
}
108+
}
109+
110+
fn inside_x(&self, start_inclusive: i32, end_exclusive: i32) -> bool {
111+
self.shape
112+
.iter()
113+
.all(|p| start_inclusive <= p.x() && p.x() < end_exclusive)
114+
}
115+
}
116+
117+
impl ShapesIterator {
118+
fn new() -> Self {
119+
Self { idx: 0 }
120+
}
121+
}
122+
123+
impl JetIterator {
124+
fn new(jets: &[Direction]) -> Self {
125+
Self {
126+
idx: 0,
127+
jets: jets.to_vec(),
128+
}
129+
}
130+
}
131+
132+
impl Iterator for ShapesIterator {
133+
type Item = Shape;
134+
135+
fn next(&mut self) -> Option<Self::Item> {
136+
let i = self.idx % SHAPES.len();
137+
self.idx += 1;
138+
Some(Shape {
139+
idx: i,
140+
shape: SHAPES[i].clone(),
141+
})
142+
}
143+
}
144+
145+
impl Iterator for JetIterator {
146+
type Item = Direction;
147+
148+
fn next(&mut self) -> Option<Self::Item> {
149+
let i = self.idx % self.jets.len();
150+
self.idx += 1;
151+
Some(self.jets[i])
152+
}
153+
}
154+
155+
impl Stack {
156+
fn new(positions: &HashSet<XY>) -> Self {
157+
Self {
158+
positions: positions.clone(),
159+
tops: Stack::get_tops(positions),
160+
top: 0,
161+
}
162+
}
163+
164+
fn get_tops(positions: &HashSet<XY>) -> HashMap<usize, i32> {
165+
let mut tops: HashMap<usize, i32> = HashMap::new();
166+
positions
167+
.iter()
168+
.map(|p| (p.x() as usize, p.y()))
169+
.for_each(|(x, y)| {
170+
match tops.get(&x) {
171+
Some(val) => tops.insert(x, *val.max(&y)),
172+
None => tops.insert(x, y),
173+
};
174+
});
175+
tops
176+
}
177+
178+
fn get_tops_normalized(&self) -> [i32; WIDTH] {
179+
let mut ans = [0_i32; WIDTH];
180+
(0..WIDTH)
181+
.for_each(|i| ans[i] = self.top - *self.tops.get(&i).unwrap());
182+
ans
183+
}
184+
185+
fn overlapped_by(&self, rock: &Rock) -> bool {
186+
rock.shape.iter().any(|p| self.positions.contains(p))
187+
}
188+
189+
fn add(&mut self, rock: &Rock) {
190+
rock.shape.iter().for_each(|p| {
191+
let val = *self.tops.get(&(p.x() as usize)).unwrap();
192+
self.tops.insert(p.x() as usize, val.max(p.y()));
193+
self.positions.insert(*p);
194+
self.top = self.top.max(p.y() + 1);
195+
});
196+
self.positions = self.get_top_rows(KEEP_ROWS);
197+
}
198+
199+
fn get_top_rows(&self, n: usize) -> HashSet<XY> {
200+
self.positions
201+
.clone()
202+
.into_iter()
203+
.filter(|p| p.y() > self.top - n as i32)
204+
.collect()
205+
}
206+
}
207+
208+
impl AoC2022_17 {
209+
fn solve(&self, jets: &[Direction], requested_drops: usize) -> usize {
210+
fn drop(
211+
drop_idx: usize,
212+
states: &mut HashMap<State, Vec<Cycle>>,
213+
stack: &mut Stack,
214+
shapes: &mut ShapesIterator,
215+
jets: &mut JetIterator,
216+
) -> State {
217+
let mut cnt = 0;
218+
let shape = shapes.next().unwrap();
219+
let start = Rock {
220+
idx: shape.idx,
221+
shape: shape.shape.into_iter().collect(),
222+
}
223+
.move_(&XY::of(OFFSET_X, stack.top + OFFSET_Y));
224+
let mut rock = start;
225+
loop {
226+
assert!(cnt < 10_000, "infinite loop");
227+
let jet = jets.next().unwrap();
228+
let state = State {
229+
shape: rock.idx,
230+
tops: stack.get_tops_normalized(),
231+
jet,
232+
};
233+
if cnt == 1 {
234+
let cycle = Cycle {
235+
cycle: drop_idx,
236+
top: stack.top,
237+
};
238+
states
239+
.entry(state)
240+
.and_modify(|cycles| cycles.push(cycle))
241+
.or_insert(vec![cycle]);
242+
}
243+
cnt += 1;
244+
let mut moved = rock.move_(&jet.try_into().unwrap());
245+
if moved.inside_x(0, WIDTH as i32)
246+
&& !stack.overlapped_by(&moved)
247+
{
248+
rock = moved;
249+
}
250+
moved = rock.move_(&Direction::Down.try_into().unwrap());
251+
if stack.overlapped_by(&moved) {
252+
stack.add(&rock);
253+
return state;
254+
}
255+
rock = moved;
256+
}
257+
}
258+
259+
let mut stack = Stack::new(&FLOOR);
260+
let mut states: HashMap<State, Vec<Cycle>> = HashMap::new();
261+
let mut shapes_iterator = ShapesIterator::new();
262+
let mut jet_iterator = JetIterator::new(jets);
263+
let mut drops = 0;
264+
loop {
265+
let state = drop(
266+
drops,
267+
&mut states,
268+
&mut stack,
269+
&mut shapes_iterator,
270+
&mut jet_iterator,
271+
);
272+
drops += 1;
273+
if drops == requested_drops {
274+
return stack.top as usize;
275+
}
276+
if drops >= LOOP_TRESHOLD
277+
&& states.get(&state).unwrap_or(&vec![]).len() > 1
278+
{
279+
let cycles = states.get(&state).unwrap();
280+
let loop_size = cycles[1].cycle - cycles[0].cycle;
281+
let diff = cycles[1].top - cycles[0].top;
282+
let loops = (requested_drops - drops) / loop_size;
283+
let left = requested_drops - (drops + loops * loop_size);
284+
(0..left).for_each(|_| {
285+
drop(
286+
drops,
287+
&mut states,
288+
&mut stack,
289+
&mut shapes_iterator,
290+
&mut jet_iterator,
291+
);
292+
drops += 1;
293+
});
294+
return stack.top as usize + loops * diff as usize;
295+
}
296+
}
297+
}
298+
}
299+
300+
impl aoc::Puzzle for AoC2022_17 {
301+
type Input = Vec<Direction>;
302+
type Output1 = usize;
303+
type Output2 = usize;
304+
305+
aoc::puzzle_year_day!(2022, 17);
306+
307+
fn parse_input(&self, lines: Vec<String>) -> Vec<Direction> {
308+
lines[0]
309+
.chars()
310+
.map(|ch| Direction::from_str(&ch.to_string()).unwrap())
311+
.collect()
312+
}
313+
314+
fn part_1(&self, jets: &Vec<Direction>) -> usize {
315+
self.solve(jets, 2022)
316+
}
317+
318+
fn part_2(&self, jets: &Vec<Direction>) -> usize {
319+
self.solve(jets, 1_000_000_000_000)
320+
}
321+
322+
fn samples(&self) {
323+
aoc::puzzle_samples! {
324+
self, part_1, TEST, 3_068,
325+
self, part_2, TEST, 1_514_285_714_288_usize
326+
};
327+
}
328+
}
329+
330+
fn main() {
331+
AoC2022_17 {}.run(std::env::args());
332+
}
333+
334+
const TEST: &str = "\
335+
>>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>
336+
";
337+
338+
#[cfg(test)]
339+
mod tests {
340+
use super::*;
341+
342+
#[test]
343+
pub fn jets() {
344+
let mut jets = JetIterator::new(
345+
&AoC2022_17 {}.parse_input(vec![String::from("<>><")]),
346+
);
347+
assert!(jets.next().unwrap() == Direction::Left);
348+
assert!(jets.next().unwrap() == Direction::Right);
349+
assert!(jets.next().unwrap() == Direction::Right);
350+
assert!(jets.next().unwrap() == Direction::Left);
351+
assert!(jets.next().unwrap() == Direction::Left);
352+
assert!(jets.next().unwrap() == Direction::Right);
353+
assert!(jets.next().unwrap() == Direction::Right);
354+
assert!(jets.next().unwrap() == Direction::Left);
355+
}
356+
357+
#[test]
358+
pub fn samples() {
359+
AoC2022_17 {}.samples();
360+
}
361+
}

0 commit comments

Comments
 (0)