Bouwsteen 9 — minimax(bord)
Hier bouw je op verder
- Python De while-loop
Leerdoel: je schrijft de functie die alle vorige bouwstenen gebruikt
om de beste zet terug te geven, direct in je lokale tictactoe.py.
Bij draai het zelf heb je je negen functies in één lokaal bestand gezet, of in de verzamel-PyRunner. Nu de tiende.
Wat doet deze functie?
minimax(bord) geeft de beste zet terug voor de speler die nu aan zet
is: niet het getal, dat doen max_value en min_value, maar de tuple
(i, j). Is X aan zet, dan kies je de zet met de hoogste
min_value(result(bord, zet)); is O aan zet, de zet met de laagste
max_value(result(bord, zet)). Kruislings, want na een zet van X is O
aan zet, dus je rekent met min_value. Voor O andersom.
Het patroon
beste_zet = None
beste_score = -math.inf # of +math.inf voor O
voor elke zet in actions(bord):
score = min_value(result(bord, zet)) # of max_value voor O
als score beter is dan beste_score:
beste_score = score
beste_zet = zet
return beste_zet
Dezelfde lus als in max_value, alleen onthoud je nu ook welke zet de
beste was.
Speciaal geval
Is het bord terminal, dan is er geen zet meer mogelijk en geef je None
terug.
Specificatie
- Input: een
bord(mag terminal zijn). - Output: een tuple
(i, j)met de beste zet, ófNoneals het bord terminal is.
Voorspel
Wat moet minimax teruggeven op deze positie? X is aan zet.
bord = [["X","O","O"], ["O","X","X"], [None, None, None]]
Antwoord
(2, 2): dat maakt de diagonaal X X X vol, directe winst.
De twee andere zetten winnen niet. Na (2, 0) of (2, 1) blokkeert O
op (2, 2) en eindigt het in remise. Van de drie zetten heeft er dus
precies één de waarde 1, en die geeft minimax terug.
Bouw zelf in tictactoe.py
Open je lokale tictactoe.py (gemaakt bij draai het zelf). Plak deze
starter onderaan, ná je negen functies:
def minimax(bord):
if terminal(bord):
return None
# Vul aan:
# 1. bepaal wie aan zet is met player(bord)
# 2. loop over actions(bord); voor elke zet, bereken de score
# (min_value als X aan zet, max_value als O aan zet)
# 3. onthoud de zet met de beste score
# 4. return die zet
return None
Tip
Het patroon is bijna hetzelfde als in max_value en min_value, met
één verschil: je onthoudt welke zet het beste getal opleverde, niet
alleen het getal. Je houdt dus twee variabelen bij, beste_score en
beste_zet, en per speler een if: voor X vergelijk je met > en
begin je op -math.inf, voor O met < en math.inf.
Vergeet je beste_zet bij te werken? Dan geeft minimax None terug.
Antwoord
def minimax(bord):
if terminal(bord):
return None
beste_zet = None
if player(bord) == "X":
beste_score = -math.inf
for zet in actions(bord):
score = min_value(result(bord, zet))
if score > beste_score:
beste_score = score
beste_zet = zet
else:
beste_score = math.inf
for zet in actions(bord):
score = max_value(result(bord, zet))
if score < beste_score:
beste_score = score
beste_zet = zet
return beste_zet
Het is max_value met een geheugen: dezelfde lus, alleen onthoud je
naast de beste score ook bij welke zet die hoorde.
Test je minimax in tictactoe.py
Plak deze tests onderaan je lokale bestand (na minimax):
# === Tests ===
# Terminal -> None
vol = [["X","O","X"],["X","O","O"],["O","X","X"]]
assert minimax(vol) is None, "Terminal bord -> None"
# X kan direct winnen op (2,2); de andere twee zetten geven hoogstens remise
bord = [["X","O","O"], ["O","X","X"], [None, None, None]]
zet = minimax(bord)
assert zet == (2, 2), f"Verwacht (2,2), kreeg {zet}"
# O moet X-winst blokkeren
# X heeft (0,0) en (1,1) -> dreigt op (2,2). O is aan zet.
bord = [["X", None, "O"], [None, "X", None], [None, None, None]]
zet = minimax(bord)
assert zet == (2, 2), f"O moet blokkeren op (2,2), kreeg {zet}"
print("Alle tests gehaald ✓")
In de terminal:
python tictactoe.py
Verwachte output: Alle tests gehaald ✓.
De AI-vs-AI demo
Werkt het? Plak dan dit blok onderaan, na de tests:
def print_bord(bord):
for rij in bord:
cellen = []
for c in rij:
if c:
cellen.append(c)
else:
cellen.append(".")
print(" | ".join(cellen))
print()
# AI vs AI
bord = initial_state()
print("\nStart:")
print_bord(bord)
while not terminal(bord):
zet = minimax(bord)
print(f"{player(bord)} speelt {zet}")
bord = result(bord, zet)
print_bord(bord)
w = winner(bord)
if w:
print(f"Winnaar: {w}")
else:
print("Remise — exact zoals voorspeld!")
Run opnieuw:
python tictactoe.py
Je AI speelt nu tegen zichzelf, en dat eindigt altijd in remise: zo zit tic-tac-toe in elkaar bij optimaal spel.
Op je laptop is dit een paar seconden, niet 30. Dat is het verschil tussen Python in de browser en Python op je eigen computer.
Bonus — CS50's runner.py met clickable GUI
CS50 levert bij dit project een tweede bestand, runner.py, dat een
venster opent waarin jij tegen je eigen AI speelt. Het gebruikt het
tictactoe.py dat jij net hebt gebouwd. Zo werkt het:
- Download het project-zip via
cs50.harvard.edu/ai/projects/0/tictactoe/ —
daar staat
runner.pyin (Engelse versie van wat jij in het Nederlands hebt gebouwd). - Vervang het bijgeleverde lege
tictactoe.pydoor jouw versie. Zet bovenaan, onder de imports, drie regels dierunner.pyverwacht:Het CS50-project spreekt jouw bestand aan met die drie namen; jouw functies gebruiken de waardes zelf, dus verder verandert er niets.X = "X"O = "O"EMPTY = None - Installeer eenmalig
pygame:python -m pip install pygame - Draai:
python runner.py
Er opent een venster. Je klikt op een vakje en je AI antwoordt: boter-kaas-en-eieren tegen een tegenstander die nooit blundert.
Geen lokale Python? Noodoplossing met PyRunner
Open dit alleen als je echt niet lokaal kan draaien
Vervang de lege minimax op de aangegeven plek door je eigen versie,
inclusief de regel def. De andere negen functies staan al klaar: ze
draaien mee, en je leest ze onder de editor via "Toon de code die al
klaarstaat". Onder minimax staan dezelfde tests als hierboven.
Halen die het, dan speelt de AI tegen zichzelf. Let op: die demo kan
in de browser zo'n 30 seconden duren — minimax rekent
honderdduizenden posities door.
Je hebt nu een werkende AI op je eigen laptop, in een Python-bestand dat je kunt kopiëren, mailen of verder uitbouwen: tien kleine functies die elkaar aanroepen, elk apart getest.
Door naar veelgemaakte fouten →.