You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

231 lines
4.8 KiB
Rust

/******************************************************************************
* @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<Vec<MapCell>>,
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<MapCell>> = 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;
}
}
}
}
}