Het spelletje Turing Machine maakt gebruik van 15 verschillende ponskaarten: kaarten waarin gaten geponst zijn. Elke ponskaart heeft een gekleurd label waarop één cijfer staat tussen 1 en 5 (grenzen inbegrepen). 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 op elkaar stapelt, dan blijft er slechts één gat over.

De ponskaarten van het spelletje Turing Machine zijn zo ontworpen dat elke stapel 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.

Elk gat heeft maximaal één ander gat in zijn orthogonaal aangrenzende cellen en geen andere gaten in zijn diagonaal aangrenzende cellen. Daarbij zijn de orthogonaal aangrenzende cellen van een cel, 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).
De 15 verschillende ponskaarten van het spelletje Turing Machine wordt op een unieke manier aangeduid met drie karakters ccc. Twee van die karakters zijn de hoofdletter X en het andere karakter is een cijfer tussen 1 en 5 (grenzen inbegrepen). De positie van het cijfer binnen de drie karakters geeft aan of de ponskaart respectievelijk staat voor het eerste, het tweede (middelste) of het derde (laatste) cijfer van een getal van drie cijfers. Zo duidt 4XX de ponskaart aan die gebruikt wordt voor een getal van drie cijfers waarvan het eerste cijfer een 4 is. Ponskaart XX1 duidt de ponskaart aan die gebruikt wordt voor een getal van drie cijfers waarvan het derde (laatste) cijfer een 1 is.
Voor elke ponskaart bevat de huidige directory een tekstbestand card.ccc.txt dat aangeeft waar er gaten zitten in de kaart. Daarbij duiden de drie karakters ccc in de bestandsnaam aan over welke ponskaart het gaat. Een ponskaart heeft een $$12 \times 12$$ rooster met $$12$$ rijen en $$12$$ kolommen, waarbij in elke cel van het rooster een gat kan geponst worden. Dit is bijvoorbeeld ponskaart 4XX waarin 25 gaten geponst werden. In de afbeelding aan de linkerkant worden de 25 gaten afzonderlijk weergegeven, en in de afbeelding aan de rechterkant worden twee aangrenzende gaten telkens samengevoegd tot één groot gat.


Om naar de cellen in het rooster te kunnen verwijzen, worden de rijen van het rooster van boven naar onder genummerd, en de kolommen van links naar rechts, telkens vanaf nul. We hebben deze rij- en kolomnummers ook weergegeven in bovenstaande afbeeldingen.
Het tekstbestand voor een ponskaart bevat 12 regels, één voor elke rij van het rooster (opgelijst van boven naar onder). Elke regel bestaat uit 12 karakters die de opeenvolgende cellen op de corresponderende rij beschrijven (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. Dit is bijvoorbeeld de inhoud van het tekstbestand card.4XX.txt (alle bestanden: cards.zip).
####O#O###O#
######O#####
#OO######OO#
#####O######
O#OO########
########O###
O#OO#######O
###########O
O#####OO####
###O########
###O#O##O###
############
Een stapel van twee of drie ponskaarten die corresponderen met cijfers op verschillende posities uit een getal van drie cijfers, duiden we ook aan met drie karakters ccc. Die drie karakters staan opnieuw voor het eerste, het middelste (tweede) en het laatste (derde) cijfer van een getal van drie cijfers. Als de stapel een kaart bevat voor een bepaalde positie in het getal van drie cijfers, dan is het karakter op die positie het corresponderende cijfer tussen 1 en 5 (grenzen inbegrepen). Anders is het karakter op die positie de hoofdletter X. Zo duidt 4X1 bijvoorbeeld een stapel van twee kaarten aan voor een getal van drie cijfers waarvan het eerste cijfer een 4 is en het derde (laatste) cijfer een 1. De stapel bevat geen kaart voor het middelste cijfer.
Schrijf een bash shell script punchcard waarmee een grafische weergave in SVG-formaat kan uitgeschreven worden naar standaard uitvoer (stdout) van 1, 2 of 3 ponskaarten van het spelletje Turing Machine die op elkaar gestapeld worden. Verderop bespreken we stap voor stap hoe deze SVG-afbeelding opgebouwd wordt. Het script moet bijvoorbeeld kunnen gebruikt worden om deze weergave te genereren van stapel 4X1 met twee ponskaarten die 5 gemeenschappelijk gaten hebben. De twee gaten die naast elkaar liggen werden samengevoegd tot één groter gat.

Aan het script moet één argument doorgegeven worden: de drie karakters ccc die een stapel aanduiden van 1, 2 of 3 ponskaarten van het spelletje Turing Machine. Elk van die karakters is ofwel een cijfer tussen 1 en 5 zijn (grenzen inbegrepen) of de hoofdletter X, en minstens één karakter moet een cijfer zijn: het argument XXX is dus ongeldig.
Het script moet de volgende opties ondersteunen:
optie -g: toon het rooster in de weergave van de ponskaarten; standaard wordt het rooster niet getoond
optie -l: toon labels in de weergave van de ponskaarten; standaard worden labels niet getoond
optie -m: voeg gaten die horizontaal of verticaal naast elkaar liggen samen tot één groter gat; standaard worden alle gaten afzonderlijk weergegeven
Het script moet voor de verwerking van de opties de flexibiliteit aan de dag leggen die gebruikelijk is bij Unix commando's: volgorde van opties speelt geen rol, opties kunnen samengenomen worden, ….
Daarnaast moet het script de volgende foutafhandeling voorzien:
als niet de gepaste opties doorgegeven worden (enkel ondersteuning voor de opties -g, -l en -m), dan moet het script een gepaste boodschap uitschrijven naar stderr en eindigen met exit status 1
als er aan het script niet juist één argument doorgegeven wordt, dan moet het script een gepaste boodschap uitschrijven naar stderr en eindigen met exit status 2
als er aan het script een argument wordt doorgegeven dat geen geldige aanduiding is voor een stapel met 1, 2 of 3 ponskaarten, dan moet het script een gepaste boodschap uitschrijven naar stderr en eindigen met exit status 3
Hierbij hebben we de foutafhandeling in volgorde van prioriteit opgelijst. De gepaste foutboodschappen vind je terug in onderstaand voorbeeld.
Scalable Vector Graphics (SVG) is een XML-gebaseerd bestandsformaat dat vectorafbeeldingen tekstueel beschrijft aan de hand van eenvoudige meetkundige bouwstenen zoals punten, lijnen, cirkels en veelhoeken.
Je moet geen SVG of XML kennen om deze opgave op te lossen. We bespreken stap voor stap hoe de grafische weergave van een stapel ponskaarten in SVG-formaat opgebouwd wordt aan de hand van zes sjablonen: hoofding, label, kaart, rooster, gat en voettekst. Daarbij zullen we de variabele onderdelen van elk sjabloon in het groen weergeven. Daar moet het script de juiste waarden invullen. Het is belangrijk dat elk sjabloon exact overgenomen wordt, zodat het resultaat dat het script uitschrijft precies overeenkomt met de beschreven specificatie.
<svg xmlns="http://www.w3.org/2000/svg" width="50%" viewBox="-1 -4 14 17" version="1.1">
<style type="text/css">
.label-digit{stroke:#e7e7e7;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round}
.card-grid{stroke:#ddd;stroke-width:12.05;stroke-dasharray:0.05 0.95}
</style>
<rect x="-1" y="-4" rx="0.25" width="14" height="17" fill="#2fa95b"/>
Dit sjabloon bevat de SVG start-tag (svg) en algemene opmaak van de afbeelding (style). Het tekent ook een afgeronde groene rechthoek (rect) als achtergrond. Op de eerste en zesde regel van het sjabloon moeten dezelfde twee natuurlijke getallen ingevuld worden: i) de $$y$$-coördinaat (y) van de linkerbovenhoek van de afbeelding is 1 als labels niet getoond worden en 4 als labels wel getoond worden en ii) de hoogte (height) van de afbeelding is 14 als labels niet getoond worden en 17 als labels wel getoond worden. Merk op dat het minteken (-) dat voorafgaat aan het eerste natuurlijke getal dat moet ingevuld worden, een vast onderdeel vormt van het sjabloon.
<rect x="0" y="-3.5" rx="0.25" width="2.5" height="3.25" fill="#53c1db"/>
<path class="label-digit" d="M0.625,-3v1h1.25v-1v2" fill="None" stroke="#e7e7e7"/>
Dit sjabloon tekent een afgeronde gekleurde rechthoek (rect) voor een label en een grijs cijfer (path) op het label. Deze twee regels moeten aan de SVG-afbeelding toegevoegd worden voor elk gekleurd label dat moet getoond worden, resp. voor het eerste, het tweede (middelste) en het derde (laatste) cijfer van een getal van drie cijfers. Onderstaande tabel geeft voor elk van deze drie labels aan welke $$x$$-offset (x) en HTML-kleur (fill) moeten ingevuld worden op de eerste regel van het sjabloon.
| label | $$x$$-offset | kleur | HTML-kleur |
|---|---|---|---|
| eerste cijfer | 0 | blauw | #53c1db |
| tweede (middelste) cijfer | 3 | geel | #e7c502 |
| derde (laatste) cijfer | 6 | paars | #9474be |
De $$x$$-offset van het label is ook de eerste waarde die moet ingevuld worden op de tweede regel van het sjabloon. De tweede waarde die moet ingevuld worden op die regel, is het pad om het cijfer op het label te tekenen. Onderstaande tabel bevat de paden voor de verschillende cijfers.
| cijfer | pad |
|---|---|
| 1 | h0.625v2h-0.625h1.25 |
| 2 | h1.25v1h-1.25v1h1.25 |
| 3 | h1.25v2h-1.25m0,-1h1.25 |
| 4 | v1h1.25v-1v2 |
| 5 | m1.25,0h-1.25v1h1.25v1h-1.25 |
<rect x="-0.5" y="-0.5" rx="0.25" width="13" height="13" fill="#e7e7e7"/>
Dit is een vast sjabloon om de afgeronde grijze rechthoek (rect) voor de kaart te tekenen.
<path class="card-grid" d="M-0.025,6L12.025,6M6,-0.025L6,12.025"/>
Dit is een vast sjabloon om een rooster op de kaart te tekenen. Die moet enkel toegevoegd worden aan de SVG-afbeelding als de optie -g gebruikt werd.
<rect x="10.1" y="2.1" rx="0.125" width="0.8" height="0.8" fill="#2fa95b"/>
Voor elke cel in het rooster waar een gat zit in alle ponskaarten die op elkaar gestapeld worden, moet dit sjabloon aan de SVG-afbeelding toegevoegd worden om een afgerond groen gat (rect) te tekenen. Daarbij moeten de gaten in leesvolgorde aan de SVG-afbeelding toegevoegd worden: van links naar rechts, en van boven naar onder.
Voor een gat in de cel op kolom $$y$$ en rij $$x$$ in het rooster van de ponskaart, zijn $$y$$ en $$x$$ de twee natuurlijke getallen die op de eerste twee plaatsen in het sjabloon moeten ingevuld worden. Merk op dat de .1 voor beide coördinaten een vast onderdeel vormt van het sjabloon.
Voor de breedte (width) en de hoogte (height) moet telkens de waarde 0 ingevuld worden om het gat afzonderlijk weer te geven. Merk op dat de .8 voor de breedte en de hoogte een vast onderdeel vormt van het sjabloon.
Als de optie -m gebruikt wordt en er zit rechts van de cel waar een gat zit nog een ander gat, dan moet voor de breedte (width) van het gat de waarde 1 ingevuld worden. Daardoor worden beide gaten die horizontaal naast elkaar staan samen getekend als één groter gat, en mag het rechtse gat niet meer afzonderlijk getekend worden.
Als de optie -m gebruikt wordt en er zit onder de cel waar een gat zit nog een ander gat, dan moet voor de hoogte (height) van het gat de waarde 1 ingevuld worden. Daardoor worden beide gaten die verticaal naast elkaar staan samen getekend als één groter gat, en mag het onderste gat niet meer afzonderlijk getekend worden.
De manier waarop de gaten over de ponskaarten verspreid liggen om de kaarten hun stevigheid niet te laten verliezen, garandeert dat bovenstaande twee gevallen nooit samen kunnen voorkomen.
</svg>
Dit is een vast sjabloon dat de SVG-afbeelding afsluit met een SVG stop-tag.
Onderstaande shell sessie toont hoe het bash script punchcard moet kunnen gebruikt worden. Daarbij gaan we ervan uit dat de huidige directory de 15 tekstbestanden card.ccc.txt bevat (cards.zip).
$ punchcard -lgm 4X1 > card.svg
$ cat card.svg
<svg xmlns="http://www.w3.org/2000/svg" width="50%" viewBox="-1 -4 14 17" version="1.1">
<style type="text/css">
.label-digit{stroke:#e7e7e7;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round}
.card-grid{stroke:#ddd;stroke-width:12.05;stroke-dasharray:0.05 0.95}
</style>
<rect x="-1" y="-4" rx="0.25" width="14" height="17" fill="#2fa95b"/>
<rect x="0" y="-3.5" rx="0.25" width="2.5" height="3.25" fill="#53c1db"/>
<path class="label-digit" d="M0.625,-3v1h1.25v-1v2" fill="None" stroke="#e7e7e7"/>
<rect x="6" y="-3.5" rx="0.25" width="2.5" height="3.25" fill="#9474be"/>
<path class="label-digit" d="M6.625,-3h0.625v2h-0.625h1.25" fill="None" stroke="#e7e7e7"/>
<rect x="-0.5" y="-0.5" rx="0.25" width="13" height="13" fill="#e7e7e7"/>
<path class="card-grid" d="M-0.025,6L12.025,6M6,-0.025L6,12.025"/>
<rect x="10.1" y="2.1" rx="0.125" width="0.8" height="0.8" fill="#2fa95b"/>
<rect x="8.1" y="5.1" rx="0.125" width="0.8" height="0.8" fill="#2fa95b"/>
<rect x="2.1" y="6.1" rx="0.125" width="1.8" height="0.8" fill="#2fa95b"/>
<rect x="11.1" y="7.1" rx="0.125" width="0.8" height="0.8" fill="#2fa95b"/>
</svg>
$ punchcard -x 431
Syntax: punchcard [-glm] stack
$ echo $?
1
$ punchcard 123 345
Syntax: punchcard [-glm] stack
$ echo $?
2
$ punchcard 9X7
punchcard: invalid stack: 9X7
$ echo $?
3