use bevy::prelude::*; use std::fs::File; use std::io::{BufRead, BufReader}; fn main() { App::build() .add_resource(WindowDescriptor { title: "Advent of Code".to_string(), width: 1920, height: 1080, ..Default::default() }) .add_event::() .add_event::() .add_resource(ClearColor(Color::rgb(0., 0., 0.))) .add_startup_system(setup_camera.system()) .add_startup_system(read_input_file.system()) .add_system(iterate_adjacent_seats.system()) .add_system(iterate_line_of_sight_seats.system()) .add_system(report_adjacent_done.system()) .add_system(report_line_of_sight_done.system()) .add_plugins(DefaultPlugins) .run(); } fn setup_camera(mut commands: Commands) { commands.spawn(Camera2dComponents::default()); } #[derive(PartialEq, Eq, Clone, Copy)] struct Position { x: i32, y: i32, } impl Position { fn distance(&self, other: &Position) -> i32 { (self.x - other.x).abs().max((self.y - other.y).abs()) } } impl std::ops::Add<&Position> for Position { type Output = Position; fn add(self, other: &Position) -> Self::Output { Position { x: self.x + other.x, y: self.y + other.y, } } } struct AdjacentSeat { occupied: bool, } struct LineOfSightSeat { occupied: bool, } struct AdjacentStableEvent; struct LineOfSightStableEvent; #[derive(Default)] struct Bounds { min_x: i32, max_x: i32, min_y: i32, max_y: i32, } fn read_input_file(mut commands: Commands) { let f = File::open("./inputs/day_11.txt").unwrap(); let mut bounds = Bounds::default(); for (y, line) in BufReader::new(f).lines().enumerate() { let line = line.unwrap(); let line = line.trim(); for (x, c) in line.chars().enumerate() { if c == 'L' { commands.spawn(( Position { x: x as i32, y: y as i32, }, AdjacentSeat { occupied: false }, LineOfSightSeat { occupied: false }, )); } bounds.max_x = x as i32; } bounds.max_y = y as i32; } commands.insert_resource(bounds); } fn iterate_adjacent_seats( mut commands: Commands, mut stable_events: ResMut>, seats: Query<(Entity, &Position, &AdjacentSeat)>, ) { let mut changes: Vec<(Entity, AdjacentSeat)> = Vec::new(); for (entity, position, seat) in seats.iter() { let surrounding_count = seats .iter() .filter(|(_, other_position, other_seat)| { other_seat.occupied && position.distance(other_position) == 1 }) .count(); let change = if !seat.occupied && surrounding_count == 0 { Some(true) } else if seat.occupied && surrounding_count >= 4 { Some(false) } else { None }; if let Some(occupied) = change { changes.push((entity, AdjacentSeat { occupied })); } } if changes.is_empty() { stable_events.send(AdjacentStableEvent); } for (entity, seat) in changes { commands.insert_one(entity, seat); } } fn iterate_line_of_sight_seats( mut commands: Commands, mut stable_events: ResMut>, bounds: Res, seats: Query<(Entity, &Position, &LineOfSightSeat)>, ) { let dir_vectors = [ Position { x: -1, y: 0 }, Position { x: -1, y: -1 }, Position { x: 0, y: -1 }, Position { x: 1, y: -1 }, Position { x: 1, y: 0 }, Position { x: 1, y: 1 }, Position { x: 0, y: 1 }, Position { x: -1, y: 1 }, ]; let mut changes: Vec<(Entity, LineOfSightSeat)> = Vec::new(); for (entity, position, seat) in seats.iter() { let mut surrounding_count = 0; for vec in dir_vectors.iter() { let mut check = position.clone(); while check.x >= bounds.min_x && check.x <= bounds.max_x && check.y >= bounds.min_y && check.y <= bounds.max_y { check = check + vec; if let Some((_, _, other_seat)) = seats.iter().find(|(_, other_pos, _)| other_pos == &&check) { if other_seat.occupied { surrounding_count += 1; } break; } } } let change = if !seat.occupied && surrounding_count == 0 { Some(true) } else if seat.occupied && surrounding_count >= 5 { Some(false) } else { None }; if let Some(occupied) = change { changes.push((entity, LineOfSightSeat { occupied })); } } if changes.is_empty() { stable_events.send(LineOfSightStableEvent); } for (entity, seat) in changes { commands.insert_one(entity, seat); } } fn report_adjacent_done( stable_events: Res>, mut stable_event_reader: Local>, seats: Query<&AdjacentSeat>, ) { for _ in stable_event_reader.iter(&stable_events) { let occupied = seats.iter().filter(|s| s.occupied).count(); println!("{} seats end up occupied in the adjacent model", occupied); } } fn report_line_of_sight_done( stable_events: Res>, mut stable_event_reader: Local>, seats: Query<&LineOfSightSeat>, ) { for _ in stable_event_reader.iter(&stable_events) { let occupied = seats.iter().filter(|s| s.occupied).count(); println!( "{} seats end up occupied in the line of sight model", occupied ); } }