Het spel dat we in deze oefening bekijken, wordt door 2 spelers gespeeld op een speelbord met genummerde vakjes. Het spel bevat 1 pion, en de spelers verplaatsen om beurten deze ene pion. Hierbij gelden volgende regels:

Je kan aantonen dat het spel voor elke waarde van N eindigt. Er bestaat m.a.w. steeds een vakje waarbij het eerstvolgende priemgetal meer dan N vakjes verder ligt. Voor N=5 en startpositie 0, is dit eindvakje het vakje met nummer 23 (eerstvolgende priemgetal is 29, dus onbereikbaar vanuit vakje 23 indien grootste stap 5 bedraagt!). Het is dus zaak om als eerste op dit eindvakje te komen, waardoor de tegenspeler geen geldige zet meer heeft!

In deze opgave bouwen we volgende klassen:

Opmerking: bij de opsomming van methoden in een klasse, wordt het argument self in de opgave nooit expliciet vermeld. Wanneer er dus aangegeven wordt dat een methode geen argumenten heeft, wordt eigenlijk bedoeld dat de methode enkel self als argument heeft. Wanneer vermeld wordt dat een methode 2 argumenten heeft, wordt bedoeld dat de methode naast het self-argument nog 2 bijkomende argumenten heeft.

Klasse Speler

Programmeer in deze klasse:

Klasse PriemSpel

Programmeer in deze klasse:

TIP:Je kan onderstaande functie gebruiken, die aangeeft een het natuurlijke argument n al dan niet priem is.

def priem(n):
    if n<2:
        return False
    for i in range(2, int((n**0.5)+1)):
        if n % i ==0:
            return False
    return True

Voorbeelden

Tab 1
speler1 = Speler('Emile')
print(speler1)  #Emile
speler2 = Speler('Zoe')
spel = PriemSpel(speler1, speler2, 0, 5)
print(spel) #[*Emile,Zoe,0]
print(spel.einde()) #23
spel += 5
print(spel) #[Emile,*Zoe,5]
spel += 2
print(spel) #[*Emile,Zoe,7]
spel += 2
print(spel) #[*Emile,Zoe,7]
Tab 2
speler1 = Speler('Emile')
speler2 = Speler('Zoe')
spel = PriemSpel(speler1, speler2, 0, 5)
print(speler1.zet(spel)) #2
print(spel) #[*Emile,Zoe,0]
spel += speler1.zet(spel)
print(spel) #[Emile,*Zoe,2]
print(speler2.zet(spel)) #1
spel += speler2.zet(spel)
print(spel) #[*Emile,Zoe,3]

spel.reset()
spelverloop, winnaar = spel.speel()
print(spelverloop)
#['[*Emile,Zoe,0]', '[Emile,*Zoe,2]', '[*Emile,Zoe,3]', '[Emile,*Zoe,5]', '[*Emile,Zoe,7]', '[Emile,*Zoe,11]', '[*Emile,Zoe,13]', '[Emile,*Zoe,17]', '[*Emile,Zoe,19]', '[+Emile,-Zoe,23]']
print(winnaar) #1

Klasse SpelerStrategie

In deze klasse, die overerft van Speler wordt een iets geavanceerdere strategie uitgewerkt (niet de optimale strategie). Hiertoe bekijkt de speler alle mogelijke sequenties van vaknummers die in het spel kunnen voorkomen, vanaf de huidige positie van de pion. Deze sequenties starten dus steeds met de huidige pion-positie en eindigen op het nummer van het eindvakje (zie klasse PriemSpel).

De speler bedenkt dat indien hij/zij als volgende zet een zet kiest zodat alle daaropvolgende sequenties een even aantal elementen elementen hebben, winst verzekerd is! Indien een zet gekozen wordt, zodat alle daarop volgende sequenties een oneven elementen hebben, dan is verlies verzekerd. Immers, wanneer alle mogelijke sequenties (volgend op deze zet) een even aantal elementen hebben, zal de speler steeds als laatste een geldige zet doen, waardoor de tegenstander verliest.

Daarom bouwt de speler 3 lijsten, namelijk:

Je mag aannemen dat minstens 1 van deze lijsten niet leeg is. De volgende zet wordt als volgt gekozen:

Programmeer in deze klasse volgende methoden:

Voorbeeld

speler1 = Speler('Emile')
speler2 = SpelerStrategie('Zoe')
speler3 = SpelerStrategie('Frank')
spel1_2 = PriemSpel(speler1, speler2, 0, 5)
s = speler2.alle_sequenties(spel1_2)
t = {tuple(e) for e in s}
print(t)
#{(0, 2, 3, 7, 11, 13, 17, 19, 23), (0, 2, 3, 5, 7, 11, 13, 17, 19, 23), (0, 3, 7, 11, 13, 17, 19, 23), (0, 2, 5, 7, 11, 13, 17, 19, 23), (0, 5, 7, 11, 13, 17, 19, 23), (0, 3, 5, 7, 11, 13, 17, 19, 23), (0, 2, 7, 11, 13, 17, 19, 23)}
w, v, o = speler2.winst_verlies(spel1_2)
print(set(w)) #{5}
print(set(v)) #set() - lege verzameling
print(set(o)) #{2, 3}

z = speler2.zet(spel1_2)
print(z) #5

spelverloop1, winnaar1 = spel1_2.speel()
print(spelverloop1)
#['[*Emile,Zoe,0]', '[Emile,*Zoe,2]', '[*Emile,Zoe,5]', '[Emile,*Zoe,7]', '[*Emile,Zoe,11]', '[Emile,*Zoe,13]', '[*Emile,Zoe,17]', '[Emile,*Zoe,19]', '[-Emile,+Zoe,23]']

print(winnaar1) #2
 
spel2_3 = PriemSpel(speler2, speler3, 0, 5)
spelverloop2, winnaar2 = spel2_3.speel()
print(spelverloop2)
#['[*Zoe,Frank,0]', '[Zoe,*Frank,5]', '[*Zoe,Frank,7]', '[Zoe,*Frank,11]', '[*Zoe,Frank,13]', '[Zoe,*Frank,17]', '[*Zoe,Frank,19]', '[+Zoe,-Frank,23]']
print(winnaar2) #1