Seven Wonders 2 – mes amis les modèles


Création du projet dans éclipse : Créons un « New Maven Project » :

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>fr.mathieujjava</groupId>
  <artifactId>sevenwonders</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>pom</packaging>
  <name>sevenwonders projet maitre</name>
  <modules>
  	<module>sevenwonders-common</module>
  	<module>sevenwonders-business</module>
  	<module>sevenwonders-web</module>
  </modules>
</project>

Créons ensuite 3 modules : sevenwonders-common, sevenwonders-business et sevenwonders-web pour faire du beau MVC tout propre.

Mettons-y la configuration du build :

<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>${java-version}</source>
					<target>${java-version}</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<configuration>
					<warName>sevenwonders</warName>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-dependency-plugin</artifactId>
				<executions>
					<execution>
						<id>install</id>
						<phase>install</phase>
						<goals>
							<goal>sources</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-resources-plugin</artifactId>
				<version>2.5</version>
				<configuration>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
		</plugins>
	</build>

Ajoutons les dépendances log4j et JUnit dans le pom parent, et nous pouvons commencer les premiers développements d’objet dans sevenwonders-common.

Pour le jeu, nous avons besoin de quelques énumérations : le type d’une carte :

package fr.mathieujjava.sevenwonders.enums;

public enum TypeCarte {
	Civil, Militaire, Science, Commerce, Manufacture, MatierePremiere, Guilde;
}

et les ressources :

package fr.mathieujjava.sevenwonders.enums;

public enum Ressource {
	Brique, Pierre, Minerai, Verre, Tissu, Papyrus, Bois;
}

Nous aurons besoin de savoir si une ressource est une matière première ou manufacturée. Créons notre premier test unitaire dans le même package que Ressource (mais dans l’arborescence du répertoire src/main/test, attention le « new JUnit » me met de base dans « src/main/java », vérifiez le répertoire) :

public class RessourceTest {
	@Test
	public void test() {
		assertTrue(Ressource.Bois.isMatierePremiere());
		assertTrue(Ressource.Minerai.isMatierePremiere());
		assertTrue(Ressource.Pierre.isMatierePremiere());
		assertTrue(Ressource.Brique.isMatierePremiere());
		assertFalse(Ressource.Papyrus.isMatierePremiere());
		assertFalse(Ressource.Tissu.isMatierePremiere());
		assertFalse(Ressource.Verre.isMatierePremiere());
	}
}

Tout est en erreur, puisque la méthode n’existe pas. Créons là donc :

public enum Ressource {
	Brique, Pierre, Minerai, Verre, Tissu, Papyrus, Bois;

	public boolean isMatierePremiere() {
		return this == Bois || this == Minerai || this == Pierre;
	}
}

Lançons le test avec un clic droit sur le projet, run as JUnit Test, et là… ça plante.

Pas de panique, il y a une subtilité : dans Seven Wonders, la brique est une matière première ; Corrigeons la fonction et le test passe cette fois.

Passons aux autres objets nécessaires au jeu. Pour l’instant, faisons des Pojo.

Rappel : chaque joueur joue avec son plateau, une « merveille », et des cartes distribuées. Les joueurs ont des sous, les cartes et les étapes des merveilles ont des coûts, et à chaque âge, les combats fournissent des pions de victoire ou de défaite. Une carte peut être jouée gratuitement si une autre est posée, et permet d’en poser d’autres gratuitement.

Enfin, nous avons une difficulté de modélisation : une carte peut être présente plusieurs fois, mais avec un âge différent, ou pour un nombre de joueur requis différent. Pour ces raisons, une carte représentera une vraie carte, et non un modèle de carte pouvant être présent en plusieurs exemplaires dans un jeu (alors que cette décision serait absurde pour un jeu tel que les Colons de Katane où les cartes sont toutes en plusieurs exemplaires). Si vous avez un autre avis dessus, je suis tout ouïe 😀
Maj : Après réflexion, une carte n’est pas « responsable » du nombre de fois où elle apparait par âge ou par nombre de joueurs.

public class Carte {
	Carte parent;

	Cout cout;

	String nomEn;

	String nomFr;

	String texte;

	TypeCarte type;
}

Une carte a un Cout, un prix en or et une liste de ressources dont on doit disposer pour prétendre le payer :

public class Cout {
	Integer prix = 0;
	List listeRessource = new ArrayList();
}

Clic droit, new JUnit test nous crée la classe de test pour Cout correspondante. Faisons quelques tests :

@Test
	public void testConstructeurs() {
		Cout cout = new Cout(1, Ressource.Bois);
		assertNotNull(cout.getListeRessources());
		cout = new Cout(0, Ressource.Brique, Ressource.Papyrus, Ressource.Brique);
		assertNotNull(cout);
		assertEquals(3, cout.getListeRessources().getSize());
	}

Revenons à Cout pour créer les constructeurs, les getters et le setter de prix pour que les tests passent :

public class Cout {
	List listeRessources = new ArrayList();

	Integer prix = 0;

	public Cout(Integer prix, Ressource ... ressources) {
		this.prix = prix;
		for (Ressource ressource : ressources) {
			listeRessources.add(ressource);
		}
	}

	public Integer getPrix() {
		return prix;
	}

	public List getListeRessources() {
		return listeRessources;
	}
}

Notre classe Cout fonctionne ; revenons à Carte pour laquelle nous générons également la classe de test :

@Test
	public void testConstructeurs() {
		Carte carte1 = new Carte(TypeCarte.MatierePremiere, "Sawmill", "Scierie", new Cout(1), null, "Fournit 2 bois");
		assertEquals(0, carte1.getCout().getListeRessources().size());
		assertTrue("Scierie".equals(carte1.getNomFr()));

		Carte carte2 = new Carte(TypeCarte.Science, "Library", "Bibliothèque", new Cout(0, Ressource.Pierre, Ressource.Pierre, Ressource.Tissu), null, "Ecriture");
		assertEquals(3, carte2.getCout().getListeRessources().size());
		assertTrue("Bibliothèque".equals(carte2.getNomFr()));

		Carte carte3 = new Carte(TypeCarte.Science, "University", "Université", new Cout(0, Ressource.Bois, Ressource.Bois, Ressource.Papyrus, Ressource.Verre), carte2, "Ecriture");
		assertEquals(4, carte3.getCout().getListeRessources().size());
		assertTrue("University".equals(carte3.getNomEn()));
		assertEquals(carte2, carte3.getParent());
	}

Tout marche, fabuleux.

Passons aux Merveilles : elles ont des couts de construction pour chaque étage (jusqu’à 4 pour la face B de la Grande pyramide de Gizah), un pouvoir fourni par chacun de ces étages et fournissent une ressource de base.

public class Merveille {
	Cout coutAgeI, coutAgeII, coutAgeIII, coutAgeIV;
	String nom;
	Ressource ressource;
	String texte;
}

Et les tests correspondants :

@Test
	public void testConstructeurs() {
		Merveille merveille = new Merveille("The Colossus of Rhodes",
				"Le Colosse de Rhodes", 1, "CC", Ressource.Minerai,
				new Cout(0, Ressource.Bois, Ressource.Bois),
				new Cout(0,	Ressource.Brique, Ressource.Brique, Ressource.Brique),
				new Cout(0, Ressource.Minerai, Ressource.Minerai, Ressource.Minerai, Ressource.Minerai));
		assertNotNull(merveille);
		assertNotNull(merveille.getCoutAgeIII());
		assertNull(merveille.getCoutAgeIV());

		merveille = new Merveille("The Lighthouse of Alexandria",
				"Le Phare d'Alexandrie", 1, "M/B/Q/P", Ressource.Verre,
				new Cout(0, Ressource.Pierre, Ressource.Pierre), new Cout(0,
						Ressource.Minerai, Ressource.Minerai), new Cout(0,
						Ressource.Verre, Ressource.Verre));
		assertNotNull(merveille);
		assertNotNull(merveille.getCoutAgeIII());
		assertNull(merveille.getCoutAgeIV());

		merveille = new Merveille("Pyramide B", "Pyramide B", 2, "3V 5V 5V 7V",
				Ressource.Pierre,
				new Cout(0, Ressource.Bois, Ressource.Bois),
				new Cout(0, Ressource.Pierre, Ressource.Pierre, Ressource.Pierre),
				new Cout(0, Ressource.Brique, Ressource.Brique, Ressource.Brique),
				new Cout(0, Ressource.Pierre, Ressource.Pierre, Ressource.Pierre, Ressource.Papyrus, Ressource.Tissu));
		assertNotNull(merveille);
		assertNotNull(merveille.getCoutAgeIII());
		assertNotNull(merveille.getCoutAgeIV());

	}

Ecriture des constructeurs et getters correspondants.

Passons au niveau en dessus, les données d’un joueur de SevenWonders. Dedans nous allons y mettre sa merveille et le nombre d’étages qu’il a construits , ses sous, ses cartes, et les médailles qu’il a gagnées.

En passant, créons l’enum Médaille, qui représente les victoires ou défaites acquises à la fin de chaque âge. Le joueur a sa pile de médailles et non un chiffre représentant ses victoires, d’une part parce que c’est plus fun d’avoir des belles médailles, d’autre part car des cartes dépendent du type de ces médailles.

public enum Medaille {
	Defaite, VictoireI, VictoireII, VictoireIII;

	public Integer getValeur() {
		switch (this) {
		case Defaite:
			return -1;
		case VictoireI:
			return 1;
		case VictoireII:
			return 3;
		case VictoireIII:
			return 5;
		}
		return 0;
	}
}

Et le joueur.
Comme le jeu a beaucoup de notions de « joueur à droite, joueur à gauche », j’ai choisi d’ajouter une variable « place » dans Joueur. Fonctionnellement j’ai l’impression que c’est pas sa place, et il faut faire attention à ne pas avoir d’erreurs (enfin bon à 7 wonders les joueurs restent à leur place du début à la fin) mais je pense que cela facilite les choses pour l’écriture de fonctions pour trouver le joueur à gauche et à droite d’un autre… Des remarques ?

public class Joueur {
	Integer place;
	Integer etageMerveille;

	Merveille merveille;

	List listeCartes = new ArrayList();

	List listeMedailles = new ArrayList();

	List main = new ArrayList();

	Integer nombrePieces;
}

et enfin les tests unitaires pour vérifier qu’un joueur commence la partie avec 3 pièces :

@Test
	public void testConstructeurs() {
		Merveille merveille = new Merveille("The Colossus of Rhodes",
				"Le Colosse de Rhodes", 1, "CC", Ressource.Minerai,
				new Cout(0, Ressource.Bois, Ressource.Bois),
				new Cout(0,	Ressource.Brique, Ressource.Brique, Ressource.Brique),
				new Cout(0, Ressource.Minerai, Ressource.Minerai, Ressource.Minerai, Ressource.Minerai));

		Joueur joueur = new Joueur(0, merveille);
		assertEquals(0, joueur.getListeMedailles().size());
		assertEquals(3, joueur.getNombrePieces().intValue());
		assertEquals(0, joueur.getEtageMerveille().intValue());

		joueur.modifieNombrePieces(4);
		assertEquals(7, joueur.getNombrePieces().intValue());
	}

Enfin, dernier objet, la Partie qui regroupe le tout :

Nous avons donc une liste de joueurs, un paquet de cartes, celui des cartes défaussées, et l’âge dans lequel on est et le tour en cours.

public class Partie {
Integer age;
Integer tour;
List<Carte> defausse = new ArrayList<Carte>();
List<Joueur> listeJoueur = new ArrayList<Joueur>();
}

Enfin les tests de la partie ; je me tâte pour les setters… faut-il mettre des setters setTour() et setAge() ou bien ne mettre que des fonctions directes, comme « incrementerAge » ou « incrementerTour » ?
Partons sur le plus général et des setters. Des remarques ?

Joueur joueurA, joueurB, joueurC, joueurD;

  Partie partie;

  @Before
  public void setUp() {
    Merveille merveilleA = new Merveille("A", "A", 1, "", null, new Cout[0]);
    Merveille merveilleB = new Merveille("B", "B", 1, "", null, new Cout[0]);
    Merveille merveilleC = new Merveille("C", "C", 1, "", null, new Cout[0]);
    Merveille merveilleD = new Merveille("D", "D", 1, "", null, new Cout[0]);
    partie = new Partie();
    partie.addJoueur(joueurA = new Joueur(merveilleA));
    partie.addJoueur(joueurB = new Joueur(merveilleB));
    partie.addJoueur(joueurC = new Joueur(merveilleC));
    partie.addJoueur(joueurD = new Joueur(merveilleD));

  }

  @Test
  public void testConstructeurs() {
    assertNotNull(partie.getListeJoueur());
    assertEquals(0, joueurA.getPlace().intValue());
    assertEquals(1, joueurB.getPlace().intValue());
    assertEquals(2, joueurC.getPlace().intValue());
    assertEquals(3, joueurD.getPlace().intValue());
    assertEquals(1, partie.getTour().intValue());
    assertEquals(1, partie.getAge().intValue());
    partie.setTour(partie.getTour() + 1);
    partie.setAge(2);
    assertEquals(2, partie.getTour().intValue());
    assertEquals(2, partie.getAge().intValue());
  }

Ces objets sont bien beaux mais ils ne font pas grand chose… Va falloir leur donner de la consistence… Passons maintenant à la suite du projet où on code toute la logique du jeu…

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion /  Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s