{"id":16423077,"url":"https://github.com/kylepls/mini-chess","last_synced_at":"2025-06-10T21:33:37.777Z","repository":{"id":131018435,"uuid":"458654668","full_name":"kylepls/mini-chess","owner":"kylepls","description":"A chess move generator written in \u003c300LoC.","archived":false,"fork":false,"pushed_at":"2022-04-18T05:02:37.000Z","size":85,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-07T11:10:52.483Z","etag":null,"topics":["chess","chess-engine","move-generator"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kylepls.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-02-12T22:31:12.000Z","updated_at":"2022-09-30T06:52:31.000Z","dependencies_parsed_at":"2023-05-25T11:45:36.228Z","dependency_job_id":null,"html_url":"https://github.com/kylepls/mini-chess","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylepls%2Fmini-chess","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylepls%2Fmini-chess/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylepls%2Fmini-chess/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylepls%2Fmini-chess/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kylepls","download_url":"https://codeload.github.com/kylepls/mini-chess/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240571029,"owners_count":19822412,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chess","chess-engine","move-generator"],"created_at":"2024-10-11T07:38:46.441Z","updated_at":"2025-02-24T22:55:08.179Z","avatar_url":"https://github.com/kylepls.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Mini Chess\n\n\u003e This project is a fully functional chess move generator with a focus on using as few lines of code as possible. Because of this, the code will be borderline unintelligible to anyone not familiar with bitboards or chess programming.\n\nI was able to pack this into \u003c300 lines of Kotlin. This is viewable [here](/src/main/kotlin/in/kyle/chess/ChessBoard.kt).\n\n## What is a move generator?\n\nA move generator is a program that generates all legal moves for a given position. This is useful\nfor tasks such as calculating the best move for a given position or checking if an arbitrary move\ninput is legal. The primary focus for a move generator is to be fast and efficient. It should be\nable to generate millions of legal moves per second and be able to run at various tree depths. Chess\nhas a branching factor of about 33, this means that a move generator can only reach a full tree\ndepth of about 7.\n\n## A high-level overview\n\nThe process of generating a legal move is as follows:\n\n1. For all pieces on the board, generate a list of pseudo-legal moves. I.e.: all moves that are\n   legal for the piece, but not necessarily legal for the board.\n2. For all the moves generated, discard moves that are illegal for the board. I.e.: moves that would\n   leave the king in check.\n\n## Bitboards\n\nBitboards are referenced throughout this project. They are used to represent the position of pieces\non a chess board using a 64-bit integer. Since there's 64 squares on a chess board, there are 64\nbits in a bitboard. Each bit represents a square on the board:\n\n| .   | .   | .   | .   | .   | .   | .   | .   | .   |\n|-----|-----|-----|-----|-----|-----|-----|-----|-----|\n| 8   | 56  | 57  | 58  | 59  | 60  | 61  | 62  | 63  |\n| 7   | 48  | 49  | 50  | 51  | 52  | 53  | 54  | 55  |\n| 6   | 40  | 41  | 42  | 43  | 44  | 45  | 46  | 47  |\n| 5   | 32  | 33  | 34  | 35  | 36  | 37  | 38  | 39  |\n| 4   | 24  | 25  | 26  | 27  | 28  | 29  | 30  | 31  |\n| 3   | 16  | 17  | 18  | 19  | 20  | 21  | 22  | 23  |\n| 2   | 8   | 9   | 10  | 11  | 12  | 13  | 14  | 15  |\n| 1   | 0   | 1   | 2   | 3   | 4   | 5   | 6   | 7   |\n| .   | A   | B   | C   | D   | E   | F   | G   | H   |\n\nThis allows for complex bitwise operations to be performed to manupulate chess pieces on the board.\nFor example, if there's a white knight on the E4 square, the bitboard for the E4 square would\nbe: `0x10000000`. We can then generate all the moves for the knight using the following bitwise\noperations:\n\n```kotlin\nfun getAllKnightAttacks(knights: Long): Long {\n   val l1 = (knights ushr 1) and 0x7f7f7f7f7f7f7f7f\n   val l2 = (knights ushr 2) and 0x3f3f3f3f3f3f3f3f\n   val r1 = (knights shl 1) and -0x101010101010102\n   val r2 = (knights shl 2) and -0x303030303030304\n   val h1 = l1 or r1\n   val h2 = l2 or r2\n   return (h1 shl 16) or (h1 ushr 16) or (h2 shl 8) or (h2 ushr 8)\n}\n```\n\nFor the example above, the knight at E4 would have a pseudo-legal move set of `0x284400442800`:\n\n```\n8 | . | . | . | . | . | . | . | . |\n7 | . | . | . | . | . | . | . | . |\n6 | . | . | . | X | . | X | . | . |\n5 | . | . | X | . | . | . | X | . |\n4 | . | . | . | . | . | . | . | . |\n3 | . | . | X | . | . | . | X | . |\n2 | . | . | . | X | . | X | . | . |\n1 | . | . | . | . | . | . | . | . |\n```\n\nThis shows just one example of how powerful bitboards can be. There's a ton of neat tricks under the\nhood.\n\n## Performance Testing\n\n### What's Perft?\n\nPerft is a chess evaluation function that calculates the number of legal moves that branch out to a\ngiven depth for a given position. It is used to test the performance and validity of a move\ngenerator as there is a well-known amount of moves from certain reference positions. You can think\nabout a perft test as visiting all the possible board states from a given starting point.\n\nThe perft code is very simple:\n\n```kotlin\nfun perft(board: ChessBoard, depth: Int): Long {\n    if (depth == 0) return 1\n\n    var nodes: Long = 0\n    for (move in board.getMoves()) {\n        board.makeMove(move)\n        nodes += perft(board, depth - 1)\n        board.undoMove()\n    }\n\n    return nodes\n}\n```\n\n### Results\n\n_All performance tests are done using an AMD Ryzen 2600._\n\n| Test             | Depth | Nodes      | My Engine | Gigantua Time | My MNodes/S | Gigantua MNodes/S |\n|------------------|-------|------------|-----------|---------------|-------------|-------------------|\n| Initial Position | 1     | 20         | 48ms      | 2ms           | 0.000416    | 0.00936768        |\n|                  | 2     | 400        | 42ms      | 1ms           | 0.009523    | 0.332502          |\n|                  | 3     | 8902       | 62ms      | 1ms           | 0.143508    | 6.27786           |\n|                  | 4     | 197281     | 201ms     | 6ms           | 0.981497    | 31.6561           |\n|                  | 5     | 4865609    | 1553ms    | 37ms          | 3.133038    | 128.306           |\n|                  | 6     | 119060324  | 28222ms   | 799ms         | 4.218706    | 148.849           |               \n|                  | 7     | 3195901860 | 817700ms  | 146.138       | 3.908403    | 146.138           |\n| Kiwipete         | 1     | 48         | 30ms      | 0ms           | 0.001600    | 4.000             | \n|                  | 2     | 2039       | 46ms      | 0ms           | 0.044326    | 75.5185           |\n|                  | 3     | 97862      | 126ms     | 0ms           | 0.776683    | 115.403           |\n|                  | 4     | 4085603    | 1432ms    | 21ms          | 2.853075    | 187.999           |\n|                  | 5     | 193690690  | 48893ms   | 963ms         | 3.961522    | 200.929           |\n|                  | 6     | 8031647685 | 1781665ms | 41264ms       | 4.507945    | 194.639           | \n\n[Gigantua Move Generator](https://github.com/Gigantua/Gigantua/tree/main/Gigantua)\n\nSeen above, except the initial position, the move generator is able to generate millions of legal\nmoves per second. When compared to the fastest legal move generator, Gigantua, there isn't much of a\ncompetition. In the highest depths, Gigantua is able to perform about 40x faster.\n\n### A note on the performance difference\n\nThis project is not intended to be a benchmark of the best move generator. As stated above, the\nprimary focus is to be minimal. The fact that we see an even remotely comparable speed is a very\ngood result.\n\nThere are some things that can be improved to close the gap between the two engines. These are\noutlined below:\n\n1. The most significant of these is the pseudo-legal move generation step. In this implementation, a\n   set of _pseudo-legal_ moves are generated for each position which are then pruned to only legal\n   moves. This means that when a move is initially generated, it may leave the king in check. This\n   adds a significant amount of time to the move generation process as many moves have to be\n   discarded. Most high-performance engines use a _legal-only_ move generation step which means that\n   the pruning step is not needed.\n2. The move generation does not use magic bitboards. A magic bitboard is a lookup technique that\n   allows for the quick generation of pseudo-legal sliding piece moves. As of right now, Hyperbola\n   Quintessence is used to calculate sliding piece moves. Replacing this with a lookup-based\n   approach would yield significant speed improvements.\n3. My implementation is written in Kotlin and is not capiable of using machine-level instructions\n   such as AVX or SSE. This means that the code lacks certain perforamnce optimizations that are\n   used throughout other move generation engines.\n\n## References and Useful Resources\n\n* https://www.chessprogramming.org/Main_Page\n* https://graphics.stanford.edu/~seander/bithacks.html\n* https://www.codeproject.com/Articles/5313417/Worlds-Fastest-Bitboard-Chess-Movegenerator\n* https://peterellisjones.com/posts/generating-legal-chess-moves-efficiently/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkylepls%2Fmini-chess","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkylepls%2Fmini-chess","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkylepls%2Fmini-chess/lists"}