advent-of-code/2021/src/bin/day18.rs

269 lines
8.2 KiB
Rust
Raw Normal View History

2021-12-18 17:06:34 +01:00
#![feature(result_option_inspect)]
2021-12-18 16:18:25 +01:00
#![feature(box_syntax)]
#![feature(test)]
extern crate test;
use aoc2021::common::*;
2021-12-18 17:06:34 +01:00
use std::fmt;
2021-12-18 16:18:25 +01:00
const DAY: usize = 18;
type Parsed = Vec<Node>;
2021-12-18 17:06:34 +01:00
#[derive(Debug, PartialEq, Clone)]
2021-12-18 16:18:25 +01:00
enum Node {
Number(usize),
Pair(Box<(Node, Node)>),
}
impl fmt::Display for Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
Node::Number(n) => write!(f, "{n}"),
Node::Pair(p) => write!(f, "[{},{}]", p.0, p.1),
}
}
}
fn parse_input(raw: &str) -> Parsed {
raw.lines().map(|l| parse_node(l).0).collect()
}
fn parse_node(raw: &str) -> (Node, usize) {
if let Some(inner) = raw.strip_prefix('[') {
let (first, offset) = parse_node(inner);
let (second, offset2) = parse_node(&inner[offset..]);
(Node::Pair(box (first, second)), 1 /* the opening [ */ + offset + offset2 + 1 /* the comma */)
} else {
let n = raw.as_bytes()[0] - b'0';
debug_assert!(n <= 9, "Number was {n}, raw was {raw}");
(Node::Number(n as _), 2)
}
}
#[derive(Debug, PartialEq)]
2021-12-18 17:06:34 +01:00
enum Explosion {
2021-12-18 16:18:25 +01:00
None,
Partial,
Full,
}
impl Node {
2021-12-18 17:06:34 +01:00
fn reduce(&mut self) {
while self.explode() || self.split() {}
}
2021-12-18 16:18:25 +01:00
fn explode(&mut self) -> bool {
2021-12-18 17:06:34 +01:00
self.explode_inner(0, &mut None, &mut None) != Explosion::None
}
fn split(&mut self) -> bool {
match self {
Node::Number(n) if *n >= 10 => {
*self = Node::Pair(box (Node::Number(*n / 2), Node::Number(*n / 2 + (*n & 1))));
true
}
Node::Pair(p) => p.0.split() || p.1.split(),
_ => false,
}
}
fn magnitude(&self) -> usize {
match self {
Node::Number(n) => *n,
Node::Pair(p) => 3 * p.0.magnitude() + 2 * p.1.magnitude(),
}
2021-12-18 16:18:25 +01:00
}
fn explode_inner<'a, 'b>(
&'a mut self,
depth: usize,
previous_number: &'b mut Option<&'a mut usize>,
for_next: &'b mut Option<usize>,
2021-12-18 17:06:34 +01:00
) -> Explosion {
2021-12-18 16:18:25 +01:00
match (self, &for_next) {
(Node::Number(n), Some(x)) => {
*n += x;
*for_next = None;
2021-12-18 17:06:34 +01:00
Explosion::Full
2021-12-18 16:18:25 +01:00
}
(Node::Number(n), None) => {
*previous_number = Some(n);
2021-12-18 17:06:34 +01:00
Explosion::None
2021-12-18 16:18:25 +01:00
}
(s @ Node::Pair(_), _) if depth == 4 => {
let (left, right) = s.number_pair_or_panic();
if let Some(prev) = previous_number {
**prev += left;
}
*for_next = Some(*right);
*s = Node::Number(0);
2021-12-18 17:06:34 +01:00
Explosion::Partial
2021-12-18 16:18:25 +01:00
}
(Node::Pair(p), _) => {
let left = p.0.explode_inner(depth + 1, previous_number, for_next);
2021-12-18 17:06:34 +01:00
if left != Explosion::Full {
2021-12-18 16:18:25 +01:00
p.1.explode_inner(depth + 1, previous_number, for_next)
} else {
left
}
}
}
}
fn number_pair_or_panic(&self) -> (&usize, &usize) {
match &self {
Node::Pair(p) => (p.0.number_or_panic(), p.1.number_or_panic()),
_ => unreachable!(),
}
}
fn number_or_panic(&self) -> &usize {
match &self {
Node::Number(n) => n,
_ => unreachable!(),
}
}
}
fn part1(parsed: &Parsed) -> usize {
2021-12-18 17:06:34 +01:00
add_and_reduce(parsed.clone()).inspect(|n| println!("{n}")).unwrap().magnitude()
}
fn add_and_reduce(parsed: Parsed) -> Option<Node> {
/*
let mut r = parsed.into_iter().reduce(move |acc, new| Node::Pair(box (acc, new))).unwrap();
r.reduce();
Some(r)
*/
parsed.into_iter().reduce(move |acc, new| {
let mut n = Node::Pair(box (acc, new));
n.reduce();
println!("{n}");
n
})
2021-12-18 16:18:25 +01:00
}
fn part2(parsed: &Parsed) -> usize {
unimplemented!()
}
fn main() {
let input = parse_input(&read_file(DAY));
println!("Part 1: {}", part1(&input));
println!("Part 2: {}", part2(&input));
}
#[cfg(test)]
mod tests {
use super::*;
use aoc2021::*;
use test_case::test_case;
const TEST_INPUT_SINGLE_ADDITION: &str = "[[[[4,3],4],4],[7,[[8,4],9]]]
[1,1]";
2021-12-18 17:06:34 +01:00
const OTHER_TEST_INPUT: &str = "[1,2]
2021-12-18 16:18:25 +01:00
[[1,2],3]
[9,[8,7]]
[[1,9],[8,5]]
[[[[1,2],[3,4]],[[5,6],[7,8]]],9]
[[[9,[3,8]],[[0,9],6]],[[[3,7],[4,9]],3]]
[[[[1,3],[5,3]],[[1,3],[8,7]]],[[[4,9],[6,9]],[[8,2],[7,3]]]]";
2021-12-18 17:06:34 +01:00
const TEST_INPUT: &str = "[[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]]
[[[5,[2,8]],4],[5,[[9,9],0]]]
[6,[[[6,2],[5,6]],[[7,6],[4,7]]]]
[[[6,[0,7]],[0,9]],[4,[9,[9,0]]]]
[[[7,[6,4]],[3,[1,3]]],[[[5,5],1],9]]
[[6,[[7,3],[3,2]]],[[[3,8],[5,7]],4]]
[[[[5,4],[7,7]],8],[[8,3],8]]
[[9,3],[[9,9],[6,[4,9]]]]
[[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]]
[[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]]";
2021-12-18 16:18:25 +01:00
#[test]
fn test_example_parsing() {
let [first, second]: [Node; 2] = parse_input(TEST_INPUT_SINGLE_ADDITION).try_into().unwrap();
assert_eq!(
first,
Node::Pair(box (
Node::Pair(box (Node::Pair(box (Node::Pair(box (Node::Number(4), Node::Number(3))), Node::Number(4))), Node::Number(4))),
Node::Pair(box (Node::Number(7), Node::Pair(box (Node::Pair(box (Node::Number(8), Node::Number(4),)), Node::Number(9),))))
))
);
assert_eq!(second, Node::Pair(box (Node::Number(1), Node::Number(1))));
}
#[test]
fn test_node_display() {
2021-12-18 17:06:34 +01:00
for (actual, expected) in parse_input(OTHER_TEST_INPUT).into_iter().zip(OTHER_TEST_INPUT.lines()) {
2021-12-18 16:18:25 +01:00
assert_eq!(expected, actual.to_string());
}
}
#[test_case("[[[[[9,8],1],2],3],4]" => "[[[[0,9],2],3],4]")]
#[test_case("[7,[6,[5,[4,[3,2]]]]]" => "[7,[6,[5,[7,0]]]]")]
#[test_case("[[6,[5,[4,[3,2]]]],1]" => "[[6,[5,[7,0]]],3]")]
#[test_case("[[3,[2,[1,[7,3]]]],[6,[5,[4,[3,2]]]]]" => "[[3,[2,[8,0]]],[9,[5,[4,[3,2]]]]]")]
#[test_case("[[3,[2,[8,0]]],[9,[5,[4,[3,2]]]]]" => "[[3,[2,[8,0]]],[9,[5,[7,0]]]]")]
fn test_single_explosion(raw: &str) -> String {
let mut i = parse_node(raw).0;
assert!(i.explode());
i.to_string()
}
2021-12-18 17:06:34 +01:00
#[test_case("[[1,2],[[3,4],5]]" => 143)]
#[test_case("[[[[0,7],4],[[7,8],[6,0]]],[8,1]]" => 1384)]
#[test_case("[[[[1,1],[2,2]],[3,3]],[4,4]]" => 445)]
#[test_case("[[[[3,0],[5,3]],[4,4]],[5,5]]" => 791)]
#[test_case("[[[[5,0],[7,4]],[5,5]],[6,6]]" => 1137)]
#[test_case("[[[[8,7],[7,7]],[[8,6],[7,7]]],[[[0,7],[6,6]],[8,7]]]" => 3488)]
#[test_case("[[[[6,6],[7,6]],[[7,7],[7,0]]],[[[7,7],[7,7]],[[7,8],[9,9]]]]" => 4140)]
fn test_magnitude(raw: &str) -> usize {
parse_node(raw).0.magnitude()
}
#[test]
fn test_full_chain() {
let lhs = parse_node("[[[[4,3],4],4],[7,[[8,4],9]]]").0;
let rhs = parse_node("[1,1]").0;
let mut res = Node::Pair(box (lhs, rhs));
assert_eq!(res.to_string(), "[[[[[4,3],4],4],[7,[[8,4],9]]],[1,1]]");
let mut res2 = res.clone();
res.explode();
assert_eq!(res.to_string(), "[[[[0,7],4],[7,[[8,4],9]]],[1,1]]");
res.explode();
assert_eq!(res.to_string(), "[[[[0,7],4],[15,[0,13]]],[1,1]]");
res.split();
assert_eq!(res.to_string(), "[[[[0,7],4],[[7,8],[0,13]]],[1,1]]");
res.split();
assert_eq!(res.to_string(), "[[[[0,7],4],[[7,8],[0,[6,7]]]],[1,1]]");
res.explode();
assert_eq!(res.to_string(), "[[[[0,7],4],[[7,8],[6,0]]],[8,1]]");
// should be done now
res.reduce();
assert_eq!(res.to_string(), "[[[[0,7],4],[[7,8],[6,0]]],[8,1]]");
// now again using .reduce() from the beginning
res2.reduce();
assert_eq!(res, res2);
}
#[test_case("[1,1]\n[2,2]\n[3,3]\n[4,4]" => "[[[[1,1],[2,2]],[3,3]],[4,4]]")]
#[test_case("[1,1]\n[2,2]\n[3,3]\n[4,4]\n[5,5]" => "[[[[3,0],[5,3]],[4,4]],[5,5]]")]
#[test_case("[1,1]\n[2,2]\n[3,3]\n[4,4]\n[5,5]\n[6,6]" => "[[[[5,0],[7,4]],[5,5]],[6,6]]")]
fn test_list_reduction(raw: &str) -> String {
let parsed = parse_input(raw);
add_and_reduce(parsed).unwrap().to_string()
}
test!(part1() == 4140);
2021-12-18 16:18:25 +01:00
/*
bench!(part1() == 0);
bench!(part2() == 0);
bench_input!(Vec::len => 0);
*/
}