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