Een rebus is een puzzel die één of meer woorden beschrijft aan de hand van een reeks emoji. Bij elke emoji horen nul of meer acties die aangeven welke letters toegevoegd, verwijderd of vervangen moeten worden door andere letters. De rebus groepeert emoji (en hun bijhorende acties) die samen één woord van de oplossing vormen. Zo beschrijft onderstaande (Engelstalige) rebus twee woorden: het eerste woord wordt beschreven door de eerste twee emoji en het tweede woord door de laatste twee emoji.
We zullen een rebus voorstellen als een lijst (list) van groepen. Elke groep beschrijft één woord (een reeks kleine letters) van de oplossing en wordt voorgesteld als een lijst (list) van patronen. Elk patroon beschrijft een deel van een woord en wordt voorgesteld als een string (str) die bestaat uit een combinatie van één of meer emoji gevolgd door nul of meer acties. Na de combinatie en tussen elke twee acties staat telkens één spatie. Acties bevatten zelf nooit spaties. Zo wordt de voorbeeldrebus voorgesteld als
[['📦📦 -s x=h', '🐜 mi+ -t'], ['🔧 +a -w -enc', '🐩 le=y o=s']]
We werken in twee stappen om de reeks letters te bepalen die door een patroon beschreven wordt. Eerst zoeken we in een lexicon op welk woord er bij de combinatie van emoji uit het patroon hoort. Een lexicon is een tekstbestand waarvan elke regel bestaat uit een combinatie van emoji, een spatie en het woord dat bij die combinatie hoort. Elke combinatie komt hoogstens één keer in het lexicon voor en alle woorden bestaan uit één of meer kleine letters. Dit is bijvoorbeeld een deel van een lexicon waarin alle combinaties van emoji uit de voorbeeldrebus voorkomen:
… 📦 box 📦📦 boxes 🐜 ant 🔧 wrench 🐩 poodle …
Deze opgave gebruikt 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 uit één enkel karakter bestaat.
Als we het woord gevonden hebben dat bij de combinatie van emoji hoort, dan moeten we daar vervolgens alle bijhorende acties op toepassen. Daarbij is het belangrijk dat deze acties in de gegeven volgorde (van links naar rechts) toegepast worden. We onderscheiden vier soorten acties:
prefix: één of meer kleine letters gevolgd door een plusteken (abc+); de letters (abc) moeten aan het begin van het woord toegevoegd worden
suffix: een plusteken gevolgd door één of meer kleine letters (+abc); de letters (abc) moeten aan het einde van het woord toegevoegd worden
deletie: een minteken gevolgd door één of meer kleine letters (-abc); het eerste voorkomen van de substring van letters (abc) moet uit het woord verwijderd worden
substitutie: een gelijkteken dat voorafgegaan en gevolgd wordt door één of meer kleine letters (abc=def); het eerste voorkomen van de substring van letters voor het gelijkteken (abc) moet in het woord vervangen worden door de substring van letters na het gelijkteken (def)
Onderstaande tabel geeft aan hoe de voorbeeldrebus kan opgelost worden door elk patroon in twee stappen om te zetten naar een deel van de oplossing.
patroon | woord (stap 1) | acties (stap 2) | deel van oplossing |
---|---|---|---|
📦📦 -s x=h | boxes | boxes → boxe → bohe | bohe |
🐜 mi+ -t | ant | ant → miant → mian | mian |
🔧 +a -w -enc | wrench | wrench → wrencha → rencha → rha | rha |
🐩 le=y o=s | poodle | poodle → poody → psody | psody |
Als we alle delen van een groep samenvoegen tot één woord en alle woorden samenvoegen met tussenliggende spaties, dan krijgen we de oplossing van de voorbeeldrebus: bohemian rhapsody2.
Schrijf een programma waarmee rebussen kunnen opgelost worden. Hiervoor ga je als volgt te werk:
Schrijf een functie lees_lexicon waaraan de locatie (str) van een lexicon $$\mathcal{L}$$ moet doorgegeven worden. De functie moet een dictionary (dict) teruggeven die elke combinatie van emoji uit het lexicon afbeeldt op het corresponderende woord. We noemen dit resultaat de dictionaryvoorstelling van lexicon $$\mathcal{L}$$.
Schrijf een functie bijwerken waaraan twee argumenten moeten doorgegeven worden: i) een woord $$w$$ (str) en ii) een actie $$a$$ (str). De functie moet de reeks letters (str) teruggeven die verkregen worden door actie $$a$$ toe te passen op woord $$w$$.
Schrijf een functie deel waaraan twee argumenten moeten doorgegeven worden: i) een patroon $$p$$ (str) en ii) de dictionaryvoorstelling (dict) van een lexicon $$\mathcal{L}$$. De functie moet de reeks letters teruggeven die volgens lexicon $$\mathcal{L}$$ beschreven worden door patroon $$p$$.
Schrijf een functie rebus waaraan twee argumenten moeten doorgegeven worden: i) de voorstelling (list) van een rebus en ii) de dictionaryvoorstelling (dict) van een lexicon $$\mathcal{L}$$. De functie moet de oplossing (str) van de rebus volgens lexicon $$\mathcal{L}$$ teruggeven.
In onderstaande voorbeeldsessie gaan we ervan uit dat het tekstbestand lexicon.txt3 zich in de huidige directory bevindt.
>>> lexicon = lees_lexicon('lexicon.txt4')
>>> lexicon['🔧']
'wrench'
>>> lexicon['🐜']
'ant'
>>> lexicon['🐩']
'poodle'
>>> lexicon['📦📦']
'boxes'
>>> bijwerken('wrench', '+a')
'wrencha'
>>> bijwerken('ant', 'mi+')
'miant'
>>> bijwerken('poodle', 'le=y')
'poody'
>>> bijwerken('boxes', '-s')
'boxe'
>>> deel('📦📦 -s x=h', lexicon)
'bohe'
>>> deel('🐜 mi+ -t', lexicon)
'mian'
>>> deel('🔧 +a -w -enc', lexicon)
'rha'
>>> deel('🐩 le=y o=s', lexicon)
'psody'
>>> deel('🔥👅 c=et p=oc', lexicon)
'society5'
>>> rebus([['📦📦 -s x=h', '🐜 mi+ -t'], ['🔧 +a -w -enc', '🐩 le=y o=s']], lexicon)
'bohemian rhapsody6'
>>> rebus([['🏨'], ['🐈 t=l', '📯 h=if', '🐈 c=i -t']], lexicon)
'hotel california7'
>>> rebus([['🧂 a=u', '🐜🐜 -t'], ['🐂 x=f'], ['💍 r=sw']], lexicon)
'sultans of swing8'
>>> rebus([['🐚🐚 h=m'], ['🚲 b=l'], ['🟢 gr=t'], ['🕷 -de', '🪁 -k -e']], lexicon)
'smells like teen spirit9'