Skip to content

Commit 2a1739d

Browse files
Year 2019, Day 3+4
Finished Day 3, hammered out a naive solution for Day 4
1 parent bf7f71e commit 2a1739d

File tree

7 files changed

+233
-36
lines changed

7 files changed

+233
-36
lines changed

advent-2019/input/day4.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
246540-787419

advent-2019/src/main.rs

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ fn main() -> Result<()> {
1313
println!("2.1) {:?}", run(2019, "day2.txt", parsers::intcode, day2::part1::solve)?);
1414
println!("2.2) {:?}", run(2019, "day2.txt", parsers::intcode, day2::part2::solve)?);
1515
println!("3.1) {:?}", run(2019, "day3.txt", vec_of, day3::part1::solve)?);
16+
println!("3.2) {:?}", run(2019, "day3.txt", vec_of, day3::part2::solve)?);
17+
println!("4.1) {:?}", run(2019, "day4.txt", pair_with_dashes, day4::part1::solve)?);
18+
println!("4.2) {:?}", run(2019, "day4.txt", pair_with_dashes, day4::part2::solve)?);
1619

1720
Ok(())
1821
}

advent-2019/src/solutions/day3.rs

+104-34
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,29 @@ pub mod part1 {
99
pub fn solve(wires: Vec<WireDescription>) -> Result<u32> {
1010
let (first, second) = (wires[0].clone(), wires[1].clone());
1111
let mut index = WireIndex::new(first.into());
12-
let intersection = index.find_closest_intersection(second);
13-
if let Some(intersection) = intersection {
14-
return Ok(manhattan_distance(intersection));
12+
let intersection = index.find_best_intersection(second, manhattan_distance);
13+
if let Some((a, b)) = intersection {
14+
return Ok(manhattan_distance(&a, &b));
15+
} else {
16+
bail!("No intersections!");
17+
}
18+
}
19+
}
20+
21+
pub mod part2 {
22+
use anyhow::*;
23+
use super::*;
24+
25+
// NOTE(pi):
26+
// This doesn't handle the following clause from the rules:
27+
// - If a wire visits a position on the grid multiple times, use the steps value from the first time it visits that position when calculating the total value of a specific intersection.
28+
// This just happened not to come up in the input.
29+
pub fn solve(wires: Vec<WireDescription>) -> Result<u32> {
30+
let (first, second) = (wires[0].clone(), wires[1].clone());
31+
let mut index = WireIndex::new(first.into());
32+
let intersection = index.find_best_intersection(second, signal_delay);
33+
if let Some((a, b)) = intersection {
34+
return Ok(signal_delay(&a, &b));
1535
} else {
1636
bail!("No intersections!");
1737
}
@@ -21,13 +41,26 @@ pub mod part1 {
2141
#[derive(Clone, Debug, PartialEq, Eq)]
2242
pub enum Direction { H, V }
2343

24-
#[derive(Clone, Debug)]
44+
impl Default for Direction {
45+
fn default() -> Self {
46+
Direction::H
47+
}
48+
}
49+
50+
#[derive(Clone, Debug, Default)]
2551
pub struct Line {
2652
dir: Direction,
53+
signal_delay: u32,
2754
c: i32,
2855
range: (i32, i32)
2956
}
3057

58+
impl Line {
59+
pub fn len(&self) -> u32 {
60+
(self.range.0 - self.range.1).abs() as u32
61+
}
62+
}
63+
3164
impl PartialEq for Line {
3265
fn eq(&self, other: &Self) -> bool {
3366
self.dir == other.dir && self.c == other.c && self.range == other.range
@@ -52,12 +85,18 @@ pub enum Instruction {
5285
}
5386

5487
impl Instruction {
55-
pub fn follow(&self, (sx, sy): (i32, i32)) -> (Line, (i32, i32)) {
88+
pub fn len(&self) -> u32 {
89+
use Instruction::*;
90+
match self {
91+
Right(l) | Left(l) | Up(l) | Down(l) => l.abs() as u32,
92+
}
93+
}
94+
pub fn follow(&self, signal_delay: u32, (sx, sy): (i32, i32)) -> (Line, (i32, i32)) {
5695
match &self {
57-
Instruction::Right(x) => (Line { dir: Direction::H, c: sy, range: (sx, sx + x) }, (sx + x, sy)),
58-
Instruction::Left(x) => (Line { dir: Direction::H, c: sy, range: (sx, sx - x) }, (sx - x, sy)),
59-
Instruction::Up(y) => (Line { dir: Direction::V, c: sx, range: (sy, sy + y )}, (sx, sy + y)),
60-
Instruction::Down(y) => (Line { dir: Direction::V, c: sx, range: (sy, sy - y )}, (sx, sy - y)),
96+
Instruction::Right(x) => (Line { dir: Direction::H, signal_delay, c: sy, range: (sx, sx + x) }, (sx + x, sy)),
97+
Instruction::Left(x) => (Line { dir: Direction::H, signal_delay, c: sy, range: (sx, sx - x) }, (sx - x, sy)),
98+
Instruction::Up(y) => (Line { dir: Direction::V, signal_delay, c: sx, range: (sy, sy + y )}, (sx, sy + y)),
99+
Instruction::Down(y) => (Line { dir: Direction::V, signal_delay, c: sx, range: (sy, sy - y )}, (sx, sy - y)),
61100
}
62101
}
63102
}
@@ -102,8 +141,10 @@ impl From<WireDescription> for Wire {
102141

103142
let (mut cx, mut cy) = (0, 0);
104143
let mut segments = vec![];
144+
let mut signal_delay = 0;
105145
for i in desc.instructions {
106-
let (segment, (nx, ny)) = i.follow((cx, cy));
146+
let (segment, (nx, ny)) = i.follow(signal_delay, (cx, cy));
147+
signal_delay += segment.len();
107148
cx = nx;
108149
cy = ny;
109150
segments.push(segment);
@@ -147,10 +188,22 @@ struct WireIndex {
147188
vertical_lines: SplitVec<Line>,
148189
cx: i32,
149190
cy: i32,
191+
signal_delay: u32,
192+
}
193+
194+
pub fn manhattan_distance(a: &Line, b: &Line) -> u32 {
195+
// |
196+
// H: +----x--------+ (a.c, (a.range.0, a.range.1))
197+
// |
198+
// + (b.c, (b.range.0, b.range.1))
199+
// The lines intersect at (a.c, b.c)
200+
return (a.c.abs() + b.c.abs()) as u32
150201
}
151202

152-
pub fn manhattan_distance((x,y): (i32, i32)) -> u32 {
153-
(x.abs() + y.abs()) as u32
203+
pub fn signal_delay(a: &Line, b: &Line) -> u32 {
204+
let delay_for_a_at_point = a.signal_delay + (b.c - a.range.0).abs() as u32;
205+
let delay_for_b_at_point = b.signal_delay + (a.c - b.range.0).abs() as u32;
206+
return delay_for_a_at_point + delay_for_b_at_point;
154207
}
155208

156209
impl WireIndex {
@@ -168,13 +221,15 @@ impl WireIndex {
168221
}
169222
}
170223
return WireIndex {
171-
horizontal_lines: SplitVec::from(horizontal_lines, Line { dir: Direction::H, c: 0, range: (0, 0) }),
172-
vertical_lines: SplitVec::from(vertical_lines, Line { dir: Direction::V, c: 0, range: (0, 0)}),
173-
cx: 0, cy: 0,
224+
horizontal_lines: SplitVec::from(horizontal_lines, Line { dir: Direction::H, signal_delay: 0, c: 0, range: (0, 0) }),
225+
vertical_lines: SplitVec::from(vertical_lines, Line { dir: Direction::V, signal_delay: 0, c: 0, range: (0, 0)}),
226+
cx: 0, cy: 0, signal_delay: 0,
174227
}
175228
}
176229

177-
pub fn ingest_instruction(&mut self, i: Instruction) -> Option<(i32, i32)> {
230+
pub fn ingest_instruction<F>(&mut self, i: Instruction, metric: F) -> Option<(Line, Line)>
231+
where
232+
F: Fn(&Line, &Line) -> u32 {
178233
// Keep track of the closest intersection we've seen so far
179234
let mut closest = None;
180235

@@ -189,6 +244,8 @@ impl WireIndex {
189244
let new_coord = match i { Left(_) | Down(_) => current_coord - dist, Right(_) | Up(_) => current_coord + dist };
190245
// If we're scanning "down" a list, stop when we hit zero, otherwise stop when we hit the end of the array
191246
let stop_condition = match i { Left(_) | Down(_) => 0, Right(_) | Up(_) => lines.elements.len() };
247+
let dir = match i { Left(_) | Right(_) => Direction::H, Up(_) | Down(_) => Direction::V };
248+
let traveling_line = Line { c: traveling_along, dir, signal_delay: self.signal_delay, range: (current_coord, new_coord) };
192249

193250
loop {
194251
if lines.split == stop_condition {
@@ -218,10 +275,9 @@ impl WireIndex {
218275
continue;
219276
}
220277

221-
let intersection = match i { Left(_) | Right(_) => (line.c, traveling_along), Up(_) | Down(_) => (traveling_along, line.c) };
222278
closest = match closest {
223-
None if manhattan_distance(intersection) != 0 => Some(intersection),
224-
Some(closest) if manhattan_distance(intersection) < manhattan_distance(closest) => Some(intersection),
279+
None if metric(&traveling_line, &line) != 0 => Some((traveling_line.clone(), line.clone())),
280+
Some((a,b)) if metric(&traveling_line, &line) < metric(&a, &b) => Some((traveling_line.clone(), line.clone())),
225281
_ => closest,
226282
}
227283
} else {
@@ -234,18 +290,21 @@ impl WireIndex {
234290
Left(_) | Right(_) => self.cx = new_coord,
235291
Up(_) | Down(_) => self.cy = new_coord,
236292
}
293+
self.signal_delay += traveling_line.len();
237294

238295
return closest;
239296
}
240297

241-
pub fn find_closest_intersection(&mut self, w: WireDescription) -> Option<(i32, i32)> {
298+
pub fn find_best_intersection<F>(&mut self, w: WireDescription, metric: F) -> Option<(Line, Line)>
299+
where
300+
F: Fn(&Line, &Line) -> u32 {
242301
let mut closest = None;
243302
for instruction in w.instructions {
244-
let intersection = self.ingest_instruction(instruction);
245-
if let Some(intersection) = intersection {
303+
let intersection = self.ingest_instruction(instruction, manhattan_distance);
304+
if let Some((a, b)) = intersection {
246305
closest = match closest {
247-
None if manhattan_distance(intersection) != 0 => Some(intersection),
248-
Some(closest) if manhattan_distance(intersection) < manhattan_distance(closest) => Some(intersection),
306+
None if metric(&a, &b) != 0 => Some((a,b)),
307+
Some((x,y)) if metric(&a, &b) < metric(&x, &y) => Some((a,b)),
249308
_ => closest,
250309
}
251310
}
@@ -296,28 +355,39 @@ mod tests {
296355

297356
#[test]
298357
fn test_manhattan_distance() {
299-
assert_eq!(2, manhattan_distance((1,1)));
300-
assert_eq!(2, manhattan_distance((-1,1)));
301-
assert_eq!(2, manhattan_distance((1,-1)));
302-
assert_eq!(2, manhattan_distance((-1,-1)));
303-
assert_eq!(10, manhattan_distance((-5,5)));
358+
assert_eq!(2, manhattan_distance(&Line { c: 1, ..Default::default() }, &Line { c: 1, ..Default::default() }));
359+
assert_eq!(2, manhattan_distance(&Line { c: -1, ..Default::default() }, &Line { c: 1, ..Default::default() }));
360+
assert_eq!(2, manhattan_distance(&Line { c: 1, ..Default::default() }, &Line { c: -1, ..Default::default() }));
361+
assert_eq!(2, manhattan_distance(&Line { c: -1, ..Default::default() }, &Line { c: -1, ..Default::default() }));
362+
assert_eq!(10, manhattan_distance(&Line { c: -5, ..Default::default() }, &Line { c: 5, ..Default::default() }));
304363
}
305364

306365
#[test]
307366
pub fn test_wire_crossing() {
308367
let wire = "R8,U5,L5,D3".parse::<Wire>().unwrap();
309368
let mut index = WireIndex::new(wire);
310-
assert_matches!(index.ingest_instruction(Instruction::Up(7)), None);
311-
assert_matches!(index.ingest_instruction(Instruction::Right(6)), None);
312-
assert_matches!(index.ingest_instruction(Instruction::Down(4)), Some((6,5)));
313-
assert_matches!(index.ingest_instruction(Instruction::Left(4)), Some((3,3)));
369+
assert_matches!(index.ingest_instruction(Instruction::Up(7), manhattan_distance), None);
370+
assert_matches!(index.ingest_instruction(Instruction::Right(6), manhattan_distance), None);
371+
assert_matches!(index.ingest_instruction(Instruction::Down(4), manhattan_distance), Some((Line { c: 6, .. }, Line { c: 5, .. })));
372+
assert_matches!(index.ingest_instruction(Instruction::Left(4), manhattan_distance), Some((Line { c: 3, .. }, Line { c: 3, .. })));
314373
}
315374

316375
#[test]
317376
pub fn test_closest_intersection() {
318377
let wire = "R8,U5,L5,D3".parse::<Wire>().unwrap();
319378
let mut index = WireIndex::new(wire);
320379
let desc = "U7,R6,D4,L4".parse::<WireDescription>().unwrap();
321-
assert_matches!(index.find_closest_intersection(desc), Some((3,3)));
380+
assert_matches!(index.find_best_intersection(desc, manhattan_distance), Some((Line { c: 3, .. }, Line { c: 3, .. })));
381+
}
382+
383+
#[test]
384+
pub fn test_signal_delay() {
385+
let wire = "R8,U5,L5,D3".parse::<Wire>().unwrap();
386+
let mut index = WireIndex::new(wire);
387+
let desc = "U7,R6,D4,L4".parse::<WireDescription>().unwrap();
388+
let intersection = index.find_best_intersection(desc, signal_delay);
389+
assert_matches!(intersection, Some((Line { c: 6, .. }, Line { c: 5, .. })));
390+
let intersection = intersection.unwrap();
391+
assert_eq!(30, signal_delay(&intersection.0, &intersection.1));
322392
}
323393
}

advent-2019/src/solutions/day4.rs

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
pub mod part1 {
2+
use super::*;
3+
use anyhow::*;
4+
pub fn solve((min, max): (u32, u32)) -> Result<u32> {
5+
Ok(count_passwords(min, max, password_matches_naive_part1))
6+
}
7+
}
8+
pub mod part2 {
9+
use super::*;
10+
use anyhow::*;
11+
pub fn solve((min, max): (u32, u32)) -> Result<u32> {
12+
Ok(count_passwords(min, max, password_matches_naive_part2))
13+
}
14+
}
15+
16+
pub fn password_matches_naive_part1(p: u32) -> bool {
17+
let pw = p.to_string();
18+
let mut prev = pw.chars().nth(0).unwrap();
19+
let mut doubles = false;
20+
let mut monotonic = true;
21+
for c in pw.chars().skip(1) {
22+
if c == prev {
23+
doubles = true;
24+
}
25+
if c.to_digit(10) < prev.to_digit(10) {
26+
monotonic = false;
27+
break;
28+
}
29+
prev = c;
30+
}
31+
return doubles && monotonic;
32+
}
33+
pub fn password_matches_naive_part2(p: u32) -> bool {
34+
let pw = p.to_string();
35+
let mut prev = pw.chars().nth(0).unwrap();
36+
let mut doubles = false;
37+
let mut stretch: u32 = 1;
38+
let mut monotonic = true;
39+
for c in pw.chars().skip(1) {
40+
if c == prev {
41+
stretch += 1;
42+
} else {
43+
if stretch == 2 {
44+
doubles = true;
45+
}
46+
stretch = 1;
47+
}
48+
if c.to_digit(10) < prev.to_digit(10) {
49+
monotonic = false;
50+
break;
51+
}
52+
prev = c;
53+
}
54+
return (doubles || stretch == 2) && monotonic;
55+
}
56+
pub fn count_passwords<R>(min: u32, max: u32, rules: R) -> u32
57+
where
58+
R: Fn(u32) -> bool {
59+
let mut count = 0;
60+
for p in min..=max {
61+
if rules(p) {
62+
count += 1;
63+
}
64+
}
65+
return count;
66+
}
67+
68+
#[cfg(test)]
69+
mod tests {
70+
use super::*;
71+
#[test]
72+
pub fn examples_part1() {
73+
assert!(password_matches_naive_part1(111111));
74+
assert_eq!(false, password_matches_naive_part1(223450));
75+
assert_eq!(false, password_matches_naive_part1(123789));
76+
}
77+
#[test]
78+
pub fn examples_part2() {
79+
assert!(password_matches_naive_part2(112233));
80+
assert_eq!(false, password_matches_naive_part2(123444));
81+
assert!(password_matches_naive_part2(111122));
82+
}
83+
}

advent-2019/src/solutions/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod day1;
22
pub mod day2;
3-
pub mod day3;
3+
pub mod day3;
4+
pub mod day4;

advent-shared/src/parsers/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
mod vec_of;
22
mod identity;
33
mod raw;
4+
mod pair;
45
pub use vec_of::*;
56
pub use identity::*;
6-
pub use raw::*;
7+
pub use raw::*;
8+
pub use pair::*;

advent-shared/src/parsers/pair.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use anyhow::*;
2+
use std::{fmt::Debug, path::PathBuf, fs, str::FromStr};
3+
4+
pub fn pair_of<T>(file: PathBuf) -> Result<(T,T)>
5+
where
6+
T: FromStr,
7+
<T as FromStr>::Err: Debug,
8+
{
9+
pair_with_delimiter(file, "\n")
10+
}
11+
12+
pub fn pair_with_commas<T>(file: PathBuf) -> Result<(T, T)>
13+
where
14+
T: FromStr,
15+
<T as FromStr>::Err: Debug,
16+
{
17+
pair_with_delimiter(file, ",")
18+
}
19+
20+
pub fn pair_with_dashes<T>(file: PathBuf) -> Result<(T, T)>
21+
where
22+
T: FromStr,
23+
<T as FromStr>::Err: Debug,
24+
{
25+
pair_with_delimiter(file, "-")
26+
}
27+
28+
pub fn pair_with_delimiter<T>(file: PathBuf, delim: &str) -> Result<(T, T)>
29+
where
30+
T: FromStr,
31+
<T as FromStr>::Err: Debug,
32+
{
33+
let contents = fs::read_to_string(file)?;
34+
let mut parts = contents.split(delim);
35+
let (a, b) = (parts.next().unwrap(), parts.next().unwrap());
36+
Ok((a.parse::<T>().unwrap(), b.parse::<T>().unwrap()))
37+
}

0 commit comments

Comments
 (0)