In 1975 ontdekte Phoebe Winch dat de 100 letters uit de Engelstalige versie van Scrabble een geheime boodschap onthullen:
I AM DIETING. I EAT QUINCE JELLY. LOTS OF GROUND MAIZE GIVES VARIETY. I COOK RHUBARB AND SODA, WEEP ANEW, OR PUT ON EXTRA FLESH.
De vraag is of dit Scrabble-dieet ook echt lekker smaakt …
Scrabble is een bordspel voor twee tot vier spelers, die bestaande woorden op een spelbord moeten leggen. Hierbij gaan geluk en vernuft hand in hand, omdat de letters willekeurig getrokken worden en het de kunst is hiermee een zo hoog mogelijke score te behalen.
Het spel wordt gespeeld op een bord bestaande uit $$15 \times 15 = 225$$ vakjes, waarbij 61 gekleurde vakjes (8 rode, 17 roze, 24 lichtblauwe en 12 donkerblauwe) een speciale betekenis hebben. De overige 164 vakjes hebben een neutrale kleur (vaak groen) en hebben geen speciale betekenis. In de Nederlandstalige versie zijn er 100 letters en twee blanco blokjes te verdelen die in een stoffen zakje bewaard worden. Daaruit kunnen de blokjes gemakkelijk blind getrokken worden.
De blokjes die een spelers trekt worden naast elkaar op een rekje gezet, uit het zicht van zijn medespelers. Elke speler probeert tijdens zijn beurt een aantal van zijn getrokken letters op het bord te leggen aan de woorden die reeds gevormd zijn om daarmee een bestaand woord van ten minste twee letters te vormen, zodanig dat een zo hoog mogelijk aantal punten behaald wordt. Blanco blokjes mogen door de speler voor eender welke letter gebruikt worden, maar leveren zelf geen punten op. Vervolgens vult de speler de letters op zijn rekje weer aan door blind letters uit het stoffen zakje te trekken. Het spel is voorbij als één van de spelers geen letters meer heeft of als niemand met zijn letters nog een woord kan bedenken.
In deze opgave stellen we een aantal letters voor als een string, een lijst, een tuple of een verzameling bestaande uit hoofdletters en underscores (_). Hierbij worden de blanco's uit het spelletje Scrabble voorgesteld door underscores, en mogen letters en blanco's meerdere keren voorkomen.
De inhoud van een zakje met letters stellen we voor als een dictionary, waarvan de sleutels enkel bestaan uit hoofdletters en eventueel een underscore (die opnieuw een blanco voorstelt). Met elke sleutel wordt een strikt positief natuurlijk getal geassocieerd, dat aangeeft hoe vaak de letter (of de blanco) voorkomt in het zakje. Merk dus op dat het niet toegelaten is om de waarde nul te associeren met een letter of een blanco. Indien een letter niet voorkomt in het zakje, dan heeft die ook geen overeenkomstig sleutel/waarde paar in de dictionary die het zakje voorstelt.
Gevraagd wordt om een klasse Zak te definiëren waarmee zakjes kunnen voorgesteld worden uit het spelletje Scrabble. Bij de start van een spelletje Scrabble moet het zakje kunnen gevuld worden met alle letters, en tijdens het spel moeten spelers letters uit het zakje kunnen wegnemen. We willen op elk moment ook een overzicht kunnen uitschrijven van de inhoud van het zakje. De klasse Zak moet minstens de volgende methoden ondersteunen:
Een initialisatiemethode __init__ waaraan een aantal letters moet doorgegeven worden. De initialisatiemethode moet ervoor zorgen dat het nieuw aangemaakt object van de klasse Zak een eigenschap inhoud krijgt die de inhoud van het zakje voorstelt dat gevuld werd met de gegeven letters.
Een methode __str__ (zonder argumenten) die een string met een overzicht van de letters in het zakje teruggeeft. Elke regel van het overzicht moet beginnen met een getal $$n \in \mathbb{N}_0$$, gevolgd door een dubbelpunt (:), een spatie en een alfabetisch oplijsting van alle letters die $$n$$ keer voorkomen in het zakje. Hierbij gaan we ervan uit dat een underscore alfabetisch na de letter Z komt. De regels van het overzicht moeten gerangschikt worden volgens oplopend aantal voorkomens van de letters in het zakje.
Een methode __repr__ (zonder argumenten) die een stringvoorstelling van het zakje letters teruggeeft. Deze string moet een Python expressie bevatten die aangeeft hoe een object van de klasse Zak kan aangemaakt worden, dat dezelfde inhoud heeft als de huidige inhoud van het object waarop deze methode wordt aangeroepen. Hierbij moeten de letters uit het zakje alfabetisch opgelijst worden (waarbij de underscore alfabetisch volgt op de letter Z) en moet elke letter even vaak opgelijst worden als hij voorkomt in het zakje.
Een methode wegnemen waaraan een aantal letters moet doorgegeven worden. De methode moet alle gegeven letters uit het zakje wegnemen, door de eigenschap inhoud die de inhoud het zakje voorstelt bij te werken. Indien het zakje niet alle gegeven letters bevat, dan mag de eigenschap inhoud niet bijgewerkt worden, en moet de methode een AssertionError opwerpen met de boodschap niet alle letters zitten in het zakje.
>>> zak = Zak('IAMDIETINGIEATQUINCEJELLYLOTSOFGROUNDMAIZEGIVESVARIETYICOOKRHUBARBANDSODAWEEPANEWORPUTONEXTRAFLESH__') >>> zak.inhoud {'U': 4, '_': 2, 'C': 2, 'K': 1, 'D': 4, 'T': 6, 'Q': 1, 'V': 2, 'A': 9, 'F': 2, 'O': 8, 'J': 1, 'I': 9, 'N': 6, 'P': 2, 'S': 4, 'M': 2, 'W': 2, 'E': 12, 'Z': 1, 'G': 3, 'Y': 2, 'B': 2, 'L': 4, 'R': 6, 'X': 1, 'H': 2} >>> print(zak) 1: JKQXZ 2: BCFHMPVWY_ 3: G 4: DLSU 6: NRT 8: O 9: AI 12: E >>> zak Zak('AAAAAAAAABBCCDDDDEEEEEEEEEEEEFFGGGHHIIIIIIIIIJKLLLLMMNNNNNNOOOOOOOOPPQRRRRRRSSSSTTTTTTUUUUVVWWXYYZ__') >>> zak.wegnemen('AEERTYOXMCNB_S') >>> print(zak) 1: BCJKMQYZ_ 2: FHPVW 3: GS 4: DLU 5: NRT 7: O 8: A 9: I 10: E >>> zak Zak('AAAAAAAABCDDDDEEEEEEEEEEFFGGGHHIIIIIIIIIJKLLLLMNNNNNOOOOOOOPPQRRRRRSSSTTTTTUUUUVVWWYZ_') >>> zak.wegnemen('XXX') Traceback (most recent call last): AssertionError: niet alle letters zitten in het zakje >>> print(zak) 1: BCJKMQYZ_ 2: FHPVW 3: GS 4: DLU 5: NRT 7: O 8: A 9: I 10: E >>> zak Zak('AAAAAAAABCDDDDEEEEEEEEEEFFGGGHHIIIIIIIIIJKLLLLMNNNNNOOOOOOOPPQRRRRRSSSTTTTTUUUUVVWWYZ_')