Scores
Javass – étape 2
1 Introduction
Le but de cette seconde étape est d'écrire les classes représentant les scores d'une partie de Jass, et d'autres énumérations et classes utilitaires.
Avant de commencer à y travailler, lisez le guide Sauvegarder son travail. Il vous donnera des conseils importants concernant la sauvegarde de votre projet au cours du semestre.
1.1 Scores du Jass
A première vue, représenter les scores d'une partie de Jass peut sembler trivial : une paire de nombres représentant les points obtenus jusqu'à ce point de la partie par chacune des deux équipes devant faire l'affaire.
En réalité, le calcul des scores au Jass est un peu plus complexe que cela, pour deux raisons :
- en plus du nombre de points total obtenu par chaque équipe au cours de la partie, il peut être utile d'avoir accès au nombre de points obtenus dans le tour courant, afin de pouvoir les afficher à l'écran,
- pour correctement déterminer le score d'une équipe à la fin d'un tour, il faut savoir combien de plis cette équipe a remporté.
La raison pour laquelle le nombre de plis remportés est important est que si une équipe remporte la totalité des 9 plis du tour, alors on dit qu'elle a fait match et elle reçoit 100 points supplémentaires.
Dès lors, les scores d'une partie de Jass sont composés de trois valeurs par équipe — donc six valeurs au total —, qui sont :
- le nombre de plis remportés dans le tour courant,
- le nombre de points remportés dans le tour courant,
- le nombre de points remportés dans la partie courante.
Chacune de ces valeurs est bien entendu positive, et est de plus bornée. La valeur maximale pour chacune d'entre elles est donnée dans la table ci-dessous, accompagnée du nombre de bits nécessaires à la représentation d'une telle valeur :
Valeur | Max | Bits |
---|---|---|
Nb de plis | 9 | 4 |
Points du tour | 257 | 9 |
Points de la partie | 2000 | 11 |
Total | 24 |
Le nombre de points maximum qu'il est possible d'obtenir en un tour vaut 257 car une équipe remportant la totalité des plis d'un tour reçoit :
- les 152 points que valent la totalité des cartes,
- les 5 points supplémentaires attribués au dernier pli (« 5 de der »),
- les 100 points supplémentaires du match.
Le nombre de points maximum qu'il est possible d'obtenir en une partie est en réalité inférieur à 2000, mais cette valeur a été retenue pour simplifier les choses.
Sachant qu'un tel triplet de valeurs occupe un total de 24 bits, et que deux triplets de ce type (un par équipe) sont nécessaires à la représentation complète des scores, on conclut que 48 bits suffisent à représenter les scores d'une partie de Jass de manière empaquetée. Une seule valeur de type long
(64 bits) suffit donc.
Pour simplifier les choses, on peut attribuer les 32 bits de poids faible au stockage des 24 bits des scores de l'équipe 1, et les 32 bits de poids fort au stockage de ceux de l'équipe 2 :
Bits | 63 à 32 | 31 à 0 |
---|---|---|
Contenu | scores de l'équipe 2 | scores de l'équipe 1 |
Chaque paquet de 32 bits est quant à lui découpé en quatre parties, les trois premières contenant les composantes décrites plus haut, la dernière étant vide :
Bits | 31 à 24 | 23 à 13 | 12 à 4 | 3 à 0 |
---|---|---|---|---|
Contenu | 0 | points partie | points tour | nb plis |
2 Mise en œuvre Java
2.1 Classe Bits64
La classe Bits64
du paquetage ch.epfl.javass.bits
, publique, finale et non instanciable, contient des méthodes statiques permettant de travailler sur des vecteurs de 64 bits stockés dans des valeurs de type long
. Elle est très similaire à la classe Bits32
de l'étape précédente, mais n'offre qu'une seule variante de la méthode pack
— la seule nécessaire au reste du projet.
Cette classe offre les méthodes publiques (et statiques) suivantes, qui ne sont pas décrites ici car elles sont équivalentes aux méthodes de même nom de la classe Bits32
, la seule différence étant qu'elles travaillent sur des entiers de type long
:
long mask(int start, int size)
,long extract(long bits, int start, int size)
,long pack(long v1, int s1, long v2, int s2)
.
Ces méthodes valident leurs arguments de la même manière que les méthodes équivalentes de la classe Bits32
.
2.1.1 Conseils de programmation
Dans la méthode mask
, faites bien attention à utiliser la constante 1 de type Long
pour construire le masque ! En d'autres termes, écrivez quelque chose comme :
1L << size // surtout pas 1 << size !!!
2.2 Type énuméré TeamId
Le type énuméré TeamId
du paquetage ch.epfl.javass.jass
permet d'identifier chacune des deux équipes. Il ne contient que deux valeurs, qui sont, dans l'ordre :
TEAM_1
,TEAM_2
.
En plus de ces deux valeurs, le type énuméré TeamId
possède les attributs statiques et finaux ALL
et COUNT
, contenant respectivement la liste de toutes les valeurs et leur nombre.
Finalement, TeamId
offre la méthode publique suivante :
TeamId other()
, qui retourne l'autre équipe que celle à laquelle on l'applique (TEAM_2
pourTEAM_1
, et inversément).
2.3 Type énuméré PlayerId
Le type énuméré PlayerId
du paquetage ch.epfl.javass.jass
permet d'identifier chacun des quatre joueurs. Il ne contient que quatre valeurs, qui sont, dans l'ordre :
PLAYER_1
,PLAYER_2
,PLAYER_3
,PLAYER_4
.
De plus, le type énuméré PlayerId
possède les attributs statiques et finaux ALL
et COUNT
, contenant respectivement la liste de toutes les valeurs et leur nombre.
Finalement, PlayerId
offre la méthode publique suivante :
TeamId team()
, qui retourne (l'identité de) l'équipe à laquelle appartient le joueur auquel on l'applique, à savoir l'équipe 1 pour les joueurs 1 et 3, et l'équipe 2 pour les joueurs 2 et 4.
2.4 Classe PackedScore
La classe PackedScore
du paquetage ch.epfl.javass.jass
, publique, finale et non instanciable, contient des méthodes statiques permettant de manipuler les scores d'une partie de Jass empaquetés dans un entier de type long
selon la technique décrite plus haut.
Elle offre un attribut public, statique et final, utile pour initialiser les scores en début de partie :
long INITIAL
, qui contient le score initial d'une partie, dont les six composantes valent 0.
En plus de cet attribut, la classe PackedScore
offre les méthodes (publiques et statiques) suivantes :
boolean isValid(long pkScore)
, qui retourne vrai ssi la valeur donnée est un score empaqueté valide, c-à-d si les six composantes contiennent des valeurs comprises dans les bornes données plus haut, et les bits inutilisés valent tous 0,long pack(int turnTricks1, int turnPoints1, int gamePoints1, int turnTricks2, int turnPoints2, int gamePoints2)
, qui empaquète les six composantes des scores dans un entier de typelong
,turnTricks1
étant le nombre de plis remportés par l'équipe 1 dans le tour courant,turnPoints1
le nombre de points remportés par l'équipe 1 dans le tour courant, et ainsi de suite,int turnTricks(long pkScore, TeamId t)
, qui retourne le nombre de plis remportés par l'équipe donnée dans le tour courant des scores empaquetés donnés,int turnPoints(long pkScore, TeamId t)
, qui retourne le nombre de points remportés par l'équipe donnée dans le tour courant des scores empaquetés donnés,int gamePoints(long pkScore, TeamId t)
, qui retourne le nombre de points reportés par l'équipe donnée dans les tours précédents (sans inclure le tour courant) des scores empaquetés donnés,int totalPoints(long pkScore, TeamId t)
, qui retourne le nombre total de points remportés par l'équipe donnée dans la partie courante des scores empaquetés donnés, c-à-d la somme des points remportés dans les tours précédents et ceux remportés dans le tour courant,long withAdditionalTrick(long pkScore, TeamId winningTeam, int trickPoints)
, qui retourne les scores empaquetés donnés mis à jour pour tenir compte du fait que l'équipewinningTeam
a remporté un pli valanttrickPoints
points ; notez que seuls le nombre de plis et le nombre de points du tour courant doivent être mis à jour, et que si cette mise à jour fait que l'équipe gagnante a remporté tous les plis du tour, alors son score doit être augmenté de 100 points additionels étant donné qu'elle a fait match (voir la constanteMATCH_ADDITIONAL_POINTS
définie dans l'étape précédente) ; par contre, les 5 points attribués au dernier pli ne doivent pas être gérés par cette méthode, ils seront gérés ailleurs,long nextTurn(long pkScore)
, qui retourne les scores empaquetés donnés mis à jour pour le tour prochain, c-à-d avec les points obtenus par chaque équipe dans le tour courant ajoutés à leur nombre de points remportés lors de la partie, et les deux autres composantes remises à 0,String toString(long pkScore)
, qui retourne la représentation textuelle des scores, que vous êtes libres de choisir, cette méthode servant uniquement au déboguage ; néanmoins, la méthodetoString
doit au moins inclure le nombre de points total de chacune des deux équipes, p.ex. séparés par une barre oblique.
2.4.1 Méthodes withAdditionalTrick
et nextTurn
Les méthodes withAdditionalTrick
et nextTurn
ont pour but d'être utilisée pour faire évoluer les scores lors d'une partie. La première est appelée à la fin de chaque pli, tandis que la seconde l'est à la fin de chaque tour.
L'extrait de programme ci-dessous illustre leur fonctionnement en montrant l'évolution des scores lors du premier tour d'une partie fictive dans laquelle les équipes gagnent à tour de rôle, les plis valant tous 18 points sauf le premier qui en vaut 13.
long s = PackedScore.INITIAL; System.out.println(PackedScore.toString(s)); for (int i = 0; i < Jass.TRICKS_PER_TURN; ++i) { int p = (i == 0 ? 13 : 18); TeamId w = (i % 2 == 0 ? TEAM_1 : TEAM_2); s = PackedScore.withAdditionalTrick(s, w, p); System.out.println(PackedScore.toString(s)); } s = PackedScore.nextTurn(s); System.out.println(PackedScore.toString(s));
En admettant que la méthode toString
affiche les six composantes des scores dans l'ordre, l'extrait de programme ci-dessus imprime ceci :
(0,0,0)/(0,0,0) (1,13,0)/(0,0,0) (1,13,0)/(1,18,0) (2,31,0)/(1,18,0) (2,31,0)/(2,36,0) (3,49,0)/(2,36,0) (3,49,0)/(3,54,0) (4,67,0)/(3,54,0) (4,67,0)/(4,72,0) (5,85,0)/(4,72,0) (0,0,85)/(0,0,72)
2.4.2 Conseils de programmation
- Validation des arguments
Comme les méthodes de
PackedCard
, celles dePackedScore
n'ont pas l'obligation de valider leurs arguments. Il est toutefois fortement conseillé de le faire au moyen d'assertions. - Méthodes privées
Pour simplifier et clarifier le code, il est conseillé de définir des méthodes privées permettant de manipuler les groupes de 32 bits contenant les scores d'une équipe, puis de les utiliser pour définir les méthodes publiques décrites plus haut.
2.5 Classe Score
La classe Score
du paquetage ch.epfl.javass.jass
, publique, finale et immuable, représente les scores d'une partie de Jass.
Tout comme PackedScore
, elle offre un attribut public, statique et final représentant le score initial d'une partie :
Score INITIAL
, qui contient des scores dont les six composantes valent 0.
Tout comme la classe Card
de l'étape précédente, la classe Score
n'offre pas de constructeur public. Il est néanmoins possible de construire une instance de cette classe au moyen de la méthode publique et statique suivante :
Score ofPacked(long packed)
, qui retourne les scores dontpacked
est la version empaquetée, ou lève l'exceptionIllegalArgumentException
si cet argument ne représente pas des scores empaquetés valides d'après la méthodeisValid
dePackedScore
.
De plus, la classe Score
offre les méthodes publiques suivantes, qui sont pour la plupart équivalentes aux méthodes de même nom de PackedScore
. Pour cette raison, leur comportement n'est souvent pas spécifié en détail, mais la manière dont elles valident leurs arguments l'est au besoin :
long packed()
, qui retourne la version empaquetée des scores,int turnTricks(TeamId t)
, qui retourne le nombre de plis remportés par l'équipe donnée dans le tour courant du récepteur,int turnPoints(TeamId t)
, qui retourne le nombre de points remportés par l'équipe donnée dans le tour courant du récepteur,int gamePoints(TeamId t)
, qui retourne le nombre de points reportés par l'équipe donnée dans les tours précédents (sans inclure le tour courant) du récepteur,int totalPoints(TeamId t)
, qui retourne le nombre total de points remportés par l'équipe donnée dans la partie courante du récepteur,Score withAdditionalTrick(TeamId winningTeam, int trickPoints)
, qui se comporte comme la méthode de même nom dePackedScore
ou lève l'exceptionIllegalArgumentException
si le nombre de points donné est inférieur à 0,Score nextTurn()
, qui se comporte comme la méthode de même nom dePackedScore
.
Finalement, la classe Score
redéfinit les méthodes equals
, hashCode
et toString
de Object
. La méthode hashCode
peut simplement retourner la valeur produite par la méthode hashCode
de la classe Long
, appliquée au score empaqueté.
2.6 Tests
À partir de cette étape, nous ne vous fournissons plus de tests unitaires. Il vous faut donc les écrire vous-même si vous désirez en avoir, ce qui est fortement conseillé.
Si nous ne vous fournissons pas de tests JUnit, nous vous fournissons néanmoins une archive Zip à importer dans votre projet et contenant un fichier nommé SignatureChecks_2.java
. La classe qu'il contient fait référence à la totalité des classes, interfaces, types énumérés et méthodes de cette étape, ce qui vous permet de vérifier que leurs noms et types sont corrects. Cela est capital, car la moindre faute à ce niveau empêcherait l'exécution de nos tests unitaires.
Attention : après avoir importé le contenu de cette archive, n'oubliez surtout pas d'ajouter le répertoire sigcheck
qu'elle contient au Java Build Path de votre projet, comme expliqué aux points 5 à 7 de notre guide d'importation. Si vous oubliez de faire cette manipulation, Eclipse ne prendra pas en compte le fichier de vérification de signature et ne vous signalera donc pas les erreurs qu'il contient, le rendant totalement inutile.
Nous vous fournirons de tels fichiers pour toutes les étapes jusqu'à la mi-semestre, et il vous faudra penser à vérifier systématiquement qu'Eclipse ne signale aucune erreur à leur sujet. Faute de cela, votre rendu se verra refusé par notre système.
3 Résumé
Pour cette étape, vous devez :
- écrire les classes et énumérations
Bits64
,TeamId
,PlayerId
,PackedScore
etScore
, selon les indications données ci-dessus, - tester votre code,
- documenter la totalité des entités publiques que vous avez définies,
- rendre votre code au plus tard le 1er mars 2019 à 16h30, via le système de rendu.
Ce rendu est un rendu testé, auquel 18 points sont attribués, au prorata des tests unitaires passés avec succès. Notez que la documentation de votre code ne sera pas évaluée avant le rendu intermédiaire. Dès lors, si vous êtes en retard, ne vous en préoccupez pas pour l'instant.
N'attendez surtout pas le dernier moment pour effectuer votre rendu, car vous n'êtes pas à l'abri d'imprévus. Souvenez-vous qu'aucun retard, aussi insignifiant soit-il, ne sera toléré !