Move Generation

31st March 2026

1. King

1.1 Basics

As described from the previous page, we can automate the generation of bitboards through calculating all squares around it with values: square << (-9, -8, -7, -1, +1, +7, +8, +9)

But this goes wrong with the king on the edge of the board. Case 1:

A white chess king on the edge of an empty board - moves wrap around to H file

King attacks warp / wrap around to the other side. How do we fix this? A simple check to see if the king is on the (bitboard) A file, then remove / stop moves generated on the H file (and vice versa)

Another check should be made to ensure the calculation does not go out of bounds. Simply perform a check something like (square + direction) < 64 && (square + direction) >= 0.

A white chess king in a corner of an empty board - moves wrap around to H file and undefined behaviour may occur

1.2 Attacks

How would we figure out attacks? We can utilize our custom bitwise operations to calculate legal moves from static pieces using pieceLegalMoves & ~ourPieces. Here is an example:

A white chess king surrounded by 2 black pieces and 1 white piece

But what about the opponents bishop on b8? After capturing the knight on d6, we put ourselves in check.

An illegal chess position where the white king is in check after taking the opponents knight

We need to see if the move from the king is in check after we move it. To do this, I applied the easist method: Copy the board position and make the move. If the king is in check, the move is illegal, revert the board position back to its original state:

An illegal chess position where the white king is in check after taking the opponents knight

The image above is the correct solution to solving king attacks. For Pseudo-legal move generation which my Chess Engine uses, moves follow piece rules but may leave the king in check, thus needs extra validation after making the move.

There is a slightly faster way to do this via Attack and Defend Maps and pinned pieces map. By keeping an incremental track of where each piece is, what squares it attacks, and pinned pieces, we can reduce move generation time by a lot. For instance, if our rook is absolutely pinned to our king, we cannot move it, so why bother calculating moves when we know they will all put the king in check, thus being illegal.

1.3 Castling

Coming soon...

2. Pawns

1.1 Basics

Pawns, which may look fairly insignificant, actually are quite complex. Let's sequentially go through what moves they can make in bullet points:

We need two separate precomputed tables otherwise black pawns would be promoting where white pawns are. Using the same principles as before for captures (this time forward diagonal ones), we can get our precomputed pawn moves (however double pawn pushes & en passants are calculated manually, as for pawn move generation they would be mostly redundant since they happen only once)

1.2 Examples

A white pawn on E2 square and black pawn on D7 - computed moves A black pawn on A5 square (board edge) - computed moves A white pawn on E4 square - computed moves A white pawn on D4 square after making double move, black pawn on E4, en passant on D3 A white pawn on D4 square with two possible captures on C5 and F5 Black and White pawns about to promote. White can capture and promote.

Validation & Test Positions

12th May 2026

1. Perft

Heard of it before? It stands for "Performance test", and chess engine developers LOVE it. To put simply, when you designed your move generator, how would you know it actually works on a larger scale? That's where perft comes in.

1.1 Iterative deepening & bottlenecks

In my own engine, I created a function with parameter depth. Assuming I called perft(6), it would first generate all moves, copy the board state, and make each (legal) move, and call perft(depth - 1) and revert the board position. By doing this, it would go through every single move in the current position.

Depth 6 for the starting position is fine (for me) as the time is fairly short. When going to depth perft(7), the number of moves grow so exponential that the time taken is 26 times more than perft(6) completion time!! Later on, I will show you how your chess engine can cut down on searches through pruning techniques and position evaluation.

2. Test Positions & Results

Having found Position Perft Results, I incorporated most of those positions from the Chess Programming Main Page into my engine with their final results (nodes calculated) for testing. The page has varied test positions with useful information and variation in each position.

Some of the positions here have even highlighted bugs found in old chess engines, which the starting position was sometimes not enough for a valid, reliable perft. The positions tested captures, castling, promotions, checks, discovered checks, double checks, and checkmates.

Running my Chess Engine, here are the correct results for the following chess positions:

Starting position for chess

Performance test results for rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1:

a2a34463267 a2a45363555 b2b35310358 b2b45293555 c2c35417640
c2c45866666 d2d38073082 d2d48879566 e2e39726018 e2e49771632
f2f34404141 f2f44890429 g2g35346260 g2g45239875 h2h34463070
h2h45385554 b1a34856835 b1c35708064 g1f35723523 g1h34877234
Depth6 Total Nodes119060324 Time1329 ms
Kiwipete test position

Performance test results for r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -

d5d63835265 d5e64727437 a2a34627439 a2a44387586 b2b33768824
g2g33472039 g2g43338154 g2h33819456 e5d74404043 e5f74164923
e5c64083458 e5g63949417 e5c43494887 e5g43415992 e5d33288812
c3b54317482 c3a44628497 c3b13996171 c3d13995761 d2h63967365
d2g54370915 d2f43941257 d2e34407041 d2c13793390 e2a63553501
e2b54032348 e2c44182989 e2d34066966 e2d13074219 e2f14095479
a1b13827454 a1c13814203 a1d13568344 h1f13685756 h1g13989454
f3f63975992 f3f55271134 f3h54743335 f3f44327936 f3g44514010
f3d33949570 f3e34477772 f3g34669768 f3h35067173 e1g14119629
e1c13551583 e1d13559113 e1f13377351
Depth5 Total Nodes193690690 Time2140 ms
Perft test position 3

Performance test results for 8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -:

e2e3745505 e2e4597519 g2g3271220 g2g4892781 b4a4745667
b4c41027199 b4d4957108 b4e4860971 b4f4174919 b4b3941129
b4b2818501 b4b11160678 a5a6968724 a5a4868162
Depth6 Total Nodes11030083 Time156 ms
Perft test position 4

Performance test results for r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1:

c4c52145218 d2d42816009 f3d42928923 b4c52027632 f1f22703427
g1h13212083
Depth5 Total Nodes15833292 Time204 ms
Perft test position 4 mirrored

Performance test results for r2q1rk1/pP1p2pp/Q4n2/bbp1p3/Np6/1B3NBn/pPPP1PPP/R3K2R b KQ - 0 1:

d7d52816009 c5c42145218 f6d52928923 b5c42027632 f8f72703427
g8h83212083
Depth5 Total Nodes15833292 Time203 ms
Perft test position 5

Performance test results for rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8:

d7c8q2106366 d7c8r1628284 d7c8b2712122 d7c8n2522065 a2a31936679
a2a42101105 b2b31904400 b2b41990854 c2c32090166 g2g31779903
g2g41830854 h2h31926608 h2h42015932 e2d42353001 e2f42274063
e2c32465011 e2g32302529 e2g12032466 b1a31777712 b1c32191455
b1d21450852 c4f71817665 c4a61644109 c4e62079471 c4b51832566
c4d51934045 c4b31686064 c4d31672391 c1h61748274 c1g52042591
c1f42380374 c1e32484771 c1d21894062 h1f11911413 h1g11781002
d1d62229266 d1d52742654 d1d42920749 d1d32792521 d1d22109913
e1g11979843 e1d21043007 e1f21667505 e1f12154511
Depth5 Total Nodes89941194 Time1093 ms
Perft test position 6

Performance test results for r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10:

a3a43878463 d3d44380579 b2b33419939 b2b43711088 g2g33878092
h2h34197369 h2h43743675 c3b53665630 c3d53699930 c3a43477406
c3a23313599 c3b12773444 c3d12638406 f3e55187884 f3d44645393
f3h43800952 f3d23640148 f3e12861872 g5f63337594 g5h63822473
g5f44338859 g5h43278233 g5e34005372 g5d23717908 g5c13146201
c4f7322511 c4a63526986 c4e63933073 c4b53445927 c4d53466775
c4b33453971 c4a23449599 a1a23183971 a1b13453891 a1c13303169
a1d13169502 a1e12889902 f1b13488438 f1c13629003 f1d13788187
f1e13778886 e2e34096975 e2d23941010 e2d13324203 e2e13476443
g1h14392620
Depth5 Total Nodes164075551 Time1844 ms