2022 - Day 21
This commit is contained in:
parent
3432c50756
commit
36fc884ba6
1579
2022/input/21/input
Normal file
1579
2022/input/21/input
Normal file
File diff suppressed because it is too large
Load Diff
15
2022/input/21/test-1
Normal file
15
2022/input/21/test-1
Normal file
|
@ -0,0 +1,15 @@
|
|||
root: pppw + sjmn
|
||||
dbpl: 5
|
||||
cczh: sllz + lgvd
|
||||
zczc: 2
|
||||
ptdq: humn - dvpt
|
||||
dvpt: 3
|
||||
lfqf: 4
|
||||
humn: 5
|
||||
ljgn: 2
|
||||
sjmn: drzm * dbpl
|
||||
sllz: 4
|
||||
pppw: cczh / lfqf
|
||||
lgvd: ljgn * ptdq
|
||||
drzm: hmdt - zczc
|
||||
hmdt: 32
|
275
2022/src/bin/day21.rs
Normal file
275
2022/src/bin/day21.rs
Normal file
|
@ -0,0 +1,275 @@
|
|||
#![feature(test)]
|
||||
use core::fmt;
|
||||
use std::{collections::HashMap, str::FromStr, borrow::Borrow};
|
||||
|
||||
use anyhow::Result;
|
||||
use aoc::Solver;
|
||||
|
||||
// -- Runners --
|
||||
fn main() -> Result<()> {
|
||||
Day::solve()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_test1() -> Result<()> {
|
||||
Day::test(Day::part1, "test-1", 152)
|
||||
}
|
||||
#[test]
|
||||
fn part1_solution() -> Result<()> {
|
||||
Day::test(Day::part1, "input", 78342931359552)
|
||||
}
|
||||
#[test]
|
||||
fn part2_test1() -> Result<()> {
|
||||
Day::test(Day::part2, "test-1", 301)
|
||||
}
|
||||
#[test]
|
||||
fn part2_solution() -> Result<()> {
|
||||
Day::test(Day::part2, "input", 3296135418820)
|
||||
}
|
||||
|
||||
// Benchmarks
|
||||
extern crate test;
|
||||
#[bench]
|
||||
#[ignore]
|
||||
fn part1_bench(b: &mut test::Bencher) {
|
||||
Day::benchmark(Day::part1, b)
|
||||
}
|
||||
#[bench]
|
||||
#[ignore]
|
||||
fn part2_bench(b: &mut test::Bencher) {
|
||||
Day::benchmark(Day::part2, b)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum Operation {
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
Equality,
|
||||
}
|
||||
|
||||
impl FromStr for Operation {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"+" => Ok(Operation::Add),
|
||||
"-" => Ok(Operation::Subtract),
|
||||
"*" => Ok(Operation::Multiply),
|
||||
"/" => Ok(Operation::Divide),
|
||||
_ => Err(anyhow::anyhow!("Invalid operation: {}", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Operation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Operation::Add => write!(f, "+"),
|
||||
Operation::Subtract => write!(f, "-"),
|
||||
Operation::Multiply => write!(f, "*"),
|
||||
Operation::Divide => write!(f, "/"),
|
||||
Operation::Equality => write!(f, "="),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Expression {
|
||||
Simple(isize),
|
||||
Complex(Box<Expression>, Operation, Box<Expression>),
|
||||
Human,
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
fn evaluate(&self) -> Result<isize, anyhow::Error> {
|
||||
match self {
|
||||
Self::Simple(number) => Ok(*number),
|
||||
Self::Complex(a, op, b) => {
|
||||
let a = a.evaluate()?;
|
||||
let b = b.evaluate()?;
|
||||
match op {
|
||||
Operation::Add => Ok(a + b),
|
||||
Operation::Subtract => Ok(a - b),
|
||||
Operation::Multiply => Ok(a * b),
|
||||
Operation::Divide => Ok(a / b),
|
||||
Operation::Equality => Err(anyhow::anyhow!("Cannot evaluate equality expression"))
|
||||
}
|
||||
},
|
||||
Self::Human => Err(anyhow::anyhow!("Cannot evaluate expression with human"))
|
||||
}
|
||||
}
|
||||
|
||||
// This function assumes a lot about the shape of the expression
|
||||
// 1. Top level is an equality
|
||||
// 2. The side with the human variabl is the left side
|
||||
// 3. We never divide a simple number by a complex expression
|
||||
// Overall this function feels very poorly written/designed
|
||||
// But it works =D
|
||||
fn invert(self) -> Expression {
|
||||
if let Expression::Complex(mut left, Operation::Equality, mut right) = self {
|
||||
loop {
|
||||
match left.clone().borrow() {
|
||||
// Once human the left side only contains human, we are done and return the right side
|
||||
Expression::Human => return *right,
|
||||
Expression::Complex(a, op, b) => {
|
||||
match (a.clone().borrow(), b.clone().borrow()) {
|
||||
(a @ (Expression::Complex(..) | Expression::Human), b @ Expression::Simple(..)) => {
|
||||
// Update left to contain the complex side
|
||||
*left = a.clone();
|
||||
match op {
|
||||
Operation::Add => *right = Expression::Complex(right.clone(), Operation::Subtract, Box::new(b.clone())),
|
||||
Operation::Subtract => *right = Expression::Complex(right.clone(), Operation::Add, Box::new(b.clone())),
|
||||
Operation::Multiply => *right = Expression::Complex(right.clone(), Operation::Divide, Box::new(b.clone())),
|
||||
Operation::Divide => *right = Expression::Complex(right.clone(), Operation::Multiply, Box::new(b.clone())),
|
||||
_ => unreachable!("Did not expect this"),
|
||||
}
|
||||
},
|
||||
(a @ Expression::Simple(..), b @ (Expression::Complex(..) | Expression::Human)) => {
|
||||
// Update left to contain the complex side
|
||||
*left = b.clone();
|
||||
match op {
|
||||
Operation::Add => *right = Expression::Complex(right.clone(), Operation::Subtract, Box::new(a.clone())),
|
||||
Operation::Multiply => *right = Expression::Complex(right.clone(), Operation::Divide, Box::new(a.clone())),
|
||||
Operation::Subtract => *right = Expression::Complex(Box::new(a.clone()), Operation::Subtract, right.clone()),
|
||||
_ => unreachable!("Did not expect this"),
|
||||
}
|
||||
},
|
||||
_ => unreachable!("Unexpected form"),
|
||||
}
|
||||
},
|
||||
_ => unreachable!("Unexpected form to expression")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("Unexpected form!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Expression {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Simple(number) => write!(f, "{number}"),
|
||||
Self::Complex(a, op, b) => {
|
||||
match op {
|
||||
Operation::Equality => write!(f, "{a} {op} {b}"),
|
||||
_ => write!(f, "({a} {op} {b})")
|
||||
}
|
||||
},
|
||||
Self::Human => write!(f, "x"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Action {
|
||||
Number(Expression),
|
||||
Result(String, Operation, String),
|
||||
Human
|
||||
}
|
||||
|
||||
impl Action {
|
||||
fn resolve(&self, map: &HashMap<String, Action>) -> Expression {
|
||||
match self {
|
||||
Action::Number(expr) => expr.clone(),
|
||||
Action::Result(a, op, b) => {
|
||||
let a = map.get(a).unwrap().resolve(map);
|
||||
let b = map.get(b).unwrap().resolve(map);
|
||||
|
||||
if let (Expression::Simple(a), Expression::Simple(b)) = (&a,&b) {
|
||||
match op {
|
||||
Operation::Add => Expression::Simple(a + b),
|
||||
Operation::Subtract => Expression::Simple(a - b),
|
||||
Operation::Multiply => Expression::Simple(a * b),
|
||||
Operation::Divide => Expression::Simple(a / b),
|
||||
Operation::Equality => unreachable!("Only appears in part 2 where at least one of the sides will not be simple")
|
||||
}
|
||||
} else {
|
||||
Expression::Complex(Box::new(a), *op, Box::new(b))
|
||||
}
|
||||
},
|
||||
Action::Human => Expression::Human,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- Solution --
|
||||
pub struct Day;
|
||||
impl aoc::Solver for Day {
|
||||
type Output1 = isize;
|
||||
type Output2 = isize;
|
||||
|
||||
fn day() -> u8 {
|
||||
21
|
||||
}
|
||||
|
||||
fn part1(input: &str) -> Self::Output1 {
|
||||
let map = input
|
||||
.trim()
|
||||
.lines()
|
||||
.map(|line| line.split_once(": ").unwrap())
|
||||
.map(|(name, action)| {
|
||||
let action = if let Ok(number) = action.parse() {
|
||||
Action::Number(Expression::Simple(number))
|
||||
} else {
|
||||
let mut split = action.split(" ");
|
||||
Action::Result(split.next().unwrap().to_owned(), Operation::from_str(split.next().unwrap()).unwrap(), split.next().unwrap().to_owned())
|
||||
};
|
||||
|
||||
(name.to_owned(), action)
|
||||
}).collect::<HashMap<_, _>>();
|
||||
|
||||
// Get the root node
|
||||
let root = map.get("root").unwrap();
|
||||
|
||||
// Build an expression tree
|
||||
let expression = root.resolve(&map);
|
||||
|
||||
// Evaluate the expression
|
||||
expression.evaluate().unwrap()
|
||||
}
|
||||
|
||||
fn part2(input: &str) -> Self::Output2 {
|
||||
let map = input
|
||||
.trim()
|
||||
.lines()
|
||||
.map(|line| line.split_once(": ").unwrap())
|
||||
.map(|(name, action)| {
|
||||
let action = if name == "humn" {
|
||||
Action::Human
|
||||
} else if let Ok(number) = action.parse() {
|
||||
Action::Number(Expression::Simple(number))
|
||||
} else {
|
||||
let mut split = action.split(" ");
|
||||
let a = split.next().unwrap().to_owned();
|
||||
let mut op = Operation::from_str(split.next().unwrap()).unwrap();
|
||||
let b = split.next().unwrap().to_owned();
|
||||
|
||||
if name == "root" {
|
||||
op = Operation::Equality;
|
||||
}
|
||||
|
||||
Action::Result(a, op, b)
|
||||
};
|
||||
|
||||
(name.to_owned(), action)
|
||||
}).collect::<HashMap<_, _>>();
|
||||
|
||||
// Get the root node
|
||||
let root = map.get("root").unwrap();
|
||||
|
||||
// Build an expression tree
|
||||
let expression = root.resolve(&map);
|
||||
|
||||
// Invert the expresion tree to get an expression that gives the value for human and then
|
||||
// evaluate that expression
|
||||
expression.invert().evaluate().unwrap()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user