aoc-rs-2021/src/day_18/model.rs
Jan Christian Grünhage c49814908e implement day 18
2021-12-18 18:08:53 +01:00

259 lines
7.6 KiB
Rust

use std::fmt::Debug;
use std::ops::Deref;
use std::{borrow::Borrow, ops::Add};
#[derive(Clone, PartialEq)]
pub enum SnailfishNumber {
Pair(Box<SnailfishNumber>, Box<SnailfishNumber>),
Regular(isize),
}
impl Debug for SnailfishNumber {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SnailfishNumber::Pair(lhs, rhs) => {
f.write_str("[")?;
Debug::fmt(&lhs, f)?;
f.write_str(",")?;
Debug::fmt(&rhs, f)?;
f.write_str("]")?;
Ok(())
}
SnailfishNumber::Regular(n) => Debug::fmt(n, f),
}
}
}
impl<T> From<T> for SnailfishNumber
where
T: Borrow<Box<SnailfishNumber>>,
{
fn from(s: T) -> Self {
s.borrow().deref().clone()
}
}
impl From<isize> for SnailfishNumber {
fn from(s: isize) -> Self {
Self::Regular(s)
}
}
impl From<&isize> for SnailfishNumber {
fn from(s: &isize) -> Self {
Self::Regular(*s)
}
}
impl<LHS, RHS> From<(LHS, RHS)> for SnailfishNumber
where
LHS: Into<SnailfishNumber>,
RHS: Into<SnailfishNumber>,
{
fn from(s: (LHS, RHS)) -> Self {
Self::Pair(Box::new(s.0.into()), Box::new(s.1.into()))
}
}
//impl From<&SnailfishNumber> for SnailfishNumberIterator {
// fn from(inner: &SnailfishNumber) -> Self {
// SnailfishNumberIterator { inner: inner.clone(), index: 0 }
// }
//}
//
//pub struct SnailfishNumberIterator {
// inner: SnailfishNumber,
// index: usize,
//}
//
//impl Iterator for SnailfishNumberIterator {
// type Item = isize;
//
// fn next(&mut self) -> Option<Self::Item> {
// let next = match self.inner {
// SnailfishNumber::Pair(rhs, lhs) => {
// let rhs = SnailfishNumberIterator::from(rhs.deref());
// let lhs = SnailfishNumberIterator::from(lhs.deref());
// let chain = rhs.chain(lhs);
// chain.nth(self.index)
// },
// SnailfishNumber::Regular(n) => {
// if self.index == 0 {
// Some(n)
// } else {
// None
// }
// }
// };
// if let Some(next) = next {
// self.index += 1;
// Some(next)
// } else {
// None
// }
// }
//}
impl SnailfishNumber {
pub fn regular(&self) -> Option<isize> {
match self {
SnailfishNumber::Regular(n) => Some(*n),
_ => None,
}
}
pub fn magnitude(&self) -> isize {
match self {
SnailfishNumber::Pair(lhs, rhs) => 3 * lhs.magnitude() + 2 * rhs.magnitude(),
SnailfishNumber::Regular(reg) => *reg,
}
}
pub fn reduce(self) -> Self {
let mut new = self;
while let (changed, true) = new.reduce_once() {
new = changed;
}
new
}
pub fn reduce_once(&self) -> (Self, bool) {
if let (_, changed, _, true) = self.explode(0) {
println!("after explode: {:?}", &changed);
(changed, true)
} else if let (changed, true) = self.split() {
println!("after split: {:?}", &changed);
(changed, true)
} else {
(self.clone(), false)
}
}
pub fn split(&self) -> (Self, bool) {
match self {
Self::Regular(n) if n >= &10 => {
let lhs = ((*n as f64) / 2.0).floor() as isize;
let rhs = ((*n as f64) / 2.0).ceil() as isize;
((lhs, rhs).into(), true)
}
Self::Regular(n) => (n.into(), false),
Self::Pair(lhs, rhs) => match lhs.split() {
(lhs, true) => ((lhs, rhs).into(), true),
(_, false) => match rhs.split() {
(rhs, true) => (Self::Pair(lhs.clone(), Box::new(rhs)), true),
(_, false) => (self.clone(), false),
},
},
}
}
pub fn explode(&self, depth: usize) -> (Option<isize>, Self, Option<isize>, bool) {
let new = self.clone();
match new {
SnailfishNumber::Pair(mut lhs, mut rhs) => {
if depth == 4 {
let (lhs, rhs) = (lhs.regular().unwrap(), rhs.regular().unwrap());
(Some(lhs), 0.into(), Some(rhs), true)
} else {
let (left_addition, inner, right_addition, exploded) = lhs.explode(depth + 1);
if exploded {
if let Some(right_addition) = right_addition {
rhs.add_right(right_addition);
}
return (left_addition, (inner, rhs).into(), None, true);
}
let (left_addition, inner, right_addition, exploded) = rhs.explode(depth + 1);
if exploded {
if let Some(left_addition) = left_addition {
lhs.add_left(left_addition);
}
return (None, (lhs, inner).into(), right_addition, true);
}
(None, self.clone(), None, false)
}
}
regular => (None, regular, None, false),
}
}
fn add_left(&mut self, other: isize) {
match self {
Self::Pair(_, rhs) => rhs.add_left(other),
Self::Regular(n) => *n += other,
}
}
fn add_right(&mut self, other: isize) {
match self {
Self::Pair(lhs, _) => lhs.add_right(other),
Self::Regular(n) => *n += other,
}
}
}
impl Add for SnailfishNumber {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::from((self, rhs)).reduce()
}
}
#[cfg(test)]
mod test {
use super::SnailfishNumber;
#[test]
fn multiple_reductions_example() {
let lhs: SnailfishNumber = ((((4, 3), 4), 4), (7, ((8, 4), 9))).into();
let rhs: SnailfishNumber = (1, 1).into();
let result: SnailfishNumber = ((((0, 7), 4), ((7, 8), (6, 0))), (8, 1)).into();
assert_eq!(lhs + rhs, result);
}
#[test]
fn explosion_1() {
let number: SnailfishNumber = (((((9, 8), 1), 2), 3), 4).into();
let result: SnailfishNumber = ((((0, 9), 2), 3), 4).into();
assert_eq!(number.reduce(), result);
}
#[test]
fn explosion_2() {
let number: SnailfishNumber = (7, (6, (5, (4, (3, 2))))).into();
let result: SnailfishNumber = (7, (6, (5, (7, 0)))).into();
assert_eq!(number.reduce(), result);
}
#[test]
fn explosion_3() {
let number: SnailfishNumber = ((6, (5, (4, (3, 2)))), 1).into();
let result: SnailfishNumber = ((6, (5, (7, 0))), 3).into();
assert_eq!(number.reduce(), result);
}
#[test]
fn explosion_4() {
let number: SnailfishNumber = ((3, (2, (1, (7, 3)))), (6, (5, (4, (3, 2))))).into();
let result: SnailfishNumber = ((3, (2, (8, 0))), (9, (5, (4, (3, 2))))).into();
assert_eq!(number.reduce_once(), (result, true));
}
#[test]
fn explosion_5() {
let number: SnailfishNumber = ((3, (2, (8, 0))), (9, (5, (4, (3, 2))))).into();
let result: SnailfishNumber = ((3, (2, (8, 0))), (9, (5, (7, 0)))).into();
assert_eq!(number.reduce(), result);
}
#[test]
fn magnitude() {
let number: SnailfishNumber = (
(((8, 7), (7, 7)), ((8, 6), (7, 7))),
(((0, 7), (6, 6)), (8, 7)),
)
.into();
assert_eq!(number.magnitude(), 3488);
}
}