use std::io; | |
use std::collections::HashMap; | |
use std::collections::VecDeque; | |
#[derive(Debug, PartialEq, Copy, Clone)] | |
enum Command { | |
Assign { bot: usize, value: usize }, | |
Give { | |
src: usize, // from which bot? | |
dst1: usize, // to which bot/bin? | |
dst1_type: ActorType, // bot or bin? | |
dst2: usize, // ditto | |
dst2_type: ActorType // ditto | |
} | |
} | |
#[derive(Debug, PartialEq, Copy, Clone)] | |
enum ActorType { | |
Bot, | |
OutputBin | |
} | |
impl ActorType { | |
fn from_str(s: &str) -> ActorType { | |
if s == "bot" { | |
ActorType::Bot | |
} else if s == "output" { | |
ActorType::OutputBin | |
} else { | |
panic!("unknown actor type: {}", s) | |
} | |
} | |
} | |
struct Simulation { | |
bots: HashMap<usize, Vec<usize>>, // The chips held by a bot | |
bins: HashMap<usize, Vec<usize>>, // The chips held by a bin | |
cmds: HashMap<usize, Command>, // The give command for each bot | |
queue: VecDeque<usize>, // The bots left to be handled | |
handler: Option<usize> // Which bot handled 17 and 61? | |
} | |
impl Simulation { | |
fn execute(&mut self, c: &Command) { | |
match *c { | |
Command::Assign { bot, value } => { | |
let v = self.bots.entry(bot).or_insert(vec![]); | |
v.push(value); | |
if v.len() == 2 { | |
self.queue.push_back(bot); | |
} | |
} | |
Command::Give | |
{ src, dst1, dst1_type, dst2, dst2_type } => { | |
let min = *self.bots.get(&src).unwrap().iter().min().unwrap(); | |
let max = *self.bots.get(&src).unwrap().iter().max().unwrap(); | |
if min == 17 && max == 61 { | |
self.handler = Some(src); | |
} | |
self.give(dst1, dst1_type, min); | |
self.give(dst2, dst2_type, max); | |
let v = self.bots.entry(src).or_insert(vec![]); | |
v.clear(); | |
} | |
} | |
} | |
fn give(&mut self, dst: usize, ty: ActorType, val: usize) { | |
match ty { | |
ActorType::Bot => { | |
let v = self.bots.entry(dst).or_insert(vec![]); | |
v.push(val); | |
if v.len() == 2 { | |
self.queue.push_back(dst); | |
} | |
} | |
ActorType::OutputBin => { | |
let v = self.bins.entry(dst).or_insert(vec![]); | |
v.push(val); | |
} | |
} | |
} | |
fn simulate(&mut self) { | |
while let Some(bot) = self.queue.pop_front() { | |
let cmd = *self.cmds.get(&bot).unwrap(); | |
self.execute(&cmd); | |
} | |
} | |
} | |
fn parse(line: &str) -> Command { | |
let words: Vec<&str> = line.split_whitespace().collect(); | |
if words[0] == "value" { | |
let value = words[1].parse::<usize>().expect("usize"); | |
let bot = words[5].parse::<usize>().expect("usize"); | |
return Command::Assign { bot: bot, value: value }; | |
} else if words[0] == "bot" { | |
let src = words[1].parse::<usize>().expect("usize"); | |
let dst1 = words[6].parse::<usize>().expect("usize"); | |
let dst1_type = ActorType::from_str(words[5]); | |
let dst2 = words[11].parse::<usize>().expect("usize"); | |
let dst2_type = ActorType::from_str(words[10]); | |
return Command::Give { | |
src: src, | |
dst1: dst1, | |
dst1_type: dst1_type, | |
dst2: dst2, | |
dst2_type: dst2_type | |
}; | |
} else { | |
panic!("unrecognized command: {:?}", line) | |
} | |
} | |
fn solve_b(sim: &Simulation) -> usize { | |
sim.bins.get(&0).unwrap()[0] * | |
sim.bins.get(&1).unwrap()[0] * | |
sim.bins.get(&2).unwrap()[0] | |
} | |
#[cfg(not(test))] | |
fn main() { | |
let stdin = io::stdin(); | |
let mut buf = String::new(); | |
let mut sim = Simulation { | |
bots: HashMap::new(), | |
bins: HashMap::new(), | |
cmds: HashMap::new(), | |
queue: VecDeque::new(), | |
handler: None | |
}; | |
while stdin.read_line(&mut buf).unwrap() > 0 { | |
let cmd = parse(&buf); | |
match cmd { | |
Command::Assign { .. } => { | |
sim.execute(&cmd); | |
} | |
Command::Give { src, .. } => { | |
sim.cmds.insert(src, cmd); | |
} | |
} | |
buf.clear(); | |
} | |
sim.simulate(); | |
println!("A: {:?}", sim.handler); | |
println!("B: {}", solve_b(&sim)); | |
} | |
#[cfg(test)] | |
mod test { | |
#[test] | |
fn test_parse() { | |
use super::{parse, Command, ActorType}; | |
let x = parse("bot 127 gives low to output 1 and high to bot 180"); | |
let targ = Command::Give { | |
src: 127, dst1: 1, dst2: 180, | |
dst1_type: ActorType::OutputBin, | |
dst2_type: ActorType::Bot | |
}; | |
assert_eq!(x, targ); | |
let x = parse("bot 127 gives low to bot 1 and high to bot 180"); | |
let targ = Command::Give { | |
src: 127, dst1: 1, dst2: 180, | |
dst1_type: ActorType::Bot, | |
dst2_type: ActorType::Bot | |
}; | |
assert_eq!(x, targ); | |
let x = parse("value 123 goes to bot 999"); | |
let targ = Command::Assign { bot: 999, value: 123 }; | |
assert_eq!(x, targ); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment