Japanse puzzels (Japanese puzzles?), hoe los ik die op? Of, om precieser te
zijn: hoe los ik die op met de programmeertaal Perl?
Eerst: wat zijn Japanse puzzels ook al weer?
Hiernaast (figuur 1) een plaatje. De getallen die erbij staan
(boven en links) geven aan hoeveel series gekleurde blokjes (gescheiden door
minstens een wit blokje) er in die rij of kolom staan. Bijvoorbeeld '2,3'
betekent: nul of meer wit, dan twee gekleurd, een of meer wit, drie gekleurd,
en dan mogelijk nog wat wit.
Het probleem zit het in het feit dat
je niet weet hoeveel witte vakjes waar zitten (afhankelijk van de grootte van
de puzzel). Als voorbeeld de '2,3' bij de 4e rij van voorbeeld in figuur 1: bij
een veld van 6 breed zou het maar op een manier passen. Op het veld van 8 breed
heb je al 8 mogelijkheden (zie figuur 2 hiernaast). Naarmate
de grootte toeneemt neemt het aantal mogelijkheden ook fors toe (10 -> 15,
11->21, 12 -> 28, ....).
De manier om uit te vissen welke correct is is om alle mogelijkheden per rij
(of kolom) eens goed te bekijken. De '2,3' in een veld van 8 kan dan op acht
manieren, toch is er al iets uit af te leiden. Zoals je in figuur
2 kan zien, is het zesde vakje in ieder geval gekleurd (want dit is in
alle mogelijkheden het geval), al weten we de rest nog niet.Deze informatie kan
je weer gebruiken om kolommen vast te leggen: is bijvoorbeeld in die kolom maar
een vakje gekleurd, dan moet het dat vakje zijn (en is de rest gegarandeerd
leeg; ook belangrijke informatie). En zo voort.
Dit is ook hoe mijn programma werkt. Eerst leest het de
beschrijving van de puzzle in, bijvoorbeeld zoals de tekstfile in het rechter
plaatje (figuur 3: de file met de puzzel uit figuur 1). Dan
gaat het voor iedere rij (en kolom) alle mogelijke patronen genereren (zonder
nog rekening te houden met wat er in andere rijen staat), zoals in de
voorbeeldrij in figuur 2. Dit levert uiteraard nog een waanzinnige hoeveelheid
mogelijkheden op, maar nu gaan we kijken welke informatie we uit de gemaakte
patronen kunnen halen, zitten er bijvoorbeeld al vlakjes in die gegarandeerd
gekleurd zijn.
In het voorbeeld in figuur 3 is dit bijvoorbeeld het geval voor de onderste
rij: er zijn hier 6 van de acht vakjes gekleurd: dus in ieder geval de
middelste 4. Probeer maar, een rijtje van 6 past er maar op drie manieren in,
met de middelste 4 gemeenschappelijk. Samen met het '2,3' verhaal van hierboven
komen we dan tot de rode vakjes in figuur 4. Maar (gezien de
cijfertjes: de kolommen eindigen op een '1') betekend dat ook dat direkt boven
de vakjes op de onderste rij witte vlakjes zitten! En daarmee weten we dan weer
....... en zo voort.
Zo werkt het programma in feite ook. Eerst wordt van
alle gemaakte mogelijkheden per rij en kolom bekeken wat we daardoor weten. Dan
gaan we hiermee net zolang kijken welke kolommen er passen bij de informatie
van de rijen, en omgedraaid, tot we nog maar een passende manier overhouden.
Dat wordt vervolgens afgedrukt. Klinkt simpel. Was het ook wel, ik had het
programma in een of twee uurtjes in elkaar gezet. Maar toch komt er wel wat bij
kijken, zoals het bijhouden van alle mogelijkheden per rij: wat is daarvoor de
beste mogelijkheid (een lijst van patronen)? Al met al is het niet echt een
programma voor beginners geworden.
Het programma levert de volgende output, waarbij eerst een aantal
tussenstappen worden weergegeven, met per rij/kolom het aantal mogelijke
posities van de blokjes op dat moment (wat je per rij/kolom dus tot 1
mogelijkheid ziet verminderen):
> perl Japans.pl Japan1.txt
Japanese puzzle #1
x alternatives: 5, 3, 6, 6, 6, 6, 4, 6, x total = 466560
y alternatives: 8, 10, 5, 6, 21, 3, x*y total = 70543872000
x alternatives: 5, 3, 3, 3, 3, 1, 4, 6, x total = 9720
y alternatives: 7, 5, 3, 5, 2, 3, x*y total = 30618000
x alternatives: 1, 2, 3, 3, 2, 1, 4, 6, x total = 864
y alternatives: 1, 2, 3, 2, 2, 1, x*y total = 20736
x alternatives: 1, 1, 1, 1, 2, 1, 1, 4, x total = 8
y alternatives: 1, 1, 1, 2, 1, 1, x*y total = 16
x alternatives: 1, 1, 1, 1, 1, 1, 1, 1, x total = 1
y alternatives: 1, 1, 1, 1, 1, 1, x*y total = 1
Final solution:
X.......
XX.XX...
..XXXX..
.XX..XXX
.X....X.
.XXXXXX.
We zien de verschillende stappen van eliminatie, en het minder worden van de
mogelijkheden. Per stap zien we eerst per kolom de aantallen mogelijkheden (x
alternatives) en dan hetzelfde per rij (als je ziet, bij de eerste 'y
alternatives' geeft de laatste rij inderdaad '3', de drie manieren waarop 6 in
8 past). Het grote aantal mogelijkheden zakt (gelukkig) rap in elkaar, en komt
vrij snel tot een oplossing, zoals met 'X'jes en puntjes afgedrukt.
Het Perl programma (als zip file): klik >> hier << om te downloaden (voor de
voorbeeldfile japan1.txt: hier). Start met
de '-?' optie om een korte uitleg over het gebruik te krijgen, of kijk in
bovenstaand voorbeeld hoe ik het programma aanriep. Met dank aan degene die het
voorbeeldpuzzeltje heeft gemaakt, weet alleen niet wie het is (had het nog
ergens liggen). Update versie 1.2, december 2003: betere checks op foutieve
invoer (typefouten en zo).
De grootste puzzel zo ver die ik hiermee gemaakt heb is 25 bij 25 vakjes,
het programma doet er zo'n 4 seconden over en gaat in 8 stappen al naar de
uiteindelijke oplossing. Maximale puzzel is 32 bij 32, omdat ik 32-bit
'integers' gebruik om bitpatronen op te slaan (opmerking voor gevorderden
;-).
Wil je zien hoe de stappen zijn die het programma neemt, gebruik dan de -d1
optie (en vang de output op in een tekstfile). Hier krijg je een file met
tussenresultaten te zien:
perl Japans.pl -d1 Japan1.txt >resultaat.txt
Interesse in web sites over puzzels in het algemeen? Kijk eens op de
Puzzel-startpagina.