summaryrefslogtreecommitdiff
path: root/docs/posts/2024-04-17-implementing-minimax-for-chess-in-swift.html
blob: 5683da6c138c5e7f87fa315e9aa4a81d52e989e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
<!DOCTYPE html>
<html lang="en">
<head>
    
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="theme-color" content="#6a9fb5">

    <title>Implementing Minimax with Alpha-Beta pruning for a simple Chess AI in Swift</title>

    <!--
    <link rel="stylesheet" href="https://unpkg.com/latex.css/style.min.css" /> 
    -->

    <link rel="stylesheet" href="/assets/c-hyde.css" />

    <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=PT+Sans:400,400italic,700|Abril+Fatface">

    <link rel="stylesheet" href="/assets/main.css" />
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="og:site_name" content="Navan Chauhan" />
    <link rel="canonical" href="https://web.navan.dev/posts/2024-04-17-implementing-minimax-for-chess-in-swift.html" />
    <meta name="twitter:url" content="https://web.navan.dev/posts/2024-04-17-implementing-minimax-for-chess-in-swift.html" />
    <meta name="og:url" content="https://web.navan.dev/posts/2024-04-17-implementing-minimax-for-chess-in-swift.html" />
    <meta name="twitter:title" content="Implementing Minimax with Alpha-Beta pruning for a simple Chess AI in Swift" />
    <meta name="og:title" content="Implementing Minimax with Alpha-Beta pruning for a simple Chess AI in Swift" />
    <meta name="description" content="Adding a bestMove method to swift-chess-neo by implementing alpha-beta pruning for minimax" />
    <meta name="twitter:description" content="Adding a bestMove method to swift-chess-neo by implementing alpha-beta pruning for minimax" />
    <meta name="og:description" content="Adding a bestMove method to swift-chess-neo by implementing alpha-beta pruning for minimax" />
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="shortcut icon" href="/images/favicon.png" type="image/png" />
    <link href="/feed.rss" type="application/atom+xml" rel="alternate" title="Sitewide Atom feed" />
    <meta name="twitter:image" content="https://web.navan.dev/images/opengraph/posts/2024-04-17-implementing-minimax-for-chess-in-swift.png" />
    <meta name="og:image" content="https://web.navan.dev/images/opengraph/posts/2024-04-17-implementing-minimax-for-chess-in-swift.png" />
    <meta name="google-site-verification" content="LVeSZxz-QskhbEjHxOi7-BM5dDxTg53x2TwrjFxfL0k" />
    <script data-goatcounter="https://navanchauhan.goatcounter.com/count"
        async src="//gc.zgo.at/count.js"></script>
    <script defer data-domain="web.navan.dev" src="https://plausible.io/js/plausible.js"></script>
    <link rel="manifest" href="/manifest.json" />
    
</head>
<body class="theme-base-0d">
    <div class="sidebar">
    <div class="container sidebar-sticky">
        <div class="sidebar-about">
            <h1><a href="/">Navan</a></h1>
            <p class="lead" id="random-lead">Alea iacta est.</p>
        </div>

        <ul class="sidebar-nav">
            <li><a class="sidebar-nav-item" href="/about/">about/links</a></li>
            <li><a class="sidebar-nav-item" href="/posts/">posts</a></li>
            <li><a class="sidebar-nav-item" href="/3D-Designs/">3D designs</a></li>
            <li><a class="sidebar-nav-item" href="/feed.rss">RSS Feed</a></li>
            <li><a class="sidebar-nav-item" href="/colophon/">colophon</a></li>
        </ul>
        <div class="copyright"><p>&copy; 2019-2024. Navan Chauhan <br> <a href="/feed.rss">RSS</a></p></div>
    </div>
</div>

<script>
let phrases = [
    "Something Funny", "Veni, vidi, vici", "Alea iacta est", "In vino veritas", "Acta, non verba", "Castigat ridendo mores",
    "Cui bono?", "Memento vivere", "अहम् ब्रह्मास्मि", "अनुगच्छतु प्रवाहं", "चरन्मार्गान्विजानाति", "coq de cheval", "我愛啤酒"
    ];

let new_phrase = phrases[Math.floor(Math.random()*phrases.length)];

let lead = document.getElementById("random-lead");
lead.innerText = new_phrase;
</script>
    <div class="content container">
    
	<div class="post">
	<h1 id="implementing-minimax-with-alpha-beta-pruning-for-a-simple-chess-ai-in-swift">Implementing Minimax with Alpha-Beta pruning for a simple Chess AI in Swift</h1>

<p>Ever since Chess24 shut down, I have been looking to find a better way to follow Chess tournaments. A few weeks ago I decided to start working on a cross-platform (macOS/iOS) app using Lichess's API.
I heavily underestimated the amount of work it would take me to build something like this in SwiftUI. You not only need a library that can parse PGNs, but also a way to display those moves! </p>

<p>I ended up forking <a rel="noopener" target="_blank" href="https://github.com/nvzqz/Sage">Sage</a> to <a rel="noopener" target="_blank" href="https://git.navan.dev/swift-chess-neo">swift-chess-neo</a>. I did have to patch the library to make it compatible with Swift 5 and create my own UI components using SwiftUI.</p>

<p>Now that I had a working Chess library that could give me all legal moves in a position, I wondered if I could write a minimax implementation.</p>

<h2 id="theory">Theory</h2>

<p>Imagine you could look far ahead into the future and predict the perfect moves in a game for both sides. This is similar to Dr. Strange seeing all 14,000,605 alternate futures.
Knowing what works and what doesn't can help you decide what you should actually play.</p>

<p>Using the example of Dr. Strange looking into the alternate futures, think of the Avengers winning being scored as +1, and Thanos winning being scored as -1.
The Avengers would try to maximize this score, whereas Thanos would try to minimize this. </p>

<p>This is the idea of "minimax".</p>

<p>Say we are playing a game of Tic-Tac-Toe, where us winning is scored positively and our opponent winning is scored negatively. We are going to try and maximize our score. A fresh game of Tic-Tac-Toe can be represented as a 3x3 grid. Which means, if we have the first turn we have 9 possible moves.</p>

<p>Say we place an X in the top left corner:</p>

<pre><code>-------------
| x |   |   |
-------------
|   |   |   |
-------------
|   |   |   |
-------------
</code></pre>

<p>Now, our oponent has 8 different moves they can play. Say they play their move in the bottom right corner</p>

<pre><code>-------------
| x |   |   |
-------------
|   |   |   |
-------------
|   |   | o |
-------------
</code></pre>

<p>We have 6 different moves we can play.</p>

<p>It would take us ages to brute force each and every combination/permutation of moves by hand. A depth-first minimax algorithm for Tick-Tac-Toe would have a max-depth of 9 (since after 9 moves from the start, we would have exhausted the search space as there would be no more available moves).</p>

<p>Since we cannot score an individual Tic-Tac-Toe position (technically we can), we can iterate through all moves (till we reach our max-depth) and then use these three terminal states:</p>

<ul>
<li>+1 (You Win)</li>
<li>-1 (You Lose)</li>
<li>0 (Draw)</li>
</ul>

<h3 id="pseudocode">Pseudocode</h3>

<pre><code>function minimax(board, depth, isMaximizingPlayer):
    score = evaluate(board)

    # +1 Win, -1 Lose, 0 Draw
    if score == 1: return score
    if score == -1: return score

    if boardFull(board):
        return 0

    if isMaximizingPlayer:
        best = -infinity

        for each cell in board:
            if cell is empty:
                place X in cell
                best = maximum of (best, minimax(board, depth + 1, false))
                remove X from cell
        return best

    else:
        best = infinity

        for each cell in board:
            if cell is empyu:
                place O in cell
                best = minimum of (best, minimax(board, depth + 1, true))
        return best

function evaluate(board):
    if three consecutive Xs: return 1
    if three consecutive 0s: return -1

    return 0

function boardFull(board):
    if all cells are filled: return true

    else:
        return false
</code></pre>

<p>Think of each move as a node, and each node having multiple continuations (each continuing move can be represented as a node).</p>

<h3 id="alpha-beta-pruning">Alpha-Beta Pruning</h3>

<p>This is quiet inefficient, as this will comb through all <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"><mrow><mn>9</mn><mo>&#x00021;</mo></mrow></math> or <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"><mrow><mn>362</mn><mo>&#x0002C;</mo><mn>880</mn></mrow></math> moves! Imagine iterating through the entire search space for a complex game like Chess. It would be impossible. Therefore we use a technique called Alpha-beta pruning wherein we reduce the number of nodes that we are evaluating. </p>

<pre><code>function minimax(board, depth, isMaximizingPlayer, alpha, beta):
    score = evaluate(board)

    # +1 Win, -1 Lose, 0 Draw
    if score == 1: return score
    if score == -1: return score

    if boardFull(board):
        return 0

    if isMaximizingPlayer:
        best = -infinity

        for each cell in board:
            if cell is empty:
                place X in cell
                best = maximum of (best, minimax(board, depth + 1, false, alpha, beta))
                remove X from cell
                alpha = max(alpha, best)
                if beta &lt;= alpha:
                    break
        return best

    else:
        best = infinity

        for each cell in board:
            if cell is empyu:
                place O in cell
                best = minimum of (best, minimax(board, depth + 1, true, alpha, beta))
                beta = min(beta, best)
                if beta &lt;= alpha:
                    break
        return best
</code></pre>

<p>Alpha and beta are initialized as <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"><mrow><mo>&#x02212;</mo><mo>&#x0221E;</mo></mrow></math> and <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"><mrow><mo>&#x0002B;</mo><mo>&#x0221E;</mo></mrow></math> respectively, with Alpha representing the best already explored option along the path to the root for the maximizer, and beta representing the best already explored option along the path to the root for the minimizer. If at any point beta is less than or equal to alpha, it means that the current branch does not need to be explored further because the parent node already has a better move elsewhere, thus "pruning" this node.</p>

<p>Thus, to implement a model you can use minimax (or similar algorithms), you need to be able to describe the following:</p>

<ul>
<li>Players
<ul>
<li>Player Count - How many players are there in this game? (Just 2)</li>
<li>Active Player - Whose turn is it?</li>
</ul></li>
<li>Game
<ul>
<li>Game State - How do you represent the current game state</li>
<li>Save/Load Game State - If you are trying out different moves, you need to be able to go to the original state before you move on to the next node</li>
<li>Score - Who is winning? Is the game over? Who lost?</li>
</ul></li>
<li>Moves
<ul>
<li>Valid Moves - What are all the legal moves in this game state? </li>
</ul></li>
</ul>

<h2 id="show-me-the-code">Show me the code!</h2>

<p>The chess library does a little bit of the heavy lifting by already providing methods to take care of the above requirements. Since we already have a way to find all possible moves in a position, we only need to figure out a few more functions/methods:</p>

<ul>
<li>Evaluation/Score - We need to be able to numerically quantify the effect of a move. For a basic implementation we can just sum over the piece values.</li>
<li>Game state management - Since we explore different possible moves in our search space up to a certain depth, we need to be able to copy/save the state and reset it back to this state after we are done exploring all of our moves. Even though I did add a <code>setGame</code> method to the <code>Game</code> class, I use the <code>undoMove()</code> method</li>
</ul>

<h3 id="evaluate">Evaluate</h3>

<p>Each piece has a different relative value. Since "capturing" the king finishes the game, the king is given a really high value.</p>

<div class="codehilite">
<pre><span></span><code><span class="kd">public</span> <span class="kd">struct</span> <span class="nc">Piece</span><span class="p">:</span> <span class="nb">Hashable</span><span class="p">,</span> <span class="n">CustomStringConvertible</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="kd">enum</span> <span class="nc">Kind</span><span class="p">:</span> <span class="nb">Int</span> <span class="p">{</span>
        <span class="p">...</span>
        <span class="kd">public</span> <span class="kd">var</span> <span class="nv">relativeValue</span><span class="p">:</span> <span class="nb">Double</span> <span class="p">{</span>
            <span class="k">switch</span> <span class="kc">self</span> <span class="p">{</span>
                <span class="k">case</span> <span class="p">.</span><span class="n">pawn</span><span class="p">:</span> <span class="k">return</span> <span class="mi">1</span>
                <span class="k">case</span> <span class="p">.</span><span class="n">knight</span><span class="p">:</span> <span class="k">return</span> <span class="mi">3</span>
                <span class="k">case</span> <span class="p">.</span><span class="n">bishop</span><span class="p">:</span> <span class="k">return</span> <span class="mf">3.25</span>
                <span class="k">case</span> <span class="p">.</span><span class="n">rook</span><span class="p">:</span> <span class="k">return</span> <span class="mi">5</span>
                <span class="k">case</span> <span class="p">.</span><span class="n">queen</span><span class="p">:</span> <span class="k">return</span> <span class="mi">9</span>
                <span class="k">case</span> <span class="p">.</span><span class="n">king</span><span class="p">:</span> <span class="k">return</span> <span class="mi">900</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="p">...</span>
    <span class="p">}</span>
    <span class="p">...</span>
<span class="p">}</span>
</code></pre>
</div>

<p>We extend the <code>Game</code> class by adding an evaluate function that adds up the value of all the pieces left on the board.</p>

<div class="codehilite">
<pre><span></span><code><span class="kd">extension</span> <span class="nc">Game</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">evaluate</span><span class="p">()</span> <span class="p">-&gt;</span> <span class="nb">Double</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nv">score</span><span class="p">:</span> <span class="nb">Double</span> <span class="p">=</span> <span class="mi">0</span>
        <span class="k">for</span> <span class="n">square</span> <span class="k">in</span> <span class="n">Square</span><span class="p">.</span><span class="n">all</span> <span class="p">{</span>
            <span class="k">if</span> <span class="kd">let</span> <span class="nv">piece</span> <span class="p">=</span> <span class="n">board</span><span class="p">[</span><span class="n">square</span><span class="p">]</span> <span class="p">{</span>
                <span class="n">score</span> <span class="o">+=</span> <span class="n">piece</span><span class="p">.</span><span class="n">kind</span><span class="p">.</span><span class="n">relativeValue</span> <span class="o">*</span> <span class="p">(</span><span class="n">piece</span><span class="p">.</span><span class="n">color</span> <span class="p">==</span> <span class="p">.</span><span class="n">white</span> <span class="p">?</span> <span class="mf">1.0</span> <span class="p">:</span> <span class="o">-</span><span class="mf">1.0</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="k">return</span> <span class="n">score</span>
<span class="p">}</span>
</code></pre>
</div>

<p>Since the values for black pieces are multiplied by -1 and white pieces by +1, material advantage on the board translates to a higher/lower evaluation.</p>

<h3 id="recursive-minimax">Recursive Minimax</h3>

<p>Taking inspiration from the pseudocode above, we can define a minimax function in Swift as:</p>

<div class="codehilite">
<pre><span></span><code><span class="kd">func</span> <span class="nf">minimax</span><span class="p">(</span><span class="n">depth</span><span class="p">:</span> <span class="nb">Int</span><span class="p">,</span> <span class="n">isMaximizingPlayer</span><span class="p">:</span> <span class="nb">Bool</span><span class="p">,</span> <span class="n">alpha</span><span class="p">:</span> <span class="nb">Double</span><span class="p">,</span> <span class="n">beta</span><span class="p">:</span> <span class="nb">Double</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nb">Double</span> <span class="p">{</span>
    <span class="k">if</span> <span class="n">depth</span> <span class="p">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">isFinished</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">evaluate</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="kd">var</span> <span class="nv">alpha</span> <span class="p">=</span> <span class="n">alpha</span>
    <span class="kd">var</span> <span class="nv">beta</span> <span class="p">=</span> <span class="n">beta</span>

    <span class="k">if</span> <span class="n">isMaximizingPlayer</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nv">maxEval</span><span class="p">:</span> <span class="nb">Double</span> <span class="p">=</span> <span class="o">-</span><span class="p">.</span><span class="n">infinity</span>

        <span class="k">for</span> <span class="n">move</span> <span class="k">in</span> <span class="n">availableMoves</span><span class="p">()</span> <span class="p">{</span>
            <span class="k">try</span><span class="p">!</span> <span class="n">execute</span><span class="p">(</span><span class="n">uncheckedMove</span><span class="p">:</span> <span class="n">move</span><span class="p">)</span>
            <span class="kd">let</span> <span class="nv">eval</span> <span class="p">=</span> <span class="n">minimax</span><span class="p">(</span><span class="n">depth</span><span class="p">:</span> <span class="n">depth</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">isMaximizingPlayer</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="n">alpha</span><span class="p">:</span> <span class="n">alpha</span><span class="p">,</span> <span class="n">beta</span><span class="p">:</span> <span class="n">beta</span><span class="p">)</span>
            <span class="n">maxEval</span> <span class="p">=</span> <span class="bp">max</span><span class="p">(</span><span class="n">maxEval</span><span class="p">,</span> <span class="n">eval</span><span class="p">)</span>
            <span class="n">undoMove</span><span class="p">()</span>
            <span class="n">alpha</span> <span class="p">=</span> <span class="bp">max</span><span class="p">(</span><span class="n">alpha</span><span class="p">,</span> <span class="n">eval</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">beta</span> <span class="o">&lt;=</span> <span class="n">alpha</span> <span class="p">{</span>
                <span class="k">break</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">maxEval</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nv">minEval</span><span class="p">:</span> <span class="nb">Double</span> <span class="p">=</span> <span class="p">.</span><span class="n">infinity</span>
        <span class="k">for</span> <span class="n">move</span> <span class="k">in</span> <span class="n">availableMoves</span><span class="p">()</span> <span class="p">{</span>
            <span class="k">try</span><span class="p">!</span> <span class="n">execute</span><span class="p">(</span><span class="n">uncheckedMove</span><span class="p">:</span> <span class="n">move</span><span class="p">)</span>
            <span class="kd">let</span> <span class="nv">eval</span> <span class="p">=</span> <span class="n">minimax</span><span class="p">(</span><span class="n">depth</span><span class="p">:</span> <span class="n">depth</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">isMaximizingPlayer</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="n">alpha</span><span class="p">:</span> <span class="n">alpha</span><span class="p">,</span> <span class="n">beta</span><span class="p">:</span> <span class="n">beta</span><span class="p">)</span>
            <span class="n">minEval</span> <span class="p">=</span> <span class="bp">min</span><span class="p">(</span><span class="n">minEval</span><span class="p">,</span> <span class="n">eval</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">beta</span> <span class="o">&lt;=</span> <span class="n">alpha</span> <span class="p">{</span>
                <span class="k">break</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">minEval</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
</div>

<h3 id="best-move">Best Move</h3>

<p>We can now get a score for a move for a given depth, we wrap this up as a public method</p>

<div class="codehilite">
<pre><span></span><code><span class="kd">extension</span> <span class="nc">Game</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">bestMove</span><span class="p">(</span><span class="n">depth</span><span class="p">:</span> <span class="nb">Int</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="n">Move</span><span class="p">?</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nv">bestMove</span><span class="p">:</span> <span class="n">Move</span><span class="p">?</span>
        <span class="kd">var</span> <span class="nv">bestValue</span><span class="p">:</span> <span class="nb">Double</span> <span class="p">=</span> <span class="p">(</span><span class="n">playerTurn</span> <span class="p">==</span> <span class="p">.</span><span class="n">white</span><span class="p">)</span> <span class="p">?</span> <span class="o">-</span><span class="p">.</span><span class="n">infinity</span> <span class="p">:</span> <span class="p">.</span><span class="n">infinity</span>
        <span class="kd">let</span> <span class="nv">alpha</span><span class="p">:</span> <span class="nb">Double</span> <span class="p">=</span> <span class="o">-</span><span class="p">.</span><span class="n">infinity</span>
        <span class="kd">let</span> <span class="nv">beta</span><span class="p">:</span> <span class="nb">Double</span> <span class="p">=</span> <span class="p">.</span><span class="n">infinity</span>

        <span class="k">for</span> <span class="n">move</span> <span class="k">in</span> <span class="n">availableMoves</span><span class="p">()</span> <span class="p">{</span>
            <span class="k">try</span><span class="p">!</span> <span class="n">execute</span><span class="p">(</span><span class="n">uncheckedMove</span><span class="p">:</span> <span class="n">move</span><span class="p">)</span>
            <span class="kd">let</span> <span class="nv">moveValue</span> <span class="p">=</span> <span class="n">minimax</span><span class="p">(</span><span class="n">depth</span><span class="p">:</span> <span class="n">depth</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">isMaximizingPlayer</span><span class="p">:</span> <span class="n">playerTurn</span><span class="p">.</span><span class="n">isBlack</span> <span class="p">?</span> <span class="kc">false</span> <span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="n">alpha</span><span class="p">:</span> <span class="n">alpha</span><span class="p">,</span> <span class="n">beta</span><span class="p">:</span> <span class="n">beta</span><span class="p">)</span>
            <span class="n">undoMove</span><span class="p">()</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">playerTurn</span> <span class="p">==</span> <span class="p">.</span><span class="n">white</span> <span class="o">&amp;&amp;</span> <span class="n">moveValue</span> <span class="o">&gt;</span> <span class="n">bestValue</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">playerTurn</span> <span class="p">==</span> <span class="p">.</span><span class="n">black</span> <span class="o">&amp;&amp;</span> <span class="n">moveValue</span> <span class="o">&lt;</span> <span class="n">bestValue</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">bestValue</span> <span class="p">=</span> <span class="n">moveValue</span>
                <span class="n">bestMove</span> <span class="p">=</span> <span class="n">move</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">bestMove</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
</div>

<h2 id="usage">Usage</h2>

<div class="codehilite">
<pre><span></span><code><span class="kd">import</span> <span class="nc">SwiftChessNeo</span>

<span class="kd">let</span> <span class="nv">game</span> <span class="p">=</span> <span class="k">try</span><span class="p">!</span> <span class="n">Game</span><span class="p">(</span><span class="n">position</span><span class="p">:</span> <span class="n">Game</span><span class="p">.</span><span class="n">Position</span><span class="p">(</span><span class="n">fen</span><span class="p">:</span> <span class="s">&quot;8/5B2/k5p1/4rp2/8/8/PP6/1K3R2 w - - 0 1&quot;</span><span class="p">)</span><span class="o">!</span><span class="p">)</span>
<span class="kd">let</span> <span class="nv">move</span> <span class="p">=</span> <span class="n">game</span><span class="p">.</span><span class="n">bestMove</span><span class="p">(</span><span class="n">depth</span><span class="p">:</span> <span class="mi">5</span><span class="p">)</span>
</code></pre>
</div>

<p>Of course there are tons of improvements you can make to this naive algorithm. A better scoring function that understands the importance of piece positioning would make this even better. The <a rel="noopener" target="_blank" href="https://www.chessprogramming.org/Main_Page">Chess Programming Wiki</a> is an amazing resource if you want to learn more about this.</p>

	</div>
	<blockquote>If you have scrolled this far, consider subscribing to my mailing list <a href="https://listmonk.navan.dev/subscription/form">here.</a> You can subscribe to either a specific type of post you are interested in, or subscribe to everything with the "Everything" list.</blockquote>
	<script data-isso="https://comments.navan.dev/"
        src="https://comments.navan.dev/js/embed.min.js"></script>
	<section id="isso-thread">
	    <noscript>Javascript needs to be activated to view comments.</noscript>
	</section>

    </div>
    <script src="assets/manup.min.js"></script>
    <script src="/pwabuilder-sw-register.js"></script>    
</body>
</html>