1
0
Fork 0

day(21): add solution

Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
Matej Focko 2023-07-02 22:55:51 +02:00
parent 05f9ffa320
commit ebe39683c7
Signed by: mfocko
GPG key ID: 7C47D46246790496
2 changed files with 275 additions and 0 deletions

15
samples/day21.txt Normal file
View 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

260
src/bin/day21.rs Normal file
View file

@ -0,0 +1,260 @@
use std::{collections::HashMap, ops::Neg, str::FromStr};
use aoc_2022::*;
type Input = Riddle;
type Output = i64;
#[derive(Debug, Clone)]
enum Operation {
Add,
Sub,
Mul,
Div,
}
impl Operation {
fn apply(&self, l: i64, r: i64) -> i64 {
match self {
Operation::Add => l + r,
Operation::Sub => l - r,
Operation::Mul => l * r,
Operation::Div => l / r,
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
enum Direction {
#[default]
None,
Left,
Right,
}
impl Neg for Direction {
type Output = Direction;
fn neg(self) -> Self::Output {
match self {
Direction::None => unreachable!(),
Direction::Left => Direction::Right,
Direction::Right => Direction::Left,
}
}
}
impl Direction {
fn next(&self) -> Direction {
match self {
Direction::None => Direction::Left,
Direction::Left => Direction::Right,
Direction::Right => unreachable!(),
}
}
}
#[derive(Debug, Clone)]
struct Expr {
left: String,
op: Operation,
right: String,
}
impl Expr {
fn get(&self, d: Direction) -> String {
match d {
Direction::Left => self.left.clone(),
Direction::Right => self.right.clone(),
Direction::None => todo!(),
}
}
fn solve(&self, d: Direction, x: i64, other: i64) -> Output {
match self.op {
Operation::Add => x - other,
Operation::Sub => match d {
Direction::Left => x + other,
Direction::Right => other - x,
_ => unreachable!(),
},
Operation::Mul => x / other,
Operation::Div => match d {
Direction::Left => x * other,
Direction::Right => other / x,
_ => unreachable!(),
},
}
}
}
#[derive(Debug, Clone)]
struct Riddle {
precomputed: HashMap<String, i64>,
expressions: HashMap<String, Expr>,
}
impl FromStr for Riddle {
type Err = Report;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let mut precomputed = HashMap::new();
let mut expressions = HashMap::new();
for parts in s
.lines()
.map(|line| line.split_ascii_whitespace().collect_vec())
{
let node = parts[0].strip_suffix(':').unwrap().to_string();
match parts.len() {
2 => {
let c: Output = parts[1].parse().unwrap();
precomputed.insert(node, c);
}
4 => {
let left = parts[1].to_string();
let right = parts[3].to_string();
let op = match parts[2] {
"+" => Operation::Add,
"-" => Operation::Sub,
"*" => Operation::Mul,
"/" => Operation::Div,
_ => return Err(eyre!("invalid operation")),
};
expressions.insert(node, Expr { left, right, op });
}
_ => return Err(eyre!("invalid count of parts")),
}
}
Ok(Riddle {
precomputed,
expressions,
})
}
}
#[derive(Default)]
enum Color {
#[default]
Unvisited,
Open,
Closed,
}
impl Riddle {
fn compute(&mut self, wanted_node: &str) {
let mut stack = vec![wanted_node];
while let Some(node) = stack.pop() {
if self.precomputed.get(node).is_some() {
continue;
}
let expr = self.expressions.get(node).unwrap();
if self.precomputed.get(&expr.left).is_none()
|| self.precomputed.get(&expr.right).is_none()
{
stack.push(node);
if self.precomputed.get(&expr.left).is_none() {
stack.push(&expr.left);
}
if self.precomputed.get(&expr.right).is_none() {
stack.push(&expr.right);
}
continue;
}
let l = *self.precomputed.get(&expr.left).unwrap();
let r = *self.precomputed.get(&expr.right).unwrap();
let res = expr.op.apply(l, r);
self.precomputed.insert(node.to_string(), res);
}
}
fn dfs(&self, seeked: &str) -> Vec<(String, Direction)> {
let mut state: HashMap<String, Color> = HashMap::new();
let mut path = vec![("root".to_string(), Direction::default())];
while !path.is_empty() && path.last().unwrap().0.as_str() != seeked {
let (node, direction) = path.pop().unwrap();
if direction == Direction::None {
state.insert(node.clone(), Color::Open);
}
match direction {
Direction::Right => {
state.insert(node.clone(), Color::Closed);
}
d => {
let next_d = d.next();
path.push((node.clone(), next_d));
if let Some(expr) = self.expressions.get(node.as_str()) {
path.push((expr.get(next_d), Direction::default()));
}
}
}
}
path
}
fn get_other(&self, node: &str, direction: Direction) -> i64 {
*self
.precomputed
.get(&self.expressions.get(node).unwrap().get(-direction))
.unwrap()
}
fn reverse(&self, path: &[(String, Direction)]) -> Output {
let mut wanted = self.get_other("root", path.first().unwrap().1);
for (node, direction) in path.iter().skip(1) {
let expr = self.expressions.get(node).unwrap();
let other = self.get_other(node, *direction);
wanted = expr.solve(*direction, wanted, other);
}
wanted
}
}
struct Day21;
impl Solution<Input, Output> for Day21 {
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
file_to_string(pathname).parse().unwrap()
}
fn part_1(input: &Input) -> Output {
let mut riddle = input.clone();
riddle.compute("root");
*riddle.precomputed.get("root").unwrap()
}
fn part_2(input: &Input) -> Output {
let mut riddle = input.clone();
riddle.compute("root");
let mut path = riddle.dfs("humn");
path.pop();
// debug!("Path: {:?}", path);
riddle.reverse(&path)
}
}
fn main() -> Result<()> {
Day21::main()
}
test_sample!(day_21, Day21, 152, 301);