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
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} |