Een kruiswoordraadsel is een woordpuzzel die je vaak in kranten, tijdschriften en puzzelboeken vindt. De puzzel wordt opgelost door woorden in te vullen in een rechthoekig rooster van witte en zwarte vakjes. Daarbij worden de woorden horizontaal (van links naar rechts) en verticaal (van boven naar onder) ingevuld in opeenvolgende witte vakjes — één letter per vakje. De rijen en kolommen van het rooster worden gelabeld. Bij het label van alle rijen en kolommen wordt voor elk gezocht woord een omschrijving gegeven. Voor, tussen en na een woord staan zwarte vakjes, zodat je op voorhand weet hoe lang elk woord is.
De oplossing van een kruiswoordraadsel met een $$m \times n$$ rooster met $$m$$ rijen en $$n$$ kolommen wordt opgeslagen in een tekstbestand dat bestaat uit $$m$$ regels die elk $$n$$ karakters bevatten. Die karakters zijn ofwel hoofdletters die in de witte vakjes komen of hekjes (#) die de zwarte vakjes aanduiden. Dit is bijvoorbeeld de inhoud van een bestand met de oplossing van een $$12 \times 12$$ kruiswoordraadsel.
ZELFSTANDIGE AMULET#ARE#I COCON#IDOLEN HERPES#EET#D T#A#GLEED#IR MITRAILLETTE OLIELEK#LEED ESEN#REIS#RA DEVENTER#SEC I#ESA#NADERT GE#SAS#KO#EI ENGERDS#GENE
Hieronder zie je een grafische weergave van dit kruiswoordraadsel: links de opgave van de puzzel met lege witte vakjes die nog moeten ingevuld worden en rechts de oplossing van de puzzel waarbij in elk wit vakje een hoofdletter ingevuld is.
Schrijf een bash shell script crossword waarmee een grafische weergave van de opgave of de oplossing van een kruiswoordraadsel kan getekend worden. Aan het script moet de padnaam doorgegeven worden van een tekstbestand met de oplossing van het kruiswoordraadsel. Het script moet de grafische voorstelling van het kruiswoordraadsel in SVG-formaat uitschrijven naar stdout. Zo moet het script voor dit bestand
DE# EN# #E#
bijvoorbeeld de volgende SVG-afbeelding uitschrijven
<svg xmlns="http://www.w3.org/2000/svg" height="82" width="82" version="1.1">
<defs><style type="text/css">
text { font-family: Verdana; font-size: 12px; fill: black; }
text { dominant-baseline: central; text-anchor: middle; }
rect { height: 20px; width: 20px; stroke: black; }
</style></defs>
<text x="30" y="10">1</text>
<text x="50" y="10">2</text>
<text x="70" y="10">3</text>
<text x="10" y="30">1</text>
<rect x="20" y="20" fill="white" />
<text x="30" y="30">D</text>
<rect x="40" y="20" fill="white" />
<text x="50" y="30">E</text>
<rect x="60" y="20" fill="black" />
<text x="10" y="50">2</text>
<rect x="20" y="40" fill="white" />
<text x="30" y="50">E</text>
<rect x="40" y="40" fill="white" />
<text x="50" y="50">N</text>
<rect x="60" y="40" fill="white" />
<text x="70" y="50">E</text>
<text x="10" y="70">3</text>
<rect x="20" y="60" fill="black" />
<rect x="40" y="60" fill="white" />
<text x="50" y="70">E</text>
<rect x="60" y="60" fill="black" />
</svg>
Verderop1 bespreken we stap voor stap hoe deze SVG-afbeelding opgebouwd wordt, maar grafisch ziet ze er als volgt uit:
Het script moet de volgende opties ondersteunen:
optie -s: teken de hoofdletters uit de oplossing van het kruiswoordraadsel in het midden van de witte vakjes; zonder deze optie wordt enkel de opgave van het kruiswoordraadsel getekend, en worden er dus geen hoofdletters in de witte vakjes getekend
optie -a: gebruik de opeenvolgende letters van het alfabet als labels van de rijen (van boven naar onder) en de kolommen (van links naar rechts); zonder deze optie worden de rijen (van boven naar onder) en de kolommen (van links naar rechts) genummerd vanaf 1
optie -f <string>: gebruik het lettertype dat aan deze optie wordt meegegeven (standaard: Verdana)
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, argument bij een optie moet niet noodzakelijk van de optieletter gescheiden worden door witruimte, …. Daarnaast moet het script de volgende foutafhandeling voorzien:
als niet de gepaste opties worden doorgegeven (enkel ondersteuning voor de opties -s, -a en -f, waarbij verplicht een argument aan de optie -f moet doorgegeven worden), dan moet het script een gepaste boodschap uitschrijven naar stderr en eindigen met exit status 1; het script moet niet expliciet controleren dat het argument dat aan de optie -f doorgegeven wordt een geldig lettertype voorstelt
als er niet juist één argument aan het script wordt doorgegeven, dan moet het script een gepaste boodschap uitschrijven naar stderr en eindigen met exit status 2
als het bestand dat aan het script wordt doorgegeven niet bestaat of geen leesbaar bestand is, dan moet het script een gepaste boodschap uitschrijven naar stderr en eindigen met exit status 3
Scalable Vector Graphics (SVG) is een op XML2 gebaseerd bestandsformaat voor vectorafbeeldingen3. Daarin worden afbeeldingen tekstueel beschreven aan de hand van eenvoudige meetkundige bouwstenen zoals tekst, punten, lijnen, cirkels en veelhoeken.
Je moet geen SVG kennen om deze opgave te kunnen oplossen. We bespreken stap voor stap aan de hand van een aantal sjablonen hoe de grafische voorstelling van een kruiswoordraadsel in SVG-formaat moet opgebouwd worden. Daarbij zullen we de variabele onderdelen van een sjabloon in het groen weergeven. Daar moet je dus zelf 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.
Als skelet voor de SVG-afbeelding gebruiken we dit sjabloon:
<svg xmlns="http://www.w3.org/2000/svg" height="82" width="82" version="1.1"> <defs><style type="text/css"> text { font-family: Verdana; font-size: 12px; fill: black; } text { dominant-baseline: central; text-anchor: middle; } rect { height: 20px; width: 20px; stroke: black; } </style></defs> … </svg>
Op de eerste regel worden de hoogte (height) en de breedte (width) van de afbeelding ingesteld. Een kruiswoordraadsel met een $$m \times n$$ rooster krijgt aan de linkerkant een extra kolom met rijlabels en aan de bovenkant een extra rij met kolomlabels. De hoogte van de afbeelding wordt dan berekend als $$20 \times (m + 1) + 2$$ en de breedte als $$20 \times (n + 1) + 2$$. De $$+ 2$$ op het einde van deze formules zorgt voor wat extra marge aan de rechterkant en onderaan. Op de derde regel wordt het lettertype (font-family) ingesteld voor de labels en de hoofdletters in de witte vakjes. Alle andere componenten van de afbeelding komen op de plaats waar de drie puntje (…) staan.
Voor het tekenen van het kruiswoordraadsel zelf, gebruiken we als bouwsteen dit sjabloon voor het tekenen van een vierkant met een witte of een zwarte achtergrond, met in het midden daarin een karakter: een cijfer of een hoofdletter.
<rect x="40" y="20" fill="white" /> <text x="50" y="30">E</text>
De rect-tag op de eerste regel tekent een vierkant met hoogte en breedte gelijk aan 20 en een zwarte rand. Die eigenschappen van het vierkant worden vastgelegd in het skelet voor de SVG-afbeelding. Om het vierkant op de juiste plaats te zetten, moeten we de $$x$$-coördinaat (x) en de $$y$$-coördinaat (y) van de linkerbovenhoek van het vierkant opgeven. Met het argument fill wordt de achtergrondkleur van het vierkant ingesteld: wit (white) of zwart (black).
De text-tag op de tweede regel tekent het karakter in het midden van het vierkant. Het karakter moet tussen de start- en eind-tag geplaatst worden (hier E als voorbeeld). Voor een vierkant met linkerbovenhoek op positie $$(x, y)$$, wordt met de argumenten x en y de positie van de tekst ingesteld op resp. $$x + 10$$ en $$y + 10$$.
Onderstaande figuur gebruikt het $$3 \times 3$$ kruiswoordraadsel van hierboven als voorbeeld om te tonen hoe de vierkanten en karakters met dit sjabloon moeten getekend worden. Hierbij is ook weer te zien dat het rooster werd uitgebreid met een extra kolom links voor de rijlabels en een extra rij bovenaan voor de kolomlabels.
Het coördinatenstelsel van SVG-afbeeldingen heeft haar oorsprong $$(0, 0)$$ in de linkerbovenhoek van de afbeelding, een X-as die loopt van links naar rechts en een Y-as die loopt van boven naar onder. De oranje pijl geeft aan dat we de vakjes van het uitgebreide rooster in de gebruikelijke leesvolgorde doorlopen: van links naar rechts en van boven naar onder. Voor elk vakje gebruiken we het sjabloon om een vierkant en een karakter te tekenen, maar het is niet altijd nodig om beide te tekenen. Soms moet enkel het vierkant getekend worden (eerste regel van het sjabloon) en soms moet enkel het karakter getekend worden (tweede regel van het sjabloon). De vakjes waarvoor geen vierkant moet getekend worden, hebben we in de figuur aangeduid door een lichtblauw vierkant met een gestippelde rand.
Voor de vakjes op de eerste rij en op de eerste kolom moet geen vierkant getekend worden. Let erop dat voor het allereerste vakje (met linkerbovenhoek in de oorsprong) niets moet getekend worden, dus ook geen karakter.
Voor vakjes die corresponderen met hoofdletters in het oplossingsbestand moet een vierkant met een witte achtergrond getekend worden.
Voor vakjes die corresponderen met hekjes (#) in het oplossingsbestand moet een vierkant met een zwarte achtergrond getekend worden.
Onderstaande voorbeeldsessie toont hoe het shell script crossword moet kunnen gebruikt worden. Hierbij gaan we ervan uit dat de huidige directory het bestand solution.txt4 bevat, en geen bestand unknown.txt bevat.
$ crossword solution.txt5 > puzzle.svg
$ echo $?
0
$ cat puzzle.svg
<svg xmlns="http://www.w3.org/2000/svg" height="82" width="82" version="1.1">
<defs><style type="text/css">
text { font-family: Verdana; font-size: 12px; fill: black; }
text { dominant-baseline: central; text-anchor: middle; }
rect { height: 20px; width: 20px; stroke: black; }
</style></defs>
<text x="30" y="10">1</text>
<text x="50" y="10">2</text>
<text x="70" y="10">3</text>
<text x="10" y="30">1</text>
<rect x="20" y="20" fill="white" />
<rect x="40" y="20" fill="white" />
<rect x="60" y="20" fill="black" />
<text x="10" y="50">2</text>
<rect x="20" y="40" fill="white" />
<rect x="40" y="40" fill="white" />
<rect x="60" y="40" fill="white" />
<text x="10" y="70">3</text>
<rect x="20" y="60" fill="black" />
<rect x="40" y="60" fill="white" />
<rect x="60" y="60" fill="black" />
</svg>
$ crossword -sa -f monospace solution.txt6
<svg xmlns="http://www.w3.org/2000/svg" height="82" width="82" version="1.1">
<defs><style type="text/css">
text { font-family: monospace; font-size: 12px; fill: black; }
text { dominant-baseline: central; text-anchor: middle; }
rect { height: 20px; width: 20px; stroke: black; }
</style></defs>
<text x="30" y="10">A</text>
<text x="50" y="10">B</text>
<text x="70" y="10">C</text>
<text x="10" y="30">A</text>
<rect x="20" y="20" fill="white" />
<text x="30" y="30">D</text>
<rect x="40" y="20" fill="white" />
<text x="50" y="30">E</text>
<rect x="60" y="20" fill="black" />
<text x="90" y="30">
</text>
<text x="10" y="50">B</text>
<rect x="20" y="40" fill="white" />
<text x="30" y="50">E</text>
<rect x="40" y="40" fill="white" />
<text x="50" y="50">N</text>
<rect x="60" y="40" fill="white" />
<text x="70" y="50">E</text>
<text x="90" y="50">
</text>
<text x="10" y="70">C</text>
<rect x="20" y="60" fill="black" />
<rect x="40" y="60" fill="white" />
<text x="50" y="70">E</text>
<rect x="60" y="60" fill="black" />
<text x="90" y="70">
</text>
</svg>
$ crossword -X solution.txt7
Syntax: crossword [-s] [-a] [-f <font>] FILE
$ echo $?
1
$ crossword
Syntax: crossword [-s] [-a] [-f <font>] FILE
$ echo $?
2
$ crossword unknown.txt
Syntax: crossword [-s] [-a] [-f <font>] FILE
$ echo $?
3
Dit is de grafische voorstelling van de kruiswoordraadsels die door de eerste twee commando's gegenereerd worden: