#![no_std]
#![deny(missing_docs)]
#![deny(unsafe_code)]
use heapless::Vec;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Point {
pub x: i32,
pub y: i32,
}
impl Point {
#[inline]
pub const fn nouveau(x: i32, y: i32) -> Self {
Self { x, y }
}
}
#[derive(Clone, Copy)]
struct Noeud {
point: Point,
cout_g: i32,
cout_f: i32,
}
#[inline]
fn heuristique(a: Point, b: Point) -> i32 {
(a.x - b.x).abs() + (a.y - b.y).abs()
}
#[inline]
fn est_valide<const L: usize, const H: usize>(
p: Point,
grille: &[[bool; L]; H],
) -> bool {
p.x >= 0
&& (p.x as usize) < L
&& p.y >= 0
&& (p.y as usize) < H
&& !grille[p.y as usize][p.x as usize]
}
pub fn trouver_chemin<const LARGEUR: usize, const HAUTEUR: usize, const CAPACITE_MAX: usize>(
depart: Point,
arrivee: Point,
grille: &[[bool; LARGEUR]; HAUTEUR],
) -> Option<Vec<Point, CAPACITE_MAX>> {
if !est_valide::<LARGEUR, HAUTEUR>(depart, grille)
|| !est_valide::<LARGEUR, HAUTEUR>(arrivee, grille)
{
return None;
}
let mut ouvert: Vec<Noeud, CAPACITE_MAX> = Vec::new();
let mut ferme: [[bool; LARGEUR]; HAUTEUR] = [[false; LARGEUR]; HAUTEUR];
let mut parents: [[Option<Point>; LARGEUR]; HAUTEUR] = [[None; LARGEUR]; HAUTEUR];
let noeud_depart = Noeud {
point: depart,
cout_g: 0,
cout_f: heuristique(depart, arrivee),
};
let _ = ouvert.push(noeud_depart);
const DIRECTIONS: [(i32, i32); 4] = [(0, -1), (1, 0), (0, 1), (-1, 0)];
while !ouvert.is_empty() {
let mut idx_meilleur = 0;
for i in 1..ouvert.len() {
if ouvert[i].cout_f < ouvert[idx_meilleur].cout_f {
idx_meilleur = i;
}
}
let courant = ouvert.swap_remove(idx_meilleur);
let pc = courant.point;
if pc == arrivee {
return reconstruire_chemin::<LARGEUR, HAUTEUR, CAPACITE_MAX>(
arrivee,
depart,
&parents,
);
}
ferme[pc.y as usize][pc.x as usize] = true;
for &(dx, dy) in &DIRECTIONS {
let voisin = Point {
x: pc.x + dx,
y: pc.y + dy,
};
if !est_valide::<LARGEUR, HAUTEUR>(voisin, grille)
|| ferme[voisin.y as usize][voisin.x as usize]
{
continue;
}
let g_tentative = courant.cout_g + 1;
let existant = ouvert.iter_mut().find(|n| n.point == voisin);
match existant {
Some(noeud) if g_tentative < noeud.cout_g => {
noeud.cout_g = g_tentative;
noeud.cout_f = g_tentative + heuristique(voisin, arrivee);
parents[voisin.y as usize][voisin.x as usize] = Some(pc);
}
None => {
parents[voisin.y as usize][voisin.x as usize] = Some(pc);
let noeud_voisin = Noeud {
point: voisin,
cout_g: g_tentative,
cout_f: g_tentative + heuristique(voisin, arrivee),
};
let _ = ouvert.push(noeud_voisin);
}
_ => { }
}
}
}
None }
fn reconstruire_chemin<const L: usize, const H: usize, const CAP: usize>(
arrivee: Point,
depart: Point,
parents: &[[Option<Point>; L]; H],
) -> Option<Vec<Point, CAP>> {
let mut chemin: Vec<Point, CAP> = Vec::new();
let mut courant = arrivee;
loop {
if chemin.push(courant).is_err() {
return None;
}
if courant == depart {
break;
}
match parents[courant.y as usize][courant.x as usize] {
Some(p) => courant = p,
None => return None, }
}
chemin.reverse();
Some(chemin)
}
#[cfg(test)]
mod tests {
use super::*;
type Grille4 = [[bool; 4]; 4];
fn grille_vide() -> Grille4 {
[[false; 4]; 4]
}
#[test]
fn depart_egal_arrivee() {
let grille = grille_vide();
let p = Point::nouveau(1, 1);
let chemin = trouver_chemin::<4, 4, 16>(p, p, &grille).unwrap();
assert_eq!(chemin.len(), 1);
assert_eq!(chemin[0], p);
}
#[test]
fn deplacement_horizontal() {
let grille = grille_vide();
let chemin = trouver_chemin::<4, 4, 16>(
Point::nouveau(0, 0),
Point::nouveau(3, 0),
&grille,
)
.unwrap();
assert_eq!(chemin.first().copied(), Some(Point::nouveau(0, 0)));
assert_eq!(chemin.last().copied(), Some(Point::nouveau(3, 0)));
assert_eq!(chemin.len(), 4); }
#[test]
fn deplacement_vertical() {
let grille = grille_vide();
let chemin = trouver_chemin::<4, 4, 16>(
Point::nouveau(0, 0),
Point::nouveau(0, 3),
&grille,
)
.unwrap();
assert_eq!(chemin.len(), 4);
}
#[test]
fn chemin_diagonal() {
let grille = grille_vide();
let chemin = trouver_chemin::<4, 4, 16>(
Point::nouveau(0, 0),
Point::nouveau(3, 3),
&grille,
)
.unwrap();
assert_eq!(chemin.first().copied(), Some(Point::nouveau(0, 0)));
assert_eq!(chemin.last().copied(), Some(Point::nouveau(3, 3)));
assert_eq!(chemin.len(), 7);
}
#[test]
fn mur_avec_passage() {
let mut grille = grille_vide();
grille[1][0] = true;
grille[1][1] = true;
grille[1][3] = true;
let chemin = trouver_chemin::<4, 4, 16>(
Point::nouveau(0, 0),
Point::nouveau(0, 2),
&grille,
)
.unwrap();
assert_eq!(chemin.first().copied(), Some(Point::nouveau(0, 0)));
assert_eq!(chemin.last().copied(), Some(Point::nouveau(0, 2)));
}
#[test]
fn aucun_chemin_mur_total() {
let mut grille = grille_vide();
grille[1][0] = true;
grille[1][1] = true;
grille[1][2] = true;
grille[1][3] = true;
let resultat = trouver_chemin::<4, 4, 16>(
Point::nouveau(0, 0),
Point::nouveau(0, 2),
&grille,
);
assert!(resultat.is_none());
}
#[test]
fn depart_obstacle() {
let mut grille = grille_vide();
grille[0][0] = true;
let resultat = trouver_chemin::<4, 4, 16>(
Point::nouveau(0, 0),
Point::nouveau(3, 3),
&grille,
);
assert!(resultat.is_none());
}
#[test]
fn arrivee_obstacle() {
let mut grille = grille_vide();
grille[3][3] = true;
let resultat = trouver_chemin::<4, 4, 16>(
Point::nouveau(0, 0),
Point::nouveau(3, 3),
&grille,
);
assert!(resultat.is_none());
}
#[test]
fn hors_bornes() {
let grille = grille_vide();
let resultat = trouver_chemin::<4, 4, 16>(
Point::nouveau(0, 0),
Point::nouveau(99, 99),
&grille,
);
assert!(resultat.is_none());
}
}