9.16 Bouwsteen 9 — minimax(bord)
Leerdoel: je schrijft de top-level AI-functie die alle vorige
bouwstenen gebruikt om de beste zet terug te geven — direct in je
lokale tictactoe.py.
Je hebt op pagina 15 je 8 functies in één lokaal bestand gezet (of in de fallback-PyRunner). Tijd voor de 9e.
Wat doet deze functie?
minimax(bord) geeft de beste zet terug voor de speler die nu aan
zet is. Niet het getal — dat is wat max_value/min_value doen.
minimax geeft de tuple (i, j).
- X-beurt: kies de zet met de hoogste
min_value(result(bord, zet)). - O-beurt: kies de zet met de laagste
max_value(result(bord, zet)).
Waarom kruislings? Omdat na X's zet, O aan zet is, dus we
evalueren met min_value. Spiegelbeeld voor O.
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-en-vergelijk-structuur als in max_value, alleen
onthoud je ook welke zet het optimum opleverde.
Speciaal geval
Als terminal(bord), is er geen zet meer mogelijk. Return None.
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 en kan
direct winnen via (2, 0) of (2, 2).
bord = [["X","O","X"], ["O","X","O"], [None, None, None]]
Antwoord
(2, 0) of (2, 2) — beide leveren een directe winst op (diagonaal).
Welke van de twee terugkomt hangt af van de volgorde waarin
actions(bord) ze opsomt. Beide zijn correct.
Bouw zelf in tictactoe.py
Open je lokale tictactoe.py (gemaakt op pagina 15). Plak deze
starter onderaan, ná je 8 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 identiek aan max_value / min_value, met één
extra ding: onthoud welke zet het optimum opleverde, niet alleen
de score.
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:
# spiegelbeeld voor O: math.inf, max_value, <
...
Let op de twee toewijzingen onder de if: zowel beste_score als
beste_zet. Vergeet je beste_zet? Dan returnt minimax None.
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 via (2,0) of (2,2) — beide is correct
bord = [["X","O","X"], ["O","X","O"], [None, None, None]]
zet = minimax(bord)
assert zet in {(2, 0), (2, 2)}, f"Verwacht (2,0) of (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:
print(" | ".join(c if c else "." for c in rij))
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. Eindigt altijd in remise — dat is een wiskundige eigenschap van tic-tac-toe bij optimaal spel.
Op je laptop is dit een paar seconden, niet 30. Da's het verschil tussen browser-Python en native Python.
Bonus — CS50's runner.py met clickable GUI
CS50 levert bij dit project een tweede bestand, runner.py, dat
een grafische tic-tac-toe-window opent waarin jij tegen je eigen AI
kunt spelen. Hetzelfde tictactoe.py dat jij hebt gebouwd is wat
runner.py gebruikt.
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. - Installeer eenmalig
pygame:pip install pygame - Draai:
python runner.py
Een venster opent. Je klikt op een vakje, je AI antwoordt. Boterkaas- en-eieren tegen een ondoorzichtbare tegenstander.
Geen lokale Python? Noodoplossing met PyRunner
Open dit alleen als je echt niet lokaal kan draaien
Plak je eigen minimax op de aangegeven plek. De andere 8 functies
staan al klaar. Let op: de AI-vs-AI demo kan in de browser zo'n
30 seconden duren — minimax rekent honderdduizenden posities door.
Wat heb je nu?
Een werkende AI op je eigen laptop. Niet in een tutorial — gewoon een Python-bestand dat je kunt kopiëren, mailen, of doorontwikkelen.
Je hebt 9 functies geschreven, ze door elkaar laten roepen met de patronen uit stap 9: functies in functies, en bovenop alles recursie ingezet (stap 14: helpers). Dit is precies hoe AI-projecten in het echt worden gemaakt: kleine functies die elkaar oproepen, getest, geassembleerd in een lokaal project.
Door naar stap 17: veelgemaakte fouten →.