// 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(), })) } }