Skip to content

Commit 48794fe

Browse files
committed
Implement basic cycling
Note that the east/west tilting is still wrong, but the code structure in general should now be stable: We cycle until we find that the state is actually stable (by checking a SHA-1 of it), and then return the load.
1 parent 6e5980e commit 48794fe

File tree

3 files changed

+227
-5
lines changed

3 files changed

+227
-5
lines changed

Cargo.lock

Lines changed: 124 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ edition = "2021"
88
[dependencies]
99
muldiv = "1.0.1"
1010
num = "0.4.1"
11+
rust-crypto = "0.2.36"
1112

1213
[[bin]]
1314
name = "adventofcode"

src/day14.rs

Lines changed: 102 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#[derive(Debug, PartialEq)]
1+
#[derive(Clone, Copy, Debug, PartialEq)]
22
enum Tile {
33
Empty = '.' as isize,
44
FixedRock = '#' as isize,
@@ -19,17 +19,32 @@ struct Platform {
1919

2020
impl Platform {
2121
pub fn tilt(&mut self, direction: Direction) {
22-
assert_eq!(direction, Direction::North);
23-
2422
let rows = self.tiles.len();
25-
for r in 0..rows {
23+
let cols = self.tiles[0].len();
24+
match direction {
25+
Direction::North => self.tilt_north_south(0..=rows-1, |r| r+1..=rows-1),
26+
Direction::West => self.tilt_west_east(0..=cols-1, |c| c+1..=cols-1),
27+
Direction::South => self.tilt_north_south(rows-1..=0, |r| r-1..=0),
28+
Direction::East => self.tilt_west_east(cols-1..=0, |c| c-1..=0),
29+
}
30+
}
31+
32+
pub fn cycle(&mut self) {
33+
self.tilt(Direction::North);
34+
self.tilt(Direction::West);
35+
self.tilt(Direction::South);
36+
self.tilt(Direction::East);
37+
}
38+
39+
fn tilt_north_south(&mut self, rows: std::ops::RangeInclusive<usize>, source_rows: impl Fn(usize) -> std::ops::RangeInclusive<usize>) {
40+
for r in rows {
2641
for c in 0..self.tiles[r].len() {
2742
if self.tiles[r][c] != Tile::Empty {
2843
continue;
2944
}
3045

3146
// Move the closest tile in South direction here.
32-
for r2 in r+1..rows {
47+
for r2 in source_rows(r) {
3348
let other_tile = &mut self.tiles[r2][c];
3449
match other_tile {
3550
Tile::Empty => {},
@@ -49,6 +64,34 @@ impl Platform {
4964
}
5065
}
5166

67+
fn tilt_west_east(&mut self, cols: std::ops::RangeInclusive<usize>, source_cols: impl Fn(usize) -> std::ops::RangeInclusive<usize>) {
68+
for c in cols {
69+
for r in 0..self.tiles.len() {
70+
if self.tiles[r][c] != Tile::Empty {
71+
continue;
72+
}
73+
74+
// Move the closest tile in South direction here.
75+
for c2 in source_cols(c) {
76+
let other_tile = &mut self.tiles[r][c2];
77+
match other_tile {
78+
Tile::Empty => {},
79+
Tile::FixedRock => {
80+
// Abort: This rock won't move, and anything further south will get stuck here.
81+
break;
82+
},
83+
Tile::RoundedRock => {
84+
// This one!
85+
self.tiles[r][c] = Tile::RoundedRock;
86+
self.tiles[r][c2] = Tile::Empty;
87+
break;
88+
},
89+
}
90+
}
91+
}
92+
}
93+
}
94+
5295
pub fn total_load(&self, beam: Direction) -> usize {
5396
assert_eq!(beam, Direction::North);
5497

@@ -63,6 +106,16 @@ impl Platform {
63106
}
64107
result
65108
}
109+
110+
pub fn checksum(&self) -> String {
111+
let mut hasher = crypto::sha1::Sha1::new();
112+
for row in self.tiles.iter() {
113+
for tile in row.iter() {
114+
crypto::digest::Digest::input(&mut hasher, &[*tile as u8]);
115+
}
116+
}
117+
crypto::digest::Digest::result_str(&mut hasher)
118+
}
66119
}
67120

68121
impl std::fmt::Display for Platform {
@@ -104,13 +157,36 @@ impl std::str::FromStr for Platform {
104157
}
105158

106159
pub fn main() {
160+
const CYCLES: usize = 1000000000;
107161
match std::fs::read_to_string("day14.input") {
108162
Ok(input) => {
109163
let mut platform: Platform = input.parse().unwrap();
110164
println!("initial\n{}", platform);
111165
platform.tilt(Direction::North);
112166
println!("after tilt\n{}", platform);
113167
println!("total load = {}", platform.total_load(Direction::North));
168+
169+
// Complete the first cycle.
170+
platform.tilt(Direction::West);
171+
platform.tilt(Direction::South);
172+
platform.tilt(Direction::East);
173+
174+
// Complete the remaining cycles
175+
let mut previous_checksum = platform.checksum();
176+
for cycle in 0..CYCLES-1 {
177+
platform.cycle();
178+
179+
let checksum = platform.checksum();
180+
println!("cycle {} checksum = {}", cycle, checksum);
181+
if checksum == previous_checksum {
182+
break;
183+
}
184+
previous_checksum = checksum;
185+
if cycle % 1000 == 0 {
186+
println!("complete {}%", cycle/CYCLES*100);
187+
}
188+
}
189+
println!("total load after {} cycles = {}", CYCLES, platform.total_load(Direction::North));
114190
},
115191
Err(reason) => println!("error = {}", reason),
116192
}
@@ -120,6 +196,7 @@ pub fn main() {
120196
mod test {
121197
use super::*;
122198

199+
static CYCLES: usize = 1000000000;
123200
static DATA: &str = "O....#....
124201
O.OO#....#
125202
.....##...
@@ -139,4 +216,24 @@ O.#..O.#.#
139216
println!("after tilt\n{}", platform);
140217
assert_eq!(platform.total_load(Direction::North), 136);
141218
}
219+
220+
#[test]
221+
fn test_part2() {
222+
let mut platform: Platform = DATA.parse().unwrap();
223+
println!("initial\n{}", platform);
224+
225+
let mut previous_checksum = platform.checksum();
226+
for cycle in 0..CYCLES {
227+
platform.cycle();
228+
229+
let checksum = platform.checksum();
230+
println!("cycle {} checksum = {}", cycle, checksum);
231+
if checksum == previous_checksum {
232+
break;
233+
}
234+
previous_checksum = checksum;
235+
}
236+
println!("after tilt\n{}", platform);
237+
assert_eq!(platform.total_load(Direction::North), 64);
238+
}
142239
}

0 commit comments

Comments
 (0)