solution: day7
This commit is contained in:
parent
0000014030
commit
00000150f0
|
@ -0,0 +1,187 @@
|
|||
use hashbrown::{HashMap, HashSet};
|
||||
use itertools::Itertools;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use Gate::*;
|
||||
use Operand::*;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Hash)]
|
||||
enum Operand {
|
||||
Address(String),
|
||||
Literal(u16),
|
||||
}
|
||||
|
||||
impl Operand {
|
||||
fn get(&self, map: &HashMap<String, u16>) -> u16 {
|
||||
match self {
|
||||
Address(x) => *map.get(x).unwrap(),
|
||||
Literal(x) => *x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Operand {
|
||||
fn from(value: &str) -> Self {
|
||||
match value.parse() {
|
||||
Ok(x) => Literal(x),
|
||||
Err(_) => Address(value.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Hash)]
|
||||
enum Gate {
|
||||
Write(Operand, Operand),
|
||||
And(Operand, Operand, Operand),
|
||||
Or(Operand, Operand, Operand),
|
||||
LShift(Operand, u8, Operand),
|
||||
RShift(Operand, u8, Operand),
|
||||
Not(Operand, Operand),
|
||||
}
|
||||
|
||||
impl Gate {
|
||||
fn can_evaluate(&self, map: &HashMap<String, u16>) -> bool {
|
||||
match self {
|
||||
Write(Address(x), _) => map.contains_key(x),
|
||||
Not(Address(x), _) => map.contains_key(x),
|
||||
LShift(Address(x), _, _) => map.contains_key(x),
|
||||
RShift(Address(x), _, _) => map.contains_key(x),
|
||||
And(x, y, _) | Or(x, y, _) => {
|
||||
let mut allow = true;
|
||||
if let Address(xx) = x {
|
||||
allow &= map.contains_key(xx);
|
||||
};
|
||||
if let Address(yy) = y {
|
||||
allow &= map.contains_key(yy);
|
||||
};
|
||||
allow
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Gate {
|
||||
fn from(value: &str) -> Self {
|
||||
lazy_static! {
|
||||
static ref RE_SIGNAL: Regex = Regex::new(r"^(\w*) -> (\w*)$").unwrap();
|
||||
static ref RE_NOT: Regex = Regex::new(r"NOT (\w*) -> (\w*)").unwrap();
|
||||
static ref RE_GATE: Regex =
|
||||
Regex::new(r"^(\w*) (AND|OR|LSHIFT|RSHIFT) (\w*) -> (\w*)$").unwrap();
|
||||
}
|
||||
|
||||
if let Some(cap) = RE_SIGNAL.captures(value) {
|
||||
return Write(Operand::from(&cap[1]), Operand::from(&cap[2]));
|
||||
};
|
||||
|
||||
if let Some(cap) = RE_NOT.captures(value) {
|
||||
return Not(Operand::from(&cap[1]), Operand::from(&cap[2]));
|
||||
};
|
||||
|
||||
if let Some(cap) = RE_GATE.captures(value) {
|
||||
match &cap[2] {
|
||||
"AND" => {
|
||||
return And(
|
||||
Operand::from(&cap[1]),
|
||||
Operand::from(&cap[3]),
|
||||
Operand::from(&cap[4]),
|
||||
)
|
||||
}
|
||||
"OR" => {
|
||||
return Or(
|
||||
Operand::from(&cap[1]),
|
||||
Operand::from(&cap[3]),
|
||||
Operand::from(&cap[4]),
|
||||
)
|
||||
}
|
||||
"LSHIFT" => {
|
||||
return LShift(
|
||||
Operand::from(&cap[1]),
|
||||
cap[3].parse().unwrap(),
|
||||
Operand::from(&cap[4]),
|
||||
);
|
||||
}
|
||||
"RSHIFT" => {
|
||||
return RShift(
|
||||
Operand::from(&cap[1]),
|
||||
cap[3].parse().unwrap(),
|
||||
Operand::from(&cap[4]),
|
||||
);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn part_one(input: &str) -> Option<u32> {
|
||||
let mut map: HashMap<String, u16> = HashMap::new();
|
||||
let mut set: HashSet<Gate> = HashSet::new();
|
||||
for line in input.trim().split('\n') {
|
||||
set.insert(Gate::from(line));
|
||||
}
|
||||
while !set.is_empty() {
|
||||
for gate in set.drain_filter(|x| x.can_evaluate(&map)).collect_vec() {
|
||||
match gate {
|
||||
Write(a, Address(b)) => map.insert(b.to_string(), a.get(&map)),
|
||||
And(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) & b.get(&map)),
|
||||
Or(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) | b.get(&map)),
|
||||
LShift(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) << b),
|
||||
RShift(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) >> b),
|
||||
Not(a, Address(b)) => map.insert(b.to_string(), !a.get(&map)),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
}
|
||||
Some(*map.get("a").unwrap() as u32)
|
||||
}
|
||||
pub fn part_two(input: &str) -> Option<u32> {
|
||||
let mut map: HashMap<String, u16> = HashMap::new();
|
||||
let mut set: HashSet<Gate> = HashSet::new();
|
||||
for line in input.trim().split('\n') {
|
||||
let g = Gate::from(line);
|
||||
if let Write(a, Address(b)) = g {
|
||||
if b == "b" {
|
||||
map.insert("b".to_string(), 46065);
|
||||
} else {
|
||||
set.insert(Write(a, Address(b)));
|
||||
}
|
||||
} else {
|
||||
set.insert(g);
|
||||
}
|
||||
}
|
||||
while !set.is_empty() {
|
||||
for gate in set.drain_filter(|x| x.can_evaluate(&map)).collect_vec() {
|
||||
match gate {
|
||||
Write(a, Address(b)) => map.insert(b.to_string(), a.get(&map)),
|
||||
And(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) & b.get(&map)),
|
||||
Or(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) | b.get(&map)),
|
||||
LShift(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) << b),
|
||||
RShift(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) >> b),
|
||||
Not(a, Address(b)) => map.insert(b.to_string(), !a.get(&map)),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
}
|
||||
Some(*map.get("a").unwrap() as u32)
|
||||
}
|
||||
fn main() {
|
||||
let input = &aoc::read_file("inputs", 7);
|
||||
aoc::solve!(1, part_one, input);
|
||||
aoc::solve!(2, part_two, input);
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_part_one() {
|
||||
let input = aoc::read_file("test_inputs", 7);
|
||||
assert_eq!(part_one(&input), Some(65533));
|
||||
}
|
||||
#[test]
|
||||
fn test_part_two() {
|
||||
let input = aoc::read_file("test_inputs", 7);
|
||||
assert_eq!(part_two(&input), Some(19470));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
123 -> x
|
||||
456 -> y
|
||||
x AND y -> d
|
||||
x OR y -> e
|
||||
x LSHIFT 2 -> f
|
||||
y RSHIFT 2 -> g
|
||||
NOT x -> h
|
||||
NOT y -> i
|
||||
2 -> b
|
||||
NOT b -> a
|
Loading…
Reference in New Issue