From 634179bac5de8e11950ec928ea11d518b8c00069 Mon Sep 17 00:00:00 2001 From: Alpha Chen Date: Thu, 5 Jan 2017 07:47:31 -0800 Subject: [PATCH] [2016][rust][12.1] Break out Assembunny into a module --- 2016/rust/src/assembunny.rs | 243 +++++++++++++++++++++++++++++++++++ 2016/rust/src/day_12.rs | 244 +----------------------------------- 2016/rust/src/lib.rs | 1 + 3 files changed, 245 insertions(+), 243 deletions(-) create mode 100644 2016/rust/src/assembunny.rs diff --git a/2016/rust/src/assembunny.rs b/2016/rust/src/assembunny.rs new file mode 100644 index 0000000..0013bb3 --- /dev/null +++ b/2016/rust/src/assembunny.rs @@ -0,0 +1,243 @@ +use std::collections::HashMap; +use std::str; + +use errors::*; + +pub struct Assembunny { + pub registers: HashMap, + pub instructions: Instructions, +} + +impl Assembunny { + fn value>(&self, v: V) -> isize { + let v: Variable = v.into(); + match v { + Variable::Register(r) => { + self.registers.get(&r).cloned().unwrap_or(0) + } + Variable::Value(i) => i, + } + } +} + +impl Iterator for Assembunny { + type Item = HashMap; + fn next(&mut self) -> Option> { + let pc = self.value(Register::PC) as usize; + let instruction = match self.instructions.0.get(pc) { + Some(i) => i, + None => { + return None; + } + }; + + match *instruction { + Instruction::Cpy(v, r) => { + let value = self.value(v); + self.registers.insert(r, value); + *self.registers.entry(Register::PC).or_insert(0) += 1; + } + Instruction::Inc(r) => { + *self.registers.entry(r).or_insert(0) += 1; + *self.registers.entry(Register::PC).or_insert(0) += 1; + } + Instruction::Dec(r) => { + *self.registers.entry(r).or_insert(0) -= 1; + *self.registers.entry(Register::PC).or_insert(0) += 1; + } + Instruction::Jnz(v, i) => { + let delta = if self.value(v) == 0 { 1 } else { i }; + let pc = self.value(Register::PC) + delta; + self.registers.insert(Register::PC, pc); + } + } + + Some(self.registers.clone()) + } +} + +pub struct Instructions(Vec); + +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub enum Register { + PC, + A, + B, + C, + D, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +enum Instruction { + Cpy(Variable, Register), + Inc(Register), + Dec(Register), + Jnz(Variable, isize), +} + +#[derive(Clone, Copy, Debug, PartialEq)] +enum Variable { + Register(Register), + Value(isize), +} + +impl From for Variable { + fn from(r: Register) -> Self { + Variable::Register(r) + } +} + +// Parsing + +impl str::FromStr for Instructions { + type Err = Error; + fn from_str(s: &str) -> Result { + s.lines() + .map(|line| line.parse()) + .collect::>>() + .map(Instructions) + } +} + +impl str::FromStr for Register { + type Err = Error; + fn from_str(s: &str) -> Result { + match s { + "a" => Ok(Register::A), + "b" => Ok(Register::B), + "c" => Ok(Register::C), + "d" => Ok(Register::D), + _ => Err(format!("invalid register '{}'", s).into()), + } + } +} + +impl str::FromStr for Instruction { + type Err = Error; + fn from_str(s: &str) -> Result { + let mut tokens = s.split_whitespace(); + match tokens.next() { + Some("cpy") => { + let v = tokens.read_variable()?; + let r = tokens.read_register()?; + Ok(Instruction::Cpy(v, r)) + } + Some("inc") => { + let r = tokens.read_register()?; + Ok(Instruction::Inc(r)) + } + Some("dec") => { + let r = tokens.read_register()?; + Ok(Instruction::Dec(r)) + } + Some("jnz") => { + let var = tokens.read_variable()?; + let val = tokens.read_value()?; + Ok(Instruction::Jnz(var, val)) + } + Some(inst) => Err(format!("invalid instruction '{}'", inst).into()), + None => Err("no instruction".into()), + } + } +} + +impl str::FromStr for Variable { + type Err = Error; + fn from_str(s: &str) -> Result { + s.parse::() + .map(Variable::Register) + .or_else(|_| s.parse::().map(Variable::Value)) + .map_err(|_| format!("invalid variable '{}'", s).into()) + } +} + +trait SplitWhitespaceExt { + fn read_variable(&mut self) -> Result; + fn read_register(&mut self) -> Result; + fn read_value(&mut self) -> Result; +} + +impl<'a> SplitWhitespaceExt for str::SplitWhitespace<'a> { + fn read_variable(&mut self) -> Result { + self.next() + .ok_or("missing variable".into()) + .and_then(|v| v.parse::()) + } + + fn read_register(&mut self) -> Result { + self.next() + .ok_or("missing register".into()) + .and_then(|v| v.parse::()) + } + + fn read_value(&mut self) -> Result { + self.next() + .ok_or("missing value".into()) + .and_then(|v| v.parse::().chain_err(|| "")) + } +} + +#[cfg(test)] +mod tests { + use super::{Assembunny, Instructions, Instruction, Register, Variable}; + use std::collections::HashMap; + use std::str::FromStr; + + #[test] + fn test_assembunny() { + let instructions: Instructions = "cpy 41 a +inc a +inc a +dec a +jnz a 2 +dec a" + .parse() + .unwrap(); + let mut assembunny = Assembunny { + registers: HashMap::new(), + instructions: instructions, + }; + + let registers = assembunny.next().unwrap(); + assert_eq!(registers.get(&Register::A), Some(&41)); + assert_eq!(registers.get(&Register::B), None); + + let registers = assembunny.next().unwrap(); + assert_eq!(registers.get(&Register::A), Some(&42)); + assert_eq!(registers.get(&Register::C), None); + + let registers = assembunny.last().unwrap(); + assert_eq!(registers.get(&Register::A), Some(&42)); + assert_eq!(registers.get(&Register::PC), Some(&6)); + } + + #[test] + fn test_instructions_from_str() { + let i: Instructions = "cpy 41 a +inc a +inc a +dec a +jnz a 2 +dec a" + .parse() + .unwrap(); + assert_eq!(i.0.len(), 6); + assert_eq!(i.0[0], Instruction::from_str("cpy 41 a").unwrap()); + } + + #[test] + fn test_instruction_from_str() { + assert!(Instruction::from_str("").is_err()); + assert!(Instruction::from_str("omg").is_err()); + assert!(Instruction::from_str("inc 5").is_err()); + + assert_eq!(Instruction::from_str("cpy 41 a").unwrap(), + Instruction::Cpy(Variable::Value(41), Register::A)); + assert_eq!(Instruction::from_str("inc a").unwrap(), + Instruction::Inc(Register::A)); + assert_eq!(Instruction::from_str("dec b").unwrap(), + Instruction::Dec(Register::B)); + assert_eq!(Instruction::from_str("jnz c 2").unwrap(), + Instruction::Jnz(Variable::Register(Register::C), 2)); + } +} diff --git a/2016/rust/src/day_12.rs b/2016/rust/src/day_12.rs index b14f9e0..080ab4d 100644 --- a/2016/rust/src/day_12.rs +++ b/2016/rust/src/day_12.rs @@ -1,6 +1,4 @@ -use std::collections::HashMap; -use std::str; - +use assembunny::*; use errors::*; pub fn solve(input: &str) -> Result { @@ -12,243 +10,3 @@ pub fn solve(input: &str) -> Result { let a = registers.get(&Register::A).ok_or("")?; Ok(a.to_string()) } - -struct Assembunny { - registers: HashMap, - instructions: Instructions, -} - -impl Assembunny { - fn new(instructions: Instructions) -> Self { - Assembunny { - registers: HashMap::new(), - instructions: instructions, - } - } - - fn value>(&self, v: V) -> isize { - let v: Variable = v.into(); - match v { - Variable::Register(r) => self.registers.get(&r).cloned().unwrap_or(0), - Variable::Value(i) => i, - } - } -} - -impl Iterator for Assembunny { - type Item = HashMap; - fn next(&mut self) -> Option> { - let pc = self.value(Register::PC) as usize; - let instruction = match self.instructions.0.get(pc) { - Some(i) => i, - None => { - return None; - } - }; - - match *instruction { - Instruction::Cpy(v, r) => { - let value = self.value(v); - self.registers.insert(r, value); - *self.registers.entry(Register::PC).or_insert(0) += 1; - } - Instruction::Inc(r) => { - *self.registers.entry(r).or_insert(0) += 1; - *self.registers.entry(Register::PC).or_insert(0) += 1; - } - Instruction::Dec(r) => { - *self.registers.entry(r).or_insert(0) -= 1; - *self.registers.entry(Register::PC).or_insert(0) += 1; - } - Instruction::Jnz(v, i) => { - let delta = if self.value(v) == 0 { 1 } else { i }; - let pc = self.value(Register::PC) + delta; - self.registers.insert(Register::PC, pc); - } - } - - Some(self.registers.clone()) - } -} - -struct Instructions(Vec); - -#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] -enum Register { - PC, - A, - B, - C, - D, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -enum Instruction { - Cpy(Variable, Register), - Inc(Register), - Dec(Register), - Jnz(Variable, isize), -} - -#[derive(Clone, Copy, Debug, PartialEq)] -enum Variable { - Register(Register), - Value(isize), -} - -impl From for Variable { - fn from(r: Register) -> Self { - Variable::Register(r) - } -} - -// Parsing - -impl str::FromStr for Instructions { - type Err = Error; - fn from_str(s: &str) -> Result { - s.lines() - .map(|line| line.parse()) - .collect::>>() - .map(Instructions) - } -} - -impl str::FromStr for Register { - type Err = Error; - fn from_str(s: &str) -> Result { - match s { - "a" => Ok(Register::A), - "b" => Ok(Register::B), - "c" => Ok(Register::C), - "d" => Ok(Register::D), - _ => Err(format!("invalid register '{}'", s).into()), - } - } -} - -impl str::FromStr for Instruction { - type Err = Error; - fn from_str(s: &str) -> Result { - let mut tokens = s.split_whitespace(); - match tokens.next() { - Some("cpy") => { - let v = tokens.read_variable()?; - let r = tokens.read_register()?; - Ok(Instruction::Cpy(v, r)) - } - Some("inc") => { - let r = tokens.read_register()?; - Ok(Instruction::Inc(r)) - } - Some("dec") => { - let r = tokens.read_register()?; - Ok(Instruction::Dec(r)) - } - Some("jnz") => { - let var = tokens.read_variable()?; - let val = tokens.read_value()?; - Ok(Instruction::Jnz(var, val)) - } - Some(inst) => Err(format!("invalid instruction '{}'", inst).into()), - None => Err("no instruction".into()), - } - } -} - -impl str::FromStr for Variable { - type Err = Error; - fn from_str(s: &str) -> Result { - s.parse::() - .map(Variable::Register) - .or_else(|_| s.parse::().map(Variable::Value)) - .map_err(|_| format!("invalid variable '{}'", s).into()) - } -} - -trait SplitWhitespaceExt { - fn read_variable(&mut self) -> Result; - fn read_register(&mut self) -> Result; - fn read_value(&mut self) -> Result; -} - -impl<'a> SplitWhitespaceExt for str::SplitWhitespace<'a> { - fn read_variable(&mut self) -> Result { - self.next() - .ok_or("missing variable".into()) - .and_then(|v| v.parse::()) - } - - fn read_register(&mut self) -> Result { - self.next() - .ok_or("missing register".into()) - .and_then(|v| v.parse::()) - } - - fn read_value(&mut self) -> Result { - self.next() - .ok_or("missing value".into()) - .and_then(|v| v.parse::().chain_err(|| "")) - } -} - -#[cfg(test)] -mod tests { - use super::{Assembunny, Instructions, Instruction, Register, Variable}; - use std::str::FromStr; - - #[test] - fn test_assembunny() { - let instructions: Instructions = "cpy 41 a -inc a -inc a -dec a -jnz a 2 -dec a" - .parse() - .unwrap(); - let mut assembunny = Assembunny::new(instructions); - - let registers = assembunny.next().unwrap(); - assert_eq!(registers.get(&Register::A), Some(&41)); - assert_eq!(registers.get(&Register::B), None); - - let registers = assembunny.next().unwrap(); - assert_eq!(registers.get(&Register::A), Some(&42)); - assert_eq!(registers.get(&Register::C), None); - - let registers = assembunny.last().unwrap(); - assert_eq!(registers.get(&Register::A), Some(&42)); - assert_eq!(registers.get(&Register::PC), Some(&6)); - } - - #[test] - fn test_instructions_from_str() { - let i: Instructions = "cpy 41 a -inc a -inc a -dec a -jnz a 2 -dec a" - .parse() - .unwrap(); - assert_eq!(i.0.len(), 6); - assert_eq!(i.0[0], Instruction::from_str("cpy 41 a").unwrap()); - } - - #[test] - fn test_instruction_from_str() { - assert!(Instruction::from_str("").is_err()); - assert!(Instruction::from_str("omg").is_err()); - assert!(Instruction::from_str("inc 5").is_err()); - - assert_eq!(Instruction::from_str("cpy 41 a").unwrap(), - Instruction::Cpy(Variable::Value(41), Register::A)); - assert_eq!(Instruction::from_str("inc a").unwrap(), - Instruction::Inc(Register::A)); - assert_eq!(Instruction::from_str("dec b").unwrap(), - Instruction::Dec(Register::B)); - assert_eq!(Instruction::from_str("jnz c 2").unwrap(), - Instruction::Jnz(Variable::Register(Register::C), 2)); - } -} diff --git a/2016/rust/src/lib.rs b/2016/rust/src/lib.rs index 8963715..1130314 100644 --- a/2016/rust/src/lib.rs +++ b/2016/rust/src/lib.rs @@ -6,6 +6,7 @@ extern crate error_chain; extern crate regex; extern crate crypto; +mod assembunny; pub mod errors; pub mod day_01; pub mod day_02;