Stardoku is een puzzel waarbij een getal $$s$$ gegeven wordt en een vierkant $$n \times n$$ rooster met $$n$$ rijen en $$n$$ kolommen. Het rooster is onderverdeeld in $$n$$ gebieden. De opdracht bestaat erin om $$s \times n$$ sterren op het rooster te plaatsen — maximaal één in elke cel van het rooster — zodat

Hieronder zie je links een voorbeeld van een Stardoku waarbij 2 sterren moeten geplaatst worden in elke rij, elke kolom en elk gebied van een $$10 \times 10$$ rooster, met rechts een correcte oplossing. In dit geval zijn de gebieden van het rooster omgeven door dikke zwarte lijnen, en zijn ze ook samenhangend (maar dit laatste leggen we niet op als voorwaarde van een puzzel). Merk op dat een Stardoku niet noodzakelijk een unieke oplossing moet hebben.

stardoku
Voorbeeld van een Stardoku puzzel (links) met een correcte oplossing (rechts).

Opgave: klasse Puzzel

Een tekstbestand met de omschrijving van een Stardoku bestaat uit een regel met een getal $$s \in \mathbb{N}_0$$. Daarna volgen $$n \in \mathbb{N}_0$$ rijen met elk $$n$$ karakters, waarbij de gebieden gevormd worden door de cellen van het $$n \times n$$ rooster met hetzelfde karakter. Hieronder zie je bijvoorbeeld de inhoud van een tekstbestand met de omschrijving van een Stardoku met een $$6 \times 6$$ rooster en waarbij in elke rij, elke kolom en elk gebied één ster moet geplaatst worden.

1
AABBCC
AABCCC
AABCCC
DDBBEE
DDBBEF
DDBBFF

De rijen van het rooster worden van boven naar onder genummerd, en de kolommen van links naar rechts, telkens vanaf nul. Een positie in het rooster wordt voorgesteld door een tuple $$(r, k)$$, waarbij $$r$$ en $$k$$ respectievelijk het rij- en kolomnummer van de cel in het rooster aangeven.

Definieer een klasse Puzzel waarmee Stardoku's kunnen voorgesteld worden. Bij initialisatie van objecten van de klasse Puzzel moet de locatie van een tekstbestand met de omschrijving van een Stardoku doorgegeven worden. Indien de inhoud van het tekstbestand niet overeenkomt met bovenstaande omschrijving, dan moet een AssertionError opgeworpen worden met de boodschap ongeldige puzzel. Elk object van de klasse Puzzel moet de volgende twee onveranderlijke eigenschappen hebben:

Indien een poging wordt ondernomen om deze eigenschappen te wijzigen, dan moet een AttributeError opgeworpen worden met de boodschap can't set attribute. Voorts moet de klasse Puzzel minstens de volgende methoden ondersteunen:

Zorg er voor dat de ingebouwde functie print kan gebruikt worden om een stringvoorstelling van de objecten van de klasse Puzzel uit te schrijven. Deze voorstelling komt overeen met de inhoud van het tekstbestand dat gebruikt werd om een object van de klasse Puzzel te initialiseren, zonder de eerste regel die het aantal sterren aangeeft.

Voorbeeld: klasse Puzzel

Bij onderstaande voorbeeldsessie gaan we ervan uit dat het bestand puzzel.txt1 zich in de huidige directory bevindt.

>>> puzzel = Puzzel('puzzel.txt')
>>> print(puzzel)
AABBCC
AABCCC
AABCCC
DDBBEE
DDBBEF
DDBBFF
>>> puzzel.sterren
1
>>> puzzel.dimensie
6
>>> puzzel.sterren = 4
Traceback (most recent call last):
AttributeError: can't set attribute
>>> puzzel.dimensie = 3
Traceback (most recent call last):
AttributeError: can't set attribute
>>> puzzel.gebied(0, 0)
'A'
>>> puzzel.gebied(3, 4)
'E'
>>> puzzel.gebied(100, 100)
Traceback (most recent call last):
AssertionError: ongeldige positie
>>> puzzel.posities('A')
{(0, 1), (0, 0), (2, 1), (2, 0), (1, 0), (1, 1)}
>>> puzzel.posities('E')
{(4, 4), (3, 4), (3, 5)}
>>> puzzel.posities('X')
Traceback (most recent call last):
AssertionError: ongeldig gebied

Hieronder zie je een grafische voorstelling van de puzzel die in bovenstaand voorbeeld gebruikt wordt. Hierbij worden de gebieden die corresponderen met de verschillende letters elk met een eigen kleur ingekleurd.

voorbeeld Stardoku
Stardoku die in het voorbeeld gebruikt wordt.

Opgave: klasse Oplossing

Een tekstbestand met de mogelijke oplossing van een Stardoku bestaat uit $$n \in \mathbb{N}_0$$ rijen met elk $$n$$ karakters: een punt (.) of een asterisk (*). Lege cellen worden aangeduid met een punt. Cellen die een sterretje bevatten, worden aangeduid met een asterisk. Hieronder zie je bijvoorbeeld de inhoud van een tekstbestand met een mogelijke oplossing van een Stardoku met een $$6 \times 6$$ rooster.

..*...
*.....
...*..
.....*
.*....
....*.

De rijen en kolommen van het rooster worden op dezelfde manier genummerd als bij het rooster van de puzzel zelf. Een positie in het rooster wordt op dezelfde manier voorgesteld door een tuple.

Definieer een klasse Oplossing waarmee mogelijke oplossingen van Stardoku's kunnen voorgesteld worden. Bij initialisatie van objecten van de klasse Oplossing moet de locatie van een tekstbestand met de mogelijke oplossing van een Stardoku doorgegeven worden. Indien de inhoud van het tekstbestand niet overeenkomt met bovenstaande omschrijving, dan moet een AssertionError opgeworpen worden met de boodschap ongeldige puzzel. Elk object van de klasse Oplossing moet de volgende twee onveranderlijke eigenschappen hebben:

Indien een poging wordt ondernomen om deze eigenschappen te wijzigen, dan moet een AttributeError opgeworpen worden met de boodschap can't set attribute. Voorts moet de klasse Oplossing minstens de volgende methoden ondersteunen:

Zorg er voor dat de ingebouwde functie print kan gebruikt worden om een stringvoorstelling van de objecten van de klasse Oplossing uit te schrijven. Deze voorstelling komt overeen met de inhoud van het tekstbestand dat gebruikt werd om een object van de klasse Oplossing te initialiseren.

Voorbeeld: klasse Oplossing

Bij onderstaande voorbeeldsessie gaan we ervan uit dat de bestanden puzzel.txt2, oplossing_01.txt3, oplossing_02.txt4, oplossing_03.txt5, oplossing_04.txt6 en oplossing_05.txt7 zich in de huidige directory bevinden.

>>> puzzel = Puzzel('puzzel.txt')

>>> oplossing = Oplossing('oplossing_01.txt')
>>> print(oplossing)
..*...
*.....
...*..
.....*
.*....
....*.
>>> oplossing.dimensie
6
>>> oplossing.posities_sterren
{(5, 4), (2, 3), (1, 0), (4, 1), (0, 2), (3, 5)}
>>> oplossing.dimensie = 3
Traceback (most recent call last):
AttributeError: can't set attribute
>>> oplossing.sterren_rijen()
[1, 1, 1, 1, 1, 1]
>>> oplossing.sterren_kolommen()
[1, 1, 1, 1, 1, 1]
>>> oplossing.sterren_met_buren()
set()
>>> oplossing.sterren_gebieden(puzzel)
{'A': 1, 'B': 1, 'C': 1, 'D': 1, 'E': 1, 'F': 1}
>>> oplossing.is_geldig(puzzel)
True

>>> oplossing = Oplossing('oplossing_02.txt')
>>> print(oplossing)
..*..*
*.....
......
.....*
.*....
....*.
>>> oplossing.sterren_rijen()
[2, 1, 0, 1, 1, 1]
>>> oplossing.sterren_kolommen()
[1, 1, 1, 0, 1, 2]
>>> oplossing.is_geldig(puzzel)
False

>>> oplossing = Oplossing('oplossing_03.txt')
>>> print(oplossing)
..*...
...*..
.....*
*.....
.*....
....*.
>>> oplossing.sterren_met_buren()
{(3, 0), (1, 3), (4, 1), (0, 2)}
>>> oplossing.sterren_gebieden(puzzel)
{'A': 0, 'B': 1, 'C': 2, 'D': 2, 'E': 0, 'F': 1}
>>> oplossing.is_geldig(puzzel)
False

>>> oplossing = Oplossing('oplossing_04.txt')
>>> print(oplossing)
..*..
*....
...*.
.*...
....*
>>> oplossing.dimensie
5
>>> puzzel.dimensie
6
>>> oplossing.sterren_met_buren()
set()
>>> oplossing.sterren_gebieden(puzzel)
Traceback (most recent call last):
AssertionError: puzzel en oplossing hebben verschillende dimensies
>>> oplossing.is_geldig(puzzel)
False

>>> oplossing = Oplossing('oplossing_05.txt')
Traceback (most recent call last):
AssertionError: ongeldige oplossing

Hieronder zie je een grafische voorstelling van de eerste drie mogelijke oplossingen die in bovenstaand voorbeeld gebruikt wordt. Enkel de eerste daarvan is ook een geldige oplossing.

mogelijke oplossingen van Stardoku
Mogelijke oplossingen van de Stardoku die in het voorbeeld gebruikt worden. Enkel de eerste daarvan is ook een geldige oplossing.