Roblox Chess Game with extremely fast bot in 100% Luau (Can see up to 15 moves ahead)

I made a chess game on Roblox with React frontend, roblox-ts, and a bot made in Luau.


Specs:

~8,108,679 NPS in Perft testing (singlethreaded, native luau)
~525,000 PPS, Depth 6-10 and 11 plys can be achieved in ~500ms singlethreaded.

The bot is a minimax algorithm with:

  • alpha-beta-pruning
  • transpositions
  • bitboards (dedicated lo & hi variabled)
  • late move reductions
  • aspiration windows
  • futility pruning
  • null move pruning
  • quiescence search
  • iterative deepening
  • magic bitboards (dedicated trainer in C++ for 32bit keys)
  • parallel luau (analyze multiple branches at once)
  • incremental PST evaluation
  • encoding of boards into buffers to pass across threads.
  • incremental attack generation
  • incremental 32bit Zobrist Hashing (much faster than using 64bit ints in this case)
  • tons of int-packing, example: castle status, move information are all represented as a single integer.

The bitboards allow for tricks such as computing all the knight attacks all with a couple bit operations, and even computing sliding piece movements without loops.

Game:

NOTE: Both of these are not the latest version.

This will be opensourced soon, alongside documentation explaining all of the optimizations.

12 Likes

I have came back to this project.

Some updates:

  • Depth 4 in under 300ms. (Can check all possible 4 move sequences and pick a best move)
  • Depth 5 soon ??
  • Single-threaded with native codegen it gets ~20,000 positions per second (legal move gen), Multithreaded is ~250,000.
  • UI will be redone soon.

This will be opensourced soon, alongside some good documentation on how the binary tricks and parallelism work.

  • Depth 4 in under 300ms. (Can check all possible 4 move sequences and pick a best move)

Depth 4 now takes a little bit above 100ms, and has ~500k nodes per second.

1,131,344 NODES/SEC SINGLE THREADED

Perft depth: 1, nodes: 20, time: 0.0001s. 212765.92 nodes/sec
Perft depth: 2, nodes: 400, time: 0.0005s. 866191.51 nodes/sec
Perft depth: 3, nodes: 8903, time: 0.0104s. 854923.97 nodes/sec
Perft depth: 4, nodes: 197315, time: 0.1886s. 1046287.50 nodes/sec
Perft depth: 5, nodes: 4866914, time: 4.3019s. 1131344.77 nodes/sec

In depth 5, the latest version of this engine can generate 1,131,344 nodes in a single thread :exploding_head: .

This is with a couple of insane optimizations such as counting how many checkers the king has, and if it is >= 2 then no need to bother adding moves which can have pieces block checks.

The perft data is from a single-threaded test with --profile in Luau CLI, with --code-gen on.

In the Roblox Game the performance (with Parallel Luau) is:

Depth 3: 6ms
Depth 4: 44ms
Depth 5: 544ms

Insane considering Depth 4 used to take ~100ms, and before that ~300ms. Depth 5 used to have moves taking 1-3 seconds.

Perft depth: 1, nodes: 20, time: 0.0001s. 207972.11 nodes/sec
Perft depth: 2, nodes: 400, time: 0.0001s. 3297834.16 nodes/sec
Perft depth: 3, nodes: 8902, time: 0.0013s. 6888316.21 nodes/sec
Perft depth: 4, nodes: 197277, time: 0.0275s. 7165664.05 nodes/sec
Perft depth: 5, nodes: 4865680, time: 0.6001s. 8108679.45 nodes/sec
Perft depth: 6, nodes: 119111138, time: 15.9997s. 7444595.15 nodes/sec

8x faster using so many optimizations:

  • 64bit integers represented as lo & hi halves, not using a library. This allows for bit32 calls to become inlined by Native Luau.
  • support for Magic Bitboards, but since 64bit int multiplication is expensive this uses a optimization, this also means that off-the-shelf magic numbers cannot be used anymore since collisions are so much more likely. I created a dedicated C++ magic number trainer for this.
  • Arrays of bitboards used to be {[number]: number, all: number}, with a dedicated index for all this can all be represented as a single integer array rather than a dictionary (using hash functions).
  • Incremental hashing & evaluation that doesnt hash/eval on leaf nodes but rather make small changes every node.

Once the perft values are perfected, this will be opensourced. In minimax many search optimizations have been added so it can reach a depth of 6-7 and 10-15 plys.

1 Like
Perft depth: 1, nodes: 20, time: 0.0001s. 216998.09 nodes/sec
Perft depth: 2, nodes: 400, time: 0.0002s. 1639623.96 nodes/sec
Perft depth: 3, nodes: 8902, time: 0.0012s. 7368951.06 nodes/sec
Perft depth: 4, nodes: 197281, time: 0.0263s. 7508446.86 nodes/sec
Perft depth: 5, nodes: 4865609, time: 0.5857s. 8307770.79 nodes/sec
Perft depth: 6, nodes: 119060324, time: 15.4850s. 7688738.57 nodes/sec

With many, many fixes the perft node count is now perfect up to depth 6. (Every possible position from 6 moves are value), cross compared with: Perft Results - Chessprogramming wiki.

I am currently running depth 7 which will check 3 billion positions.

1 Like

Holy, that’s fast. I’ll really be looking forward to reading the code, as though I don’t play chess, I love to see clever usages of roblox! Also, documentation = chefs kiss.

I should really try messing around with parallel luau more, I think it’ll help with my projects a lot.

5 Likes

As a Roblox Dev and Chess player this is pretty interesting. Nice work. UI looks a lot like Chess.com, I like it.

1 Like

This is really impressive for a work in progress! Are you planning on making this a full game or a showcase?

1 Like

Originally I was thinking full game, but I have gotten busy. Maybe once I open source it I can consider a closed source version with a full elo system and everything.

This is now opensource.

1 Like