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
directionsPossibles
avec 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.length
est égale à 4directionsPossibles[0]
est égale àArrowUp
c'est à dire la touche de flèche de direction HautdirectionsPossibles[1]
est égale àArrowDown
c'est à dire la touche de flèche de direction BasdirectionsPossibles[2]
est égale àArrowLeft
c'est à dire la touche de flèche de direction GauchedirectionsPossibles[3]
est égale àArrowRight
c'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
React
et du composantView
dans votre fichierFantome.js
:import React from 'react';
import { View } from 'react-native'; -
Créez une fonction
Fantome
dans 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
choisirDirectionAleatoire
dans 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
deplacerFantome
dans 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.js
pour 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
clearInterval
est utilisé pour nettoyer et arrêter l'intervalle créé parsetInterval
lorsque 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.