-
Notifications
You must be signed in to change notification settings - Fork 9
/
day13.rs
118 lines (102 loc) · 3.43 KB
/
day13.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! # Transparent Origami
//!
//! There are 2 possible approaches to tracking the position of dots after each fold:
//! * A `HashSet` that will collapse duplicate entries
//! * An array of sufficient dimensions to track every possible coordinate.
//!
//! We will use both approaches for speed, the first in part 1 and the second in part 2.
//!
//! For part 2 we can determine the final size of the paper by taking the *last* x and y
//! coordinates from the fold instructions. It's then faster and more convenienent to process
//! each point completely and update the final location, than to step through intermediate folds.
use crate::util::hash::*;
use crate::util::iter::*;
use crate::util::parse::*;
use crate::util::point::*;
#[derive(Clone, Copy)]
pub enum Fold {
Horizontal(i32),
Vertical(i32),
}
pub struct Input {
points: Vec<Point>,
folds: Vec<Fold>,
}
/// Parse the input into collections of [`Point`] and [`Fold`] structs.
pub fn parse(input: &str) -> Input {
let (prefix, suffix) = input.split_once("\n\n").unwrap();
let points: Vec<_> = prefix.iter_signed().chunk::<2>().map(|[x, y]| Point::new(x, y)).collect();
let folds: Vec<_> = suffix
.lines()
.map(|line| match line.split_once('=').unwrap() {
("fold along x", x) => Fold::Horizontal(x.signed()),
("fold along y", y) => Fold::Vertical(y.signed()),
_ => unreachable!(),
})
.collect();
Input { points, folds }
}
/// Fold once then count dots. The sample data folds along `y` and my input folded along `x`
/// testing both possibilities.
pub fn part1(input: &Input) -> usize {
match input.folds[0] {
Fold::Horizontal(x) => {
input.points.iter().map(|&p| fold_horizontal(x, p)).collect::<FastSet<_>>().len()
}
Fold::Vertical(y) => {
input.points.iter().map(|&p| fold_vertical(y, p)).collect::<FastSet<_>>().len()
}
}
}
/// Decode secret message.
///
/// The output is a multi-line string to allow integration testing. The final dimensions of the
/// paper are found from the last `x` and `y` fold coordinates.
pub fn part2(input: &Input) -> String {
let mut width = 0;
let mut height = 0;
for &fold in &input.folds {
match fold {
Fold::Horizontal(x) => width = x,
Fold::Vertical(y) => height = y,
}
}
let mut grid = vec![false; (width * height) as usize];
for point in &input.points {
let mut point = *point;
for &fold in &input.folds {
point = match fold {
Fold::Horizontal(x) => fold_horizontal(x, point),
Fold::Vertical(y) => fold_vertical(y, point),
}
}
grid[(point.y * width + point.x) as usize] = true;
}
let mut code = String::new();
for y in 0..height {
code.push('\n');
for x in 0..width {
let c = if grid[(y * width + x) as usize] { '#' } else { '.' };
code.push(c);
}
}
code
}
/// Fold point at `x` coordinate, doing nothing if the point is to the left of the fold line.
#[inline]
fn fold_horizontal(x: i32, p: Point) -> Point {
if p.x < x {
p
} else {
Point::new(2 * x - p.x, p.y)
}
}
/// Fold point at `y` coordinate, doing nothing if the point is above the fold line.
#[inline]
fn fold_vertical(y: i32, p: Point) -> Point {
if p.y < y {
p
} else {
Point::new(p.x, 2 * y - p.y)
}
}