108 lines
2.9 KiB
Rust
108 lines
2.9 KiB
Rust
|
// Copyright 2022 Christian Ulrich
|
||
|
//
|
||
|
// This program is free software: you can redistribute it and/or modify
|
||
|
// it under the terms of the GNU General Public License as published by
|
||
|
// the Free Software Foundation, either version 3 of the License, or
|
||
|
// (at your option) any later version.
|
||
|
//
|
||
|
// This program is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
// GNU General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU General Public License
|
||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
use std::io;
|
||
|
use std::str::FromStr;
|
||
|
|
||
|
#[derive(Debug, Clone, Copy)]
|
||
|
pub enum Instruction {
|
||
|
Noop,
|
||
|
Addx { arg: i64 },
|
||
|
}
|
||
|
|
||
|
impl Instruction {
|
||
|
pub fn duration(&self) -> usize {
|
||
|
match self {
|
||
|
Self::Noop => 1,
|
||
|
Self::Addx { arg: _ } => 2,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub struct Processor {
|
||
|
x: i64,
|
||
|
instruction: Instruction,
|
||
|
cycle_count: usize,
|
||
|
}
|
||
|
|
||
|
impl FromStr for Instruction {
|
||
|
type Err = &'static str;
|
||
|
|
||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||
|
match s {
|
||
|
"noop" => Ok(Self::Noop),
|
||
|
_ if { s.starts_with("addx ") } => Ok(Self::Addx {
|
||
|
arg: s[5..].parse().map_err(|_| "cannot parse instrution")?,
|
||
|
}),
|
||
|
_ => Err("cannot parse instruction"),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Processor {
|
||
|
pub fn new() -> Self {
|
||
|
Self {
|
||
|
x: 1,
|
||
|
instruction: Instruction::Noop,
|
||
|
cycle_count: 0,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn set_instruction(&mut self, instruction: Instruction) {
|
||
|
self.instruction = instruction;
|
||
|
self.cycle_count = instruction.duration();
|
||
|
}
|
||
|
|
||
|
pub fn step(&mut self) -> Result<i64, &'static str> {
|
||
|
match self.instruction {
|
||
|
Instruction::Noop => self.cycle_count -= 1,
|
||
|
Instruction::Addx { arg } => {
|
||
|
self.cycle_count -= 1;
|
||
|
if self.cycle_count == 0 {
|
||
|
self.x += arg;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
Ok(self.x)
|
||
|
}
|
||
|
|
||
|
pub fn cycles(&mut self) -> Cycles {
|
||
|
Cycles { processor: self }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub struct Cycles<'a> {
|
||
|
processor: &'a mut Processor,
|
||
|
}
|
||
|
|
||
|
impl Iterator for Cycles<'_> {
|
||
|
type Item = Result<i64, &'static str>;
|
||
|
|
||
|
fn next(&mut self) -> Option<Self::Item> {
|
||
|
if self.processor.cycle_count == 0 {
|
||
|
let mut line = String::new();
|
||
|
match io::stdin().read_line(&mut line) {
|
||
|
Ok(_) if { line == "" || line == "\n" } => return None,
|
||
|
Ok(_) => match Instruction::from_str(line.trim()) {
|
||
|
Ok(instruction) => self.processor.set_instruction(instruction),
|
||
|
Err(e) => return Some(Err(e)),
|
||
|
},
|
||
|
Err(_) => assert!(false),
|
||
|
}
|
||
|
}
|
||
|
Some(self.processor.step())
|
||
|
}
|
||
|
}
|