pub trait Maze {
fn width(&self) -> usize;
fn height(&self) -> usize;
fn solid(&self, x: usize, y: usize) -> bool;
}
pub fn astar(maze: &dyn Maze, start: (usize, usize), end: (usize, usize)) -> Option<Vec<(usize, usize)>> {
let start = Node::new(None, start, 0, 0, 0);
let end = Node::new(None, end, 0, 0, 0);
let mut open_list = vec![start];
let mut closed_list = vec![];
loop {
if open_list.len() == 0 {
return None;
}
let current_node = {
let mut node = Node::new(None, (0, 0), 0, 0, std::isize::MAX);
let mut remove = 0;
for (i, on) in open_list.iter().enumerate() {
if on.f < node.f {
node = on.clone();
remove = i;
}
}
open_list.swap_remove(remove);
node
};
closed_list.push(current_node.clone());
if current_node == end {
let mut path = vec![];
let mut current = current_node.clone();
loop {
path.push((current.pos.0, current.pos.1));
match current.parent {
Some(parent) => current = *parent,
None => break,
}
}
path.reverse();
return Some(path);
}
let mut children = vec![];
for dir in [(-1,-1), (0,-1), (1,-1), (-1, 0), (1, 0), (-1,1), (0,1), (1,1)] {
let x = current_node.pos.0 as isize + dir.0;
let y = current_node.pos.1 as isize + dir.1;
if x < 0 || maze.width() <= x as usize {
continue;
}
if y < 0 || maze.height() <= y as usize {
continue;
}
let x = x as usize;
let y = y as usize;
if maze.solid(x, y) {
continue;
}
let parent = Some(Box::new(current_node.clone()));
let node = Node::new(parent, (x, y), 0, 0, 0);
children.push(node);
}
'children: for mut child in children.into_iter() {
for cn in closed_list.iter() {
if *cn == child {
continue 'children;
}
}
child.g = current_node.g + 1;
let x = (child.pos.0 as isize - end.pos.0 as isize).pow(2);
let y = (child.pos.1 as isize - end.pos.1 as isize).pow(2);
child.h = x + y;
child.f = child.g + child.h;
for on in open_list.iter() {
if *on == child && child.g > on.g {
continue 'children;
}
}
open_list.push(child);
}
}
}
#[derive(Clone)]
struct Node {
parent: Option<Box<Node>>,
pos: (usize, usize),
g: isize,
h: isize,
f: isize,
}
impl Node {
fn new(parent: Option<Box<Self>>, pos: (usize, usize), g: isize, h: isize, f: isize) -> Self {
Self {
parent, pos,
g, h, f
}
}
}
impl PartialEq for Node {
fn eq(&self, other: &Self) -> bool {
let x = self.pos.0 == other.pos.0;
let y = self.pos.1 == other.pos.1;
x && y
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Solvable {}
impl Maze for Solvable {
fn height(&self) -> usize {
10
}
fn width(&self) -> usize {
10
}
fn solid(&self, x: usize, y: usize) -> bool {
if x == 1 && y == 0 {
return true;
} else if x == 1 && y == 1 {
return true;
}
false
}
}
struct Unsolvable {}
impl Maze for Unsolvable {
fn height(&self) -> usize {
10
}
fn width(&self) -> usize {
10
}
fn solid(&self, x: usize, _: usize) -> bool {
if x == 2 {
return true;
}
false
}
}
#[test]
fn test_astar() {
let result = astar(&Solvable {}, (0, 0), (9, 0));
let solution = vec![(0, 0), (0, 1), (1, 2), (2, 1), (3, 0), (4, 0), (5, 0), (6, 0), (7, 0), (8, 0), (9, 0)];
assert_eq!(Some(solution), result);
let result = astar(&Unsolvable {}, (0, 0), (9, 9));
assert_eq!(None, result);
}
}