Minimax¶
Het Minimax Algoritme Uitgelegd¶
Het minimax algoritme wordt vooral gebruikt in spellen waar twee spelers tegen elkaar spelen, zoals schaak of boter-kaas-en-eieren. Het idee is dat één speler probeert te winnen (maximaliseren) terwijl de andere speler probeert te voorkomen dat dit gebeurt (minimaliseren).
Stel je voor dat je schaakt. Bij elke beurt:
Kijk je naar alle mogelijke zetten die je kunt doen
Voor elke zet kijk je naar alle mogelijke tegenzetten van je tegenstander
Dit ga je een aantal stappen diep door
Aan het einde geef je elke situatie een score
Je kiest de zet die uiteindelijk tot de beste score leidt
Laten we dit visualiseren met een eenvoudig voorbeeld:
graph TD
A[A: MAX] --> B[B: MIN]
A --> C[C: MIN]
B --> D[D: MAX<br/>Score: 3]
B --> E[E: MAX<br/>Score: 5]
C --> F[F: MAX<br/>Score: 2]
C --> G[G: MAX<br/>Score: 8]
%% propagatie van waarden
D -- "3" --> B
E -- "5" --> B
F -- "2" --> C
G -- "8" --> C
B -- "3" --> A
C -- "2" --> A
style A fill:#f9f,stroke:#333,stroke-width:2px
style B fill:#bbf,stroke:#333,stroke-width:2px
style C fill:#bbf,stroke:#333,stroke-width:2px
style D fill:#dfd,stroke:#333,stroke-width:2px
style E fill:#dfd,stroke:#333,stroke-width:2px
style F fill:#dfd,stroke:#333,stroke-width:2px
style G fill:#dfd,stroke:#333,stroke-width:2px
In dit voorbeeld:
De MAX speler (bijvoorbeeld jij) begint bovenaan
De MIN speler (je tegenstander) is het niveau eronder
De getallen onderaan zijn de scores van elke eindpositie
Bij MAX-nodes kies je het hoogste getal
Bij MIN-nodes kiest je tegenstander het laagste getal
Alpha-Beta Pruning¶
Nu komt het slimme deel: alpha-beta pruning. Dit is een techniek om veel sneller tot dezelfde beslissing te komen, door takken van de boom te “snoeien” die we toch nooit zullen kiezen.
Laten we dit visualiseren:
graph TD
A[A: MAX<br/>α=-∞ β=∞] --> B[B: MIN<br/>α=3 β=∞]
A --> C[C: MIN<br/>α=3 β=∞]
B --> D[D: MAX<br/>Score: 3]
B --> E[E: MAX<br/>Score: 5]
C --> F[F: MAX<br/>Score: 2]
C --> G[G: MAX<br/>❌ Gesnoeid]
style A fill:#f9f,stroke:#333,stroke-width:2px
style B fill:#bbf,stroke:#333,stroke-width:2px
style C fill:#bbf,stroke:#333,stroke-width:2px
style D fill:#dfd,stroke:#333,stroke-width:2px
style E fill:#dfd,stroke:#333,stroke-width:2px
style F fill:#dfd,stroke:#333,stroke-width:2px
style G fill:#fdd,stroke:#333,stroke-width:2px
Hoe werkt alpha-beta pruning?
Alpha (α) is de beste score die MAX tot nu toe heeft gevonden
Beta (β) is de beste score die MIN tot nu toe heeft gevonden
Als we een tak tegenkomen die nooit gekozen zal worden (omdat er al een betere optie is), dan kunnen we die tak “snoeien”
In het voorbeeld hierboven:
We beginnen bij A met α=-∞ en β=∞
We verkennen tak B:
D geeft score 3
E geeft score 5
B kiest dus 5 (MIN kiest altijd het laagste)
We gaan naar tak C:
F geeft score 2
We weten dat MIN bij B al 5 heeft gekozen
Als we nu een score > 5 vinden bij G, zal MIN deze tak toch nooit kiezen
Dus kunnen we G overslaan (snoeien)
Het voordeel van alpha-beta pruning is dat je veel minder posities hoeft te evalueren, terwijl je exact dezelfde beslissing neemt. In grote spellen zoals schaak kan dit het verschil zijn tussen secondes of uren rekentijd!
Boter-kaas-en-eieren¶
graph TD
A["Start<br/>□ □ □<br/>□ X □<br/>□ □ □<br/>[MAX]"] --> B["□ □ O<br/>□ X □<br/>□ □ □<br/>[MIN]"]
A --> C["□ □ □<br/>□ X □<br/>□ □ O<br/>[MIN]"]
B --> D["□ □ O<br/>□ X □<br/>X □ □<br/>[MAX]"]
B --> E["□ □ O<br/>□ X □<br/>□ X □<br/>[MAX]"]
C --> F["X □ □<br/>□ X □<br/>□ □ O<br/>[MAX]"]
C --> G["□ □ X<br/>□ X □<br/>□ □ O<br/>[MAX]"]
D --> H["□ □ O<br/>□ X O<br/>X □ □<br/>Score: -1"]
D --> I["O □ O<br/>□ X □<br/>X □ □<br/>Score: 0"]
E --> J["□ □ O<br/>O X □<br/>□ X □<br/>Score: 1"]
F --> K["X □ O<br/>□ X □<br/>□ □ O<br/>Score: 1"]
G --> L["□ □ X<br/>□ X O<br/>□ □ O<br/>Score: 0"]
style A fill:#f9f,stroke:#333,stroke-width:2px
style B fill:#bbf,stroke:#333,stroke-width:2px
style C fill:#bbf,stroke:#333,stroke-width:2px
style D fill:#f9f,stroke:#333,stroke-width:2px
style E fill:#f9f,stroke:#333,stroke-width:2px
style F fill:#f9f,stroke:#333,stroke-width:2px
style G fill:#f9f,stroke:#333,stroke-width:2px
style H fill:#dfd,stroke:#333,stroke-width:2px
style I fill:#dfd,stroke:#333,stroke-width:2px
style J fill:#dfd,stroke:#333,stroke-width:2px
style K fill:#dfd,stroke:#333,stroke-width:2px
style L fill:#dfd,stroke:#333,stroke-width:2px
Laten we dit voorbeeld stap voor stap doornemen:
Uitgangssituatie
We beginnen met een X in het midden (speler MAX)
O is aan zet (speler MIN)
De lege vakjes zijn aangegeven met □
Scores
+1: Als MAX (X) wint
-1: Als MIN (O) wint
0: Bij gelijkspel of tussenposities
Hoe werkt het proces?
MIN (O) heeft in dit voorbeeld twee logische eerste zetten: rechtsboven of rechtsonder
Voor elke zet van MIN kijkt MAX naar zijn mogelijke tegenzetten
Dit proces gaat door tot we een eindsituatie bereiken
Laten we nu zien hoe alpha-beta pruning hier kan helpen:
graph TD
A["Start<br/>□ □ □<br/>□ X □<br/>□ □ □<br/>α=-∞ β=∞"] --> B["□ □ O<br/>□ X □<br/>□ □ □<br/>α=-∞ β=1"]
A --> C["□ □ □<br/>□ X □<br/>□ □ O<br/>❌ Gesnoeid"]
B --> D["□ □ O<br/>□ X □<br/>X □ □<br/>α=0 β=1"]
B --> E["□ □ O<br/>□ X □<br/>□ X □<br/>α=1 β=1"]
D --> H["□ □ O<br/>□ X O<br/>X □ □<br/>Score: -1"]
D --> I["O □ O<br/>□ X □<br/>X □ □<br/>Score: 0"]
E --> J["□ □ O<br/>O X □<br/>□ X □<br/>Score: 1"]
style A fill:#f9f,stroke:#333,stroke-width:2px
style B fill:#bbf,stroke:#333,stroke-width:2px
style C fill:#fdd,stroke:#333,stroke-width:2px
style D fill:#f9f,stroke:#333,stroke-width:2px
style E fill:#f9f,stroke:#333,stroke-width:2px
style H fill:#dfd,stroke:#333,stroke-width:2px
style I fill:#dfd,stroke:#333,stroke-width:2px
style J fill:#dfd,stroke:#333,stroke-width:2px
In dit voorbeeld van alpha-beta pruning:
Na het verkennen van tak B vinden we dat MIN daar maximaal een score van 1 kan behalen
Als we dan bij tak C beginnen, weten we dat:
MAX al een pad heeft gevonden dat minstens 1 oplevert
Als MIN slim speelt, zal deze tak nooit gekozen worden
Daarom kunnen we de hele tak C (en alle onderliggende posities) overslaan
Praktische voordelen voor boter-kaas-en-eieren:
In een echt spel zijn er ongeveer 255.168 mogelijke spelposities
Met alpha-beta pruning hoef je maar een fractie hiervan te evalueren
Dit maakt het mogelijk om in milliseconden de beste zet te bepalen
Dit voorbeeld laat ook zien waarom een X in het midden een sterke openingszet is: het geeft MAX (X) de meeste mogelijkheden om later drie-op-een-rij te maken, terwijl het MIN (O) dwingt om defensief te spelen.
Wil je dat ik nog dieper inga op bepaalde aspecten van het algoritme of heb je interesse in hoe dit zou werken voor andere spellen?
Genetisch algoritme¶
flowchart TD
A[Initiële Populatie] -->|Genereer willekeurig| B[Evaluatie]
B --> C{Fitness Check}
C -->|Goede dekking| D[Selectie]
C -->|Slechte dekking| E[Eliminatie]
D --> F[Crossover]
D --> G[Mutatie]
F --> H[Nieuwe Generatie]
G --> H
H --> B
Wat is een genetisch algoritme? Het is een programmeertechniek die geïnspireerd is door natuurlijke evolutie. Net zoals in de natuur organismen evolueren om beter aangepast te raken aan hun omgeving, kunnen we computerprogramma’s laten “evolueren” om beter te worden in een taak.
Toepassing op Picobot:
Een “individu” is een complete verzameling regels
Elke regel heeft het format:
huidige_staat omgeving -> nieuwe_staat bewegingEen “populatie” is een verzameling van verschillende reeksen regels
Het evolutieproces:
Initiële populatie: We beginnen met willekeurig gegenereerde verzamelingen regels
Evaluatie: We testen elke verzameling:
Hoeveel % van de ruimte wordt bezocht?
Hoeveel stappen zijn nodig?
Hoe efficiënt zijn de regels?
Evolutionaire operatoren:
Selectie: De beste verzamelingen regels krijgen een hogere kans om “ouder” te worden
Crossover: We combineren regels van twee goede verzamelingen
Bijvoorbeeld: de eerste helft van verzameling A met de tweede helft van verzameling B
Mutatie: We maken kleine willekeurige aanpassingen
Bijvoorbeeld: een bewegingsrichting veranderen van N naar O
Optimalisatieproces:
Dit proces herhaalt zich voor meerdere generaties
Elke generatie wordt gemiddeld beter dan de vorige
Uiteindelijk vinden we verzamelingen regels die de ruimte efficiënt kunnen verkennen
Waarom is een een genetisch algoritme interessant?
Dit is een voorbeeld van hoe we natuurlijke processen kunnen gebruiken als inspiratie voor probleemoplossing
Het laat zien dat computers zelf oplossingen kunnen vinden zonder dat wij expliciet programmeren wat ze moeten doen
Het demonstreert hoe kleine, willekeurige veranderingen gecombineerd met selectie kunnen leiden tot complexe, effectieve oplossingen
Zou je geïnteresseerd zijn in meer details over een specifiek aspect van dit proces? Bijvoorbeeld hoe we precies de fitness berekenen of hoe we crossover implementeren?