mirror of
https://github.com/Cpt-Adok/SNAKE.git
synced 2026-01-25 12:34:07 +00:00
changement de nom de dossier
This commit is contained in:
99
src/personnage/IAQLearning.java
Normal file
99
src/personnage/IAQLearning.java
Normal file
@@ -0,0 +1,99 @@
|
||||
package personnage;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import IA.*;
|
||||
import connexion.Channel;
|
||||
import environnement.Grid;
|
||||
import environnement.Map;
|
||||
import types.Mouvement;
|
||||
|
||||
/**
|
||||
* La classe IAQLearning représente un joueur contrôlé par une Intelligence Artificielle
|
||||
* utilisant l'algorithme de Q-learning pour prendre des décisions dans un jeu.
|
||||
*/
|
||||
public class IAQLearning extends Personnage {
|
||||
private QLearning qLearning; // L'algorithme de Q-learning utilisé par l'IA.
|
||||
|
||||
/**
|
||||
* @param coordinate Les coordonnées initiales de l'IA sur la grille de jeu.
|
||||
* @param qTable La table Q utilisée par l'IA pour stocker les valeurs Q.
|
||||
* @param alpha Le taux d'apprentissage de l'algorithme de Q-learning.
|
||||
* @param gamma Le facteur de récompense future de l'algorithme de Q-learning.
|
||||
* @param epsilon Le taux d'exploration de l'algorithme de Q-learning.
|
||||
*/
|
||||
public IAQLearning(int[] coordinate, QTable qTable, double alpha, double gamma, double epsilon) {
|
||||
super(coordinate); // Appel au constructeur de la classe mère.
|
||||
|
||||
// Initialisation de l'algorithme de Q-learning avec les paramètres spécifiés.
|
||||
this.qLearning = new QLearning(qTable, alpha, gamma, epsilon);
|
||||
|
||||
// Attribution d'un nom unique à l'IA.
|
||||
this.name = "IA : " + UUID.randomUUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* ce constructeur est uniquement pour lancer le programme en tant que joueur,
|
||||
* il n'apprendra rien du tout dans cette configuration.
|
||||
* @param coordinate
|
||||
* @param qTable
|
||||
*/
|
||||
public IAQLearning(int[] coordinate, QTable qTable) {
|
||||
super(coordinate); // Appel au constructeur de la classe mère.
|
||||
this.qLearning = new QLearning(qTable, 0.0, 0.0, 0.0);
|
||||
this.name = "IA : " + UUID.randomUUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* cette classe obtient l'état actuel de l'IA à partir de la grille de jeu.
|
||||
*
|
||||
* @param grid La grille de jeu.
|
||||
* @return L'état actuel de l'IA.
|
||||
*/
|
||||
public State getCurrentState(Grid[][] grid) {
|
||||
return new State(grid, this.getCoordinate());
|
||||
}
|
||||
|
||||
/**
|
||||
* cette classe sélectionne le meilleur mouvement à effectuer dans l'état actuel selon l'algorithme de Q-learning.
|
||||
*
|
||||
* @param state L'état actuel de l'IA.
|
||||
* @return Le meilleur mouvement à effectuer.
|
||||
*/
|
||||
public Mouvement bestMouvement(State state) {
|
||||
return qLearning.chooseMouvements(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* cette classe reçoit une récompense pour l'action effectuée dans l'état actuel et met à jour la valeur Q correspondante.
|
||||
*
|
||||
* @param state L'état actuel de l'IA.
|
||||
* @param mouvement Le mouvement effectué dans l'état actuel.
|
||||
* @param reward La récompense reçue pour l'action effectuée.
|
||||
* @param nextState L'état suivant vers lequel l'IA se déplace après avoir effectué l'action.
|
||||
*/
|
||||
public void receiveReward(State state, Mouvement mouvement, double reward, State nextState) {
|
||||
qLearning.updateQValue(state, mouvement, reward, nextState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean round(Map map, String channel) {
|
||||
Mouvement mouvement = this.bestMouvement(this.getCurrentState(map.getGrid()));
|
||||
this.moveSnake(mouvement);
|
||||
|
||||
int[] coordinate = this.getHeadCoordinate();
|
||||
|
||||
if (channel != null) Channel.envoyerMessage(mouvement);
|
||||
if(map.isGameOver(coordinate) || this.applyEffects(map.getEffect(coordinate))) return true;
|
||||
map.deleteItems(coordinate);
|
||||
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
this.increaseRound();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
179
src/personnage/Personnage.java
Normal file
179
src/personnage/Personnage.java
Normal file
@@ -0,0 +1,179 @@
|
||||
package personnage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import environnement.*;
|
||||
import types.*;
|
||||
|
||||
/**
|
||||
* Cette classe est la primitive des classes
|
||||
* {@link Player} et {@link Robot}. Elle contient
|
||||
* tout le necessaire pour tout les personnages
|
||||
* jouable du jeu.
|
||||
*/
|
||||
public abstract class Personnage {
|
||||
/**
|
||||
* cette variable contient la valeur dans laquelle on sait
|
||||
* quand le corps du snake grandi. il est le même pour tout
|
||||
* les personnages du jeu et c'est pour ça que c'est un
|
||||
* static. on peut le changer directement dans le main en
|
||||
* faisant la commande par exemple : <pre><code>
|
||||
* Personnage.n = 2</code></pre> sans appeler
|
||||
* player ou robot, les deux valeurs vont "automatiquement"
|
||||
* changer.
|
||||
*/
|
||||
public static int n;
|
||||
|
||||
/**
|
||||
* cette variable est le nombre de round fait par le snake,
|
||||
* il s'incremente tout les tours quand le snake avance.
|
||||
*/
|
||||
private int round;
|
||||
|
||||
/**
|
||||
* cette variable est le nom du snake présent dans le tournois,
|
||||
* il peut etre un mot aléatoire ou quelque chose de personnalisé
|
||||
*/
|
||||
protected String name;
|
||||
|
||||
/**
|
||||
* cette variable accumule toute les coordonnées du serpent, elle
|
||||
* contient le corps et la tête et la première coordonnées est la tête.
|
||||
* Le programme peut ajouter des coordonnées en fonction de round et n,
|
||||
* exemple :
|
||||
* <pre><code>
|
||||
* si n > 0 et round > 0 ->
|
||||
* retourner round%n == 0
|
||||
* sinon
|
||||
* retourner faux
|
||||
* </code></pre>
|
||||
*/
|
||||
private ArrayList<int[]> coordinate;
|
||||
|
||||
/**
|
||||
* Le constructor initialise la variable {@link
|
||||
* #coordinate} et prend en paramètre <strong>
|
||||
* personnageCoordinate</strong> qui est la tête
|
||||
* de notre snake. Il place dans la liste {@link
|
||||
* #coordinate} la coordonnée. Nous pourrons
|
||||
* recuperer les coordonnées de la tête en
|
||||
* utilisant la fonction {@link #getHeadCoordinate()}.
|
||||
*/
|
||||
protected Personnage(int[] personnageCoordinate) {
|
||||
coordinate = new ArrayList<>();
|
||||
coordinate.add(personnageCoordinate);
|
||||
}
|
||||
|
||||
/**
|
||||
* cette fonction applique tout les effets defini dans
|
||||
* {@link Effects} et en même temps, sert de fermeture
|
||||
* du programme avec le boolean si l'effet choisi est
|
||||
* IMPASSABLE.
|
||||
* @param effect est la variable qui contient tout les
|
||||
* effets de {@link Effects}
|
||||
* @return <pre><code>
|
||||
* si effect == IMPASSABLE ->
|
||||
* retourner true
|
||||
*sinon
|
||||
* retourner false
|
||||
* </code></pre>
|
||||
*/
|
||||
public boolean applyEffects(Effect effect) {
|
||||
switch (effect) {
|
||||
case DECREASESIZE:
|
||||
if (this.coordinate.size() > 1) {this.coordinate.removeLast();} break;
|
||||
|
||||
case VOID: break;
|
||||
case IMPASSABLE: return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* cette fonction renvoie la tête du snake mais en clone
|
||||
* donc elle fait une copie de la position existante pour
|
||||
* eviter de prendre l'adresse mémoire de la liste et pouvoir
|
||||
* utiliser celle là comme on veut sans affecter la liste.
|
||||
*/
|
||||
public int[] getHeadCoordinate() {
|
||||
return this.coordinate.getFirst().clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* cette fonction renvoie la queue du snake mais en clone
|
||||
* donc elle fait une copie de la position existante pour
|
||||
* eviter de prendre l'adresse mémoire de la liste et pouvoir
|
||||
* utiliser celle là comme on veut sans affecter la liste.
|
||||
*/
|
||||
public int[] getLatestCoordinate() {
|
||||
return this.coordinate.getLast().clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* cette fonction renvoie un clone de la liste de coordonnée
|
||||
* donc elle fait une copie des positions existante pour
|
||||
* eviter de prendre l'adresse mémoire de l'ArrayList et pouvoir
|
||||
* utiliser celle là comme on veut sans affecter l'ArrayList.
|
||||
*/
|
||||
public ArrayList<int[]> getCoordinate() {
|
||||
return new ArrayList<>(this.coordinate);
|
||||
}
|
||||
|
||||
/**
|
||||
* cette fonction incremente la variable {@link #round}.
|
||||
*/
|
||||
public void increaseRound() {
|
||||
++this.round;
|
||||
}
|
||||
|
||||
/**
|
||||
* cette fonction renvoie la variable {@link #round}.
|
||||
*/
|
||||
public int getRound() {
|
||||
return round;
|
||||
}
|
||||
|
||||
/**
|
||||
* cette fonction renvoie la variable {@link #name}.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* cette fonction renvoie la longueur de {@link #getCoordinate()}.
|
||||
*/
|
||||
public int getSize() {
|
||||
return this.getCoordinate().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* cette fonction change tout les coordonnées du serpent en mettant
|
||||
* la coordonnée de celui d'avant, il prend [1, 2, 3, 4] et fais en
|
||||
* premier temps [1, 1, 2, 3] puis modifie la premiere coordonnée
|
||||
* avec Mouvement, si il y a -1, ça fait [0, 1, 2, 3].
|
||||
* @param mouvement est le mouvement du personnage, comme HAUT, BAS,
|
||||
* GAUCHE et DROITE.
|
||||
*/
|
||||
public void moveSnake(Mouvement mouvement) {
|
||||
int[] latestCoordinate = getLatestCoordinate();
|
||||
|
||||
for (int i = this.coordinate.size() - 1; i > 0; i--) {
|
||||
this.coordinate.set(i, this.coordinate.get(i - 1).clone());
|
||||
}
|
||||
|
||||
mouvement.updateCoordinate(this.coordinate.getFirst());
|
||||
increaseSnake(latestCoordinate);
|
||||
}
|
||||
|
||||
/**
|
||||
* ajoute la coordonnée mis en paramètre dans le dernier emplacement si
|
||||
* round > 0 et n > 0 et round%n = 0.
|
||||
*/
|
||||
private void increaseSnake(int[] coordinate) {
|
||||
if(round > 0 && n > 0) if (round%n == 0) this.coordinate.add(coordinate);
|
||||
}
|
||||
|
||||
public abstract boolean round(Map map, String channel);
|
||||
}
|
||||
97
src/personnage/Player.java
Normal file
97
src/personnage/Player.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package personnage;
|
||||
|
||||
import java.util.Scanner;
|
||||
|
||||
import connexion.Channel;
|
||||
import environnement.Map;
|
||||
import types.*;
|
||||
|
||||
/**
|
||||
* la classe Player a comme classe parent {@link Personnage}
|
||||
* et qui contient tout les besoins primaire pour le bon
|
||||
* fonctionnement de la classe Player. cette classse est très
|
||||
* utile pour qu'un humain puisse jouer.
|
||||
*/
|
||||
public class Player extends Personnage {
|
||||
/**
|
||||
* la classe Player a comme classe parent {@link Personnage}
|
||||
* et qui contient tout les besoins primaire pour le bon
|
||||
* fonctionnement de la classe Player. Il comporte les coordonnées
|
||||
* initiales pour placer correctement le personnage dans la grille
|
||||
* du jeu.
|
||||
* @param coordinate
|
||||
* @param name
|
||||
*/
|
||||
public Player(int[] coordinate, String name) {
|
||||
super(coordinate);
|
||||
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean moveCoordinate(int keys) {
|
||||
Mouvement value = getMouvement(keys);
|
||||
|
||||
if (value != null) {
|
||||
moveSnake(value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Mouvement getMouvement(Integer keys) {
|
||||
switch (keys) {
|
||||
case 0x77: case 0x7A: return Mouvement.HAUT; // w ou z
|
||||
case 0x73: return Mouvement.BAS; // s
|
||||
case 0x61: case 0x71: return Mouvement.GAUCHE; // a ou q
|
||||
case 0x64: return Mouvement.DROITE; // d
|
||||
case null: return null;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* transforme le String en prennant le premier char et
|
||||
* le mets en ascii dans la classe Integer.
|
||||
* @param input
|
||||
* @return
|
||||
*/
|
||||
private Integer changeCoordinate(String input) {
|
||||
if (input.length() > 0) {
|
||||
return (int)input.charAt(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cette fonction est uniquement destiné pour la classe
|
||||
* Players pour recuperer l'input dans le terminal.
|
||||
* @param player
|
||||
* @return il retourne int qui est le char en ascii
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
private int getInput() {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
Integer input = null;
|
||||
|
||||
while(this.getMouvement(input) == null) {
|
||||
input = this.changeCoordinate(scanner.nextLine());
|
||||
}
|
||||
|
||||
// scanner.close();
|
||||
return input.intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean round(Map map, String channel) {
|
||||
int keys = this.getInput();
|
||||
this.moveCoordinate(keys);
|
||||
|
||||
int[] coordinate = this.getHeadCoordinate();
|
||||
if (channel != null) Channel.envoyerMessage(getMouvement(keys));
|
||||
if(map.isGameOver(coordinate) || this.applyEffects(map.getEffect(coordinate))) return true;
|
||||
map.deleteItems(coordinate);
|
||||
|
||||
this.increaseRound();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
160
src/personnage/Robot.java
Normal file
160
src/personnage/Robot.java
Normal file
@@ -0,0 +1,160 @@
|
||||
package personnage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
|
||||
import types.*;
|
||||
import connexion.*;
|
||||
import environnement.*;
|
||||
|
||||
public class Robot extends Personnage {
|
||||
|
||||
Map m;
|
||||
Mouvement move;
|
||||
String name;
|
||||
|
||||
public Robot(String name, int[] coordinate) {
|
||||
super(coordinate);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**Fonction commune aux sous-classes de Personnage
|
||||
* permettant le renvoi d'un mouvement pour chacun.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean round(Map map, String channel){
|
||||
this.m = map;
|
||||
|
||||
this.move=this.compare(this.choix(),this.getHeadCoordinate());
|
||||
// System.out.println("Mouvement choisi : "+this.move);
|
||||
this.moveSnake(move);
|
||||
|
||||
int[] coordinate = this.getHeadCoordinate();
|
||||
if (channel != null) Channel.envoyerMessage(this.move);
|
||||
|
||||
if(map.isGameOver(coordinate) || this.applyEffects(map.getEffect(coordinate))) return true;
|
||||
|
||||
map.deleteItems(coordinate);
|
||||
this.increaseRound();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accès à la variable move
|
||||
* @return Mouvement
|
||||
*/
|
||||
public Mouvement getMove(){
|
||||
if (this.move!=null){
|
||||
return move;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de savoir si une case de la map est vide ou si elle est impassable
|
||||
* @param x coordonnée longueur
|
||||
* @param y coordonnée largeur
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean estPossible(int x,int y){
|
||||
Grid [][] grille=this.m.getGrid();
|
||||
if (x>=0 && x<grille.length && y>=0 && y<grille[0].length){
|
||||
if (m.getEffect(this.creerTab(x, y))!=Effect.IMPASSABLE){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction pour éviter les répétitions
|
||||
* Permet de créer un tableau à partir de
|
||||
* @param x et
|
||||
* @param y
|
||||
* @return int []
|
||||
*/
|
||||
public int [] creerTab(int x,int y){
|
||||
int [] t=new int [] {x,y};
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifie les cases présentes autour de la tête du serpent
|
||||
* @param co coordonnées de la tête
|
||||
* @return ArrayList<int []>, ArrayList des positions autour
|
||||
*/
|
||||
public ArrayList<int []> casesAutour(int [] co){
|
||||
ArrayList<int []> autour=new ArrayList<>();
|
||||
int x=co[0]; // x= ligne
|
||||
int y=co[1]; // y= colonne
|
||||
autour.add(creerTab(x+1, y));
|
||||
autour.add(creerTab(x-1, y));
|
||||
autour.add(creerTab(x, y+1));
|
||||
autour.add(creerTab(x, y-1));
|
||||
return autour;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'identifier les cases valables parmi les voisines de la tête du serpent
|
||||
* @param co
|
||||
* @return ArrayList<int[]> regroupant les cases voisines qui sont jouables sans mourir
|
||||
*/
|
||||
|
||||
public ArrayList<int []> coupsPossibles(int [] co) {
|
||||
ArrayList<int []> coupsValables=new ArrayList<int []> ();
|
||||
ArrayList<int []> autour=casesAutour(co);
|
||||
for (int [] e:autour){
|
||||
if (this.estPossible(e[0], e[1])){
|
||||
coupsValables.add(e);
|
||||
}
|
||||
}
|
||||
return coupsValables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Décision finale aléatoire du coup parmi ceux possibles
|
||||
* @return int [] des coordonnées du coup
|
||||
*/
|
||||
|
||||
public int [] choix(){
|
||||
Random r=new Random();
|
||||
ArrayList <int[]> cases=coupsPossibles(this.getHeadCoordinate());
|
||||
if (cases.size()==0){
|
||||
return this.suicide();
|
||||
}
|
||||
int [] choix=cases.get(r.nextInt(cases.size()));
|
||||
return choix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de mettre fin à la partie quand le serpent est coincé, et que aucun n'est valable
|
||||
* @return int [] des coordonnées du coup
|
||||
*/
|
||||
|
||||
public int [] suicide(){
|
||||
ArrayList <int []> murs=this.casesAutour(getHeadCoordinate());
|
||||
Random r=new Random();
|
||||
return murs.get(r.nextInt(4));
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparaison des coordonnées de la tête par rapport à celle du coup
|
||||
* @param t coup
|
||||
* @param t2 tête
|
||||
* @return Mouvement pour jouer le coup
|
||||
*/
|
||||
|
||||
public Mouvement compare(int[] t,int[] t2){
|
||||
if (t[0]>t2[0]){
|
||||
return Mouvement.DROITE;
|
||||
}else if (t[0]<t2[0]){
|
||||
return Mouvement.GAUCHE;
|
||||
}else if (t[1]<t2[1]){
|
||||
return Mouvement.HAUT;
|
||||
}else if (t[1]>t2[1]){
|
||||
return Mouvement.BAS;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user