-
Notifications
You must be signed in to change notification settings - Fork 0
Conception
Il s’agit littéralement de “jouer son tour de jeu” et non pas “jouer une carte”. Le joueur a la possibilité de poser une carte, piocher une carte ou passer son tour. Nous avons donc utilisé le lien de généralisation entre "Jouer" et ces trois cas d’utilisation puisque lorsqu’un utilisateur joue, il est obligé d'exécuter au moins une de ces trois actions.
Lien d’extension entre Piocher une carte et Poser une carte : Lorsqu’un joueur pioche une carte et qu’elle est jouable, il a la possibilité de jouer la carte. Il a donc le choix d’où l’usage d’un lien d’extension.
Nous avons défini "Recevoir un effet" sans rapport avec "Passer son tour" ou "Inverser le sens de jeu" car ce ne sont pas des effets que subit directement le joueur, autrement dit le joueur n’a pas d’action à faire pour ces effets.
Lorsqu’un joueur joue son avant-dernière carte, il doit immédiatement signaler « uno » pour indiquer à ses adversaires qu’il n’a plus qu’une seule carte en main.
Lorsque quelqu’un joue une carte "+4", le joueur suivant peut se méfier, et peut demander à voir son jeu pour vérification. Et si cette carte ne devait logiquement pas être jouée, le joueur qui vient de la poser à mauvais escient la reprend, et c’est alors à lui de tirer 4 cartes de la pioche et de passer son tour. En revanche, s’il avait bien le droit de jouer cette carte, le joueur « suspicieux » à tort doit alors tirer 2 cartes de plus dans la pioche et passer son tour. On peut également recevoir des pénalités si l’on oublie de signaler « uno » lorsque l’on a qu’une carte et donc pour la même raison que Recevoir un effet, il s’agit bien d’un lien de généralisation.
Pour rendre le diagramme de classe le plus clair possible, nous avons préféré éviter d’y mettre les getters et setters ainsi que les constructeurs.
Nous avons distingué 2 types de cartes, les cartes numériques et les cartes spéciales. Ces 2 types de cartes n’ont comme différence que l’effet que peut avoir une carte.
Au vu de l’importance que peut avoir les caractéristiques des cartes nous avons estimé que les cartes UNO devaient être le plus flexible possible. Par exemple on peut s’imaginer des bonus de carte. Ainsi, nous ne faisons pas de différence de type entre chaque carte. Pour représenter de manière structurée les différentes données qui restent fixes comme les couleurs ou les symboles, nous avons opté pour l’énumération qui permet de contenir une série de données constantes.
L'énumération Symbol : Comme dit précédemment, il y a des cartes numériques et des cartes spéciales. Mais dans l’absolu, pour chacune des cartes, on compare le symbole ou sa couleur pour tester sa validité. Ainsi nous avons trouvé intéressant de généraliser davantage la carte.
L'énumération Effect : A première vue, les différentes « cartes spéciales » ont chacune un effet propre. Et pourtant, on remarque des similitudes plus ou moins différentes. Par exemple une carte "+4" nécessite de piocher et choisir une couleur ce que l’on retrouve dans la carte "+2" et "Joker". Mais ce n’est pas tout, imaginons que de nouvelles cartes voient le jour, notre application doit permettre de rendre les effets manipulables avec le moins d’effort possible. Ainsi pour satisfaire ce besoin, nous avons conçu les effets avec le patron de conception Composite dont les composants (effets) sont au plus bas niveau. Ainsi une carte possède un composé d’effet et dont chacun possède une priorité. Effectivement, l’ordre n’est pas commutatif et sans celui-ci, le comportement pourrait différer. Par exemple si la carte avec le symbole "+2" est jouée, alors le composé de cette carte possède les composants suivants : { Skip, Draw, Draw }. Et donc si on applique les effets sans priorité, on va sauter le tour du joueur suivant puis faire piocher à celui qui le suit.
Lorsqu’une carte à effet Wild est jouée, nous avons supposé que la carte prenne la couleur que le joueur a choisie. Cela permet de clarifier le code et de comparer toujours les cartes entre elles sans utiliser un attribut « currentColor » ce qui rend l’interface plus intuitif et ergonomique.
Nous avons identifié différents paquets de carte avec des points en commun à savoir la main du joueur « PlayerHand », le talon « DiscardPile » et la pioche « DrawPile ». C’est pour cette raison qu’on a généralisé avec une classe générique abstrait pour éviter la redondance et instancier le type de liste dynamiquement. En effet, le talon et la pioche n’est pas manipulable de la même façon que la main du joueur. Ils ont les caractéristiques pour être utilisés comme des piles. Il nous a semblé intéressant de séparer ces 2 piles pour pouvoir facilement y apporter des modifications puisque ce sont des éléments clés de l’application. De plus le talon et la pioche n’ont pas la même approche. Par exemple on ne va jamais retirer une carte du talon directement (juste pour mélanger). Pour assurer l’unicité d’un talon et d’une pioche sur le plateau de jeu nous avons adopté le patron Singleton. Il serait effectivement aberrant d’avoir plusieurs pioches ou plusieurs talons.
Pour satisfaire notre volonté de maintenir l’application avec le moins d’effort possible, nous avons conçu cette classe pour permettre le paramétrage des différentes règles du jeu. Ceci permet de mieux maintenir l’application puis d’intégrer facilement des tests unitaires. Pour cela nous avons utilisé le patron Fabrique qui permet d’initialiser le mode de jeu selon le choix d’un utilisateur. Cela permet donc de prévoir des extensions de variantes de jeu. Malgré qu’il soit déconseillé de rendre la visibilité des attributs en publique, nous en avons déclaré certains mais qui sont des constantes et donc pas modifiable.
Il s’agit ici de la classe prépondérante à notre application. Naturellement il ne peut y avoir qu’un seul plateau de jeu, d’où la nécessité d’utiliser le pattern Singleton. Le plateau de jeu est composé de 4 éléments nécessaires au déroulement d’une partie UNO : Un talon, une pioche, plusieurs joueurs, et enfin les règles du jeu. Ce plateau possède également 2 données essentielles: un curseur permettant d’identifier à qui est le tour de jouer et le sens de jeu qui a donc 2 valeurs possibles. Nous avons préféré utilisé 1 et – 1(respectivement le sens horaire et antihoraire) pour s’en servir à déplacer le curseur directement. Nous avons pensé à intégrer une classe partie et manche pour avoir accès à un historique. Par la suite, nous avons constaté que nous n’avions pas besoin de le sauvegarder au sein du programme mais en externe (fichier ou base de données).
Nous avons identifié 2 types de joueurs ayant un grand nombre de point en commun. C’est pour cette raison que nous avons créé une classe mère Player. Pour éliminer la possibilité d’instancier cette classe, ce qui serait aberrant, nous l’avons déclaré abstraite.
Un joueur ordinateur possède une intelligence artificielle. Ce type de joueur doit donc pouvoir jouer automatiquement de manière imprévisible. Naturellement, le joueur ordinateur peut décider de changer de stratégie au cours du jeu. Pour remédier à cela nous avons adopté le patron Strategy. Il sélectionne des algorithmes à la volée selon certaines conditions. Il utilise pour cela le polymorphisme en encapsulant les comportements pour avoir une hiérarchie plus souple. Par exemple : Si le joueur ordinateur a identifié que sa stratégie n‘est plus valable, il va donc s’adapter et la changer.
Les différentes stratégies que possède ce type de joueur :
- Priorité aux cartes ayant le même symbole que celle du talon et ayant la couleur la plus fréquente dans la main du joueur
- Priorité aux cartes ayant la même couleur que celle du talon et ayant le score le plus élevé
- Priorité aux cartes ayant des effets
- Aucune priorité


