Welke geheime boodschap zit er in onderstaand rooster verborgen?
De boodschap is "East, west, home's best.". Om die boodschap te kunnen ontcijferen, wordt er een grille gebruikt: een blad papier of een stuk karton met daarop een vierkant $$n \times n$$ rooster waarin enkele openingen uitgeknipt werden. In dit geval een $$5 \times 5$$ rooster waarin zes openingen uitgeknipt worden volgens onderstaand patroon.
Als de grille op het vierkant rooster gelegd wordt, dan kan doorheen de openingen de tekst "East, " uitgelezen worden (in de leesrichting van links naar rechts, en van boven naar onder). Dit is het eerste deel van de boodschap.
Door de grille 90° in wijzerzin te draaien, kan het tweede deel van de boodschap uitgelezen worden: "West, ". Daarna kan de grille nog tweemaal 90° in wijzerzin gedraaid worden om de twee laatste delen van de boodschap te kunnen uitlezen: "home's" en " best."
De oudste gekende beschrijving van deze techniek dateert van 1550 en is van de hand van de Italiaanse geleerde Girolamo Cardano1. Om die reden wordt een grille soms ook een Cardanrooster genoemd.
Een grille met een vierkant $$n \times n$$ ($$n \in \mathbb{N}_0$$) rooster kan gebruikt worden om geheime boodschappen te ontcijferen die bestaan uit $$n^2$$ karakters. Daarvoor moet zo'n geheime boodschap opgedeeld worden in $$n$$ reeksen van $$n$$ opeenvolgende karakters. Als je die reeksen onder elkaar zet, krijg je ook een vierkant $$n \times n$$ rooster. Zo kunnen de 25 karakters van de geheime boodschap "E hWeaosbtseVtsm,e,'t. s" als volgt omgezet worden naar het $$5 \times 5$$ rooster waarin de geheime boodschap uit de inleiding verborgen zit:
E hWe aosbt seVts m,e,' t. s
Dat rooster moet dan zo uitgeschreven worden dat het rooster van de grille er netjes op past.
Om te kunnen verwijzen naar de cellen van een rooster (zowel voor een rooster gevormd met de karakters van een geheime boodschap als voor een rooster met de openingen van een grille) worden de rijen van boven naar onder genummerd, en de kolommen van links naar rechts, telkens vanaf nul. Hierdoor kan de positie van een cel voorgesteld worden als een reeks $$[r, k]$$ (Array), waarbij $$r \in \mathbb{N}$$ (Number) het rijnummer en $$k \in \mathbb{N}$$ (Number) het kolomnummer aanduidt.
Definieer een klasse Grille om grilles voor te stellen waarmee geheime boodschappen kunnen ontcijferd worden. Bij het aanmaken van een grille (Grille) moeten twee argumenten doorgegeven worden: i) het aantal rijen/kolommen $$n$$ (Number) van het vierkant rooster op de grille en ii) een reeks (Array) of een verzameling (Set) met de posities van de openingen in de grille. Een grille $$g$$ (Grille) moet minstens de volgende eigenschappen hebben:
dimensie: aantal rijen/kolommen $$n$$ (Number) van het vierkant rooster op grille $$g$$
openingen: verzameling (Set) met de posities (Array) van de openingen in grille $$g$$
Daarnaast moet je op een grille $$g$$ (Grille) minstens de volgende methoden kunnen aanroepen:
Een methode toString waaraan geen argumenten moeten doorgegeven worden. De methode moet een stringvoorstelling (String) van het rooster met openingen van grille $$g$$ teruggeven. Daarin vormt elke rij een afzonderlijke regel, worden cellen met openingen voorgesteld door de hoofdletter O, en worden cellen zonder openingen voorgesteld door een hekje (#).
Een methode decodeer waaraan een geheime boodschap (String) moet doorgegeven worden. Als de geheime boodschap niet uit $$n^2$$ karakters bestaat, dan moet een Error opgeworpen worden met de boodschap ongeldige cijfertekst. Anders moet het deel van de klare tekst (String) teruggegeven worden dat kan uitgelezen worden door grille $$g$$ op het rooster te leggen dat gevormd wordt met de karakters van een geheime boodschap (in de leesrichting van links naar rechts en van boven naar onder).
Een methode roteer met een optionele parameter wijzerzin waaraan een Booleaanse waarde (Boolean) kan doorgegeven worden. Op basis van de waarde van de parameter wijzerzin moet de methode ervoor zorgen dat grille $$g$$ 90° in wijzerzin (true, standaardwaarde) of in tegenwijzerzin (false) gedraaid wordt. De methode moet een verwijzing naar grille $$g$$ teruggeven.
Als een $$n \times n$$ rooster 90° in wijzerzin geroteerd wordt, dan komt een cel met positie $$(r, k)$$ op positie $$(k, n - r - 1)$$ terecht (zie oranje cel in onderstaande figuur).
Als een $$n \times n$$ rooster 90° in tegenwijzerzin geroteerd wordt, dan komt een cel met positie $$(r, k)$$ op positie $$(n - k - 1, r)$$ terecht (zie groene cel in bovenstaande figuur).
Een methode equals waaraan een grille $$g'$$ (Grille) moet doorgegeven worden. De methode moet een Booleaanse waarde (Boolean) teruggeven die aangeeft of de twee grilles $$g$$ en $$h$$ zijn gelijk. Dat is het geval als ze hetzelfde aantal rijen/kolommen hebben en als ze kunnen gedraaid worden (veelvouden van 90°) zodat ze al hun openingen op dezelfde posities hebben. De methode mag de grilles $$g$$ en $$g'$$ niet wijzigen.
Een methode stapel waaraan een grille $$g'$$ (Grille) moet doorgegeven worden. De methode moet een nieuwe grille $$g''$$ (Grille) teruggeven die eruit ziet alsof de grilles $$g$$ en $$g'$$ bovenop elkaar gelegd worden. Er zitten met andere woorden in grille $$g''$$ enkel openingen op posities waar in beide grilles $$g$$ en $$g'$$ openingen zitten. De twee grilles $$g$$ en $$g'$$ passen enkel op elkaar als ze hetzelfde aantal rijen/kolommen hebben. Als dat niet het geval is, dan moet een Error opgeworpen worden met de boodschap grilles passen niet op elkaar. De methode mag de grilles $$g$$ en $$g'$$ niet wijzigen.
> const cijfertekst = "E hWeaosbtseVtsm,e,'t. s"
> const grille = new Grille(5, [[0, 0], [1, 0], [1, 2], [1, 4], [3, 3], [4, 2]])
> grille.dimensie
5
> grille.openingen
new Set([[1, 2], [0, 0], [3, 3], [1, 4], [4, 2], [1, 0]])
> grille.toString()
O####
O#O#O
#####
###O#
##O##
> grille.decodeer(cijfertekst)
"East, "
> grille.roteer().openingen
new Set([[3, 1], [2, 0], [2, 3], [4, 3], [0, 4], [0, 3]])
> grille.toString()
###OO
#####
O##O#
#O###
###O#
> grille.decodeer(cijfertekst)
"West, "
> grille.roteer().openingen
new Set([[3, 2], [3, 0], [4, 4], [1, 1], [3, 4], [0, 2]])
> grille.toString()
##O##
#O###
#####
O#O#O
####O
> grille.decodeer(cijfertekst)
"home's"
> grille.roteer().openingen
new Set([[0, 1], [1, 3], [2, 1], [4, 1], [2, 4], [4, 0]])
> grille.toString()
#O###
###O#
#O##O
#####
OO###
> grille.decodeer(cijfertekst)
" best."
> grille.roteer(false).openingen
new Set([[3, 2], [3, 0], [4, 4], [1, 1], [3, 4], [0, 2]])
> grille.toString()
##O##
#O###
#####
O#O#O
####O
> grille.decodeer(cijfertekst)
"home's"
> const grille1 = new Grille(4, [[1, 3], [0, 0], [2, 3], [1, 1]])
> const grille2 = new Grille(4, new Set([[2, 0], [1, 0], [3, 3], [2, 2]]))
> const grille3 = new Grille(4, [[3, 0], [2, 3], [2, 0], [1, 1]])
> const grille4 = new Grille(5, new Set([[1, 3], [0, 0], [2, 3], [1, 1]]))
> grille1.equals(grille2)
true
> grille1.equals(grille3)
false
> grille1.equals(grille4)
false
> const grille5 = grille1.stapel(grille3)
> grille5.dimensie
4
> grille5.openingen
new Set([[2, 3], [1, 1]])
> grille5.toString()
####
#O##
###O
####
> grille1.stapel(grille4)
Error: grilles passen niet op elkaar