day(21): add solution
Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
parent
05f9ffa320
commit
ebe39683c7
2 changed files with 275 additions and 0 deletions
15
samples/day21.txt
Normal file
15
samples/day21.txt
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
|
260
src/bin/day21.rs
Normal file
260
src/bin/day21.rs
Normal 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);
|
Loading…
Reference in a new issue