Annonce au Jass
Série 6 – corrigé
Introduction
Le code du corrigé vous est fourni dans une archive Zip et les solutions aux différents exercices sont rapidement discutées ci-dessous.
Notez que pour des questions de présentation, les modificateurs private
, public
et static
des différentes méthodes ont été omis ci-dessous. Ils figurent néanmoins bien entendu dans le code fourni.
Exercice 1 : classe Meld
Méthode addAllQuartetsInto
La version de addAllQuartetsInto
basée sur une boucle consiste à parcourir la totalité des rangs à partir de 9 et à obtenir les cartes de ce rang pour chacune des couleurs.
Notez que nous aurions pu utiliser la classe EnumSet
plutôt que HashSet
pour l'ensemble quartet
, mais comme cette classe n'a pas été vue au cours, nous ne l'avons pas fait. EnumSet
est une classe représentant de manière très efficace un ensemble dont les éléments proviennent d'un type énuméré, et est décrite en détail dans sa documentation.
void addAllQuartetsInto(List<Meld> melds) { List<Rank> ranksFrom9 = Rank.ALL.subList(Rank.NINE.ordinal(), Rank.COUNT); for (Rank rank : ranksFrom9) { Set<Card> quartet = new HashSet<>(); for (Color color : Color.ALL) quartet.add(Card.ALL_OF.get(color).get(rank.ordinal())); melds.add(new Meld(quartet, quartetPoints(rank))); } }
Méthode addAllSuitsInto
La méthode addAllSuitsInto
s'écrit au moyen de 3 boucles imbriquées qui permettent respectivement de parcourir :
- les quatre couleurs,
- les différentes tailles de suites (3, 4 et 5),
- les différentes suites de la couleur et de la taille en question.
La dernière de ces boucles est la seule méritant quelques explications. Comme expliqué dans l'énoncé, l'idée consiste à avoir deux index, nommés ici i1
et i2
, qui référencent respectivement la première carte et la dernière carte (en réalité, la position juste après la dernière carte) de la suite. Au moyen de ces deux index, il est facile d'extraire la sous-liste des cartes composant chacune des suites.
Notez que la troisième boucle utilise une caractéristique peu connue de Java, à savoir la possibilité de gérer plusieurs variables (ici i1
et i2
) dans une seule boucle for
.
void addAllSuitsInto(List<Meld> melds) { for (Color color : Color.ALL) { for (int size = 3; size <= 5; size += 1) { List<Card> cards = Card.ALL_OF.get(color); for (int i1 = 0, i2 = size; i2 <= cards.size(); i1 += 1, i2 += 1) { Set<Card> suit = new HashSet<>(cards.subList(i1, i2)); melds.add(new Meld(suit, suitPoints(size))); } } } }
Méthode allIn
La méthode allIn
consiste simplement à parcourir la totalité des annonces et à déterminer lesquelles sont totalement incluses dans la main, ce qui se fait trivialement au moyen de la méthode containsAll
.
List<Meld> allIn(Collection<Card> hand) { List<Meld> allIn = new ArrayList<>(); for (Meld m : ALL) { if (hand.containsAll(m.cards())) allIn.add(m); } return allIn; }
Exercice 2 : classe Sets
En utilisant la propriété mentionnée dans l'énoncé, à savoir que deux ensembles sont disjoints si et seulement si la cardinalité de leur union est égale à la somme de leurs cardinalités, il est relativement simple d'écrire la méthode mutuallyDisjoint
.
L'idée est de calculer progressivement l'union de tous les ensembles (dans union
) et la somme des cardinalités des ensembles (dans totalSize
). Si à un instant donné la cardinalité de l'union devient inférieure à la somme des cardinalités, cela signifie que les ensembles ne sont pas disjoints deux à deux, et on retourne donc faux.
<T> boolean mutuallyDisjoint(Collection<Set<T>> sets) { Set<T> union = new HashSet<>(); int totalSize = 0; for (Set<T> s : sets) { union.addAll(s); totalSize += s.size(); if (union.size() < totalSize) return false; } return true; }
Exercice 3 : classe MeldSet
La plus grosse partie de la définition de la classe MeldSet
ne pose pas de problème particulier et est donc donnée ici sans commentaires additionnels. Les méthodes mutuallyDisjoint
et allIn
sont présentées séparément plus bas.
public final class MeldSet { private final Set<Meld> melds; // … méthode statique mutuallyDisjoint // … méthode statique allIn public static MeldSet of(Collection<Meld> melds) { if (!mutuallyDisjoint(melds)) throw new IllegalArgumentException(); return new MeldSet(melds); } private MeldSet(Collection<Meld> melds) { this.melds = Set.copyOf(melds); } public int points() { int points = 0; for (Meld m : melds) points += m.points(); return points; } @Override public String toString() { StringJoiner s = new StringJoiner(", ", "{", "}"); for (Meld m : melds) s.add(m.cards().toString()); return String.format("%3d: %s", points(), s); } }
La méthode mutuallyDisjoint
s'écrit très facilement en utilisant celle de Sets
, il faut toutefois extraire les ensembles de cartes des annonces individuelles pour pouvoir l'appeler.
static boolean mutuallyDisjoint(Collection<Meld> melds) { List<Set<Card>> allSetsOfCards = new ArrayList<>(); for (Meld m : melds) allSetsOfCards.add(m.cards()); return Sets.mutuallyDisjoint(allSetsOfCards); }
La méthode allIn
est une retranscription en Java de la technique de calcul donnée dans l'énoncé :
- au moyen de
allIn
deMeld
, on calcule l'ensemble des annonces individuelles présentes dans la main, - au moyen de
powerSet
deSets
, on calcule l'ensemble des parties de cet ensemble d'annonces individuelles, - au moyen de
mutuallyDisjoint
on filtre l'ensemble des parties pour ne garder que les ensembles d'annonces compatibles, et on construit les instances deMeldSet
correspondantes.
static List<MeldSet> allIn(Collection<Card> hand) { List<MeldSet> r = new ArrayList<>(); for (Set<Meld> melds : Sets.powerSet(Meld.allIn(hand))) { if (mutuallyDisjoint(melds)) r.add(new MeldSet(melds)); } return r; }
Une fois la classe MeldSet
terminée, on peut écrire un comparateur comparant deux instances via leurs points :
class MeldSetByPointsComparator implements Comparator<MeldSet> { @Override public int compare(MeldSet m1, MeldSet m2) { return Integer.compare(m1.points(), m2.points()); } }
et compléter le programme principal de la manière demandée dans l'énoncé :
System.out.printf("%nLes ensembles d'annonces […] :%n"); List<MeldSet> melds = MeldSet.allIn(hand); melds.sort(new MeldSetByPointsComparator()); for (MeldSet m : melds) System.out.printf(" %s%n", m);