// 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 .
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 {
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;
fn next(&mut self) -> Option {
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(),
}))
}
}