use crate::position::Position;
use crate::generator::{dig_maze, find_path_position, find_path_position_from_bottom};
pub const WALL: char = '#';
pub const PATH: char = ' ';
pub const PLAYER: char = 'P';
pub const GOAL: char = 'G';
pub struct Maze {
width: usize,
height: usize,
grid: Vec<Vec<char>>,
player: Position,
goal: Position,
}
pub struct MazeState {
pub player_position: Position,
pub goal_position: Position,
pub is_completed: bool,
}
impl Maze {
pub fn new() -> Option<Self> {
Self::with_size(17, 11)
}
pub fn with_size(width: usize, height: usize) -> Option<Self> {
let width = if width < 5 { 5 } else { width };
let height = if height < 5 { 5 } else { height };
let width = if width % 2 == 0 { width + 1 } else { width };
let height = if height % 2 == 0 { height + 1 } else { height };
let mut rng = rand::rng();
let mut grid = vec![vec![WALL; width]; height];
dig_maze(&mut grid, width, height, 1, 1, &mut rng);
let player = match find_path_position(&grid, width, height, 1, 1) {
Some(pos) => pos,
None => return None,
};
let goal = match find_path_position_from_bottom(&grid, width, height, width - 2, height - 2, &player) {
Some(pos) => pos,
None => return None,
};
let mut maze = Maze { width, height, grid, player, goal };
maze.update_grid();
Some(maze)
}
fn update_grid(&mut self) {
for y in 0..self.height {
for x in 0..self.width {
if self.grid[y][x] == PLAYER || self.grid[y][x] == GOAL {
self.grid[y][x] = PATH;
}
}
}
self.grid[self.player.y][self.player.x] = PLAYER;
self.grid[self.goal.y][self.goal.x] = GOAL;
}
pub fn display(&self) {
println!("\nMaze: (P=Player, G=Goal, #=Wall)\n");
for y in 0..self.height {
for x in 0..self.width {
print!("{}", self.grid[y][x]);
}
println!();
}
println!();
}
pub fn get_maze_as_string(&self) -> String {
let mut result = String::new();
result.push_str("Maze: (P=Player, G=Goal, #=Wall)\n\n");
for y in 0..self.height {
for x in 0..self.width {
result.push(self.grid[y][x]);
}
result.push('\n');
}
result
}
pub fn move_player(&mut self, direction: &str) -> bool {
let mut new_x = self.player.x;
let mut new_y = self.player.y;
match direction {
"w" | "up" => {
if new_y > 0 {
new_y -= 1;
}
}
"s" | "down" => {
if new_y < self.height - 1 {
new_y += 1;
}
}
"a" | "left" => {
if new_x > 0 {
new_x -= 1;
}
}
"d" | "right" => {
if new_x < self.width - 1 {
new_x += 1;
}
}
_ => return false,
}
if self.grid[new_y][new_x] != WALL {
self.player.x = new_x;
self.player.y = new_y;
self.update_grid();
return self.player.x == self.goal.x && self.player.y == self.goal.y;
}
false
}
pub fn get_state(&self) -> MazeState {
MazeState {
player_position: self.player,
goal_position: self.goal,
is_completed: self.player.x == self.goal.x && self.player.y == self.goal.y,
}
}
pub fn get_width(&self) -> usize {
self.width
}
pub fn get_height(&self) -> usize {
self.height
}
}