Een rebus is een puzzel die één of meer woorden beschrijft aan de hand van een reeks emoji. Bij elke emoji horen ook nul of meer acties die aangeven welke letters toegevoegd, verwijderd of vervangen moeten worden door andere letters. De rebus groepeert emoji (en bijhorende acties) die samen één woord van de oplossing vormen. Zo beschrijft onderstaande rebus twee woorden: het eerste woord wordt beschreven door de eerste drie emoji en het tweede woord door de laatste twee emoji.

rebus (opgave)
De opgave van een rebus.

We zullen de opgave van een rebus beschrijven in een tekstbestand met een reeks patronen, waarbij elk patroon op een afzonderlijke regel staat. Zo wordt bovenstaande voorbeeldrebus beschreven door dit bestand:

🐸 f=p
🚊 -t
💍 r=m

✋ h=l -d
🍇 r=u p=g

Opmerking

Tekstbestanden die een rebus beschrijven, gebruiken de UTF-81 (8-bit Unicode Transformation Format) tekencodering om Unicode-tekens voor te stellen als een stroom van bytes. Daarbij is het belangrijk om weten dat een emoji uit meerdere bytes kan bestaan. Je mag er dus niet van uitgaan dat elke emoji bestaat uit één enkel karakter.

Elk patroon beschrijft een deel van de oplossing. Er zijn twee verschillende soorten patronen:

Voor deze opgave is enkel de beschrijving van de rebus belangrijk. Je hoeft niet te weten hoe een rebus moet opgelost worden.

Opgave

Schrijf een bash shell script rebus waaraan de padnaam moet doorgegeven worden van een tekstbestand dat een rebus beschrijft. Daarbij mag het script ervan uitgaan dat de padnaam effectief wordt doorgegeven en dat het bestand een geldige beschrijving van een rebus bevat, zonder dat dit expliciet moet gecontroleerd worden.

Het script moet een afbeelding in SVG-formaat uitschrijven naar standard output (stdout). Die afbeelding moet de rebus weergeven op een donkergrijze rechthoek. Daarop worden lichtgrijze rechthoeken getekend voor elke groep van emoji (en bijhorende acties) die samen één woord vormen in de oplossing van de rebus. Tenslotte worden daarop dan de emoji en de acties zelf getekend. Verderop2 bespreken we stap voor stap hoe deze SVG-afbeelding opgebouwd wordt. Het script moet bijvoorbeeld kunnen gebruikt worden om de afbeelding uit de inleiding te genereren op basis van het tekstbestand uit de inleiding. Dit is dan de afbeelding die het script naar standard output moet schrijven (rebus.svg3):

<svg xmlns="http://www.w3.org/2000/svg" width="430" height="160" version="1.1">
<style>text{font-weight:bold;text-anchor:middle;dominant-baseline:middle}</style>
<rect x="0" y="0" rx="10" width="430" height="160" fill="#D9D9D9" />
<rect x="10" y="10" rx="10" width="240" height="140" fill="#F2F2F2" />
<rect x="260" y="10" rx="10" width="160" height="140" fill="#F2F2F2" />
<text x="50" y="50" font-size="42">🐸</text>
<text x="50" y="110" font-family="monospace" font-size="14">f=p</text>
<text x="130" y="50" font-size="42">🚊</text>
<text x="130" y="110" font-family="monospace" font-size="14">-t</text>
<text x="210" y="50" font-size="42">💍</text>
<text x="210" y="110" font-family="monospace" font-size="14">r=m</text>
<text x="300" y="50" font-size="42">✋</text>
<text x="300" y="110" font-family="monospace" font-size="14">h=l</text>
<text x="300" y="130" font-family="monospace" font-size="14">-d</text>
<text x="380" y="50" font-size="42">🍇</text>
<text x="380" y="110" font-family="monospace" font-size="14">r=u</text>
<text x="380" y="130" font-family="monospace" font-size="14">p=g</text>
</svg>

Opbouw van de SVG-afbeelding

Scalable Vector Graphics (SVG) is een op XML4 gebaseerd bestandsformaat dat vectorafbeeldingen5 tekstueel beschrijft aan de hand van eenvoudige meetkundige bouwstenen zoals punten, lijnen, cirkels en veelhoeken. Het script rebus moet een SVG-afbeelding genereren die als volgt opgebouwd wordt.

SVG-structuur
Structuur van de SVG-afbeelding die gegenereerd wordt door het shell script rebus.

Je moet geen SVG of XML kennen om deze opgave op te lossen. We bespreken stap voor stap hoe de grafische voorstelling van de rebus in SVG-formaat opgebouwd wordt aan de hand van vijf sjablonen: hoofding6, groep7, emoji8, actie9 en voettekst10. 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 uitgeschreven wordt precies overeenkomt met de beschreven specificatie.

Hoofding
<svg xmlns="http://www.w3.org/2000/svg" width="430" height="160" version="1.1">
<style>text{font-weight:bold;text-anchor:middle;dominant-baseline:middle}</style>
<rect x="0" y="0" rx="10" width="430" height="160" fill="#D9D9D9" />

Op de eerste regel van het sjabloon worden de breedte (width) en de hoogte (height) van de SVG-afbeelding ingesteld (in pixels). Deze afmetingen hangen af van een aantal eigenschappen van de rebus: het aantal patronen met emoji ($$p$$), het aantal groepen ($$g$$) en het maximum aantal acties per emoji ($$a$$). De breedte van de afbeelding wordt dan berekend als $$(1 + g + 8p)u$$ en de hoogte als $$(12 + 2a)u$$. Daarbij is $$u$$ een eenheid (unit) van 10 pixels. De waarden die in het sjabloon in het groen weergegeven worden corresponderen met de voorbeeldrebus: die heeft $$p = 5$$ patronen met een emoji (en bijhorende acties), $$g = 2$$ groepen en maximum $$a = 2$$ acties per emoji.

De tweede regel blijft altijd hetzelfde en stelt enkele stijleigenschappen in voor de weergave van tekst in de SVG-afbeelding. De derde regel tekent als achtergrond een (afgeronde) donkergrijze rechthoek die de volledige afbeelding bedekt. Vandaar dat die dezelfde breedte (width) en hoogte (height) heeft als de afbeelding.

Groep
<rect x="10" y="10" rx="10" width="240" height="140" fill="#F2F2F2" />

Daarna tekenen we een (afgronde) lichtgrijze rechthoek voor elke groep van emoji (en bijhorende acties) die samen één woord van de oplossing vormen. Het sjabloon tekent één zo'n rechthoek. De waarden uit het sjabloon corresponderen met de eerste groep van de voorbeeldrebus (die in totaal twee groepen heeft).

Om de plaatsing en de grootte van de rechthoek te kunnen berekenen, moet je weten dat SVG-afbeeldingen een coördinatenstelsel hebben met oorsprong in de linkerbovenhoek, een X-as die horizontaal naar rechts gericht is en een Y-as die verticaal naar onder gericht is. Dit wordt aangegeven door de zwarte cirkel en de zwarte assen in onderstaande afbeelding.

SVG-structuur (groep)
Structuur van de SVG-afbeelding die gegenereerd wordt door het shell script rebus. Het coördinatenstelsel van de SVG-afbeelding wordt aangegeven door de zwarte cirkel (oorsprong) en de zwarte assen. Voor de twee groepen geven de groene cirkels aan waar de rechthoeken moeten geposititioneerd worden. Die posities vallen samen met de linkerbovenhoek van de rechthoeken.

Horizontaal geven we de afbeelding links en rechts telkens een marge van 1 eenheid breed. Voor elk patroon met een emoji (en bijhorende acties) reserveren we een vierkant gebied van 8 eenheden breed. Er is geen marge tussen twee gebieden van dezelfde groep en een marge van 1 eenheid breed tussen twee groepen. Verticaal geven we de afbeelding bovenaan en onderaan telkens ook een marge van 1 eenheid hoog. Onder de bovenmarge komen achtereenvolgens het gebied voor een emoji van 8 eenheden hoog, een marge van 1 eenheid hoog, rechthoekige gebieden voor de acties die telkens 2 eenheden hoog zijn en dan nog een marge van 1 eenheid hoog. De marge van 1 eenheid aan de buitenrand van de afbeelding en de marge van 1 eenheid breed tussen twee groepen moeten niet door lichtgrijze rechthoeken bedekt worden. De andere gebieden van de afbeelding moeten wel door lichtgrijze rechthoeken bedekt worden.

In het sjabloon geven de parameters x en y de positie aan waar de linkerbovenhoek van de rechthoek moet komen, zoals aangeduid door de groene cirkels in bovenstaande figuur. De $$y$$-positie ligt altijd onder de bovenmarge die 1 eenheid (10 pixels) hoog is, en verandert dus niet. De breedte (width) en hoogte (height) van de rechthoek moeten ook ingesteld worden.

Emoji
<text x="50" y="50" font-size="42">🐸</text>

Dit sjabloon tekent een emoji als tekst op positie $$(x, y)$$. Die positie staat zowel horizontaal als verticaal gecentreerd in het vierkante gebied van 8 eenheden breed en 8 eenheden hoog dat voor de emoji gereserveerd is (aangeduid met groene cirkels in onderstaande figuur). De emoji zelf komt op de plaats waar de groene kikker (🐸) staat in het sjabloon.

SVG-structuur
Structuur van de SVG-afbeelding die gegenereerd wordt door het shell script rebus. Het coördinatenstelsel van de SVG-afbeelding wordt aangegeven door de zwarte cirkel (oorsprong) en de zwarte assen. Voor de vijf emoji geven de groene cirkels aan waar de tekst met de emoji moet gepositioneerd worden. Die posities staan zowel horizontaal als verticaal gecentreerd in het vierkante gebied van 8 eenheden breed en 8 eenheden hoog dat voor de emoji gereserveerd is.

Na een regel die een emoji tekent, volgen in de SVG-afbeelding eerst de regels die de bijhorende acties tekenen. Dit is dus vóór een eventuele volgende regel die de volgende emoji tekent.

Actie
<text x="50" y="110" font-family="monospace" font-size="14">f=p</text>

Dit sjabloon tekent de tekst van een actie op positie $$(x, y)$$. Die positie staat zowel horizontaal als verticaal gecentreerd in het rechthoekige gebied van 8 eenheden breed en 2 eenheden hoog dat voor de actie gereserveerd is (aangeduid met groene cirkels in onderstaande figuur). De tekst van de actie zelf komt op de plaats waar f=p staat in het sjabloon.

SVG-structuur
Structuur van de SVG-afbeelding die gegenereerd wordt door het shell script rebus. Het coördinatenstelsel van de SVG-afbeelding wordt aangegeven door de zwarte cirkel (oorsprong) en de zwarte assen. Voor de zeven acties geven de groene cirkels aan waar de tekst met de beschrijving van de acties moet gepositioneerd worden. Die posities staan zowel horizontaal als verticaal gecentreerd in het rechthoekige gebied van 8 eenheden breed en 2 eenheden hoog dat voor de acties gereserveerd is.
Voettekst
</svg>

Dit laatste sjabloon sluit de SVG-afbeelding af met een SVG stop-tag.

Voorbeeld

Onderstaande voorbeeldsessie toont hoe het shell script rebus moet kunnen gebruikt worden. Hierbij gaan we ervan uit dat de huidige directory het bestand rebus.txt11 bevat.

$ rebus rebus.txt12 > rebus.svg13
$ cat rebus.svg14
<svg xmlns="http://www.w3.org/2000/svg" width="430" height="160" version="1.1">
<style>text{font-weight:bold;text-anchor:middle;dominant-baseline:middle}</style>
<rect x="0" y="0" rx="10" width="430" height="160" fill="#D9D9D9" />
<rect x="10" y="10" rx="10" width="240" height="140" fill="#F2F2F2" />
<rect x="260" y="10" rx="10" width="160" height="140" fill="#F2F2F2" />
<text x="50" y="50" font-size="42">🐸</text>
<text x="50" y="110" font-family="monospace" font-size="14">f=p</text>
<text x="130" y="50" font-size="42">🚊</text>
<text x="130" y="110" font-family="monospace" font-size="14">-t</text>
<text x="210" y="50" font-size="42">💍</text>
<text x="210" y="110" font-family="monospace" font-size="14">r=m</text>
<text x="300" y="50" font-size="42">✋</text>
<text x="300" y="110" font-family="monospace" font-size="14">h=l</text>
<text x="300" y="130" font-family="monospace" font-size="14">-d</text>
<text x="380" y="50" font-size="42">🍇</text>
<text x="380" y="110" font-family="monospace" font-size="14">r=u</text>
<text x="380" y="130" font-family="monospace" font-size="14">p=g</text>
</svg>