|
|
|
|
|
Les IA:
Les 'IA' sont l'intelligence des bestioles;
ce sont elles qui, dans un monde donné, vont décider
des actions à effectuer pour survivre. Il est possible
de les programmer, ou de configurer une IA déja
existante (si celle ci peut être configurée...).
Les fichiers contenant ces IA sont des .class java (des programmes java compilés),
et se trouvent dans le repertoire 'iaengine/user '. Celles qui
peuvent être configurées à travers une
interface possèdent en plus un fichier pour l'IHM,
qui se trouve dans 'iaengine/user/configuig '.
Une IA ne fait que calculer
une proposition d'action pour une bestiole. Elle ne modifie JAMAIS la bestiole
elle-même (ex : sa position pour une action de déplacement).
En fait, toute action calculée est d'abord envoyée
au monde pour approbation, et c'est le manager, lorsqu'il
reçoit l'accord du monde, qui execute l'action
pour la bestiole concernée.
Les bugs dans cette classe peuvent coûter très
cher à la bestiole. Si par exemple, l'action calculée
par l'IA est de manger une nourriture inexistante, l'action
sera refusée. La bestiole peut donc rester immobile,
ce qui en fera une proie facile pour les autres. Les mondes envoient des messages de
refus d'actions précisant en quoi la décision de l'IA
était erronée; ce qui peut être utile
pour le débugage.
Deux astuces peuvent être essayées dans les IA (astuces que je n'ai pas codé dans les IA par défaut) :
- il est possible de créer une "mémoire"
de la bestiole en conservant les parties du monde vues à
chaque action, et donc d'améliorer l'IA.
- il doit aussi etre possible, en mettant
des données en 'static' (Cf. vos cours de Java), de mettre en
commun des données de chaque IA, pour en créer
une globale qui régit toutes les bestioles en tant
que groupe. A essayer, donc...
Les cadres qui suivent donnent des informations techniques et des obligations à suivre
pour programmer sa propre IA.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Le codage
d'une IA:
Une IA n'a qu'un accès limité
à l'instance de la bestiole qu'il contrôle,
par l'intermédiare de la classe 'bestioleFacade '.
Une IA est sensée pouvoir être utilisée pour chacune
des trois espèces de NeoNoe (celles ci n'ont pas
les mêmes IAs (pour ce qui est des combats et de l'alimentation)), et
pour les mondes ouverts ou les arènes (les sauts
entre deux domaines, en particulier, sont impossible dans
les arènes). Pour des raisons de sécurité, les droits des IA sont très limités: elles ne peuvent accèder au disque, au réseau, etc.
Si vous utilisez des sous-classes pour votre IA: soit elles sont
placées dans un repertoire, lui même placé
dans le repertoire des IAs, (ex: "iaengine/user/mon_ia_classes/mon_ia_fichier1.class "),
soit vous les codez en interne à la classe principale
(et les .class générés auront le nom
"classePrincipale$classeInterne.class "
et seront automatiquement reconnues comme classes internes
par NeoNoe).
Vous pouvez programmer votre IA comme vous
le voulez, la seule condition est que le moteur de NeoNoe
puisse appeler certaines fonctions bien précises.
La classe java doit donc hériter de la classe 'iaengine.skeleton.BestioleIA' .
Voici ce que doivent faire les fonctions:
public
void initIA(BestioleFacade b);
Crée les caracteristiques par defaut
de l'IA pour la bestiole b. Il faut appeler super.initIA(b) au début de la focntion.
public void
initIA(BestioleFacade b, Object[] config);
Configure l'IA par rapport
aux valeurs 'config' . Ces valeurs sont
celles retournées par l'IHM de configuration
de l'IA (voir l'IHM de configuration), si elle existe (sinon la fonction ne sera jamais appelée).
public
BestioleActionNotification computeAction(World world);
Fonction appelée pour calculer
l'IA de la bestiole.
Le monde passé en paramètre
est la portion du monde vue par la bestiole, donc
un carré dont la taille du coté est égal à 2 fois la capacité de vision, plus la case de
la bestiole. La case centrale de cette portion de monde est celle sur laquelle se trouve la bestiole.
(la classe Monde)
L'objet retourné est une notification
d'action qui sera envoyée au monde. Pas la
peine de retourner des actions bidons (du genre avancer
de 6000 cases), au mieux les actions de ce genre seront refusées.
La liste des action possibles se trouve plus bas.
public
String toString();
Retourne une description de l'état
de l'IA : par exemple, 'en fuite', 'attaque xx', 'cherche
a manger'... Ce texte sera visible dans l'écran des informations de la bestiole, dans l'onglet 'IA'.
public
void copy(BestioleIA bestioleIA);
Initialise l'IA en copiant les réglages
de 'bestioleIA'. Utilisé lors du chargement d'un fichier de sauvegarde, ou pour la naissance de bestioles
héritant du IA d'un des parents.
'bestioleIA' est TOUJOURS de la même classe que l'instance
sur laquelle est appelée cette fonction.
public
void doIACross(BestioleIA cb1, BestioleIA
cb2);
Copie une partie des deux IA
cb1 et cb2. Utilisé pour la naissance de bestioles
héritant de l'IA commune de ses parents.
'cb1' et 'cb2' sont TOUJOURS de la même classe
que l'instance sur laquelle est appelée cette
fonction.
public
void addToMutationSystem(MutationSystem mutSys);
Fonction utilisée pour spécifier
les variables de la classe qui seront soumises aux
mutations (voir L'IA
et l'évolution artificielle).
|
L'IHM de configuration de l'IA n'est pas stockée dans la classe
elle même, mais dans une classe à part. Il
faut indiquer le nom de cette classe dans le champ statique 'iAConfigWindow' de la classe de l'IA.
public static String iAConfigWindow
= "BasicIAConfig";
Les textes apparaissant dans NeoNoe concernant une IA, par exemple la description de la classe
(visible dans les fenêtres de configuration des managers et des bestioles pour la selection de l'IA),
ou les libellés pour la fenêtre de configuration de l'IA, sont
enregistrés dans un fichier texte à part, dans le répertoire 'messages' .
Ce fichier doit avoir pour nom nomIA_langue.properties , avec 'nomIA' le nom de la classe java de l'IA, et 'langue' la langue des textes (fr, en, de...).
Le fichier doit contenir au moins la clé DESCRIPTION=... qui sera la description de l'IA visible dans l'interface de NeoNoe.
Dans le code Java, ces textes peuvent être récupérés à l'aide du code getMessage("NOM_CLE") .
Par exemple, pour une IA enregistrée dans le fichier monIA.java :
1. Le fichier contenant les textes sera 'messages/monIA_fr.properties' pour les textes en français,
'messages/monIA_en.properties' pour les textes anglais...
2. Il contiendra, par exemple :
#Exemple de fichier contenant les textes nécessaires à une IA
DESCRIPTION=Ceci est la description de mon IA. Elle doit expliquer son fonctionnement pour d'éventuels autres utilisateurs.
#Textes de l'interface
Configuration.TITRE=Le titre de ma fenêtre
Configuration.LibelléChamp1=Entrez la valeur pour xxxx.
[...]
|
3. Ces textes seront utilisés dans le code Java de la façon suivante (ici, dans le code de l'interface de configuration):
[...]
public BasicIAConfig(String iAClassName, PropertyResourceBundle resourceBundle){
super( iAClassName, resourceBundle );
setLayout (new GridLayout(3,0));
JLabel titre = new JLabel(getMessage("Configuration.TITRE"));
titre.setFont(NeoNoe.DEFAULT_FONT);
add(titre);
[...]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Les
actions possibles:
Voici la liste des actions qu'il est possible d'envoyer au monde pour que sa bestiole les execute,
et leur utilisation (ce n'est pas bien compliqué !):
public
BestioleActionNotificationMove(int bestioleID, int
dx, int dy);
Indique un déplacement
de la bestiole, de vecteur (dx,dy) cases.
L'action est acceptée si :
1. -capacité_de_marche
<= dx <= -capacité_de_marche ,
2. -capacité_de_marche
<= dy <= -capacité_de_marche ,
3. 0<=
dx + position x < taille x du monde ,
4. 0<=
dy + position y < taille y du monde .
Notes : 1. le coùt en
force d'un déplacement est égal à
dx+dy.
2. dx = 1 représente
un déplacement d'une case à droite,
dy = 1 d'une case en bas.
public
BestioleActionNotificationEat(int idBestiole);
Indique que la bestiole va consommer
la nourriture placée sur sa case. L'action
est acceptée si de la nourriture se trouve
bien sur la case, et si elle à le droit de
la manger : une herbivore ne peut manger de cadavres,
une carnivore ne peut manger de nourriture naturelle.
Il faut généralement plusieurs tours
pour consommer totalement la ressource.
Note: Une omnivore qui envoie
cette notification pour une case qui contient un cadavre
et de la nourriture naturelle commence par manger
le cadavre. Elle a de plus des malus : elle assimile
moins la nourriture par action (pour compenser son
aptitude à utiliser toutes les ressources de
la carte).
public
BestioleActionNotificationAttaq(int idBestiole, int
iDCible);
Indique que la bestiole idBestiole
veut attaquer
une autre bestiole iDCible sur sa case. L'action est
acceptée si une bestiole dont l'identifiant
est iDCible se trouve bien sur la case. Détails
qui peuvent servir :
- si idBestiole=iDCible,
la bestiole s'attaque elle même... ;-P
- la bestiole
peut perdre son combat et se faire tuer
- le combat
peut être nul, même si les forces ne sont
pas exactement égales, à cause du 'flou
de combat' du monde. Par exemple, un flou de combat
de 10% provoquera des matchs nuls si la bestiole attaquante
n'a pas 10% de force de plus que sa cible.
- les forces
des bestioles attaquées sont influencées
par le terrain. Par exemple, en montagne, une bestiole
à 100% de force en plus en défense.
(Ce bonus ne s'applique pas aux attaques.)
- les forces
des bestioles sont influencées par les malus
inhérents à leur espèce. Par
exemple, une herbivore à un malus de 80% (par
défaut, mais cela peut changer suivant les mondes) de sa force si elle se fait attaquer.
La meilleure solution pour elle est donc de courir
vite!
public BestioleActionNotificationJump(int
idBestiole, int dx, int dy, int dz);
Indique un saut d'un domaine à un autre
de la bestiole idBestiole. Les trois valeurs permettent
de calculer la 'dimension' du saut (Cf les
sauts).
L'action est acceptée si :
1. dx<=capacité_de_marche
et dy<=capacité_de_marche
et dz<=capacité_de_marche
,
2. Le mouvement
(dx, dy) fait sortir la bestiole des limites du domaine
dans lequel est évolue.
3. La bestiole n'est pas dans une arène.
Note : le coùt en force d'un saut est égal
à dz si le saut se fait sur Z, dx s'il se fait
sur X, et dy s'il se fait sur Y.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Les
sauts entre domaines:
Les domaines sont disposés sur trois dimensions
: les deux premières correspondent aux dimensions
des mondes (X = largeur et Y = hauteur) , la troisième
à une sorte "épaisseur", comme si
les domaines étaient empilés les uns sur les
autres (Z). Un saut peut se faire dans deux sens, selon
si dx (ou dy ou dz) est supérieur à zéro
ou non.
Un saut sur une dimension, dans un sens,
suivit d'un saut sur la même dimension, dans l'autre
sens doit ramener la bestiole dans son domaine d'origine
(à moins qu'un domaine intermédiare ne se
soit créé entre les deux sauts!) . Dans certains
cas, un saut peut ramener sur le même domaine (surtout
les sauts sur Z).
Un saut ne peut s'effectuer que sur
une seule dimension à la fois : si une bestiole est
dans le coin bas-droit de la carte et que son saut est de
dx=1 et dy=1, elle sautera dans le domaine "de droite",
et non pas dans le domaine d'"en bas à droite".
Le calcul de la dimension se fait dans l'ordre :
1. si dz != 0 : saut sur la dimension
Z.
2. si dx != 0 : saut sur la dimension
X.
3. si dy != 0 : saut sur la dimension
Y.
La position d'arrivée de la bestiole
après un saut dépend de la dimension du saut.
Dans le cas d'un saut sur la dimension X, la bestiole arrivera
sur le bord gauche (ou droit, selon le sens) du domaine
voisin.Sa position en hauteur sera la même position
qu'au départ, relativement à la taille du
domaine. Un saut sur Z fera atterrir la bestiole sur la
même position qu'au départ, relativement à
la taille du domaine.
Deux exemples plus parlant, de sauts d'un domaine de taille
(10, 10) à un domaine de taille(20,20):
1. position de départ = (5,9)
==> un saut sur Y amène en position (10,0).
2. position de départ = (2,5) ==>
un saut sur Z amène en position (2x2,2x5) = (4,10).
Pour info : l'ordre des domaines est décidé à
partir des IP et ports des mondes. La dimension Z correspond
au port, la dimension X aux deux premiers octets de l'IP,
la dimension Y aux deux derniers octets.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
L'API pour les IA
Aucune objet (monde, bestiole) n'est lié directement
à une IA, pour éviter
qu'elle ait accès à des fonctions
dont elle ne doit pas avoir l'usage. Des classes intermédaire
, 'xxxFacade', sont programmées pour limiter ces
accès.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Les
fonctions utiles de la classe World:
Si vous avez bien suivi, le monde est composé
de cases de différents terrains. Ces terrains apportent
(ou pas) un bonus de défense, et (ou pas) de la nourriture.
Le monde contient aussi, pour chaque case, le liste des
bestioles qui y sont placées.
public
int getWidth();
Retourne la largeur du monde.
public
int getHeight();
Retourne la hauteur du monde.
public
Ground getGroundAt(int x, int y);
Retourne l'instance du terrain
de la case (x,y).
public int getVegetableFoodSizeAt(int
x, int y);
Retourne la taille de la nourriture
naturelle de la case (x,y) (0 si pas de nourriture).
public String
getVegetableFoodNameAt(int x, int y);
Retourne le nom de la nourriture
naturelle de la case (x,y) (null si pas de nourriture).
public
int getNonNaturalFoodSizeAt(int x, int y);
Retourne la taille des cadavres
de la case (x,y) (0 si pas de nourriture).
public
String getNonNaturalFoodNameAt(int
x, int y);
Retourne le nom de la nourritre
de la case (x,y) (null si pas de nourriture).
public
BasicBestioleList getBestioleListAt(int x, int y);
Retourne la liste des bestioles
présentes sur la case (x,y).
public
boolean cellCoordsOK(int x, int y);
Retourne true si la case (x,y) existe sur le monde.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Les fonctions
utiles de la classe Ground:
Chaque case de terrain peut contenir de la nourriture.
public
int getDefenceBonus();
Retourne le bonnus de défense
(50 => +50%).
public
int getGroundName();
Retourne le nom du terrain.
public int getVegetationFoodSize();
Retourne la taille de la nourriture
naturelle (0 si pas de nourriture).
public String
getVegetationFoodName();
Retourne l'instance de la nourriture
naturelle (null si pas de nourriture).
public
boolean isVegetationFoodPossible();
Retourne true si la nourriture
naturelle peut pousser sur ce type de terrain.
public
boolean isVegetationFood();
Retourne true s'il y a de la
nourriture naturelle sur le terrain.
public
int getNonNaturalFoodSize();
Retourne la taille des cadavres
de la case (0 si pas de nourriture).
public
NonNaturalFood getNonNaturalFood();
Retourne l'instance des cadavres
de la case (null si pas de nourriture).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Les fonctions
utiles de la classe Bestiole:
Les fonctions pour une bestiole sont :
public
int getBestioleID();
Retourne l'identifiant de la bestiole.
public
int getIDClient();
Retourne
l'identifiant du client propriétaire de la
bestiole.
public int getPower();
Retourne la force brute de la
bestiole.
public
int getVisionCapacity();
Retourne la capacité de
vision de la bestiole.
public
int getMovementCapacity();
Retourne la capacité de
déplacement de la bestiole.
public
int getPosX();
Retourne la position (en largeur)
de la bestiole dans le monde.
public
int getPosY();
Retourne la position (en hauteur)
de la bestiole dans le monde.
public int getDefenceMalus();
Retourne le malus de défense
inhérent à l'espèce de la bestiole.
public int getAttaqMalus();
Retourne le malus d'attaque inhérent
à l'espèce de la bestiole.
public int getFoodEatCapacity();
Retourne la quantité de
nourriture naturelle mangeable en un tour.
public int getCorpseEatCapacity();
Retourne la quantité de
chair mangeable en un tour.
public int getFoodMalus();
Retourne
le malus d'absorption de nourriture naturelle inhérent
à l'espèce de la bestiole.
public int getCorpseMalus();
Retourne
le malus d'absorption de de cadavres inhérent
à l'espèce de la bestiole.
public
boolean sameBestiole(Bestiole b);
Retourne true si la bestiole b
est la bestiole à qui appartient l'instance
de l'IA qui appelle la fonction.
public ServerType getServerType();
Retourne le typt de serveur (arène ou domaine) dans lequel la bestiole évolue.
public Specie getSpecie();
Retourne l'espèce de la bestiole (omnivore, carnivore ou herbivore)
public
Component getParentComponent();
Cette fonction retourne un composant
de NeoNoe sur lequel rattacher d'eventuels boites
de messages, de dialogue...
public
boolean hasLastActionFailed();
Cette fonction retourne true,
si l'action précédente n'a pas été
validée par le monde. Utile pour débuger!
|
Les
modificateurs : une IA peut forcer certaines
caractéristiques de la bestiole, comme par exemple
la capacité de vision, ou la possiblité que
la vision evolue, etc. Dans ce cas, il est préférable
de le signaler clairement dans la description
de votre IA, et ces modifications doivent être faites
à l'initialisation, JAMAIS pendant le jeu.
public setMovementCapacityEvoluable(boolean);
Active / désactive l'evolution de la capacité
de marche.
public setVisionCapacityEvoluable(boolean);
Active / désactive
l'evolution de la capacité de vision.
public setVisionCapacity(int cv);
Modifie
la capacité de vision.
public setMovementCapacity(int cm);
Modifie
la capacité de marche.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Les fonctions
utiles de la classe BestioleList:
public Bestiole getBestioleAt(int index);
Retourne la i-ème bestiole
de la liste (la première est au rang 0).
public int size();
Retourne
le nombre de bestioles de la liste (0 si vide).
public Bestiole getByBestioleID(int bestioleID);
Retourne la bestiole dont l'identifiant
est 'bestioleID ', null si elle n'est
pas dans la liste.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
L'IHM de configuration de l'IA:
Vous pouvez programmer votre IHM de configuration
comme vous le voulez, la seule condition est que le moteur
de NeoNoe puisse appeler certaines fonctions précises. La
classe java doit donc hériter de la classe 'iaengine.skeleton.BestioleIAConfig' ,
qui contient une fonction appelée à la
validation de la configuration, et une autre appelée
pour initialiser les valeurs de champs de l'IHM en fonction
de la bestiole à configurer.
public
Object[] submit();
Retourne un tableau contenant
les parametres réglés par l'utilisateur.
C'est ce tableau qui est passé en parametre
à la fonction initIA.
public
void show(BestioleIA ia);
Fonction
appelée à l'affichage de l'IHM. Pour une création d'IA : ia == null,
sinon il faut remplir les champs de l'IHM avec les
valeurs de l'IA passée en paramètre.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
L'IA et l'évolution artificielle:
L'IA étant un programme, ce n'est pas
le code lui même qui évolue, mais ses variables.
Pour indiquer qu'une variable est 'mutable' (c'est à dire que sa valeur peut changer par mutation), il faut ajouter
une ligne dans la fonction 'addToMutationSystem(MutationSystem
mutSys) ' de la classe Java de l'IA, ce qui ajoutera cetet variable dans le système qui
gère les mutations de NeoNoe. Par exemple, dans la
classe BasicIA, le code suivant indique au système
de mutation que la variable représentant le seuil
en dessous duquel la bestiole cherche de la nourriture est
mutable, indique sa valeur minimale, sa valeur maximale,
et un texte la décrivant, pour un affichage éventuel
de la mutation.
mutSys.add(
new IAMutation(
getClass().getDeclaredField("SEUIL_SEUL_CHERCHE_MANGER"),
// Champ de l'objet
new Integer(0), // valeur
minimum
new Integer(MAX_SEUIL_SEUL_CHERCHE_MANGER),
// valeur maximum
"Seuil 'cherche_a_manger'" //
description pour affichage
)); |
Notes:
- Deux types
de mutations existent : 'IAMutation ',
pour les mutations de l'IA, et 'FeatureMutation '
pour celles de la bestiole elle même. N'ajoutez
pas de 'FeatureMutation ' au système
de mutation d'une IA, ca ne servirait à rien.
- Le système de mutation ne gère pour l'instant
que les entiers et les tableaux d'entiers... je sais,
ce n'est pas pratique, et ça fait la 2564ème
chose qu'il faut améliorer dans NeoNoe.
- Dans le cas d'un tableau de valeur, la mutation ne s'applique
qu'à une seule valeur, pas à toutes.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|