Je wordt geblinddoekt en krijgt daarna een stapel kaarten in je hand. Dit moeten niet noodzakelijk alle 52 kaarten uit een standaard kaartspel zijn. Sommige kaarten van die stapel liggen met hun beeldzijde naar boven, de andere met hun beeldzijde naar onder. Als we je nu vertellen dat de stapel exact $$b$$ kaarten bevat die met hun beeldzijde naar boven liggen, hoe kan je hem dan in twee verdelen zodat beide stapels evenveel kaarten bevatten die met hun beeldzijde naar boven liggen? Beide stapels moeten niet noodzakelijk evenveel kaarten bevatten.
Ook al zouden we geblinddoekt het aantal kaarten in de stapel kunnen tellen, we hoeven eigenlijk zelfs niet te weten hoeveel kaarten er in de oorspronkelijke stapel liggen. Maar laten we zeggen dat de stapel uit $$n$$ kaarten bestaat. Dan liggen er $$b$$ kaarten met hun beeldzijde naar boven en $$n - b$$ kaarten met hun beeldzijde naar onder. We verdelen nu de oorspronkelijke stapel in twee, waarbij de eerste stapel bestaat uit de eerste $$b$$ kaarten, en de tweede stapel uit de resterende $$n - b$$ kaarten.
Als de eerste stapel $$m$$ kaarten bevat die met hun beeldzijde naar boven liggen, dan bevat de tweede stapel de resterende kaarten die met hun beeldzijde naar boven liggen en dat zijn er $$b - m$$. Dat laatste is ook exact het aantal kaarten van de eerste stapel die met hun beeldzijde naar onder liggen. Als we dus de eerste stapel volledig omdraaien, dan hebben beide stapels nu $$b - m$$ kaarten die met hun beeldzijde naar boven liggen. Voila!
Concreet, stel dat we een stapel met 16 kaarten krijgen, waarvan er 7 met hun beeldzijde naar boven liggen. We verdelen die stapel dan in twee, waarbij de eerste stapel bestaat uit de eerste 7 kaarten en de tweede stapel uit de laatste 9 kaarten. Als er in de eerste stapel 3 kaarten met hun beeldzijde naar boven liggen, dan moeten er in de tweede stapel $$7 - 3 = 4$$ kaarten met hun beeldzijde naar boven liggen. In de eerste stapel liggen er ook $$7 - 3 = 4$$ kaarten met hun beeldzijde naar onder. Als we alle kaarten in die stapel omdraaien, dan liggen er in de eerste stapel nu dus 4 kaarten met hun beeldzijde naar boven, net als in de tweede stapel.
Een standaard kaartspel bestaat uit 52 verschillende kaarten die onderverdeeld worden in vier kleuren van elk 13 kaarten: 13 klaveren (♣), 13 ruiten (♦), 13 harten (♥) en 13 schoppen (♠). Klaveren en schoppen zijn zwart, ruiten en harten zijn rood, maar het zijn niet deze fysieke kleuren, maar de soorten die met de term kleur aangeduid worden. Van elke kleur zijn er telkens kaarten met een rang van 2 tot en met 10, een boer, een vrouw, een heer en een aas. Daarnaast bevat een spel soms twee of drie jokers, maar die laten we hier even buiten beschouwing.
We stellen elk van de 52 kaarten van een standaard kaartspel voor als een string die bestaat uit de rang van de kaart, gevolgd door de kleur van de kaart:
rangen: 2, 3, 4, 5, 6, 7, 8, 9, 10, J (boer), Q (vrouw), K (heer) en A (aas)
kleuren: C (klaveren; ♣), D (ruiten; ♦), H (harten; ♥) en S (schoppen; ♠)
Zo stelt AS bijvoorbeeld schoppenaas voor, 10H hartentien en KC klaverenheer. Bovendien leggen we vast dat de voorstelling van een kaart die met zijn beeldzijde naar boven ligt gebruikmaakt van hoofdletters (bijvoorbeeld AS of 10H), en dat de voorstelling van een kaart die met zijn beeldzijde naar onder ligt gebruikmaakt van kleine letters (bijvoorbeeld as of 10h). Merk op dat het laatste karakter van de voorstelling altijd een hoofdletter of een kleine letter is (geen cijfer) die de kleur van de kaart aanduidt.
De lijstvoorstelling van een stapel kaarten bestaat uit een lijst met de voorstellingen van de opeenvolgende kaarten uit de stapel, waarbij de bovenste kaart van de stapel het eerste element van de lijst is. Een stapel hoeft niet noodzakelijk alle 52 kaarten van een kaartspel te bevatten.
Definieer een klasse Stapel die kan gebruikt worden om stapels kaarten voor te stellen, waarbij sommige kaarten met hun beeldzijde naar boven en de andere met hun beeldzijde naar onder liggen. Bij het instantiëren van objecten van de klasse Stapel moet de lijstvoorstelling van de stapel kaarten doorgegeven worden. De klasse mag ervan uitgaan dat de gegeven lijstvoorstelling geldig is, zonder dat dit expliciet moet gecontroleerd worden. Voorts moet de klasse Stapel minstens de volgende methoden ondersteunen:
Een methode beeldzijdeBoven waaraan geen argumenten kunnen doorgegeven worden. De methode moet een natuurlijk getal teruggeven dat aangeeft hoeveel kaarten van de stapel momenteel met hun beeldzijde naar boven liggen.
Een methode splitsen waarmee de stapel kaarten $$s$$ waarop de methode wordt aangeroepen in twee nieuwe stapels $$s_1$$ en $$s_2$$ (zelf objecten van de klasse Stapel) kan gesplitst worden. Hierbij bestaat de stapel $$s_1$$ uit de eerste $$n$$ kaarten van de stapel $$s$$ en de stapel $$s_2$$ uit de resterende kaarten van de stapel $$s$$, waarbij de volgorde van de kaarten uit de stapel $$s$$ telkens behouden blijft. Aan de methode kan een getal $$n \in \mathbb{N}$$ doorgegeven worden, dat aangeeft hoeveel kaarten de stapel $$s_1$$ moet bevatten. Als er geen argument wordt doorgegeven aan de methode, dan wordt verondersteld dat $$n$$ gelijk is aan het aantal kaarten die momenteel met hun beeldzijde naar boven liggen in stapel $$s$$. De methode moet het tuple $$(s_1, s_2)$$ teruggeven.
Een methode omdraaien waarmee een aantal kaarten van de stapel kunnen omgedraaid worden. Een kaart die met zijn beeldzijde naar boven lag, moet na het omdraaien met zijn beeldzijde naar onder liggen. Een kaart die met zijn beeldzijde naar onder lag, moet na omdraaien met zijn beeldzijde naar boven liggen. De posities van de kaarten in de stapel veranderen nooit bij het omdraaien. Aan de methode kan een argument doorgegeven worden dat de posities van de kaarten aanduidt die moeten omgedraaid worden. Daarvoor bestaan de volgende mogelijkheden:
geen argument: alle kaarten van de stapel moeten omgedraaid worden
argument is een integer: geeft positie aan van de ene kaart die moet omgedraaid worden
argument is een collectie (een lijst, een tuple of een verzameling) van integers: geeft posities aan van de kaarten die moeten omgedraaid worden
Hierbij worden de kaarten van boven naar onder genummerd vanaf 0 om hun positie binnen de stapel te bepalen. De methode moet de toestand van de kaarten in de stapel wijzigen (beeldzijde naar boven of naar onder) en moet een verwijzing teruggeven naar het object dat de stapel voorstelt.
De conversie van een object $$o$$ van de klasse Stapel naar een string — bekomen met de ingebouwde functies repr en str — bevat een voorstelling van de stapel kaarten zoals aangegeven in onderstaande voorbeeldsessie. De voorstelling die de functie repr teruggeeft, is een Python expressie die een nieuw object van de klasse Stapel aanmaakt met dezelfde toestand als de huidige toestand van object $$o$$. De voorstelling die de functie str teruggeeft, bestaat uit de voorstellingen van de opeenvolgende kaarten van de stapel (van boven naar onder), telkens van elkaar gescheiden door een spatie. Hierbij wordt een kaart die met zijn beeldzijde naar boven ligt op de standaard manier voorgesteld. Een kaart die met zijn beeldzijde naar onder ligt wordt voorgesteld door twee sterretjes (**).
Zorg er ook voor dat de ingebouwde operator == kan gebruikt worden om te bepalen of twee objecten van de klasse Stapel gelijk zijn. Dit is het geval als de twee stapels evenveel kaarten bevatten die met hun beeldzijde naar boven liggen.
>>> kaarten = Stapel(['AH', '3S', 'KC', '4H', '3D', '10H', '8D', '5D', '7C', 'QS', 'JC', '3H', 'KS', '4C', 'KD', '8S'])
>>> print(kaarten)
AH 3S KC 4H 3D 10H 8D 5D 7C QS JC 3H KS 4C KD 8S
>>> kaarten
Stapel(['AH', '3S', 'KC', '4H', '3D', '10H', '8D', '5D', '7C', 'QS', 'JC', '3H', 'KS', '4C', 'KD', '8S'])
>>> kaarten.beeldzijdeBoven()
16
>>> kaarten.omdraaien()
Stapel(['ah', '3s', 'kc', '4h', '3d', '10h', '8d', '5d', '7c', 'qs', 'jc', '3h', 'ks', '4c', 'kd', '8s'])
>>> kaarten.omdraaien(1).omdraaien(4).omdraaien([5, 9]).omdraaien((10, 11, 13))
Stapel(['ah', '3S', 'kc', '4h', '3D', '10H', '8d', '5d', '7c', 'QS', 'JC', '3H', 'ks', '4C', 'kd', '8s'])
>>> print(kaarten)
** 3S ** ** 3D 10H ** ** ** QS JC 3H ** 4C ** **
>>> kaarten.beeldzijdeBoven()
7
>>> kaarten1, kaarten2 = kaarten.splitsen(4)
>>> print(kaarten1)
** 3S ** **
>>> kaarten1
Stapel(['ah', '3S', 'kc', '4h'])
>>> kaarten1.beeldzijdeBoven()
1
>>> print(kaarten2)
3D 10H ** ** ** QS JC 3H ** 4C ** **
>>> kaarten2
Stapel(['3D', '10H', '8d', '5d', '7c', 'QS', 'JC', '3H', 'ks', '4C', 'kd', '8s'])
>>> kaarten2.beeldzijdeBoven()
6
>>> kaarten1 == kaarten2
False
>>> kaarten3, kaarten4 = kaarten.splitsen()
>>> print(kaarten3)
** 3S ** ** 3D 10H **
>>> kaarten3
Stapel(['ah', '3S', 'kc', '4h', '3D', '10H', '8d'])
>>> kaarten3.beeldzijdeBoven()
3
>>> print(kaarten4)
** ** QS JC 3H ** 4C ** **
>>> kaarten4
Stapel(['5d', '7c', 'QS', 'JC', '3H', 'ks', '4C', 'kd', '8s'])
>>> kaarten4.beeldzijdeBoven()
4
>>> kaarten3 == kaarten4
False
>>> kaarten3.omdraaien()
Stapel(['AH', '3s', 'KC', '4H', '3d', '10h', '8D'])
>>> print(kaarten3)
AH ** KC 4H ** ** 8D
>>> kaarten3.beeldzijdeBoven()
4
>>> kaarten3 == kaarten4
True
De opeenvolgende statements uit bovenstaande voorbeeldsessie komen overeen met het voorbeeld van de kaartentruc zoals die gegeven werd op het einde van inleiding van deze opgave. Klik op onderstaande afbeelding voor een visuele voorstelling van de opeenvolgende stappen van de kaartentruc.
kaarten = Stapel(['AH', '3S', 'KC', '4H', '3D', '10H', '8D', '5D', '7C', 'QS', 'JC', '3H', 'KS', '4C', 'KD', '8S'])
kaarten.omdraaien()
kaarten.omdraaien(1)
kaarten.omdraaien(4)
kaarten.omdraaien([5, 9])
kaarten.omdraaien((10, 11, 13))
kaarten3, kaarten4 = kaarten.splitsen()
kaarten3.omdraaien()