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.

Flow Free
Voorbeeld van een Flow Free puzzel (links) en zijn oplossing (rechts).

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.

Opgave

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:

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:

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:

Voorbeeld

> 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.
BEC.
> 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|
BEC
> 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