parent
3bef0b087d
commit
c174a9b573
@ -0,0 +1,86 @@
|
||||
package projet.iut.jeu_de_la_vie.model;
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.Cellule;
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.observer.ObserverCellule;
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.Position;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Représentation des cellules vivantes sur le plateau. Elle se met à jours automatiquement.
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public class CellulesVivantes implements ObserverCellule {
|
||||
|
||||
/**
|
||||
* Dictionaire contenant toutes les cellules vivantes
|
||||
*/
|
||||
private HashMap<Position, Cellule> cellVivantes;
|
||||
|
||||
public CellulesVivantes(){
|
||||
this(new HashMap<>());
|
||||
}
|
||||
|
||||
private CellulesVivantes(HashMap<Position, Cellule> cellVivantes){
|
||||
this.cellVivantes = cellVivantes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperer une cellule vivante
|
||||
* @param x Absisse de la cellule
|
||||
* @param y Ordonée de la cellule
|
||||
* @return La cellule (x; y) si elle est vivante. Sinon null
|
||||
*/
|
||||
public Cellule getAt(int x, int y){
|
||||
Position p = new Position(x, y);
|
||||
return cellVivantes.get(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute une paire clef:valeur (Postion:Cellule) dans le dictionaire contenant les cellules vivantes
|
||||
* @param cell Cellule à ajouter
|
||||
* @see Position
|
||||
* @see Cellule
|
||||
*/
|
||||
private void addPeer(Cellule cell){ cellVivantes.put(cell.getPosition(), cell); }
|
||||
|
||||
/**
|
||||
* Retir une paire clef:valeur (Postion:Cellule) du dictionaire contenant les cellules vivantes
|
||||
* @param cellule Cellule à retirer
|
||||
* @see Position
|
||||
* @see Cellule
|
||||
*/
|
||||
private void rmPeer(Cellule cellule){
|
||||
cellVivantes.remove(cellule.getPosition());
|
||||
}
|
||||
|
||||
/**
|
||||
* Comportement lors ce que le cellule notifit l'objet CellulesVivantes.
|
||||
* Ici on ajoute ou retire la cellule du dictionaire qui contient les cellules vivante en fonction de la cellule qui à notifiée.
|
||||
* @param cellule Cellule qui à notifiée
|
||||
*/
|
||||
@Override
|
||||
public void update(Cellule cellule) {
|
||||
if(cellule.isAlive()){
|
||||
addPeer(cellule);
|
||||
} else {
|
||||
rmPeer(cellule);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cloner l'objet
|
||||
* @return Le meme objet CellulesVivantes avec une référence diférente
|
||||
*/
|
||||
public CellulesVivantes clone(){
|
||||
return new CellulesVivantes(new HashMap<>(cellVivantes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Nétoie le dictionaire contenant les cellules vivantes
|
||||
*/
|
||||
public void reset(){
|
||||
cellVivantes = new HashMap<>();
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package projet.iut.jeu_de_la_vie.model;
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.actualiseur.ActualiseurCellule;
|
||||
import projet.iut.jeu_de_la_vie.model.actualiseur.ActualiseurEtatCellule;
|
||||
import projet.iut.jeu_de_la_vie.model.arbitre.ArbitreConwayStyle;
|
||||
import projet.iut.jeu_de_la_vie.model.arbitre.ArbitreKiller;
|
||||
import projet.iut.jeu_de_la_vie.model.plateau.Plateau;
|
||||
|
||||
/**
|
||||
* Permet de gerer le changement de règles
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public class ChangeurRegle {
|
||||
private Regle regleEnCours;
|
||||
public Regle getRegleEnCours() { return regleEnCours; }
|
||||
public void setRegleEnCours(Regle regleEnCours) { this.regleEnCours = regleEnCours; }
|
||||
|
||||
public ChangeurRegle(){
|
||||
setRegleEnCours(Regle.values()[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change l'actualiseur en fonction des règles
|
||||
* @param plateau Plateau actuel du jeu
|
||||
* @return Un ActualiseurCellule avec le bon arbitre
|
||||
* @see projet.iut.jeu_de_la_vie.model.arbitre.Arbitre
|
||||
* @see ActualiseurCellule
|
||||
*/
|
||||
public ActualiseurCellule changerRegle(Plateau plateau) {
|
||||
switch (getRegleEnCours()){
|
||||
case REGLE_KILLER:
|
||||
return new ActualiseurEtatCellule(new ArbitreKiller(plateau));
|
||||
default:
|
||||
return new ActualiseurEtatCellule(new ArbitreConwayStyle(plateau));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package projet.iut.jeu_de_la_vie.model;
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.Cellule;
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.Position;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Classe permtant de compter des cellules
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public class CompteurDeCellule {
|
||||
|
||||
/**
|
||||
* Compte le nombre de voisinne de la cellule (x; y)
|
||||
* @param x Absisse de la cellule à compter
|
||||
* @param y Ordoné de la cellule à compter
|
||||
* @param cellulesVivantes Toutes les cellule vivantes
|
||||
* @return Le nombre de voisinne de la cellule (x; y)
|
||||
*/
|
||||
public int compteNombreCellulesAutour(int x, int y, CellulesVivantes cellulesVivantes){
|
||||
int cpt = 0;
|
||||
Cellule c;
|
||||
List<Position> positionsAVerifier = new LinkedList<>();
|
||||
|
||||
// Définition des 8 diréction autour de la position (x; y)
|
||||
positionsAVerifier.add(new Position(x-1, y-1));
|
||||
positionsAVerifier.add(new Position(x, y-1));
|
||||
positionsAVerifier.add(new Position(x+1, y-1));
|
||||
positionsAVerifier.add(new Position(x+1, y));
|
||||
positionsAVerifier.add(new Position(x+1, y+1));
|
||||
positionsAVerifier.add(new Position(x, y+1));
|
||||
positionsAVerifier.add(new Position(x-1, y+1));
|
||||
positionsAVerifier.add(new Position(x-1, y));
|
||||
|
||||
for (Position p: positionsAVerifier) {
|
||||
c = cellulesVivantes.getAt(p.getX(), p.getY());
|
||||
cpt = c == null ? cpt : cpt+1;
|
||||
}
|
||||
return cpt;
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package projet.iut.jeu_de_la_vie.model;
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.actualiseur.ActualiseurCellule;
|
||||
import projet.iut.jeu_de_la_vie.model.actualiseur.ActualiseurTour;
|
||||
import projet.iut.jeu_de_la_vie.model.actualiseur.ActualiseurTourUnParUn;
|
||||
import projet.iut.jeu_de_la_vie.model.boucleDeJeu.BoucleDeJeu5FPS;
|
||||
import projet.iut.jeu_de_la_vie.model.boucleDeJeu.IBoucleDeJeu;
|
||||
import projet.iut.jeu_de_la_vie.model.boucleDeJeu.observer.ObservableBDJ;
|
||||
import projet.iut.jeu_de_la_vie.model.boucleDeJeu.observer.ObserverBDJ;
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.Cellule;
|
||||
import projet.iut.jeu_de_la_vie.model.plateau.Plateau;
|
||||
|
||||
/**
|
||||
* Point d'entré du model
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public class Manager implements ObserverBDJ {
|
||||
private ActualiseurTour actualiseurTour;
|
||||
private ActualiseurCellule actualiseurCellule;
|
||||
private IBoucleDeJeu boucleDeJeu;
|
||||
private ChangeurRegle changeurRegle;
|
||||
private boolean jeuLance;
|
||||
|
||||
|
||||
|
||||
public Manager(){
|
||||
boucleDeJeu = new BoucleDeJeu5FPS();
|
||||
((ObservableBDJ)boucleDeJeu).attacher(this);
|
||||
changeurRegle = new ChangeurRegle();
|
||||
Thread thread = new Thread(boucleDeJeu);
|
||||
thread.start();
|
||||
actualiseurCellule = changeurRegle.changerRegle(new Plateau());
|
||||
actualiseurTour = (ActualiseurTour) new ActualiseurTourUnParUn();
|
||||
jeuLance = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change l'actualiseur de cellule en fonction des règles
|
||||
*/
|
||||
private void actualiserActualiseurCellule(){
|
||||
actualiseurCellule = changeurRegle.changerRegle(getActualiseurCellule().getArbitre().getPlateau());
|
||||
}
|
||||
|
||||
/**
|
||||
* Comportement à adopter quand la boucle de jeu notifie le manager.
|
||||
* (Actualier les cellules et le numéro de génération(tours))
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
if(jeuLance) {
|
||||
deleguerChangementCellule();
|
||||
actualiseurTour.changerTour();
|
||||
}
|
||||
}
|
||||
|
||||
public ActualiseurTour getActualiseurTour(){
|
||||
return actualiseurTour;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actualiser l'état des cellules
|
||||
*/
|
||||
private void deleguerChangementCellule() {
|
||||
CellulesVivantes reference = getActualiseurCellule().getArbitre().getPlateau().getCellulesVivantes().clone();
|
||||
for (int y = 0; y<actualiseurCellule.getArbitre().getPlateau().getLigne(); ++y){
|
||||
for(int x = 0; x<actualiseurCellule.getArbitre().getPlateau().getColone(); ++x){
|
||||
actualiseurCellule.changerCellule(x, y, reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Demende d'inversion de l'état d'une cellule
|
||||
* @param c Cellue à inverser
|
||||
*/
|
||||
public void inverserEtatCellule(Cellule c){
|
||||
getActualiseurCellule().getArbitre().getPlateau().getCell(c.getPosition().getX(), c.getPosition().getY()).inverseAlive();
|
||||
}
|
||||
|
||||
public ActualiseurCellule getActualiseurCellule(){
|
||||
return actualiseurCellule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorise le lancement du jeu
|
||||
*/
|
||||
public void lancerJeu(){
|
||||
jeuLance = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Met en pause le jeu
|
||||
*/
|
||||
public void pauseJeu(){jeuLance = false;}
|
||||
|
||||
/**
|
||||
* Recommencer le jeu
|
||||
*/
|
||||
public void stoperJeu(){
|
||||
actualiseurTour.resetTour();
|
||||
actualiseurCellule.getArbitre().getPlateau().getCellulesVivantes().reset();
|
||||
jeuLance = false;
|
||||
}
|
||||
|
||||
public ChangeurRegle getChangeurRegle() {
|
||||
return changeurRegle;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package projet.iut.jeu_de_la_vie.model;
|
||||
|
||||
/**
|
||||
* Toutes les règles disponibles
|
||||
*/
|
||||
public enum Regle {
|
||||
/**
|
||||
* La cellule nait si elle a exactement 3 voisin
|
||||
* Elle reste en vie avec 2 ou 3 voisin
|
||||
* Elle meurt dans les autres sitations
|
||||
*/
|
||||
CONWAY_STYLE,
|
||||
|
||||
/**
|
||||
* Toutes les cellules meurts
|
||||
*/
|
||||
REGLE_KILLER
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package projet.iut.jeu_de_la_vie.model.actualiseur;
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.CellulesVivantes;
|
||||
import projet.iut.jeu_de_la_vie.model.arbitre.Arbitre;
|
||||
|
||||
/**
|
||||
* Gère l'actualisation des cellules (Abstraction)
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public abstract class ActualiseurCellule {
|
||||
private Arbitre arbitre;
|
||||
|
||||
/**
|
||||
* Change l'état d'une cellule si besoin
|
||||
* @param x Absisse de le cellule à changer
|
||||
* @param y Ordonné de la cellule à changer
|
||||
* @param reference CellulesVivantes au début du tour qui sert de référence
|
||||
*/
|
||||
public abstract void changerCellule(int x, int y, CellulesVivantes reference);
|
||||
|
||||
ActualiseurCellule(Arbitre arbitre) throws IllegalArgumentException{
|
||||
if(arbitre == null) {
|
||||
throw new IllegalArgumentException("L'arbitre ne peut pas être null!");
|
||||
}
|
||||
this.arbitre = arbitre;
|
||||
}
|
||||
|
||||
public Arbitre getArbitre() {
|
||||
return arbitre;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package projet.iut.jeu_de_la_vie.model.actualiseur;
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.CellulesVivantes;
|
||||
import projet.iut.jeu_de_la_vie.model.arbitre.Arbitre;
|
||||
|
||||
/**
|
||||
* Permet de changer l'état d'une cellule si besoin
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public class ActualiseurEtatCellule extends ActualiseurCellule{
|
||||
public ActualiseurEtatCellule(Arbitre a) throws IllegalArgumentException{
|
||||
super(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Peremet de changer l'état d'une cellule si besoin
|
||||
* @param x Absisse de le cellule à changer
|
||||
* @param y Ordonné de la cellule à changer
|
||||
* @param reference CellulesVivantes au début du tour qui sert de référence
|
||||
*/
|
||||
@Override
|
||||
public void changerCellule(int x, int y, CellulesVivantes reference) {
|
||||
switch(getArbitre().verifierChangementCellules(x, y, reference)) {
|
||||
case DIE:
|
||||
getArbitre().getPlateau().getCell(x, y).setAlive(false);
|
||||
break;
|
||||
case LIVE:
|
||||
case BIRTH:
|
||||
getArbitre().getPlateau().getCell(x, y).setAlive(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package projet.iut.jeu_de_la_vie.model.actualiseur;
|
||||
|
||||
/**
|
||||
* Comportement à adopter quand on veut actualiser des tours
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public interface ActualiseurTour {
|
||||
/**
|
||||
* Changer le numéro de génération
|
||||
*/
|
||||
void changerTour();
|
||||
|
||||
/**
|
||||
* Réinitialiser le numéro de génération
|
||||
*/
|
||||
void resetTour();
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package projet.iut.jeu_de_la_vie.model.actualiseur;
|
||||
|
||||
|
||||
/**
|
||||
* Actualiseur de tours qui incrémente le numéro de génération 1 par 1
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public class ActualiseurTourUnParUn implements ActualiseurTour{
|
||||
|
||||
private int cptTour ;
|
||||
public int getcptTour(){return cptTour;}
|
||||
private void setCptTour(int valeur){cptTour =valeur;}
|
||||
|
||||
public ActualiseurTourUnParUn(){
|
||||
resetTour();
|
||||
}
|
||||
|
||||
/**
|
||||
* Incrémneter le numéro de génération de 1
|
||||
*/
|
||||
@Override
|
||||
public void changerTour() {
|
||||
setCptTour(getcptTour()+1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Réinitialiser le numéro de génération à 0
|
||||
*/
|
||||
@Override
|
||||
public void resetTour(){
|
||||
setCptTour(0);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package projet.iut.jeu_de_la_vie.model.arbitre;
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.CellulesVivantes;
|
||||
import projet.iut.jeu_de_la_vie.model.CompteurDeCellule;
|
||||
import projet.iut.jeu_de_la_vie.model.plateau.Plateau;
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.CellState;
|
||||
|
||||
/**
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public abstract class Arbitre {
|
||||
|
||||
private Plateau plateau;
|
||||
private CompteurDeCellule compteurCell;
|
||||
|
||||
/**
|
||||
* @param plateau Plateau à arbitrer
|
||||
* @throws IllegalArgumentException Lève une exception si le plateau est null
|
||||
*/
|
||||
public Arbitre(Plateau plateau) throws IllegalArgumentException {
|
||||
if(plateau == null){
|
||||
throw new IllegalArgumentException("Le plateau ne doit pat être null");
|
||||
}
|
||||
this.plateau = plateau;
|
||||
compteurCell = new CompteurDeCellule();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Le plateau en cours d'arbitrage
|
||||
*/
|
||||
public Plateau getPlateau(){
|
||||
return plateau;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Le compteur de cellule du jeu
|
||||
* @see CompteurDeCellule
|
||||
*/
|
||||
protected CompteurDeCellule getCompteurCell(){
|
||||
return compteurCell;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x Coordonée x de la cellule à checker
|
||||
* @param y Coordonée y de la cellule à checker
|
||||
* @param reference Toutes les cellules qui était vivantes au début du tour et qui servent donc de references
|
||||
* @return L'état de la cellule au prochain tours
|
||||
* @see CellState
|
||||
*/
|
||||
public abstract CellState verifierChangementCellules(int x, int y, CellulesVivantes reference);
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package projet.iut.jeu_de_la_vie.model.arbitre;
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.CellulesVivantes;
|
||||
import projet.iut.jeu_de_la_vie.model.plateau.Plateau;
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.CellState;
|
||||
|
||||
/**
|
||||
* Arbitre selon les régles de Conway (3 voisinnes pour naitre, 2 ou 3 voisinnes pour survivre, meurt dans d'autre situations)
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public class ArbitreConwayStyle extends Arbitre{
|
||||
|
||||
/**
|
||||
*
|
||||
* @param plateau Plateau à arbitrer
|
||||
* @see Arbitre
|
||||
*/
|
||||
public ArbitreConwayStyle(Plateau plateau) {
|
||||
super(plateau);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x Coordonée x de la cellule à checker
|
||||
* @param y Coordonée y de la cellule à checker
|
||||
* @param reference Toutes les cellules qui était vivantes au début du tour et qui servent donc de references
|
||||
* @return L'état de la cellule au prohain tour
|
||||
*/
|
||||
@Override
|
||||
public CellState verifierChangementCellules(int x, int y, CellulesVivantes reference) {
|
||||
if(verifierNaissance(x, y, reference)) {
|
||||
return CellState.BIRTH;
|
||||
}
|
||||
if(verifierMort(x, y, reference)) {
|
||||
return CellState.DIE;
|
||||
}
|
||||
return reference.getAt(x, y) != null ? CellState.LIVE : CellState.DIE;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x Coordonée x de la cellule à checker
|
||||
* @param y Coordonée y de la cellule à checker
|
||||
* @param reference Toutes les cellules qui était vivantes au début du tour et qui servent donc de references
|
||||
* @return True si la cellule doit naître. Sinon false
|
||||
*/
|
||||
private boolean verifierNaissance(int x, int y, CellulesVivantes reference) {
|
||||
int cpt = getCompteurCell().compteNombreCellulesAutour(x, y, reference);
|
||||
if(cpt == 3 && !getPlateau().getCell(x, y).isAlive()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param x Coordonée x de la cellule à checker
|
||||
* @param y Coordonée y de la cellule à checker
|
||||
* @param reference Toutes les cellules qui était vivantes au début du tour et qui servent donc de references
|
||||
* @return True si la cellule doit mourir. Sinon false
|
||||
*/
|
||||
private boolean verifierMort(int x, int y, CellulesVivantes reference) {
|
||||
int cpt = getCompteurCell().compteNombreCellulesAutour(x, y, reference);
|
||||
if(!(cpt == 2 || cpt == 3) && getPlateau().getCell(x, y).isAlive()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package projet.iut.jeu_de_la_vie.model.arbitre;
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.CellulesVivantes;
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.CellState;
|
||||
import projet.iut.jeu_de_la_vie.model.plateau.Plateau;
|
||||
|
||||
/**
|
||||
* Arbitre qui tue toute les cellules
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public class ArbitreKiller extends Arbitre{
|
||||
|
||||
/**
|
||||
*
|
||||
* @param plateau Plateau à arbitrer
|
||||
* @see Arbitre
|
||||
*/
|
||||
public ArbitreKiller(Plateau plateau) {
|
||||
super(plateau);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x Coordonée x de la cellule à checker
|
||||
* @param y Coordonée y de la cellule à checker
|
||||
* @param reference Toutes les cellules qui était vivantes au début du tour et qui servent donc de references
|
||||
* @return L'état de la cellule au prohain tour
|
||||
*/
|
||||
@Override
|
||||
public CellState verifierChangementCellules(int x, int y, CellulesVivantes reference) {
|
||||
return CellState.DIE;
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package projet.iut.jeu_de_la_vie.model.boucleDeJeu;
|
||||
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.boucleDeJeu.observer.ObservableBDJ;
|
||||
|
||||
import java.util.LinkedList;
|
||||
/**
|
||||
* Boucle de jeu qui notifit tout les 33ms pour avoir 60 Image Par Seconde dans un thread (fils d'execution) séparé.
|
||||
* @author Yohann Breui
|
||||
* @author Allan Point
|
||||
*/
|
||||
public class BoucleDeJeu30FPS extends ObservableBDJ implements IBoucleDeJeu {
|
||||
public BoucleDeJeu30FPS(){
|
||||
setObserveurs(new LinkedList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Déffinition du comportement de la boucle de jeu pour le thread
|
||||
*/
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
while (true){
|
||||
try {
|
||||
Thread.sleep(33);
|
||||
beep();
|
||||
}
|
||||
|
||||
// Gestion des exceptions : si le processus à été intérompu pendant le sleep, on arrete la boucle.
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifier les abonnés
|
||||
*/
|
||||
public void beep() {
|
||||
notifier();
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package projet.iut.jeu_de_la_vie.model.boucleDeJeu;
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.boucleDeJeu.observer.ObservableBDJ;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Boucle de jeu qui notifit tout les 200ms pour avoir 5 Image Par Seconde dans un thread (fils d'execution) séparé.
|
||||
* @author Yohann Breui
|
||||
* @author Allan Point
|
||||
*/
|
||||
public class BoucleDeJeu5FPS extends ObservableBDJ implements IBoucleDeJeu {
|
||||
public BoucleDeJeu5FPS(){
|
||||
setObserveurs(new LinkedList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Déffinition du comportement de la boucle de jeu pour le thread
|
||||
*/
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
while (true){
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
beep();
|
||||
}
|
||||
|
||||
// Gestion des exceptions : si le processus à été intérompu pendant le sleep, on arrete la boucle.
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifier les abonnés
|
||||
*/
|
||||
public void beep() {
|
||||
notifier();
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package projet.iut.jeu_de_la_vie.model.boucleDeJeu;
|
||||
|
||||
/**
|
||||
* Perrmet d'avoir une boucle de jeu Runnable dans un thread
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public interface IBoucleDeJeu extends Runnable {}
|
@ -0,0 +1,53 @@
|
||||
package projet.iut.jeu_de_la_vie.model.boucleDeJeu.observer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public abstract class ObservableBDJ {
|
||||
/**
|
||||
* Liste des observeurs de la boucle de jeu. Cette liste servira à notifier les abonnés de la boucle de jeu
|
||||
*/
|
||||
private List<ObserverBDJ> observeurs;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Les observeur de la boucle observée
|
||||
*/
|
||||
protected List<ObserverBDJ> getObserveurs(){
|
||||
return observeurs;
|
||||
}
|
||||
|
||||
protected void setObserveurs(List<ObserverBDJ> valeur){
|
||||
observeurs = valeur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de s'abonner l'observable
|
||||
* @param o Observateur qui veut s'abboner
|
||||
*/
|
||||
public void attacher(ObserverBDJ o) {
|
||||
observeurs.add(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de se déabonner de la liste de notification de la boucle.
|
||||
* @param o Observateur qui veut s'abbonner
|
||||
*/
|
||||
public void detacher(ObserverBDJ o){
|
||||
observeurs.remove(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de notifier les abonnés de la boucle de jeu
|
||||
*/
|
||||
protected void notifier() {
|
||||
/*
|
||||
for (ObserverBDJ observeur : observeurs) {
|
||||
Platform.runLater(()->observeur.update());
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package projet.iut.jeu_de_la_vie.model.boucleDeJeu.observer;
|
||||
|
||||
/**
|
||||
* Permet d'obbserver une boucle de jeu
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public interface ObserverBDJ {
|
||||
/**
|
||||
* Réaction en cas de notification
|
||||
*/
|
||||
void update();
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package projet.iut.jeu_de_la_vie.model.cellule;
|
||||
|
||||
/**
|
||||
* Tout les etats possible pour la manipulation de cellules
|
||||
*/
|
||||
public enum CellState {
|
||||
/**
|
||||
* La cellule est vivante
|
||||
*/
|
||||
LIVE,
|
||||
|
||||
/**
|
||||
* La cellule est morte ou meur
|
||||
*/
|
||||
DIE,
|
||||
|
||||
/**
|
||||
* La cellule née
|
||||
*/
|
||||
BIRTH
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package projet.iut.jeu_de_la_vie.model.cellule;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.observer.ObservableCellule;
|
||||
|
||||
/**
|
||||
* Classe métier représentant une cellule
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public class Cellule extends ObservableCellule {
|
||||
/**
|
||||
* Position de la cellule
|
||||
* @see Position
|
||||
*/
|
||||
private Position position;
|
||||
|
||||
/**
|
||||
* État de la cellule
|
||||
*/
|
||||
private boolean alive;
|
||||
|
||||
/**
|
||||
* Permet d'avoir une propriété qui représente la coulleur de toutes les cellules vivantes
|
||||
*/
|
||||
private static int livingColor;
|
||||
public int getLivingColor() { return livingColor; }
|
||||
public void setLivingColor(int color) { livingColor = color; }
|
||||
|
||||
/**
|
||||
* Permet d'avoir une propriété qui représente la coulleur actuel d'une cellule en fonction de son etat
|
||||
*/
|
||||
private int activeColor;
|
||||
public int getActiveColor() { return activeColor; }
|
||||
public void setActiveColor(int color) { activeColor =color; }
|
||||
|
||||
/**
|
||||
* Couleur des cellules mortes
|
||||
*/
|
||||
private int deathColor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x position x de la cellule
|
||||
* @param y position y de la cellule
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
public Cellule(int x, int y) throws IllegalArgumentException {
|
||||
deathColor = Color.BLACK;
|
||||
setActiveColor(deathColor);
|
||||
position = new Position(x,y);
|
||||
alive = false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return True si la cellule est vivante. Sinon false.
|
||||
*/
|
||||
public Boolean isAlive() { return alive; }
|
||||
|
||||
/**
|
||||
* Change l'état de la cellule en changant le couleur actve ainsi qu'en notifiant tout les abonnés du changement
|
||||
* @param alive Booléen assigné a l'état de la cellule
|
||||
*/
|
||||
public void setAlive(Boolean alive) {
|
||||
setActiveColor(alive ? getLivingColor() : deathColor);
|
||||
this.alive = alive;
|
||||
notifier(this);
|
||||
}
|
||||
|
||||
public Position getPosition(){
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param o Objet à comparrer
|
||||
* @return True si les cellules ont les mêmes positions. Sinon false
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null) return false;
|
||||
if (o == this) return true;
|
||||
if (o.getClass() != this.getClass()) return false;
|
||||
|
||||
if (position.getY() == ((Cellule) o).position.getY() && position.getX() == ((Cellule) o).position.getX() && isAlive() == ((Cellule) o).isAlive()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverset l'état d'une cellule. La tue si elle est vivante et vice versa.
|
||||
*/
|
||||
public void inverseAlive(){
|
||||
setAlive(!alive);
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package projet.iut.jeu_de_la_vie.model.cellule;
|
||||
|
||||
/**
|
||||
* Représente une position dans un axe x et y (2 dimentions)
|
||||
* @author Yohann Breil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public class Position {
|
||||
|
||||
/**
|
||||
* Position x
|
||||
*/
|
||||
private int x;
|
||||
|
||||
/**
|
||||
* Position y
|
||||
*/
|
||||
private int y;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x position x
|
||||
* @param y position y
|
||||
*/
|
||||
public Position(int x, int y){
|
||||
setX(x);
|
||||
setY(y);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return position x
|
||||
*/
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param valeur position x
|
||||
*/
|
||||
public void setX(int valeur){
|
||||
x = valeur;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return position y
|
||||
*/
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param valeur position y
|
||||
*/
|
||||
public void setY(int valeur) throws IllegalArgumentException{
|
||||
y = valeur;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param o L'objet à comparer
|
||||
* @return True si les 2 positions on les mêmes coordonées. Sinon false.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Position position = (Position) o;
|
||||
return x == position.x && y == position.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (x*31 + y*41)/5;
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package projet.iut.jeu_de_la_vie.model.cellule.créateur;
|
||||
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.CellulesVivantes;
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.Cellule;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Permet de créer des cellules de manières sécurisé et standardiser
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public class CreateurCellule implements ICreateurCellule {
|
||||
/**
|
||||
* Nombre de colones à créer
|
||||
*/
|
||||
private int w;
|
||||
|
||||
/**
|
||||
* Nombre de ligne à créer
|
||||
*/
|
||||
private int h;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param w Nombre de colones. Doit être supperieur ou égal à 0.
|
||||
* @param h Nombre de lignes. Doit être supperieur ou égal à 0.
|
||||
* @throws IllegalArgumentException Si w ou h sont strictement inferieur à 0.
|
||||
*/
|
||||
public CreateurCellule(int w, int h) throws IllegalArgumentException{
|
||||
if(w<0 || h<0){
|
||||
throw new IllegalArgumentException("La longueur et la largeur doivent être supperieur à 0");
|
||||
}
|
||||
this.w = w;
|
||||
this.h = h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Créer des cellules selon les dimentions précisé dans le constructeur.
|
||||
* @param observer Permet d'abonner un objet CellulesVivantes à toute les cellules.
|
||||
* @return Une liste observable pour fxml avec toutes les cellules standardisées .
|
||||
*/
|
||||
public List<List<Cellule>> creerCellules(CellulesVivantes observer){
|
||||
return creerCellules(w, h, observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Créer des cellules.
|
||||
* @param colone Nombre de colones à créer.
|
||||
* @param ligne Nombre de lignes à créer.
|
||||
* @param observer Permet d'abonner un objet CellulesVivantes à toute les cellules.
|
||||
* @return Une liste observable pour fxml avec toutes les cellules standardisées.
|
||||
*/
|
||||
public List<List<Cellule>> creerCellules(int colone, int ligne, CellulesVivantes observer){
|
||||
List<List<Cellule>> cells = new LinkedList<>();
|
||||
List<Cellule> tmp;
|
||||
Cellule c;
|
||||
for (int i = 0; i < ligne; i++) {
|
||||
tmp = new LinkedList<>();
|
||||
for (int j = 0; j < colone; j++) {
|
||||
c = new Cellule(j, i);
|
||||
c.attacher(observer);
|
||||
tmp.add(c);
|
||||
}
|
||||
cells.add(tmp);
|
||||
}
|
||||
return cells;
|
||||
}
|
||||
|
||||
/**
|
||||
* Créer une ligne de cellule
|
||||
* @param ligne nombre de ligne à créer
|
||||
* @return Les lignes avec les cellules.
|
||||
*/
|
||||
public List<Cellule> creerLigneCellule(int ligne){
|
||||
List<Cellule> cells = new LinkedList<>();
|
||||
for(int i=0; i<ligne; ++i){
|
||||
cells.add(new Cellule(i, ligne));
|
||||
}
|
||||
return cells;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package projet.iut.jeu_de_la_vie.model.cellule.créateur;
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.CellulesVivantes;
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.Cellule;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Couche d'abstraction de la création des cellules
|
||||
* @author Yohann Breil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public interface ICreateurCellule {
|
||||
|
||||
/**
|
||||
* Créer des cellules selon les dimentions précisé dans le constructeur.
|
||||
* @param observer Permet d'abonner un objet CellulesVivantes à toute les cellules.
|
||||
* @return Une liste observable pour fxml avec toutes les cellules standardisées .
|
||||
*/
|
||||
List<List<Cellule>> creerCellules(CellulesVivantes observer);
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package projet.iut.jeu_de_la_vie.model.cellule.observer;
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.Cellule;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Permet à une cellule d'être observée
|
||||
* @author Yohann Breil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public abstract class ObservableCellule {
|
||||
/**
|
||||
* Liste des observeurs de la boucle de jeu. Cette liste servira à notifier les abonnés de la boucle de jeu
|
||||
*/
|
||||
List<ObserverCellule> observeurs;
|
||||
|
||||
public ObservableCellule(){
|
||||
observeurs = new LinkedList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param o observeur à attacher
|
||||
* @throws IllegalArgumentException L'observeur ne peut pas être null
|
||||
*/
|
||||
public void attacher(ObserverCellule o) throws IllegalArgumentException{
|
||||
if(o == null){
|
||||
throw new IllegalArgumentException("L'observer ne doit pas être null");
|
||||
}
|
||||
observeurs.add(o);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param o observeur à détacher
|
||||
* @throws IllegalArgumentException L'observer ne peut pas être null
|
||||
*/
|
||||
public void detacher(ObserverCellule o) throws IllegalArgumentException{
|
||||
if(o == null){
|
||||
throw new IllegalArgumentException("L'observer ne doit pas être null");
|
||||
}
|
||||
observeurs.remove(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de notifier les abonnés de la boucle de jeu
|
||||
*/
|
||||
public void notifier(Cellule cellule) {
|
||||
for (ObserverCellule observeur : observeurs) {
|
||||
observeur.update(cellule);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package projet.iut.jeu_de_la_vie.model.cellule.observer;
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.Cellule;
|
||||
|
||||
/**
|
||||
* Permet d'obbserver une boucle de jeu
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public interface ObserverCellule {
|
||||
/**
|
||||
* Réaction en cas de notification
|
||||
*/
|
||||
void update(Cellule cellule);
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
package projet.iut.jeu_de_la_vie.model.plateau;
|
||||
|
||||
import projet.iut.jeu_de_la_vie.model.CellulesVivantes;
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.Cellule;
|
||||
import projet.iut.jeu_de_la_vie.model.cellule.créateur.CreateurCellule;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Représentation du plateau de jeu
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public class Plateau implements PrototypePlateau{
|
||||
|
||||
/**
|
||||
* Pour créer corréctement des cellules
|
||||
* @see CreateurCellule
|
||||
*/
|
||||
private CreateurCellule createurCellule;
|
||||
|
||||
/**
|
||||
* Propriété qui permet de mettre en relation le nombre de colones avec la vue
|
||||
*/
|
||||
private int colone;
|
||||
public int getColone() { return colone;}
|
||||
public void setColone(int valeur) { colone = valeur; resetGrille(valeur, getLigne());}
|
||||
|
||||
/**
|
||||
* Propriété qui permet de mettre en relation le nombre de ligne avec la vue
|
||||
*/
|
||||
private int ligne;
|
||||
public int getLigne() { return ligne; }
|
||||
public void setLigne(int valeur ) { ligne =valeur; resetGrille(getColone(), valeur);}
|
||||
|
||||
/**
|
||||
* Représentation du plateau dans une liste à 2 dimention
|
||||
*/
|
||||
private List<List<Cellule>> grille;
|
||||
public List<List<Cellule>> getGrille() { return grille; }
|
||||
public void setGrille(List<List<Cellule>> cells) {grille = cells;}
|
||||
|
||||
/**
|
||||
* Enssemble des cellules vivante du plateau
|
||||
* @see CellulesVivantes
|
||||
*/
|
||||
private CellulesVivantes cellulesVivantes;
|
||||
|
||||
/**
|
||||
* Récuperer une cellule sur le plateau
|
||||
* @param x Coordonée x
|
||||
* @param y Coordonée y
|
||||
* @return La cellule positioné en (x; y)
|
||||
* @throws IllegalArgumentException x et y doivent être > 0 et respéctivement inferieur au nombre de colones et de ligne
|
||||
*/
|
||||
public Cellule getCell(int x, int y) throws IllegalArgumentException{
|
||||
if(x < 0 || y < 0) {
|
||||
throw new IllegalArgumentException("X ou Y est inférieur à 0");
|
||||
}
|
||||
if(y >= getGrille().size()){
|
||||
throw new IllegalArgumentException("Y est trop grand !!!");
|
||||
}
|
||||
if(x >= getGrille().get(y).size()){
|
||||
throw new IllegalArgumentException("X est trop grand !!!");
|
||||
}
|
||||
return grille.get(y).get(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Netoyer la grille
|
||||
*/
|
||||
public void resetGrille(){
|
||||
resetGrille(getColone(), getLigne());
|
||||
}
|
||||
|
||||
/**
|
||||
* Créer une nouvelle grille
|
||||
* @param colone nombre de colone de la grille
|
||||
* @param ligne nombre de ligne de la grille
|
||||
*/
|
||||
public void resetGrille(int colone, int ligne){
|
||||
setGrille(createurCellule.creerCellules(colone, ligne, cellulesVivantes));
|
||||
}
|
||||
|
||||
public Plateau(){
|
||||
createurCellule = new CreateurCellule(0, 0);
|
||||
cellulesVivantes = new CellulesVivantes();
|
||||
setGrille(new LinkedList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param colone Nombre de colones du plateau
|
||||
* @param ligne Nombre de lignes du plateau
|
||||
*/
|
||||
public Plateau(int colone, int ligne) {
|
||||
this(colone, ligne, new CellulesVivantes());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param colone Nombre de colone du plateau
|
||||
* @param ligne Nombre de ligne du plateau
|
||||
* @param observer CellulesVivantes qui veux observer les cellules crées
|
||||
*/
|
||||
public Plateau(int colone, int ligne, CellulesVivantes observer) {
|
||||
createurCellule = new CreateurCellule(colone, ligne);
|
||||
setLigne(ligne);
|
||||
setColone(colone);
|
||||
cellulesVivantes = observer;
|
||||
setGrille(createurCellule.creerCellules(cellulesVivantes));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param colone Nombre de colones du plateau
|
||||
* @param ligne Nombre de lignes du plateau
|
||||
* @param cellules Liste en 2 dimentions de cellules
|
||||
*/
|
||||
public Plateau(int colone, int ligne, List<List<Cellule>> cellules)
|
||||
{
|
||||
this(colone, ligne);
|
||||
setGrille(cellules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clonne un plateau
|
||||
* @return Le même plateau mais avec une référence différente
|
||||
*/
|
||||
@Override
|
||||
public Plateau cloner() {
|
||||
return new Plateau(getColone(), getLigne(), getGrille());
|
||||
}
|
||||
|
||||
public CellulesVivantes getCellulesVivantes() {
|
||||
return cellulesVivantes;
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package projet.iut.jeu_de_la_vie.model.plateau;
|
||||
|
||||
/**
|
||||
* Abstraction du clonnage de plateau
|
||||
* @author Yohann Breuil
|
||||
* @author Allan Point
|
||||
*/
|
||||
public interface PrototypePlateau {
|
||||
Plateau cloner();
|
||||
}
|
Loading…
Reference in new issue