Les fantômes
Objectif:
Découvrir les bases de l'intelligence artificielle (IA) en introduisant un comportement simple pour les fantômes dans notre jeu Pacman.

Notions théoriques
L'intelligence artificielle dans les jeux peut sembler complexe, mais elle débute souvent avec des règles simples.
Dans notre cas, un fantôme se déplacera aléatoirement dans le labyrinthe.
Pour cela, nous utiliserons la fonction Math.random() pour choisir une direction parmi les possibilités autorisées (haut, bas, gauche, droite) en évitant les murs.
Ce comportement représente une IA très basique, mais il constitue un bon point de départ.
Exemple pratique
Dans notre jeu de Pacman, en utilisant la fonction Math.random(), nous pourrons déplacer un fantôme de façon aléatoire.
Voici un exemple d'une fonction choisirDirectionAleatoire qui :
- contient un tableau 
directionsPossiblesavec les 4 directions possibles ; - tire au sort une des 4 directions ;
 - et retourne la direction tirée au sort.
 
function choisirDirectionAleatoire(positionFantome) {
  let directionsPossibles = [];
  // Vérifier chaque direction pour s'assurer qu'elle ne mène pas à un mur
  if (labyrinthe[positionFantome.y - 1][positionFantome.x] === 0) {
    directionsPossibles.push('ArrowUp');
  }
  if (labyrinthe[positionFantome.y + 1][positionFantome.x] === 0) {
    directionsPossibles.push('ArrowDown');
  }
  if (labyrinthe[positionFantome.y][positionFantome.x - 1] === 0) {
    directionsPossibles.push('ArrowLeft');
  }
  if (labyrinthe[positionFantome.y][positionFantome.x + 1] === 0) {
    directionsPossibles.push('ArrowRight');
  }
  // Choisir une direction aléatoire parmi les possibilités
  return directionsPossibles[Math.floor(Math.random() * directionsPossibles.length)];
}
Ainsi :
directionsPossibles.lengthest égale à 4directionsPossibles[0]est égale àArrowUpc'est à dire la touche de flèche de direction HautdirectionsPossibles[1]est égale àArrowDownc'est à dire la touche de flèche de direction BasdirectionsPossibles[2]est égale àArrowLeftc'est à dire la touche de flèche de direction GauchedirectionsPossibles[3]est égale àArrowRightc'est à dire la touche de flèche de direction Droite
Quelques méthodes à connaître
Math.random(): Renvoie un nombre flottant pseudo-aléatoire compris dans l'intervalle [0, 1].Math.floor(): Arrondit à l'entier inférieur.
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Mission 1 - Afficher un fantôme
Votre première mission consiste à ajouter un fantôme dans le jeu (qui doit se déplacer de manière aléatoire).
Le fantôme :
- ne doit pas pouvoir traverser les murs ;
 - et doit changer de direction lorsqu'il atteint une intersection dans le labyrinthe.
 

Voici un exemple de code qui permet à un fantôme de se déplacer dans le labyrinthe de façon aléatoire, sans traverser les murs :
- 
Créez un nouveau fichier
Fantome.jsà la racine de votre projet (à côté deApp.js).// Fantome.js - 
Ajoutez l'import de
Reactet du composantViewdans votre fichierFantome.js:import React from 'react';
import { View } from 'react-native'; - 
Créez une fonction
Fantomedans le fichierFantome.js:
...
function Fantome(props) {
} - 
Complétez le code de votre fonction
Fantome, afin de définir les attributs et le style d'affichage de votre fantôme :
...
function Fantome(props) {
const position = props.position;
const styleFantome = {
width: tailleCase,
height: tailleCase,
borderRadius: tailleCase / 2,
backgroundColor: 'red', // Les fantômes sont souvent représentés en rouge
position: 'absolute',
left: position.x * tailleCase,
top: position.y * tailleCase,
transition: 'left 0.5s, top 0.5s', // Transition douce pour le mouvement
};
return <View style={styleFantome} />;
} - 
Ajoutez l'export de la fonction
Fantomeà la fin du fichierFantome.js:
...
export default Fantome; 
- 
Créez un nouveau fichier
DeplacementFantome.jsà la racine de votre projet (à côté deApp.js).// DeplacementFantome.js - 
Créez une fonction
choisirDirectionAleatoiredans le fichierDeplacementFantome.js:
...
function choisirDirectionAleatoire(position, labyrinthe) {
} - 
Complétez le code de votre fonction
choisirDirectionAleatoire, afin de choisir une direction aléatoire parmi les 4 possibilités :
...
function choisirDirectionAleatoire(position, labyrinthe) {
var directionsPossibles = [];
// Vérifier chaque direction pour s'assurer qu'elle ne mène pas à un mur
if (labyrinthe[position.y - 1][position.x] === 0) {
directionsPossibles.push({ x: 0, y: -1 });
}
if (labyrinthe[position.y + 1][position.x] === 0) {
directionsPossibles.push({ x: 0, y: 1 });
}
if (labyrinthe[position.y][position.x - 1] === 0) {
directionsPossibles.push({ x: -1, y: 0 });
}
if (labyrinthe[position.y][position.x + 1] === 0) {
directionsPossibles.push({ x: 1, y: 0 });
}
// Choisir une direction aléatoire parmi les possibilités
return directionsPossibles[Math.floor(Math.random() * directionsPossibles.length)];
} - 
Créez une fonction
deplacerFantomedans le fichierDeplacementFantome.js:
...
function deplacerFantome(fantome1Position, labyrinthe, setFantome1Position) {
} - 
Complétez le code de votre fonction
deplacerFantome, afin de déplacer votre fantôme, si c'est possible :
...
function deplacerFantome(fantome1Position, labyrinthe, setFantome1Position) {
setFantome1Position(function (prevPosition) {
var direction = choisirDirectionAleatoire(prevPosition, labyrinthe);
if (!direction) {
return prevPosition; // Si aucune direction n'est possible, on ne bouge pas
}
var nouvellePosition = { x: prevPosition.x + direction.x, y: prevPosition.y + direction.y };
return nouvellePosition;
});
} - 
Ajoutez l'export de la fonction
deplacerFantomeà la fin du fichierDeplacementFantome.js:
...
export default deplacerFantome; 
- 
Modifiez le code du fichier
App.jspour qu'il ressemble à ceci :// App.js
import React, { useState, useEffect } from "react";
import { StyleSheet, View } from "react-native";
import genererLabyrinthe from "./GenererationLabyrinthe";
import Labyrinthe from "./Labyrinthe";
import Pacman from "./Pacman";
import deplacerPacman from "./DeplacementPacman";
import Fantome from "./Fantome";
import deplacerFantome from "./DeplacementFantome";
const labyrinthe = genererLabyrinthe(11, 11);
export default function App() {
const [pacmanPosition, setPacmanPosition] = useState({ x: 1, y: 1 }); // Démarre en position x=1 et y=1
const [fantome1Position, setFantome1Position] = useState({ x: 3, y: 5 }); // Démarre en position x=5 et y=5
useEffect(() => {
function handleKeyDown(event) {
deplacerPacman(event.key, pacmanPosition, labyrinthe, setPacmanPosition);
}
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, [pacmanPosition]);
useEffect(() => {
const intervalId = setInterval(() => {
deplacerFantome(fantome1Position, labyrinthe, setFantome1Position);
}, 1000); // Se déplace toutes les secondes (1000 millisecondes)
return () => clearInterval(intervalId);
// Ajoutez fantome1Position à la liste des dépendances pour réagir aux changements de position
}, [fantome1Position]);
return (
<View style={styles.container}>
<View>
<Labyrinthe labyrinthe={labyrinthe} />
<Pacman position={pacmanPosition} />
<Fantome position={fantome1Position} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});A quoi sert l'appel de la fonction
clearInterval?La fonction
clearIntervalest utilisé pour nettoyer et arrêter l'intervalle créé parsetIntervallorsque le composant est démonté ou lorsque les dépendances de l'effet changent, afin de s'assurer que les fonctions ne continuent pas à s'exécuter inutilement, pour ne pas bloquer l'exécution de l'application. 
Vous devriez obtenir quelque chose qui ressemble à cela :

Une solution
Vous devez être connecté pour voir le contenu.
Mission 2 - Afficher un 2ème fantôme
Votre nouvelle mission consiste à ajouter un 2ème fantôme.

Maintenant que vous avez réussi à afficher un premier fantôme, vous êtes capable d'ajouter un deuxième fantôme, en ajoutant quelques lignes de code.
Une solution
Vous devez être connecté pour voir le contenu.