116 lines
3.0 KiB
Rust
116 lines
3.0 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 instruction")?,
|
|
}),
|
|
_ => 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) -> i64 {
|
|
match self.instruction {
|
|
Instruction::Noop => self.cycle_count -= 1,
|
|
Instruction::Addx { arg } => {
|
|
self.cycle_count -= 1;
|
|
if self.cycle_count == 0 {
|
|
self.x += arg;
|
|
}
|
|
}
|
|
}
|
|
self.x
|
|
}
|
|
|
|
pub fn cycles(&mut self) -> Cycles {
|
|
Cycles { processor: self }
|
|
}
|
|
}
|
|
|
|
pub struct Cycle {
|
|
pub x_during: i64,
|
|
pub x_after: i64,
|
|
}
|
|
|
|
pub struct Cycles<'a> {
|
|
processor: &'a mut Processor,
|
|
}
|
|
|
|
impl Iterator for Cycles<'_> {
|
|
type Item = Result<Cycle, &'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(Ok(Cycle {
|
|
x_during: self.processor.x,
|
|
x_after: self.processor.step(),
|
|
}))
|
|
}
|
|
}
|