Day 18: add split and tests
This commit is contained in:
parent
35e85c9737
commit
68cade10ac
@ -1,25 +1,19 @@
|
|||||||
|
#![feature(result_option_inspect)]
|
||||||
#![feature(box_syntax)]
|
#![feature(box_syntax)]
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
extern crate test;
|
extern crate test;
|
||||||
use aoc2021::common::*;
|
use aoc2021::common::*;
|
||||||
use std::{fmt, ops::Add};
|
use std::fmt;
|
||||||
|
|
||||||
const DAY: usize = 18;
|
const DAY: usize = 18;
|
||||||
type Parsed = Vec<Node>;
|
type Parsed = Vec<Node>;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
enum Node {
|
enum Node {
|
||||||
Number(usize),
|
Number(usize),
|
||||||
Pair(Box<(Node, Node)>),
|
Pair(Box<(Node, Node)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add for Node {
|
|
||||||
type Output = Self;
|
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
|
||||||
Node::Pair(box (self, rhs))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Node {
|
impl fmt::Display for Node {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match &self {
|
match &self {
|
||||||
@ -46,15 +40,37 @@ fn parse_node(raw: &str) -> (Node, usize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
enum Reduction {
|
enum Explosion {
|
||||||
None,
|
None,
|
||||||
Partial,
|
Partial,
|
||||||
Full,
|
Full,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node {
|
impl Node {
|
||||||
|
fn reduce(&mut self) {
|
||||||
|
while self.explode() || self.split() {}
|
||||||
|
}
|
||||||
|
|
||||||
fn explode(&mut self) -> bool {
|
fn explode(&mut self) -> bool {
|
||||||
self.explode_inner(0, &mut None, &mut None) != Reduction::None
|
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(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn explode_inner<'a, 'b>(
|
fn explode_inner<'a, 'b>(
|
||||||
@ -62,16 +78,16 @@ impl Node {
|
|||||||
depth: usize,
|
depth: usize,
|
||||||
previous_number: &'b mut Option<&'a mut usize>,
|
previous_number: &'b mut Option<&'a mut usize>,
|
||||||
for_next: &'b mut Option<usize>,
|
for_next: &'b mut Option<usize>,
|
||||||
) -> Reduction {
|
) -> Explosion {
|
||||||
match (self, &for_next) {
|
match (self, &for_next) {
|
||||||
(Node::Number(n), Some(x)) => {
|
(Node::Number(n), Some(x)) => {
|
||||||
*n += x;
|
*n += x;
|
||||||
*for_next = None;
|
*for_next = None;
|
||||||
Reduction::Full
|
Explosion::Full
|
||||||
}
|
}
|
||||||
(Node::Number(n), None) => {
|
(Node::Number(n), None) => {
|
||||||
*previous_number = Some(n);
|
*previous_number = Some(n);
|
||||||
Reduction::None
|
Explosion::None
|
||||||
}
|
}
|
||||||
(s @ Node::Pair(_), _) if depth == 4 => {
|
(s @ Node::Pair(_), _) if depth == 4 => {
|
||||||
let (left, right) = s.number_pair_or_panic();
|
let (left, right) = s.number_pair_or_panic();
|
||||||
@ -80,11 +96,11 @@ impl Node {
|
|||||||
}
|
}
|
||||||
*for_next = Some(*right);
|
*for_next = Some(*right);
|
||||||
*s = Node::Number(0);
|
*s = Node::Number(0);
|
||||||
Reduction::Partial
|
Explosion::Partial
|
||||||
}
|
}
|
||||||
(Node::Pair(p), _) => {
|
(Node::Pair(p), _) => {
|
||||||
let left = p.0.explode_inner(depth + 1, previous_number, for_next);
|
let left = p.0.explode_inner(depth + 1, previous_number, for_next);
|
||||||
if left != Reduction::Full {
|
if left != Explosion::Full {
|
||||||
p.1.explode_inner(depth + 1, previous_number, for_next)
|
p.1.explode_inner(depth + 1, previous_number, for_next)
|
||||||
} else {
|
} else {
|
||||||
left
|
left
|
||||||
@ -109,7 +125,21 @@ impl Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn part1(parsed: &Parsed) -> usize {
|
fn part1(parsed: &Parsed) -> usize {
|
||||||
unimplemented!()
|
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
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(parsed: &Parsed) -> usize {
|
fn part2(parsed: &Parsed) -> usize {
|
||||||
@ -131,7 +161,7 @@ mod tests {
|
|||||||
const TEST_INPUT_SINGLE_ADDITION: &str = "[[[[4,3],4],4],[7,[[8,4],9]]]
|
const TEST_INPUT_SINGLE_ADDITION: &str = "[[[[4,3],4],4],[7,[[8,4],9]]]
|
||||||
[1,1]";
|
[1,1]";
|
||||||
|
|
||||||
const TEST_INPUT: &str = "[1,2]
|
const OTHER_TEST_INPUT: &str = "[1,2]
|
||||||
[[1,2],3]
|
[[1,2],3]
|
||||||
[9,[8,7]]
|
[9,[8,7]]
|
||||||
[[1,9],[8,5]]
|
[[1,9],[8,5]]
|
||||||
@ -139,6 +169,17 @@ mod tests {
|
|||||||
[[[9,[3,8]],[[0,9],6]],[[[3,7],[4,9]],3]]
|
[[[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]]]]";
|
[[[[1,3],[5,3]],[[1,3],[8,7]]],[[[4,9],[6,9]],[[8,2],[7,3]]]]";
|
||||||
|
|
||||||
|
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]]]";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_example_parsing() {
|
fn test_example_parsing() {
|
||||||
let [first, second]: [Node; 2] = parse_input(TEST_INPUT_SINGLE_ADDITION).try_into().unwrap();
|
let [first, second]: [Node; 2] = parse_input(TEST_INPUT_SINGLE_ADDITION).try_into().unwrap();
|
||||||
@ -154,7 +195,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_node_display() {
|
fn test_node_display() {
|
||||||
for (actual, expected) in parse_input(TEST_INPUT).into_iter().zip(TEST_INPUT.lines()) {
|
for (actual, expected) in parse_input(OTHER_TEST_INPUT).into_iter().zip(OTHER_TEST_INPUT.lines()) {
|
||||||
assert_eq!(expected, actual.to_string());
|
assert_eq!(expected, actual.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,9 +211,56 @@ mod tests {
|
|||||||
i.to_string()
|
i.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
test!(part1() == 0);
|
|
||||||
test!(part2() == 0);
|
|
||||||
bench!(part1() == 0);
|
bench!(part1() == 0);
|
||||||
bench!(part2() == 0);
|
bench!(part2() == 0);
|
||||||
bench_input!(Vec::len => 0);
|
bench_input!(Vec::len => 0);
|
||||||
|
Loading…
Reference in New Issue
Block a user