Een score per zet
In het vorige project kreeg slim de truc van Nim mee, als functie. Nu
krijgt de computer geen truc. Hij speelt duizenden keren tegen zichzelf, en
onthoudt na elk spel welke zetten de winnaar deed en welke de verliezer.
Zetten van de winnaar krijgen een punt erbij, zetten van de verliezer een punt
eraf. Zo leert hij, zonder dat iemand het hem vertelt, welke zet bij elke
stapel de beste is. Dit heet reinforcement learning: leren van beloning en
straf. Eerst heeft hij een plek nodig voor de score van elke zet. Nieuw is een
tuple als sleutel van een dictionary.
Je gebruikt in dit project het spel en de while-loop uit
Nim met een vaste strategie,
tuples uit Tuples, tellen met een dictionary
uit woorden tellen,
een loop in een loop uit
de weddenschap van de Méré,
en random.seed uit
oefenen en toetsen.
Voorspel
score = {(10, 1): 4, (10, 2): -1, (10, 3): -3}
print(score[(10, 2)])
beste = 1
for pak in [1, 2, 3]:
if score[(10, pak)] > score[(10, beste)]:
beste = pak
print(beste)
Wat print dit programma?
Antwoord
-1
1
score[(10, 2)] is de score van de zet "pak 2 als er 10 liggen". De loop
zoekt de zet met de hoogste score, en dat is pak 1, met 4.
Zo werkt het
Nieuw is dat de sleutel van een dictionary een tuple is. Bij het spamfilter
was de sleutel één woord; hier is hij (stapel, pak): hoeveel er liggen, en
hoeveel de speler pakt. score[(10, 1)] is dus de score van de zet "pak 1 als
er 10 liggen". De waarde zegt hoe goed die zet tot nu toe uitpakte.
Een lijst kan geen sleutel zijn. Een lijst kun je later nog veranderen, en dan zou de dictionary zijn sleutel niet meer terugvinden. Een tuple kan niet meer veranderen, en mag dus wel.
Opdracht: de beste zet bij elke stapel
In de editor staan de scores van de zetten bij 5, 6 en 7 lucifers. Print voor elke stapel de zet met de hoogste score:
5 lucifers: pak 3
6 lucifers: pak 1
7 lucifers: pak 2
Begin klein, en breid de code stap voor stap uit:
- Draai de code: je ziet de score van pak 1 bij 6 lucifers.
- Zoek met een loop over
[1, 2, 3]de zet met de hoogste score bij 6 lucifers, zoals in Voorspel, en print hem. - Zet daar een loop over
[5, 6, 7]omheen, zodat je de beste zet van elke stapel ziet.
Tip
Zie je bij 6 of 7 lucifers een zet die niet de hoogste score heeft? Kijk dan
waar beste = 1 staat. Bij elke stapel begin je opnieuw met zoeken.
Antwoord
score = {
(5, 1): -3, (5, 2): -4, (5, 3): -2,
(6, 1): 6, (6, 2): -1, (6, 3): -5,
(7, 1): -2, (7, 2): 8, (7, 3): 0,
}
for stapel in [5, 6, 7]:
beste = 1
for pak in [1, 2, 3]:
if score[(stapel, pak)] > score[(stapel, beste)]:
beste = pak
print(stapel, "lucifers: pak", beste)
5 lucifers: pak 3
6 lucifers: pak 1
7 lucifers: pak 2
Bij 5 lucifers zijn alle drie de scores negatief. De loop kiest dan de minst slechte: pak 3, met -2.
Er gaat iets mis
TypeError: unhashable type: 'list'
Oorzaak: je gebruikt een lijst als sleutel van de dictionary. Een sleutel mag niet kunnen veranderen, en een lijst kan dat wel.
Oplossing: gebruik een tuple, met ronde haakjes.
# FOUT
score = {}
score[[10, 1]] = 0
# GOED
score = {}
score[(10, 1)] = 0
Meer uitleg: Zo werkt het.
Door naar een spel onthouden.