use bevy::{app::AppExit, prelude::*}; use std::convert::TryFrom; use std::io::{BufRead, BufReader}; use std::{collections::BTreeSet, fs::File}; fn main() { App::build() .add_resource(WindowDescriptor { title: "Advent of Code".to_string(), width: 1920, height: 1080, ..Default::default() }) .add_resource(ClearColor(Color::rgb(0., 0., 0.))) .add_startup_system(setup_camera.system()) .add_startup_system(read_input_file.system()) .add_stage("check_line") .add_system_to_stage("check_line", track_program_line_execution.system()) .add_system_to_stage("check_line", track_program_termination.system()) .add_stage("exec") .add_system_to_stage("exec", tick_computer.system()) .add_system_to_stage("exec", exit.system()) .add_plugins(DefaultPlugins) .run(); } fn setup_camera(mut commands: Commands) { commands.spawn(Camera2dComponents::default()); } struct Program { ops: Vec, } #[derive(Clone)] enum Op { Acc(i32), Jmp(i32), Nop(i32), } struct UnalteredProgram; #[derive(Default)] struct Computer { program_counter: i32, accumulator: i32, termiated: bool, } #[derive(Default)] struct LineTracker(BTreeSet); impl Computer { fn exec(&mut self, program: &Program) { if self.termiated { return; } let x = usize::try_from(self.program_counter).ok(); match x.and_then(|x| program.ops.get(x)) { None => {} Some(Op::Acc(val)) => { self.accumulator += val; self.program_counter += 1; } Some(Op::Jmp(val)) => { self.program_counter += val; } Some(Op::Nop(_)) => { self.program_counter += 1; } } if self.program_counter >= program.ops.len() as i32 { self.termiated = true; } } } fn read_input_file(mut commands: Commands) { let f = File::open("./inputs/day_8.txt").unwrap(); let ops: Vec = BufReader::new(f) .lines() .map(|line| { let line = line.unwrap(); let line = line.trim(); let mut line_parts = line.split_whitespace(); let opcode = line_parts.next().unwrap(); let val: i32 = line_parts.next().unwrap().parse().unwrap(); let op = match opcode { "acc" => Op::Acc(val), "jmp" => Op::Jmp(val), "nop" => Op::Nop(val), _ => panic!("Invalid opcode"), }; op }) .collect(); commands.spawn(( UnalteredProgram, Program { ops: ops.clone() }, Computer::default(), LineTracker::default(), )); for i in 0..ops.len() { let mut ops = ops.clone(); ops[i] = match ops[i] { Op::Nop(v) => Op::Jmp(v), Op::Jmp(v) => Op::Nop(v), Op::Acc(v) => Op::Acc(v), }; commands.spawn(( Program { ops: ops.clone() }, Computer::default(), LineTracker::default(), )); } } fn track_program_line_execution( mut commands: Commands, entity: Entity, mut line_tracker: Mut, computer: &Computer, unaltered: Option<&UnalteredProgram>, ) { if line_tracker.0.contains(&computer.program_counter) { if unaltered.is_some() { println!( "About to execute a line for the second time! Line {}. Accumulator: {}", computer.program_counter, computer.accumulator ); } commands.despawn(entity); } else { line_tracker.0.insert(computer.program_counter); } } fn track_program_termination(mut commands: Commands, entity: Entity, computer: &Computer) { if computer.termiated { println!("Program terminated! Accumulator: {}", computer.accumulator); commands.despawn(entity); } } fn tick_computer(mut computer: Mut, program: &Program) { computer.exec(program); } fn exit(mut exit_events: ResMut>, computers: Query<&Computer>) { if computers.iter().len() == 0 { exit_events.send(AppExit); } }