Aller au contenu

Projet : Tamagotchi

Séances 5 et 6 · ~8 périodes

Dépôt de départ : https://github.com/opmvpc/tamagotchi-26

Vous allez élever une créature en terminal. Elle a faim, elle s'ennuie, elle s'épuise, et si vous la négligez, elle meurt. Personne ne vous donne le plan. Vous recevez un cahier des charges, trois tests d'acceptation et quatre signatures imposées. Les autres classes, leurs noms et leurs relations, c'est vous qui les décidez.

À la fin de ce projet, vous serez capables de :

  1. modéliser un domaine complet en diagramme de classes, puis coder ce diagramme ;
  2. choisir entre héritage, composition, interface et enum, et justifier votre choix ;
  3. écrire vos propres tests sur les règles du jeu ;
  4. expliquer votre modèle à quelqu'un d'autre en cinq minutes.

1. Le cahier des charges

Le socle obligatoire

  • Une créature avec un nom et au moins trois jauges bornées de 0 à 100. Les valeurs sont des nombres entiers : les tests fournis refusent les décimales. Une jauge ne descend jamais sous 0 et ne dépasse jamais 100, quelle que soit l'action.
  • Un temps simulé : la méthode tick() fait avancer l'horloge du jeu. À chaque tick, au moins une jauge monte et au moins une descend. C'est le jeu qui décide quand un tick a lieu, pas l'heure réelle.
  • Au moins trois actions : nourrir, jouer, dormir. Certaines sont impossibles selon l'état de la créature. On ne nourrit pas une créature qui dort, on ne joue pas avec une créature épuisée. Le refus doit être affiché clairement au joueur.
  • Une fin de partie : au moins une condition qui termine la partie. Une fois atteinte, le jeu refuse les actions et affiche le bilan.
  • Un menu de démarrage : reprendre la partie, nouvelle partie, quitter.
  • Une sauvegarde JSON dans le dossier du script. On quitte, on relance, on retrouve sa créature dans l'état où on l'a laissée. Prévoyez le cas où le fichier n'existe pas et celui où il est illisible.
  • Un affichage terminal : le nom, l'état des jauges, l'humeur du moment, rafraîchi à chaque tour.
  • Un bilan de fin de partie : le nombre de tours joués, la cause de la fin, ce que le joueur a fait.
  • Un README qui donne vos règles : ce que signifie une jauge haute, combien de tours dure un sommeil, ce qui termine la partie. C'est aussi ce qui rend votre jeu jouable par quelqu'un d'autre.

Note

Une jauge déjà à 100 ne peut plus monter, et une jauge à 0 ne peut plus descendre. Décidez ce qui se passe alors, et écrivez-le dans votre README : la valeur reste à la borne, et c'est une autre jauge qui bouge.

Les bonus

À faire après le socle, jamais avant : un magasin et une monnaie, un inventaire d'objets, une difficulté qui augmente avec le temps, un visage ASCII qui suit l'humeur, des espèces de créatures, un système d'âges. Ajoutez un bonus seulement quand tout le socle fonctionne.

Attention

Le piège classique : commencer par le magasin parce que c'est amusant, et rendre un jeu où la créature ne meurt jamais. Le socle d'abord.

2. Le contrat minimal

Forkez tamagotchi-26 sur votre compte, clonez votre fork, puis :

composer install
composer test
composer play

Les deux dernières commandes sont définies dans le composer.json du dépôt : test lance Pest, play exécute php bin/play.php. Le premier composer test donne ceci :

   FAIL  Tests\Feature\AcceptanceTest
  ⨯ it fait naitre une creature avec des jauges dans les bornes
  ⨯ it fait evoluer les jauges quand le temps passe
  ⨯ it modifie une jauge quand on nourrit la creature

  La classe Tamagotchi\Creature n'existe pas encore. Creez src/Creature.php
  avec le namespace Tamagotchi et un constructeur qui recoit un nom.

  Tests:    3 failed (3 assertions)

Trois rouges : c'est le point de départ. Les trois tests de tests/Feature/AcceptanceTest.php sont les seules contraintes techniques du projet. Ne les modifiez pas.

Imposé Détail
Tamagotchi\Creature dans src/Creature.php, construite avec un nom : new Creature('Pixel')
stats(): array au moins 3 entrées, clé string vers valeur int comprise entre 0 et 100
tick(): void après un tick, au moins une jauge a changé, et les clés de stats() sont les mêmes qu'avant
feed(): void après l'action, au moins une jauge a changé

Tout le reste est libre : le nom de vos jauges, le nombre de classes, l'usage d'un enum, d'une interface ou d'un inventaire, la forme de votre affichage. Si votre tick() appelle trois autres objets, les tests ne le sauront pas. Ils vérifient un comportement vu de l'extérieur, pas une organisation interne.

Attention

Trois tests verts ne veulent pas dire que le socle est fini. Ils ne vérifient ni le menu, ni la sauvegarde, ni la fin de partie, ni les deux autres actions. Ce sont vos propres tests et vos parties d'essai qui couvrent le reste.

3. Votre diagramme de classes

docs/model.puml est un livrable, au même titre que le code. Vous ne trouverez pas ici le diagramme du Tamagotchi : le produire est l'exercice.

Voici la forme attendue, sur le Donjon du cours, avec la syntaxe du chapitre 3 :

@startuml
class Hero {
  -hp: int
  +takeDamage(amount: int): void
}
class Inventory {
  -maxWeight: float
  +add(item: Item): void
}
class Item {
  #name: string
}
Hero *-- "1" Inventory : inventory
Inventory o-- "0..*" Item : items
note right of Hero : Le personnage joué.
@enduml

Trois classes, les compartiments remplis, les visibilités marquées, les relations nommées avec leurs multiplicités. Votre modèle en contiendra plus, et il montrera vos décisions : composition ou agrégation, héritage ou possession, interface ou méthodes directes. Une note PlantUML suffit à justifier un choix qui se discute.

Mettez le diagramme à jour avant de rendre. Un diagramme qui ne correspond plus au code ne sert plus à rien.

Comptez 20 minutes pour un premier jet, sur papier, sans ordinateur.

4. Les paliers

Cet ordre n'est pas obligatoire, mais il évite de rester bloqué. Les trois premiers paliers sont le minimum à atteindre avant la séance 6.

5. Où chaque chapitre vous sert

L'exigence Une piste Chapitre
Un nom qui ne change plus readonly, promotion de constructeur, declare(strict_types=1) 1
Des jauges bornées de 0 à 100 Propriétés privées et une seule méthode qui applique la borne. Ni tick() ni les actions ne modifient la valeur directement 1
Afficher l'état __toString(), ou une classe dédiée à l'affichage 1
Un fichier par classe, composer test qui fonctionne Namespace Tamagotchi\, PSR-4, autoload 2
Savoir ce que vous allez coder Le diagramme avant le code : noms vers classes, données vers attributs, verbes vers méthodes 3
Un inventaire, un magasin Composition pour l'inventaire, qui n'appartient qu'à une créature. Agrégation pour les objets, qui existent avant d'être achetés 4
Des espèces de créatures Héritage, mais seulement si « un Dragounet est une créature » est vrai. Sinon, faites posséder au lieu d'hériter 5
Une action impossible Une exception à vous, CreatureIsAsleepException extends RuntimeException, attrapée par la boucle de jeu 5
Les actions du joueur Une interface Action avec label(), isAvailable(Creature): bool et apply(Creature): void. Ou trois méthodes sur la créature, plus simple. Dites pourquoi vous avez choisi l'une ou l'autre 6
L'humeur Un enum calculé à partir des jauges, avec un label() 6
La sauvegarde JSON JsonSerializable pour écrire, une fabrique statique fromArray() pour relire 1, 6
Séparer le jeu de son affichage Le couple Canvas et SVGRenderer de l'atelier : la logique d'un côté, le rendu de l'autre 7
Parcourir l'inventaire Countable et IteratorAggregate : count() et foreach fonctionnent alors sans rien écrire de plus 6, 8

Ce tableau donne des pistes, pas une architecture. Plusieurs réponses sont bonnes. Si vous choisissez autre chose, faites-le apparaître dans votre diagramme.

6. Ce que vous rendez

À la fin de la séance 6, votre fork contient :

  1. Le jeu, jouable avec composer play : menu, actions, sauvegarde, fin de partie, bilan.
  2. docs/model.puml, à jour du code rendu.
  3. Les tests : les 3 tests d'acceptation verts, plus au moins 3 tests à vous. Testez des règles du jeu, pas des méthodes de lecture. Par exemple : une jauge qui ne dépasse jamais 100 après dix actions, une action refusée quand la créature dort, une sauvegarde relue à l'identique.
  4. La CI verte : onglet Actions, coche verte sur la dernière version poussée.
  5. Le README avec vos règles du jeu.
  6. Le carnet d'usage de l'IA, une page, un par personne.

Le projet se fait seul ou à deux. À deux, chacun rend son carnet, et vous dites qui a fait quoi.

Le carnet d'usage de l'IA

L'IA est autorisée sur ce projet. Notez sur une page les questions que vous avez posées, ce que vous avez gardé des réponses, et comment vous avez vérifié que c'était juste. « Aucune IA utilisée » est une réponse valable.

7. Les critères

Ce projet est formatif : il est lu et commenté, il n'est pas coté.

Ce qui est regardé Ce qui fait dire que c'est acquis
Le modèle Le diagramme correspond au code. Les classes portent des noms de choses du jeu. Aucune classe ne fait tout.
Les relations Composition et agrégation sont employées à bon escient, et vous savez dire pourquoi. L'héritage, s'il y en a, passe le test du « est-un ».
L'encapsulation Les jauges ne se modifient pas depuis l'extérieur, et aucune écriture ne contourne les règles. La borne 0-100 est écrite à un seul endroit.
Les contrats Interface, enum ou trait employés là où ils servent. N'en utiliser aucun est une réponse valable si vous savez dire pourquoi.
Les erreurs Une action impossible est refusée avec un message clair, jamais avec une trace d'erreur PHP.
Les tests Les 3 tests d'acceptation passent. Vos 3 tests vérifient des règles du jeu.
Le jeu Il se lance, se joue, se sauvegarde, se reprend et se termine. Le bilan est lisible.
La CI Verte sur la dernière version poussée.
Le carnet IA Rempli et honnête.

À retenir

  • Le cahier des charges dit ce que le jeu fait ; le découpage en classes, c'est vous.
  • Quatre signatures sont imposées : Creature, stats(), tick() et feed(). Le reste est libre.
  • Socle d'abord, bonus ensuite.
  • La borne 0-100 s'écrit à un seul endroit ; écrite à quatre endroits, c'est un futur bug.
  • Un test d'acceptation décrit un comportement, pas une organisation interne.

Premiers pas

  1. Forkez tamagotchi-26, clonez votre fork, composer install, composer test : les trois rouges.
  2. Dessinez votre diagramme sur papier. Vingt minutes, sans ordinateur.
  3. Écrivez src/Creature.php : le nom, trois jauges, la méthode qui borne, stats().
  4. tick(), puis feed(). Les trois tests verts, la CI verte.
  5. Ensuite seulement : le menu, la boucle de jeu, la sauvegarde, l'humeur, le bilan.