Het spelletje Turing Machine maakt gebruik van ponskaarten: kaarten waarin gaten geponst zijn. Elke ponskaart heeft een gekleurd label waarop één cijfer staat. Blauwe labels corresponderen met het eerste cijfer uit een getal van drie cijfers, gele labels met het middelste (tweede) cijfer, en paarse labels met het laatste (derde) cijfer. Dit zijn bijvoorbeeld de drie ponskaarten die corresponderen met het getal 421.

Als je deze drie ponskaarten bovenop elkaar legt, dan blijft er slechts één gat over.

De ponskaarten van het spelletje Turing Machine zijn zo ontworpen dat elke combinatie van drie kaarten die corresponderen met een getal van drie cijfers, juist één gat op dezelfde positie heeft. Bovendien liggen de gaten voldoende verspreid over de ponskaarten, zodat de kaarten hun stevigheid niet verliezen.
Definieer een klasse Ponskaart waarmee ponskaarten met een rechthoekig $$m \times n$$ rooster met $$m$$ rijen en $$n$$ kolommen kunnen voorgesteld worden. In elke cel van het rooster kan een gat geponst worden. Anders dan bij het spelletje Turing Machine moeten ponskaarten (Ponskaart) niet per se vierkant zijn, en hebben ze geen label. Dit is bijvoorbeeld een ponskaart met een $$12 \times 12$$ rooster waarin 25 gaten geponst werden.

De rijen van het rooster worden van boven naar onder genummerd, en de kolommen van links naar rechts, telkens vanaf nul. Daardoor kunnen we de positie van een cel in het rooster voorstellen als een tuple $$(r, k)$$, met $$r$$ (int) het nummer van rij en $$k$$ (int) het nummer van de kolom waarop de cel gelegen is.
Bij het aanmaken van een nieuwe ponskaart (Ponskaart) moet het aantal rijen $$m \in \mathbb{N}$$ (int) en het aantal kolommen $$n \in \mathbb{N}$$ (int) van het rooster doorgegeven worden. Optioneel kan als derde argument een collectie (list, tuple of set) doorgegeven worden met posities (tuple) waar gaten zitten in de ponskaart. Als er geen collectie wordt doorgegeven, dan zitten er geen gaten in de ponskaart. Als de ponskaart niet minstens twee rijen en twee kolommen heeft, of als er een positie wordt doorgegeven die buiten het rooster ligt, dan moet een AssertionError opgeworpen worden met de boodschap ongeldige ponskaart.
Als er een ponskaart $$p$$ (Ponskaart) wordt doorgegeven aan de ingebouwde functie repr, dan moet de functie een stringvoorstelling (str) van ponskaart $$p$$ teruggeven. Die moet lezen als een Python-expressie waarmee een nieuwe ponskaart (Ponskaart) aangemaakt wordt met dezelfde afmetingen en dezelfde gaten als ponskaart $$p$$. Daarbij moet als derde argument altijd een lijst (list) doorgegeven worden, met de posities (tuple) van de gaten opgelijst in leesvolgorde: van links naar rechts en van boven naar onder.
Als er een ponskaart $$p$$ (Ponskaart) wordt doorgegeven aan de ingebouwde functie str, dan moet de functie een stringvoorstelling (str) van ponskaart $$p$$ teruggeven. Daarin worden de rijen voorgesteld als opeenvolgende regels (opgelijst van boven naar onder). Elke regel bestaat uit een voorstelling van de individuele cellen op de corresponderende rij (opgelijst van links naar rechts): een hoofdletter O als er gat in de cel zit of een hekje (#) als er geen gat in de cel zit.
Op een ponskaart $$p$$ (Ponskaart) met $$m$$ rijen en $$n$$ kolommen moet je minstens de volgende methoden kunnen aanroepen:
Een methode pons waaraan een rijnummer $$r$$ (int) en een kolomnummer $$k$$ (int) moeten doorgegeven worden. Als $$(r, k)$$ geen positie van een cel in het rooster van ponskaart $$p$$ aanduidt ($$0 \leq r < m$$ en $$0 \leq k < n$$) dan moet een AssertionError opgeworpen worden met de boodschap ongeldige positie. Als ponskaart $$p$$ al een gat heeft op positie $$(r, k)$$ dan moet een AssertionError opgeworpen worden met de boodschap kaart al geponst op positie (r, k), waarbij de cursieve fragmenten respectievelijk ingevuld worden met rijnummer $$r$$ en kolomnummer $$k$$. Anders moet de methode een gat maken op positie $$(r, k)$$ in het rooster van ponskaart $$p$$ en een verwijzing naar ponskaart $$p$$ teruggeven.
Een methode vul waaraan een rijnummer $$r$$ (int) en een kolomnummer $$k$$ (int) moeten doorgegeven worden. Als $$(r, k)$$ geen positie van een cel in het rooster van ponskaart $$p$$ aanduidt ($$0 \leq r < m$$ en $$0 \leq k < n$$) dan moet een AssertionError opgeworpen worden met de boodschap ongeldige positie. Als ponskaart $$p$$ geen gat heeft op positie $$(r, k)$$ dan moet een AssertionError opgeworpen worden met de boodschap kaart niet geponst op positie (r, k), waarbij de cursieve fragmenten respectievelijk ingevuld worden met rijnummer $$r$$ en kolomnummer $$k$$. Anders moet de methode het gat op positie $$(r, k)$$ in het rooster van ponskaart $$p$$ verwijderen en een verwijzing naar ponskaart $$p$$ teruggeven.
Een methode isstevig waaraan geen argumenten moeten doorgegeven worden. De methode moet een Booleaanse waarde (bool) teruggeven die aangeeft of ponskaart $$p$$ voldoende stevig is. Dat is het geval als elk gat maximaal één ander gat heeft in zijn orthogonaal aangrenzende cellen en geen andere gaten in zijn diagonaal aangrenzende cellen. De orthogonaal aangrenzende cellen van een cel, zijn de cellen die een zijde gemeenschappelijk hebben met die cel (dus de cellen boven, onder, links en rechts van de cel). De diagonaal aangrenzende cellen van een cel, zijn de cellen die een hoekpunt gemeenschappelijk hebben met die cel (dus de cellen linksboven, linksonder, rechtsboven en rechtsonder van de cel).

Als de operator == gebruikt wordt om twee ponskaarten $$p$$ en $$q$$ (Ponskaart) met elkaar te vergelijken (p == q), dan moeten ze gelijk zijn als en slechts als ze dezelfde afmetingen en gaten op dezelfde posities hebben.
Als de operator + gebruikt wordt om twee ponskaarten $$p$$ en $$q$$ (Ponskaart) bij elkaar op te tellen (p + q), dan moeten de twee ponskaarten dezelfde afmetingen hebben. Als dat niet het geval is, dan moet een AssertionError opgeworpen worden met de boodschap ponskaarten zijn niet even groot. Anders moet de som een nieuwe ponskaart (Ponskaart) opleveren met dezelfde afmetingen als ponskaarten $$p$$ en $$q$$, en gaten op posities waarop zowel ponskaart $$p$$ als ponskaart $$q$$ een gat hebben.
>>> kaart = Ponskaart(4, 8)
>>> kaart
Ponskaart(4, 8, [])
>>> print(kaart)
########
########
########
########
>>> kaart.isstevig()
True
>>> kaart = Ponskaart(1, 2)
Traceback (most recent call last):
AssertionError: ongeldige ponskaart
>>> kaart = Ponskaart(4, 8, {(1, 2), (3, 6), (5, 12)})
Traceback (most recent call last):
AssertionError: ongeldige ponskaart
>>> kaart = Ponskaart(4, 8, ((1, 2), (2, 7), (3, 6)))
>>> kaart
Ponskaart(4, 8, [(1, 2), (2, 7), (3, 6)])
>>> print(kaart)
########
##O#####
#######O
######O#
>>> kaart.isstevig()
False
>>> kaart.pons(0, 5)
Ponskaart(4, 8, [(0, 5), (1, 2), (2, 7), (3, 6)])
>>> print(kaart)
#####O##
##O#####
#######O
######O#
>>> kaart.isstevig()
False
>>> kaart.pons(0, 1).pons(0, 4).pons(3, 5)
Ponskaart(4, 8, [(0, 1), (0, 4), (0, 5), (1, 2), (2, 7), (3, 5), (3, 6)])
>>> print(kaart)
#O##OO##
##O#####
#######O
#####OO#
>>> kaart.isstevig()
False
>>> kaart.pons(5, 10)
Traceback (most recent call last):
AssertionError: ongeldige positie
>>> kaart.pons(0, 4)
Traceback (most recent call last):
AssertionError: kaart al geponst op positie (0, 4)
>>> kaart.vul(2, 7)
Ponskaart(4, 8, [(0, 1), (0, 4), (0, 5), (1, 2), (3, 5), (3, 6)])
>>> print(kaart)
#O##OO##
##O#####
########
#####OO#
>>> kaart.isstevig()
False
>>> kaart.vul(1, 2).vul(3, 5).vul(0, 4)
Ponskaart(4, 8, [(0, 1), (0, 5), (3, 6)])
>>> print(kaart)
#O###O##
########
########
######O#
>>> kaart.isstevig()
True
>>> kaart.vul(5, 10)
Traceback (most recent call last):
AssertionError: ongeldige positie
>>> kaart.vul(0, 4)
Traceback (most recent call last):
AssertionError: kaart niet geponst op positie (0, 4)
>>> Ponskaart(4, 8, [(0, 1), (0, 5), (1, 2)]) == Ponskaart(4, 8, ((1, 2), (0, 1), (0, 5)))
True
>>> Ponskaart(4, 8, [(0, 1), (0, 5), (1, 2)]) == Ponskaart(4, 8, [(3, 5), (1, 2), (3, 6), (0, 4)])
False
>>> Ponskaart(4, 8, [(0, 1), (0, 5), (1, 2)]) == Ponskaart(5, 9, ((1, 2), (0, 1), (0, 5)))
False
>>> Ponskaart(4, 8, [(0, 1), (0, 5), (1, 2)]) + Ponskaart(4, 8, [(3, 5), (1, 2), (3, 6), (0, 4)])
Ponskaart(4, 8, [(1, 2)])
>>> print(Ponskaart(4, 8, [(0, 1), (0, 5), (1, 2)]) + Ponskaart(4, 8, [(3, 5), (1, 2), (3, 6), (0, 4)]))
########
##O#####
########
########
>>> Ponskaart(4, 8, [(0, 1), (0, 5), (1, 2)]) + Ponskaart(5, 9, [(3, 5), (1, 2), (3, 6), (0, 4)])
Traceback (most recent call last):
AssertionError: ponskaarten zijn niet even groot