/****************************************************************************** * @file map.rs * @author Joey Pollack * @date 2024/12/09 (y/m/d) * @modified 2024/12/09 (y/m/d) * @copyright Joseph R Pollack * @brief Represents the map and the guard on the map. ******************************************************************************/ use nalgebra_glm::I32Vec2; #[derive(Copy, Clone, PartialEq, Debug)] pub enum MapCell { Space, Block, Guard, Visited, } #[derive(Copy, Clone, PartialEq, Debug)] pub enum Direction { North, South, East, West, } #[derive(Clone, Debug)] pub struct Map { map: Vec>, guard_start_pos: I32Vec2, guard_direction: Direction, guard_pos: nalgebra_glm::I32Vec2, } impl Map { pub fn new() -> Map { Map { map: vec![], guard_start_pos: I32Vec2::new(0, 0), guard_direction: Direction::North, guard_pos: I32Vec2::new(0, 0) } } pub fn parse(data: &str) -> Map { let mut split_pattern = "\n"; if data.contains("\r\n") { split_pattern = "\r\n"; } let rows = data.split(split_pattern); let height = rows.clone().count(); let mut map: Vec> = vec![vec![]; height]; let mut guard_pos = I32Vec2::new(0, 0); for (i, row) in rows.enumerate() { map[i] = vec![MapCell::Space; row.len()]; for (j, c) in row.as_bytes().into_iter().enumerate() { match c { b'.' => map[i][j] = MapCell::Space, b'#' => map[i][j] = MapCell::Block, b'^' => { map[i][j] = MapCell::Guard; guard_pos.x = i as i32; guard_pos.y = j as i32; } _ => panic!("Map ERROR: Failed to parse map data, Invalid map input: {}", c) } } } Map { map, guard_start_pos: guard_pos, guard_direction: Direction::North, guard_pos } } pub fn print(self: &Map) { print!("MAP:"); for (i, row) in self.map.iter().enumerate() { print!("\n\t"); for (j, c) in row.iter().enumerate() { if i as i32 == self.guard_pos.x && j as i32 == self.guard_pos.y { self.print_guard(); } else { match *c { MapCell::Space => print!("."), MapCell::Block => print!("#"), MapCell::Visited => print!("X"), MapCell::Guard => self.print_guard(), } } } } println!(); } fn print_guard(self: &Map) { match self.guard_direction { Direction::North => print!("^"), Direction::South => print!("v"), Direction::East => print!(">"), Direction::West => print!("<"), } } pub fn width(self: &Map) -> i32 { self.map[0].len() as i32 } pub fn height(self: &Map) -> i32 { self.map.len() as i32 } pub fn add_blocker_at(self: &mut Map, at: &I32Vec2) { self.map[at.x as usize][at.y as usize] = MapCell::Block; } /// Step Rules: /// /// If there is something directly in front of you, turn right 90 degrees. /// Otherwise, take a step forward. /// /// Returns true if the guard is still on the map after the step. pub fn step_guard(self: &mut Map) -> bool { if !self.guard_is_on_map() { return false; } let next_guard_pos = self.guard_pos + self.guard_direction_vector(); if !self.pos_is_on_map(&next_guard_pos) { self.map[self.guard_pos.x as usize][self.guard_pos.y as usize] = MapCell::Visited; self.guard_pos = next_guard_pos; return false; } if self.map[next_guard_pos.x as usize][next_guard_pos.y as usize] == MapCell::Block { self.rotate_guard_90(); return true; } self.map[self.guard_pos.x as usize][self.guard_pos.y as usize] = MapCell::Visited; self.guard_pos = next_guard_pos; self.guard_is_on_map() } pub fn guard_is_on_map(self: &Map) -> bool { self.guard_pos.x >= 0 && self.guard_pos.y >= 0 && (self.guard_pos.x as usize) < self.map[0].len() && (self.guard_pos.y as usize) < self.map.len() } fn pos_is_on_map(self: &Map, pos: &I32Vec2) -> bool { pos.x >= 0 && pos.y >= 0 && (pos.x as usize) < self.map.len() && (pos.y as usize) < self.map[0].len() } pub fn guard_position(self: &Map) -> I32Vec2 { self.guard_pos } pub fn guard_direction_vector(self: &Map) -> I32Vec2 { match self.guard_direction { Direction::North => I32Vec2::new(-1,0), Direction::South => I32Vec2::new(1, 0), Direction::East => I32Vec2::new(0,1), Direction::West => I32Vec2::new(0, -1), } } // Rotate the guard clockwise 90 degrees fn rotate_guard_90(self: &mut Map) { match self.guard_direction { Direction::North => self.guard_direction = Direction::East, Direction::South => self.guard_direction = Direction::West, Direction::East => self.guard_direction = Direction::South, Direction::West => self.guard_direction = Direction::North, }; } pub fn reset_guard(self: &mut Map) { self.guard_pos = self.guard_start_pos; self.guard_direction = Direction::North; } pub fn clear_visited(self: &mut Map) { for row in &mut self.map { for c in row { if *c == MapCell::Visited { *c = MapCell::Space; } } } } }