Flow Free is een puzzel die gespeeld wordt op een rechthoekig $$m \times n$$ rooster met $$m$$ rijen en $$n$$ kolommen, waarvan sommige cellen een gekleurde cirkel bevatten. Voor elke kleur zijn er exact twee cellen met een cirkel van die kleur. De andere cellen zijn initieel nog leeg.
Het doel is om cirkels met dezelfde kleur te verbinden met "pijpleidingen", zodat elke cel van het rooster uiteindelijk bezet is. Pijpleidingen kunnen enkel naar boven, onder, links en rechts bewegen, en mogen noch zichzelf noch andere pijpleidingen kruisen. Hieronder zie je bijvoorbeeld hoe een Flow Free puzzel op een $$9 \times 9$$ rooster opgelost wordt.
Om te kunnen verwijzen naar de cellen in het rooster van een Flow Free puzzel, nummeren we de rijen (resp. kolommen) van boven naar onder (resp. van links naar rechts) vanaf 0. Daardoor kunnen we de positie van een cel aanduiden met een Array $$[r, k]$$, waarbij $$r$$ (Number) de rij en $$k$$ (Number) de kolom aanduidt waarop de cel voorkomt in het rooster.
Definieer een klasse FlowFree waarmee we het spelverloop van Flow Free puzzels kunnen voorstellen. Bij het aanmaken van een nieuwe puzzel (FlowFree) moeten drie argumenten doorgegeven worden: i) het aantal rijen van het rooster (Number), ii) het aantal kolommen van het rooster (Number) en iii) een reeks (Array) met posities (Array) van cellen in het rooster waarin gekleurde cirkels voorkomen. Daarbij bevat elk paar cellen op de twee volgende posities telkens cirkels met dezelfde kleur, die voorgesteld worden door de volgende hoofdletter in het alfabet: de eerste en tweede cel hebben dezelfde kleur (voorgesteld door de letter A), de derde en vierde cel hebben dezelfde kleur, maar een andere dan de vorige (voorgesteld door de letter B), enzoverder. Voor de configuratie van de gekleurde cellen in het rooster (derde argument) moet dus gelden dat:
ze wordt voorgesteld als een array (Array)
ze een even aantal posities bevat
ze enkel geldige posities van cellen in het rooster bevat
ze allemaal verschillende posities bevat
Als niet aan deze voorwaarden voldaan is, dan moet een Error opgeworpen worden met de boodschap ongeldige configuratie.
Voorts moet je op een puzzel $$p$$ (FlowFree) minstens de volgende methoden kunnen aanroepen:
Een methode toString waaraan geen argumenten moeten doorgegeven worden. De methode moet een stringvoorstelling (String) van het rooster van puzzel $$p$$ teruggeven, met een opmaak zoals aangegeven in onderstaand voorbeeld. Hierbij stelt een hoofdletter een cel met een cirkel van de corresponderende kleur voor. Een punt (.) stelt een lege cel voor. Een segment van een pijpleiding die door een cel loopt, wordt voorgesteld door één van deze karakters:
━ | ┛ ┗ ┏ ┓
Een methode isPositie waaraan twee argumenten moeten
doorgegeven worden: i) een getal $$r$$ (Number),
en ii) een getal $$k$$ (Number). De methode moet
een Booleaanse waarde (Boolean) teruggeven, die aangeeft
of $$[r, k]$$ een geldige positie aanduidt van een cel binnen het
rooster van puzzel $$p$$.
Een methode pijpleiding waaraan drie argumenten moeten doorgegeven worden: i) een getal $$r$$ (Number), ii) een getal $$k$$ (Number) en iii) een reeks letters (String) die de routebeschrijving van de pijpleiding naar naburige cellen aangeeft: U (naar boven), D (naar onder), L (naar links) en R (naar rechts). Bij de interpretatie van de routebeschrijving mag geen onderscheid gemaakt worden tussen hoofdletters en kleine letters. De methode moet een array (Array) teruggeven met de posities (Array) van de opeenvolgende cellen waarlangs de pijpleiding loopt, te beginnen bij startpositie $$[r, k]$$. De methode mag het rooster van puzzel $$p$$ niet aanpassen.
Een methode verbinden waaraan dezelfde drie argumenten moeten doorgegeven worden als aan de methode pijpleiding, met dezelfde betekenis. De methode moet het rooster van puzzel $$p$$ aanpassen, zodat de gegeven pijpleiding er wordt in weergegeven. De methode moet een verwijzing naar puzzel $$p$$ (FlowFree) teruggeven.
Een methode isOpgelost waaraan geen argumenten moeten doorgegeven worden. De methode moet een Booleaanse waarde (Boolean) teruggeven, die aangeeft of elke cel van het rooster van puzzel $$p$$ bezet is door een cirkel of door het segmenten van een pijpleiding.
De drie argumenten die aan de methoden pijpleiding en verbinden doorgegeven worden, moeten een geldig pijpleiding door het rooster van puzzel $$p$$ beschrijven. Als dat niet het geval is, dan moet een Error opgeworpen worden met de boodschap ongeldige pijpleiding, zonder dat daarbij het rooster van puzzel $$p$$ aangepast wordt. Een pijpleiding moet immers voldoen aan de volgende voorwaarden:
de eerste twee argumenten moeten een geldige positie van een cel binnen het rooster aanduiden
het derde argument moet een string (String) zijn die enkel uit de opgegeven letters bestaat (zonder onderscheid te maken tussen hoofdletters en kleine letters)
de pijpleiding moet binnen het rooster blijven
de pijpleiding moet beginnen en eindigen bij een cel met dezelfde hoofdletter
de pijpleiding moet eindigen in een andere cel dan waar de pijpleiding begonnen is
de pijpleiding mag zichzelf niet kruisen
de pijpleiding mag geen andere pijpleidingen op het rooster (inclusief hun start- en eindposities) kruisen
> const spel = new FlowFree(5, 5, [[0, 1], [3, 0], [0, 2], [4, 0], [0, 3], [4, 3], [1, 3], [2, 2], [4, 2], [3, 3]])
> console.log(spel.toString())
.ABC.
...D.
..D..
A..E.
B.EC.
> spel.isPositie(2, 3)
true
> spel.isPositie(8, 3)
false
> spel.pijpleiding(1, 3, "dl") // D (rood)
[[1, 3], [2, 3], [2, 2]]
> console.log(spel.verbinden(1, 3, "dl").toString())
.ABC.
...D.
..D┛.
A..E.
B.EC.
> spel.pijpleiding(4, 2, "RU") // pijpleiding loopt door andere cirkel
Error: ongeldige pijpleiding
> spel.pijpleiding(4, 2, "UR") // E (oranje)
[[4, 2], [3, 2], [3, 3]]
> console.log(spel.verbinden(4, 2, "UR").toString())
.ABC.
...D.
..D┛.
A.┏E.
B.EC.
> spel.pijpleiding(0, 1, "lddd") // A (geel)
[[0, 1], [0, 0], [1, 0], [2, 0], [3, 0]]
> console.log(spel.verbinden(0, 1, "lddd").toString())
┏ABC.
|..D.
|.D┛.
A.┏E.
B.EC.
> spel.pijpleiding(0, 2, "dLLdRddL") // pijpleiding loopt door andere pijpleiding
Error: ongeldige pijpleiding
> spel.pijpleiding(0, 2, "dLdddL") // B (blauw)
[[0, 2], [1, 2], [1, 1], [2, 1], [3, 1], [4, 1], [4, 0]]
> console.log(spel.verbinden(0, 2, "dLdddL").toString())
┏ABC.
|┏┛D.
||D┛.
A|┏E.
B┛EC.
> spel.isOpgelost()
false
> spel.pijpleiding(0, 3, "RddddL") // C (groen)
[[0, 3], [0, 4], [1, 4], [2, 4], [3, 4], [4, 4], [4, 3]]
> console.log(spel.verbinden(0, 3, "RddddL").toString())
┏ABC┓
|┏┛D|
||D┛|
A|┏E|
B┛EC┛
> spel.isOpgelost()
true
> new FlowFree[5, 5, [[0, 1], [3, 0], [0, 2]]] // oneven aantal posities
Error: ongeldige configuratie
> new FlowFree[5, 5, [[0, 1], [3, 0], [0, 2], [0, 1]]] // zelfde positie komt meerdere keren voor
Error: ongeldige configuratie
> new FlowFree[5, 5, [[0, 1], [3, 0], [0, 2], [5, 3]]] // positie buiten het rooster
Error: ongeldige configuratie