De term cryptex is een neologisme dat Dan Brown1 bedacht voor zijn roman De Da Vinci Code2 uit 2003, en duidt op een draagbare kluis die gebruikt wordt om geheime berichten te verbergen. Het is een samentrekking van het Griekse woord κρυπτός (kryptós, "verborgen, geheim") en het Latijnse woord codex.
Een cryptex bestaat uit $$n$$ schijven die onafhankelijk van elkaar kunnen roteren. Rondom elke schijf staan $$m$$ verschillende karakters (zowel letters, cijfers, witruimtekarakters als leestekens zijn toegelaten). Daardoor kan een cryptex op $$m^n$$ verschillende combinaties ingesteld worden. Een cryptex verschilt van een traditioneel cijferslot door het feit dat er ook letters op de schijven kunnen staan, in plaats van enkel cijfers. Daardoor kan de combinatie waarmee de cryptex ontgrendeld wordt een woord of een naam zijn — net zoals een wachtwoord — wat mogelijks makkelijker te onthouden is dan een reeks cijfers.
schijf 0 | schijf 1 | schijf 2 | schijf 3 | schijf 4 | |
---|---|---|---|---|---|
ring 0 | A | A | A | A | A |
ring 1 | D | E | E | C | D |
ring 2 | E | I | I | D | E |
ring 3 | G | L | L | E | |
ring 4 | H | N | N | K | H |
ring 5 | L | O | O | L | K |
ring 6 | M | P | R | N | R |
ring 7 | R | R | S | R | S |
ring 8 | S | T | T | S | T |
ring 9 | W | U | U | T | Y |
Hierboven staat bijvoorbeeld een cryptex die bestaat uit 5 schijven, met 10 karakters op elke schijf. Merk op dat er naast hoofdletters ook één spatie voorkomt op de laatste schijf, waardoor zowel vier- als vijfletterwoorden kunnen gevormd worden. Er kunnen met deze cryptex in totaal zo'n 2.000 woorden gevormd worden, waarbij zelfs nog niet eens rekening wordt gehouden met quasi-woorden (BLATS of WOOT), eigennamen (DILAN of MOSES), afkortingen, anderstalige woorden of onbestaande woorden die enkel gekend zijn door de eigenaar van de cryptex.
Een combinatie van $$n$$ letters die naast elkaar staan op een cryptex noemen we een ring. In totaal heeft een cryptex $$m$$ ringen die we indexeren vanaf nul. Om na te gaan of de cryptex ontgrendeld is, wordt per definitie de combinatie op ring 0 gecontroleerd.
De configuratie van een cryptex met $$n$$ schijven en $$m$$ ringen wordt omschreven in een tekstbestand dat bestaat uit $$n$$ regels. Deze regels beschrijven de opeenvolgende schijven en bevatten elk de $$m$$ karakters op een schijf. Opeenvolgende karakters in de omschrijving van een schijf staan initieel op ringen $$0, 1, \ldots, m - 1$$ van de cryptex. De cryptex uit de inleiding wordt dan bijvoorbeeld als volgt omschreven:
ADEGHLMRSW AEILNOPRTU AEILNORSTU ACDEKLNRST ADE HKRSTY
Merk op dat de cryptex in het tekstbestand schijf per schijf wordt voorgesteld, terwijl dezelfde cryptex in de inleiding ring per ring werd voorgesteld.
Definieer een klasse Cryptex waarmee cryptexen kunnen voorgesteld worden. Bij het aanmaken van een nieuwe cryptex (Cryptex) moet de locatie (str) van een tekstbestand met de omschrijving van een cryptex doorgegeven worden. Hierbij moet gelden dat:
een cryptex heeft minstens één schijf
alle schijven bevatten evenveel karakters (minstens één)
rondom elke schijf staan allemaal verschillende karakters
Als niet aan deze voorwaarden voldaan is, dan moet een AssertionError opgeworpen worden met de boodschap ongeldige schijven.
Er is juist één combinatie waarmee een cryptex kan ontgrendeld worden. Na het aanmaken van een cryptex (Cryptex) is dit standaard de combinatie die initieel voorkomt op ring 0. In plaats daarvan kan bij het aanmaken van een nieuwe cryptex (Cryptex) aan een optionele tweede parameter combinatie ook nog de combinatie (str) doorgegeven worden waarmee de cryptex kan ontgrendeld worden. Als de gegeven combinatie niet één van de $$m^n$$ combinaties is waarop de cryptex kan ingesteld worden, dan moet een AssertionError opgeworpen worden met de boodschap ongeldige combinatie.
Als er een cryptex (Cryptex) wordt doorgegeven aan de ingebouwde functie str, dan moet die een beschrijving (str) van de cryptex teruggeven die bestaat uit $$m$$ regels. Elk van die regels beschrijft een ring van de cryptex, te beginnen bij ring 0. De karakters van elke ring worden telkens van elkaar gescheiden door één enkele spatie.
Op een cryptex $$c$$ (Cryptex) moet je minstens de volgende methoden kunnen aanroepen:
Een methode ring waaraan een getal $$i$$ (int) moet doorgegeven worden. De methode moet de opeenvolgende karakters (str) op ring $$i$$ van cryptex $$c$$ teruggeven.
Een methode ontgrendeld waaraan geen argumenten moeten doorgegeven worden. De methode moet een Booleaanse waarde (bool) teruggeven, die aangeeft of cryptex $$c$$ ontgrendeld is.
Een methode instellen waaraan een combinatie $$x$$ (str) moet doorgegeven worden waarmee cryptex $$c$$ vanaf nu kan ontgrendeld worden. De huidige combinatie waarmee cryptex $$c$$ kan ontgrendeld worden, mag enkel aangepast worden als de combinatie $$x$$ geldig is. Als combinatie $$x$$ niet één van de $$m^n$$ combinaties is waarop cryptex $$c$$ kan ingesteld worden, dan moet een AssertionError opgeworpen worden met de boodschap ongeldige combinatie.
Een methode roteer waarmee nul of meer schijven over een gegeven aantal posities kunnen geroteerd worden. Aan deze methode moet een dictionary (dict) doorgegeven worden, waarvan de sleutels gevormd worden door de indexen (int) van de schijven die moeten geroteerd worden. De waarde (int) die correspondeert met een sleutel geeft telkens aan over hoeveel posities de schijf moet geroteerd worden. Als een schijf over één positie geroteerd wordt, dan schuift elke letter van die schijf op naar de vorige ring. Als de waarde die correspondeert met een sleutel negatief is, dan roteert de schijf in de tegenovergestelde richting. De methode mag ervan uitgaan dat de acties die beschreven worden door de dictionary geldig zijn, zonder dat dit expliciet moet gecontroleerd worden.
In onderstaande voorbeeldsessie gaan we ervan uit dat het tekstbestand cryptex.txt3 zich in de huidige directory bevindt.
>>> cryptex = Cryptex('cryptex.txt4')
>>> cryptex.ring(0)
'AAAAA'
>>> print(cryptex)
A A A A A
D E E C D
E I I D E
G L L E
H N N K H
L O O L K
M P R N R
R R S R S
S T T S T
W U U T Y
>>> cryptex.ontgrendeld()
True
>>> cryptex.instellen('APPLE')
Traceback (most recent call last):
AssertionError: ongeldige combinatie
>>> cryptex.instellen('HOUSE')
>>> cryptex.ring(0)
'AAAAA'
>>> print(cryptex)
A A A A A
D E E C D
E I I D E
G L L E
H N N K H
L O O L K
M P R N R
R R S R S
S T T S T
W U U T Y
>>> cryptex.ontgrendeld()
False
>>> cryptex.roteer({0:4})
>>> cryptex.ring(0)
'HAAAA'
>>> print(cryptex)
H A A A A
L E E C D
M I I D E
R L L E
S N N K H
W O O L K
A P R N R
D R S R S
E T T S T
G U U T Y
>>> cryptex.ontgrendeld()
False
>>> cryptex.roteer({1:5, 3:5})
>>> cryptex.ring(0)
'HOALA'
>>> print(cryptex)
H O A L A
L P E N D
M R I R E
R T L S
S U N T H
W A O A K
A E R C R
D I S D S
E L T E T
G N U K Y
>>> cryptex.ontgrendeld()
False
>>> cryptex.roteer({2:-1, 3:3, 4:12})
>>> cryptex.ring(0)
'HOUSE'
>>> print(cryptex)
H O U S E
L P A T
M R E A H
R T I C K
S U L D R
W A N E S
A E O K T
D I R L Y
E L S N A
G N T R D
>>> cryptex.ontgrendeld()
True
In hoofdstuk 47 van Dan Brown's De Da Vinci Code vinden agente Sophie Deneuve en professor Robert Langdon een cryptex in een houten kistje.
Het was een stenen cilinder van glanzend wit marmer, met ongeveer de afmetingen van een blik tennisballen. De cilinder was echter niet een eenvoudige kolom van steen, maar leek uit vele delen te bestaan. Vijf schijven van marmer, elk ter grootte van een donut, waren op elkaar gestapeld en aan elkaar bevestigd door middel van een teer, geel koperen frame. Het geheel zag eruit als een soort kokervormige caleidoscoop met meerdere draaischijven. Aan beide uiteinden van de cilinder zat een afsluiter, ook van marmer, zodat het onmogelijk was erin te kijken. Omdat hij vloeistof had horen klotsen, nam Langdon aan dat de cilinder hol was.