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.

rebus (opgave)
Een rebus.

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
…

Opmerking

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:

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 boxesboxebohe bohe
🐜 mi+ -t ant antmiantmian mian
🔧 +a -w -enc wrench wrenchwrencharencharha rha
🐩 le=y o=s poodle poodlepoodypsody 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.

rebus (oplossing)
De oplossing van de rebus is bohemian rhapsody.

Opgave

Schrijf een programma waarmee rebussen kunnen opgelost worden. Hiervoor ga je als volgt te werk:

Voorbeeld

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'