add 2023/08/2
This commit is contained in:
parent
13daf93f49
commit
bf6619d638
|
@ -7,7 +7,7 @@ use aoc2023::{boilerplate, common::*};
|
||||||
const DAY: usize = 08;
|
const DAY: usize = 08;
|
||||||
type Parsed<'a> = (Vec<Direction>, HashMap<&'a str, (&'a str, &'a str)>);
|
type Parsed<'a> = (Vec<Direction>, HashMap<&'a str, (&'a str, &'a str)>);
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
|
||||||
enum Direction {
|
enum Direction {
|
||||||
Left,
|
Left,
|
||||||
Right,
|
Right,
|
||||||
|
@ -27,30 +27,48 @@ fn parse_input(raw: &str) -> Parsed {
|
||||||
(directions, map)
|
(directions, map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1((directions, map): &Parsed) -> usize {
|
fn part1(parsed: &Parsed) -> usize {
|
||||||
|
steps_until(parsed, "AAA", "ZZZ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn steps_until((directions, map): &Parsed, start: &str, target: &str) -> usize {
|
||||||
directions
|
directions
|
||||||
.iter()
|
.iter()
|
||||||
.cycle()
|
.cycle()
|
||||||
.scan("AAA", |pos, dir| {
|
.scan(start, |pos, dir| {
|
||||||
let next = match dir {
|
let next = match dir {
|
||||||
Direction::Left => map.get(pos)?.0,
|
Direction::Left => map.get(pos)?.0,
|
||||||
Direction::Right => map.get(pos)?.1,
|
Direction::Right => map.get(pos)?.1,
|
||||||
};
|
};
|
||||||
if next == "ZZZ" {
|
(!next.ends_with(target)).then(|| {
|
||||||
None
|
|
||||||
} else {
|
|
||||||
*pos = next;
|
*pos = next;
|
||||||
Some(next)
|
Some(next)
|
||||||
}
|
})
|
||||||
})
|
})
|
||||||
.count()
|
.count()
|
||||||
+ 1
|
+ 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// I’m honestly not sure why this works. It seems each path only has a single ghost node, and that
|
||||||
|
// node occurs right before looping, so we can just compute the least common multiple of their step counts.
|
||||||
|
// I assume this holds true for other inputs (it can’t have been random),
|
||||||
|
// but I don’t see it anywhere in the task and only found out by experimentation.
|
||||||
fn part2(parsed: &Parsed) -> usize {
|
fn part2(parsed: &Parsed) -> usize {
|
||||||
unimplemented!()
|
parsed.1.keys().filter(|start| start.ends_with("A")).map(|start| steps_until(parsed, start, "Z")).fold(1, |acc, n| lcm(acc, n))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
const TEST_INPUT_P2: &str = "LR
|
||||||
|
|
||||||
|
11A = (11B, XXX)
|
||||||
|
11B = (XXX, 11Z)
|
||||||
|
11Z = (11B, XXX)
|
||||||
|
22A = (22B, XXX)
|
||||||
|
22B = (22C, 22C)
|
||||||
|
22C = (22Z, 22Z)
|
||||||
|
22Z = (22B, 22B)
|
||||||
|
XXX = (XXX, XXX)";
|
||||||
|
|
||||||
boilerplate! {
|
boilerplate! {
|
||||||
TEST_INPUT == "LLR
|
TEST_INPUT == "LLR
|
||||||
|
|
||||||
|
@ -59,9 +77,9 @@ BBB = (AAA, ZZZ)
|
||||||
ZZZ = (ZZZ, ZZZ)",
|
ZZZ = (ZZZ, ZZZ)",
|
||||||
tests: {
|
tests: {
|
||||||
part1: { TEST_INPUT => 6 },
|
part1: { TEST_INPUT => 6 },
|
||||||
part2: { TEST_INPUT => 0 },
|
part2: { TEST_INPUT_P2 => 6 },
|
||||||
},
|
},
|
||||||
bench1 == 12083,
|
bench1 == 12083,
|
||||||
bench2 == 0,
|
bench2 == 13385272668829,
|
||||||
bench_parse: |(v, m): &Parsed| { assert_eq!(m["AAA"], ("MJJ", "QBJ")); (v.len(), v[0]) } => (281, Direction::Left),
|
bench_parse: |(v, m): &Parsed| { assert_eq!(m["AAA"], ("MJJ", "QBJ")); (v.len(), v[0]) } => (281, Direction::Left),
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,3 +68,17 @@ pub fn parse_num<I: ParseableNumber<I>>(s: &str) -> I {
|
||||||
let start = unsafe { digits.next().unwrap_unchecked() };
|
let start = unsafe { digits.next().unwrap_unchecked() };
|
||||||
digits.fold(start, |acc, n| acc * I::from(10) + n)
|
digits.fold(start, |acc, n| acc * I::from(10) + n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn gcd(mut x: usize, mut y: usize) -> usize {
|
||||||
|
let mut remainder;
|
||||||
|
while y != 0 {
|
||||||
|
remainder = x % y;
|
||||||
|
x = y;
|
||||||
|
y = remainder;
|
||||||
|
}
|
||||||
|
x
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lcm(x: usize, y: usize) -> usize {
|
||||||
|
x * y / gcd(x, y)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user