#![allow(clippy::print_with_newline)]
use std::{
fs::{self},
io::Write,
};
#[derive(Debug, Clone, Copy, PartialEq)]
enum Instruction {
Clear(u8),
Increase(u8),
Print(u8),
LoopStart(u8),
LoopClose(u8),
}
#[derive(Clone)]
struct Loop {
loop_var: u8,
start_point: u16,
current_loop_var: u32,
depth: u8,
}
fn parse<'a>(input: impl Iterator<Item = &'a [u8]>) -> (Vec<Instruction>, Vec<Loop>) {
let mut instructions = Vec::with_capacity(1024);
let mut loops = Vec::new();
let mut depth = 0;
for (line_num, line) in input.enumerate() {
let line = line.trim_ascii();
if line.iter().next() == Some(&b'#') {
continue;
}
match line.split(|b| *b == b' ').collect::<Box<_>>().as_ref() {
[b"CLEAR", var] => instructions.push(Instruction::Clear(var_idx(var[0]) as u8)),
[b"INCREASE", var] => instructions.push(Instruction::Increase(var_idx(var[0]) as u8)),
[b"PRINT", var] => instructions.push(Instruction::Print(var_idx(var[0]) as u8)),
[b"REPEAT", var, _, _] => {
depth += 1;
loops.push(Loop {
loop_var: var_idx(var[0]) as u8,
start_point: instructions.len() as u16,
current_loop_var: 0,
depth,
});
instructions.push(Instruction::LoopStart(loops.len() as u8 - 1));
}
[b")"] => {
instructions.push(Instruction::LoopClose(
loops
.iter()
.enumerate()
.rfind(|(_, l)| l.depth == depth)
.unwrap()
.0 as u8,
));
depth -= 1;
}
_ => {
println!(
"syntax error on line {line_num}: \"{}\" starting execution",
str::from_utf8(line).unwrap()
);
break;
}
}
}
(instructions, loops)
}
fn main() {
let (instructions, loops) = if let Some(file) = std::env::args().nth(1) {
let file = fs::read(file).expect("invalid source file");
let input = file.split(|b| *b == b'\n');
parse(input)
} else {
let file = std::io::stdin();
parse(
file.lines()
.map(|l| l.expect("couldnt read stdin line").leak().as_bytes()),
)
};
println!(
"instructions executed: {}",
(1..=1000)
.into_iter()
.map(|x| run(&instructions, loops.clone(), x))
.sum::<u32>()
)
}
fn run(instructions: &[Instruction], mut loops: Vec<Loop>, input: u32) -> u32 {
let mut instruction_pointer = 0usize;
let mut vars = [0u32; 26];
vars[var_idx(b'X')] = input;
let mut counter = 0;
while instruction_pointer < instructions.len() {
let instruction = instructions[instruction_pointer];
match instruction {
Instruction::Clear(var) => vars[var as usize] = 0,
Instruction::Increase(var) => vars[var as usize] += 1,
Instruction::Print(var) => {
print!("{}\n", vars[var as usize])
}
Instruction::LoopStart(idx) => {
let this_loop = &mut loops[idx as usize];
let iterations = vars[this_loop.loop_var as usize];
if iterations > 0 {
this_loop.current_loop_var = iterations;
} else {
while instructions[instruction_pointer] != Instruction::LoopClose(idx) {
instruction_pointer += 1;
}
}
}
Instruction::LoopClose(idx) => {
let this_loop = &mut loops[idx as usize];
this_loop.current_loop_var -= 1;
if this_loop.current_loop_var > 0 {
instruction_pointer = this_loop.start_point as usize;
}
}
}
instruction_pointer += 1;
counter += 1;
}
std::io::stdout().flush().unwrap();
counter
}
fn var_idx(name: u8) -> usize {
(name - b'A') as usize
}