{"id":30462684,"url":"https://github.com/tristan852/kite","last_synced_at":"2026-02-27T13:21:56.375Z","repository":{"id":305223704,"uuid":"1022208419","full_name":"tristan852/kite","owner":"tristan852","description":"🪁🎯 Kite - Java Connect Four solver","archived":false,"fork":false,"pushed_at":"2026-02-12T17:27:00.000Z","size":26800,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-13T01:05:03.481Z","etag":null,"topics":["ai","alpha-beta-pruning","bitboards","bot","connect-four","connect4","engine","gradle","heuristics","java","java-connect-four","kite","library","maven","maven-central","minimax","move-ordering","opening-book","solver","transposition-table"],"latest_commit_sha":null,"homepage":"https://tristan852.github.io/kite","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tristan852.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-18T16:31:10.000Z","updated_at":"2026-02-12T17:27:39.000Z","dependencies_parsed_at":"2025-10-26T10:03:41.793Z","dependency_job_id":"de772afa-55db-4de5-b07b-07eefafbc51b","html_url":"https://github.com/tristan852/kite","commit_stats":null,"previous_names":["tristan852/kite"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tristan852/kite","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tristan852%2Fkite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tristan852%2Fkite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tristan852%2Fkite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tristan852%2Fkite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tristan852","download_url":"https://codeload.github.com/tristan852/kite/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tristan852%2Fkite/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29508742,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-16T09:05:14.864Z","status":"ssl_error","status_checked_at":"2026-02-16T08:55:59.364Z","response_time":115,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["ai","alpha-beta-pruning","bitboards","bot","connect-four","connect4","engine","gradle","heuristics","java","java-connect-four","kite","library","maven","maven-central","minimax","move-ordering","opening-book","solver","transposition-table"],"created_at":"2025-08-23T23:02:04.297Z","updated_at":"2026-02-21T17:08:30.859Z","avatar_url":"https://github.com/tristan852.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cspan align=\"center\"\u003e\n    \u003cbr\u003e\n    \n[![The Kite logo](assets/images/brand/logo.svg)](assets/images/brand/logo.svg)\n    \n# Kite\n    \n[![Java version](https://img.shields.io/badge/Java-25+-blue?style=for-the-badge)](https://openjdk.org/projects/jdk/25/)\n[![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/tristan852/kite?style=for-the-badge)](https://github.com/tristan852/kite/tree/main/src)\n[![GitHub license](https://img.shields.io/github/license/tristan852/kite?style=for-the-badge)](https://github.com/tristan852/kite/blob/main/LICENSE)\n    \u003cbr\u003e\n[![Java documentation](https://javadoc.io/badge2/io.github.tristan852/kite/javadoc.svg?style=for-the-badge)](https://javadoc.io/doc/io.github.tristan852/kite)\n[![Test Status](https://img.shields.io/github/actions/workflow/status/tristan852/kite/teavm.yml?style=for-the-badge\u0026label=Tests)](https://github.com/tristan852/kite/actions/workflows/teavm.yml)\n[![Demo Status](https://img.shields.io/website?url=https://tristan852.github.io/kite\u0026style=for-the-badge\u0026label=Demo\u0026up_message=Online\u0026down_message=Offline)](https://tristan852.github.io/kite)\n    \n\u003c/span\u003e\n\nKite is a lightweight, high-performance Connect Four solver capable of solving any board position blazingly fast, even on modest hardware. It can be used to power AI bots with adjustable playing strength, from deliberately weak to perfectly optimal, making only provably best moves. Kite is well-suited for integration into GUI applications, backend systems, or for programmatic position analysis.\n\nInternally, Kite leverages **alpha-beta pruning**, **symmetry reduction**, **bitboards**, **position hashing** and **opening book lookups** to provide fast and accurate game tree evaluation.\n\n---\n\n## 🚀 Features\n\n* **Alpha-Beta Pruning**: Reduces search space by skipping suboptimal branches early.\n* **Symmetry Pruning**: Mirrored game states are considered equivalent and cached accordingly.\n* **Move Ordering**: Uses heuristics that favor center columns and winning threats.\n* **Bitboard Representation**: Game states use 64-bit integers for fast updates and operations.\n* **Transposition Caching**: Hashes each position and stores scores in an efficient score cache.\n* **Opening Book**: Stores lots of precomputed scores for early-game positions.\n* **Claim-even**: Applies the claim-even strategy to solve certain special positions in *O(1)* time.\n* *and much more...*\n\n---\n\n## 🕹️ Online Demo\n\nTry the solver directly in your browser:\n\n👉 **[Launch the Demo](https://tristan852.github.io/kite)**\n\nYou can set up and analyze positions, or play against AI opponents of varying strength, no installation required.\n\nThe demo runs natively in *WebAssembly* and is generally slower than the Java library, though still fast enough for typical use.\n\n---\n\n## 💻 Command-Line Interface\n\nKite is also available as a **command-line tool**. Native executables for Linux (Ubuntu), macOS, and Windows are published with each release on GitHub:\n\n👉 **[Download CLI Binaries](https://github.com/tristan852/kite/releases)**\n\nYou can use the CLI to analyze positions, run benchmarks, or play directly from your terminal, without needing to install Java.\n\nThe CLI tool runs completely natively, built with *GraalVM native images* for each platform.\n\nYou can also install via your platform's package manager:\n\n**macOS \u0026 Linux**\n\n```bash\nbrew tap tristan852/kite\nbrew install kite\n```\n\n**Windows**\n\n```powershell\nscoop bucket add kite https://github.com/tristan852/kite-scoop\nscoop install kite\n```\n\n\u003e **⚠️ Note:** Ensure your terminal is set to UTF-8, or run the program with `kite --plain` for highly compatible output.\n\n**Guide**\n\nUse `kite` to start the interactive CLI tool.\n\nThe following arguments can be passed to `kite`:\n\n| Argument                 | Description                                                                                                                                 |\n|--------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|\n| `--help`                 | Display a help message                                                                                                                      |\n| `--skill-levels`         | List all available skill levels                                                                                                             |\n| `--version`              | Display the solver version                                                                                                                  |\n| `--quiet`                | Suppress interactive messages. Quiet mode is automatically enabled if the CLI is not connected to a terminal or when running a script file. |\n| `--verbose`              | Override quiet mode and show interactive messages even when quiet mode would normally be active                                             |\n| `--plain`                | Disable all prompts, Unicode characters and ANSI escape codes, including colors and screen clearing.                                        |\n| `--script \u003cscript-file\u003e` | Run the specified script file instead of starting interactive mode                                                                          |\n| `--game [skill-level]`   | Start a game immediately, optionally specifying a skill level                                                                               |\n\n---\n\n## 📊 Empty-Board Benchmark\n\n\u003e **Note:** This benchmark is no longer relevant for the development of this solver. Instead, the [benchmark created by Pascal Pons](#-pascal-pons-benchmark) is now used.\n\nThe empty Connect Four board is considered the most challenging position to solve, as it represents the root of the entire game tree. Successfully evaluating this state is a significant achievement and serves as an excellent benchmark for testing the performance of a Connect Four solver.\n\nTypically, an **opening book** is used to store precomputed evaluations of early-game positions, including the empty board, allowing such evaluations to be retrieved instantly via a simple table lookup. However, to properly assess the solver's raw computational strength, the opening book was **turned off**, and the **transposition table was cleared** before evaluating the empty board.\n\nTwo hardware configurations were used to run this benchmark, representing different levels of processing power:\n\n* **Setup 1**: Modern laptop with an *Intel i7-1165G7* processor (1.8 Mnodes/s per thread)\n* **Setup 2**: Modern desktop PC with an *Intel i9-11900KF* processor (6.9 Mnodes/s per thread)\n\nThe benchmark results are as follows:\n\n| Kite version | Node evaluations | Compute time (Setup 1)     | Compute time (Setup 2) |\n|--------------|------------------|----------------------------|------------------------|\n| 1.8.1        | `233,863,140`    | *2 minutes and 9 seconds*  | *34 seconds*           |\n| 1.8.0        | `264,328,020`    | *2 minutes and 26 seconds* | *39 seconds*           |\n| 1.7.9        | `282,023,140`    | *2 minutes and 35 seconds* | *41 seconds*           |\n| 1.7.8        | `298,565,585`    | *2 minutes and 45 seconds* | *44 seconds*           |\n| 1.7.7        | `312,998,949`    | *2 minutes and 53 seconds* | *47 seconds*           |\n\n**Note:** \"Node evaluations\" refers to the number of times the *negamax* function (see [here](https://github.com/tristan852/kite/blob/d35a0d06e755cb4e5bb3fa0dd3eae5bfc6a924fc/src/main/java/net/kite/board/Board.java#L526)) was invoked to evaluate different game states.\nAdditionally, all versions since `1.7.7` that did not affect the results of this benchmark have been omitted from the table.\n\nSome internal constants, such as the transposition table size and the minimum depth threshold for enhanced transposition table lookups, were tuned specifically for the task of evaluating the empty board. These settings differ from those optimized for use with an opening book.\n\nAlso note that Kite is a lightweight Java solver library designed to support running multiple solvers in parallel. However, each individual solver evaluates boards using a single thread only. As a result, compute times reflect single-threaded performance per solver.\n\n---\n\n## 📊 Pascal Pons Benchmark\n\nRather than evaluating only the empty board, Kite is evaluated using a [benchmark created by Pascal Pons](http://blog.gamesolver.org/solving-connect-four/02-test-protocol/), which contains 6,000 positions from different stages of the game.\n\nKite is compared against the following modern and historical solvers:\n\n* [John Tromp's Fhourstones solver](https://tromp.github.io/c4/fhour.html) (1996–2008), written in C\n* [Pascal Pons' Connect Four solver](http://blog.gamesolver.org/) (2016–2019), written in C++\n* [Ben Rall's Connect Four solver](https://github.com/benjaminrall/connect-four-ai) (2025), written in Rust\n\nFor this benchmark, neither the Fhourstones solver nor Pascal Pons' solver uses an opening book.\nAdditionally, the Fhourstones solver evaluates each benchmark position only *weakly*: it determines which player is winning, but does not compute the exact score of the position.\n\n| Position set       | Solver      | Average evaluation time | Average node evaluations | Node throughput (in Mnodes/s) |\n|--------------------|-------------|-------------------------|--------------------------|-------------------------------|\n| **endgame-easy**   | Fhourstones | 4.27 µs                 | 39.80                    | 9.33                          |\n|                    | Pascal Pons | 4.57 µs                 | 51.28                    | 11.23                         |\n|                    | Ben Rall    | 3.32 µs                 | 51                       | 14.80                         |\n|                    | **Kite**    | **1.90 µs**             | **33.81**                | **17.84**                     |\n| **midgame-easy**   | Fhourstones | 137 µs                  | 2,101                    | 15.30                         |\n|                    | Pascal Pons | 37.45 µs                | 449.60                   | 12                            |\n|                    | Ben Rall    | 32.20 µs                | 449                      | 13.95                         |\n|                    | **Kite**    | **22.22 µs**            | **379.49**               | **17.08**                     |\n| **midgame-medium** | Fhourstones | 1.70 ms                 | 28,725                   | 16.94                         |\n|                    | Pascal Pons | 3.21 ms                 | 39,900                   | 12.42                         |\n|                    | Ben Rall    | 2.87 ms                 | 39,855                   | 13.89                         |\n|                    | **Kite**    | **961.50 µs**           | **20,137.98**            | **20.94**                     |\n| **opening-easy**   | Fhourstones | 150 ms                  | 2,456,184                | 16.33                         |\n|                    | Pascal Pons | 254.60 µs               | 3,298                    | 12.95                         |\n|                    | Ben Rall    | 42 µs                   | 619                      | 14.71                         |\n|                    | **Kite**    | **47.56 µs**            | **953.61**               | **20.05**                     |\n| **opening-medium** | Fhourstones | 80.60 ms                | 1,296,896                | 16.09                         |\n|                    | Pascal Pons | 96.63 ms                | 1,201,000                | 12.43                         |\n|                    | Ben Rall    | 7.44 ms                 | 95,156                   | 12.79                         |\n|                    | **Kite**    | **715.97 µs**           | **14,677.80**            | **20.50**                     |\n| **opening-hard**   | Fhourstones | 5.58 s                  | 93,425,554               | 16.74                         |\n|                    | Pascal Pons | 5.49 s                  | 65,920,000               | 12.01                         |\n|                    | Ben Rall    | 1.40 ms                 | 17,631                   | 12.62                         |\n|                    | **Kite**    | **22.05 µs**            | **456.86**               | **20.72**                     |\n\nAs of early 2026, Kite appears to be both the most efficient Connect Four solver, measured by the number of node evaluations required per position, and the fastest overall.\n\nDespite being written in Java, Kite outperforms the C++ and Rust solvers included in this comparison.\n\nYou can run the benchmark on your own machine, for example to test whether a change you made improves the solver, using the code snippet below. Keep in mind that results may vary depending on the hardware used.\n\n```java\nimport net.kite.api.Kite;\n\npublic class Main {\n\t\n\tpublic static void main(String[] programArguments) {\n\t\tKite.runBenchmark();\n\t}\n\t\n}\n```\n\n---\n\n## 📦 Installation\n\nKite is available via **Maven Central** and can be easily added to any **Gradle** or **Maven** project.\n\n### Gradle (Kotlin DSL)\n\nAdd the following code snippet to your `build.gradle.kts` file:\n\n```kotlin\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation(\"io.github.tristan852:kite:1.17.1\")\n}\n```\n\n### Gradle (Groovy DSL)\n\nAdd the following code snippet to your `build.gradle` file:\n\n```groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation 'io.github.tristan852:kite:1.17.1'\n}\n```\n\n### Maven\n\nAdd the following code snippet to your `pom.xml` file:\n\n```xml\n\u003crepositories\u003e\n    \u003crepository\u003e\n        \u003cid\u003ecentral\u003c/id\u003e\n        \u003curl\u003ehttps://repo.maven.apache.org/maven2\u003c/url\u003e\n    \u003c/repository\u003e\n\u003c/repositories\u003e\n\n\u003cdependencies\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003eio.github.tristan852\u003c/groupId\u003e\n        \u003cartifactId\u003ekite\u003c/artifactId\u003e\n        \u003cversion\u003e1.17.1\u003c/version\u003e\n    \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\nIf your project uses the **Java Module System (JPMS)**, regardless of whether you build with Gradle or Maven, also add the following to your `module-info.java`:\n\n```java\nmodule your.module.name {\n    requires kite.main;\n}\n```\n\n---\n\n## 🔧 Development Setup\n\nTo start developing **Kite** locally, ensure the following requirements are installed:\n\n* **JDK 25+** (*GraalVM* is recommended and required for building the CLI tool)\n* Latest version of **Gradle**\n* An **IDE** (*IntelliJ IDEA* is highly recommended)\n\n\u003e **Note:** The JDK can be installed directly through *IntelliJ IDEA*.\n\nClone the repository:\n\n```bash\ngit clone https://github.com/tristan852/kite.git\ncd kite\n```\n\nOpen the project as a **Gradle project** in your IDE and wait for the Gradle import/sync to complete.\n\nYou can then build, run, and test the project using Gradle. Feel free to improve the solver or extend the project with new features.\n\n### Native Binary Build Setup\n\nNative images of the CLI tool can be built with `./gradlew clean nativeCompile`.\nIn IntelliJ IDEA, you can also run this Gradle task directly by creating a **run configuration**.\n\nInstall the required system toolchain for your OS before building native images of the CLI tool.\n\n#### 🪟 Windows\n\nInstall **[Visual Studio Build Tools](https://visualstudio.microsoft.com/downloads/)**.\n\nDuring setup select:\n\n* **Desktop development with C++**\n* MSVC (latest)\n* Windows 10/11 SDK\n\nEnsure MSVC is installed and on your *PATH*.\n\n#### 🐧 Linux (Ubuntu/Debian)\n\n```bash\nsudo apt update\nsudo apt install build-essential zlib1g-dev\n```\n\nThis installs GCC, development headers, and linker tools.\n\n#### 🍎 macOS\n\nInstall Xcode Command Line Tools:\n\n```bash\nxcode-select --install\n```\n\nProvides Clang and macOS SDK headers. Full Xcode is not required.\n\n---\n\n## 🚀 Getting Started\n\nThe Kite solver can be used by obtaining a newly created solver instance.\nNote that each solver instance cannot be used by multiple threads in parallel.\nIf your project involves only a single game (even with two bots playing in it), a single solver instance is sufficient. However, if you're running multiple games in parallel, each game will need its own solver instance to avoid delays caused by mutual exclusion. In that case, the best approach is to recycle solver instances when possible and create new ones as needed. A single Connect Four game should use only one solver instance, as each maintains its own transposition table. Additionally, a solver instance should not alternate between different games, as this can pollute the table with irrelevant entries and negatively impact performance.\n\nThe first time a Kite solver instance is obtained, a warm-up and additional initialization is done, which may take a bit of time.\n\nThe following code snippet demonstrates how the Kite solver should ideally be used:\n\n```java\n// obtain access to a new Kite solver instance\nKite solver = Kite.createInstance();\n\n// Newly created solver instances will\n// have the empty board state set up.\n// Playing new moves will therefore add\n// them in sequence to the empty board.\n\n// red plays in the 4th column\n// and yellow plays in the 6th column\nsolver.playMoves(4, 6);\n\n// it is now red's turn, and they are going\n// to win with their second to last stone\nSystem.out.println(solver.evaluateBoard()); // = 2\n\n// red plays in the 5th column\nsolver.playMove(5);\n\n// it is now yellow's turn\n\n// if yellow plays in the 6th column they\n// are going to win with their last stone\nSystem.out.println(solver.evaluateMove(6)); // = 1\n\n// print a string representation\n// of the current game state\n// as well as analysis information\n\n// +---+---+---+---+---+---+---+\n// |   |   |   |   |   |   |   |\n// +---+---+---+---+---+---+---+\n// |   |   |   |   |   |   |   |\n// +---+---+---+---+---+---+---+\n// |   |   |   |   |   |   |   |\n// +---+---+---+---+---+---+---+\n// |   |   |   |   |   |   |   |\n// +---+---+---+---+---+---+---+\n// |   |   |   |   |   |   |   |\n// +---+---+---+---+---+---+---+\n// |   |   |   | X | X | O |   |\n// +---+---+---+---+---+---+---+\n// | 0 | 1 | 2 | 3 | 4 | 5 | 6 |\n// +---+---+---+---+---+---+---+\n//  -3   0  -2  +2   0  +1  -2 \n// \n// moves: 465\n// moves left (optimal play): 37\n// outcome: undecided\nSystem.out.println(solver.boardAnalysisString());\n\n// clear the board (i.e. go back to\n// the starting game state)\nsolver.clearBoard();\n```\n\nPlease keep in mind that Java classes are being loaded lazily.\n\n```java\n// a class that is not used during program startup\nclass A {\n\t\n\tprivate static final Kite SOLVER = Kite.createInstance();\n\t\n}\n```\n\nIn the above setup, if class `A` is not loaded at program startup, but rather at some later point, the solver instance creation and initialization will also not happen at startup, but rather when you first use class `A`, which might introduce an unwanted delay before your first use of the solver instance.\n\nIn the following class, the method `onProgramStartup` is assumed to be called when your program is booting up. The method obtains a reference to a new solver instance, which ensures that the solver is already initialized and ready to go after your program has started.\n\n```java\nclass B {\n\t\n\tprivate static Kite solver;\n\t\n\t// a method that is called during program startup\n\tpublic void onProgramStartup() {\n\t\tsolver = Kite.createInstance();\n\t}\n\t\n}\n```\n\n---\n\n## 🧪 Try It Out\nWant to quickly try out and experiment with the Kite solver? Here's a simple demo class that pits you against the solver using a fixed skill level:\n\n```java\nimport net.kite.api.Kite;\nimport net.kite.api.board.outcome.BoardOutcome;\nimport net.kite.api.skill.level.SkillLevel;\n\nimport java.util.Random;\nimport java.util.Scanner;\nimport java.util.concurrent.ThreadLocalRandom;\n\npublic class Main {\n\t\n\t// opponent of advanced skill level\n\tprivate static final SkillLevel OPPONENT_SKILL_LEVEL = SkillLevel.ADVANCED;\n\t\n\tpublic static void main(String[] programArguments) {\n\t\t// initialize a new solver instance and a scanner\n\t\tKite solver = Kite.createInstance();\n\t\tScanner scanner = new Scanner(System.in);\n\t\t\n\t\t// randomly choose who goes first\n\t\tRandom random = ThreadLocalRandom.current();\n\t\tif(random.nextBoolean()) solver.playMove(solver.skilledMove(OPPONENT_SKILL_LEVEL));\n\t\t\n\t\tSystem.out.println(solver.boardString());\n\t\t\n\t\twhile(true) {\n\t\t\t\n\t\t\tSystem.out.println(\"Enter your move\");\n\t\t\tint x = scanner.nextInt();\n\t\t\t\n\t\t\tif(!solver.moveLegal(x)) {\n\t\t\t\t\n\t\t\t\tSystem.err.println(\"Illegal move!\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tsolver.playMove(x);\n\t\t\t\n\t\t\tif(solver.gameOver()) {\n\t\t\t\t\n\t\t\t\tSystem.out.println(solver.boardString());\n\t\t\t\tSystem.out.println(solver.gameOutcome() == BoardOutcome.DRAW ? \"It's a draw!\" : \"You win!\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tsolver.playMove(solver.skilledMove(OPPONENT_SKILL_LEVEL));\n\t\t\tSystem.out.println(solver.boardString());\n\t\t\t\n\t\t\tif(solver.gameOver()) {\n\t\t\t\t\n\t\t\t\tSystem.out.println(solver.gameOutcome() == BoardOutcome.DRAW ? \"It's a draw!\" : \"You lose.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\t\n}\n```\n\n---\n\n## 🧠 Evaluation Scale\n\nKite uses the following score metric to represent the value of a board or a move under perfect play:\n\n| Evaluation score | Interpretation                                                                                                                                               |\n|------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `0`              | The position is a guaranteed draw if both players play perfectly.                                                                                            |\n| `n \u003e 0`          | The current player will win, assuming perfect play, by placing their `n`th-to-last stone, the fastest possible win against perfect defense in this position. |\n| `n \u003c 0`          | The opponent will win, assuming perfect play, by placing their `-n`th-to-last stone, the fastest possible win against perfect defense in this position.      |\n\n**Examples:**\n\n* A score of `1` means the player to move can win, but only with their final stone.\n* A score of `-2` means the opponent will win, and they will still have one stone remaining after the win.\n\n---\n\n## ⚔️ Skill levels\n\nThe Kite solver is capable of not only playing perfectly but also generating moves at fixed skill levels.\nIts API supports thirteen distinct skill levels, ranging from `SkillLevel.RANDOM` up to `SkillLevel.PERFECT`, including intermediate levels like `SkillLevel.BEGINNER`, `SkillLevel.NOVICE`, ..., and `SkillLevel.SUPER_GRANDMASTER`.\n\nThese skill levels are ordered by increasing playing strength, with each level designed to be stronger than the previous one.\nA special skill level, `SkillLevel.ADAPTIVE`, adjusts move selection to match the opponent's playing strength.\nYou can use all available skill levels or choose a subset that fits your project.\n\nThe `SkillLevel.SUPER_GRANDMASTER` skill level always plays perfectly and is therefore equivalent to `SkillLevel.PERFECT`.\n`SkillLevel.BEGINNER` plays slightly better than the random bot.\nThe Elo ratings of all the different skill levels are given in the table below:\n\n| Skill level       | Elo rating estimate |\n|-------------------|---------------------|\n| Random            | 1120                |\n| Beginner          | 1200                |\n| Novice            | 1400                |\n| Amateur           | 1600                |\n| Intermediate      | 1800                |\n| Skilled           | 2000                |\n| Advanced          | 2200                |\n| Expert            | 2400                |\n| Master            | 2600                |\n| Grandmaster       | 2800                |\n| Super Grandmaster | 3000                |\n| Perfect           | 3000                |\n\nAn Elo rating difference of approximately *400* corresponds to a *91%* win rate for the higher-rated player. A difference in Elo of *200* corresponds to a *76%* win probability.\n\nFor reference, the Elo ratings have been normalized so that the `SkillLevel.PERFECT` bot has a rating of *3000*.\nSince `SkillLevel.SUPER_GRANDMASTER` and `SkillLevel.PERFECT` represent the same level of play, they share the same rating estimate.\n\nIf you want to translate these Elo ratings to your own scale, or vice versa, try to identify a reference point by comparing one of these skill levels to a skill level in your system with a known Elo rating.\n\n---\n\n## 🔗 References\n\nThe following resources were instrumental in shaping the design and implementation of this solver:\n\n1. **[Pascal Pons' Connect Four solver](http://blog.gamesolver.org/)** – A detailed breakdown of the core architecture behind an efficient alpha-beta solver for Connect Four.\n2. **[Chris Steininger's Connect Four solver](https://github.com/ChristopheSteininger/c4)** – Offers additional optimizations, tips, and implementation insights for Connect Four solvers.\n\n---\n\n## ⚖️ License\n\nKite is licensed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.html).\n\nYou are free to use, modify, and distribute this software under the terms of the GPL-3.0. However, if you distribute a modified version or derivative work, it must also be licensed under the GPL-3.0 and include the source code.\n\nFor full terms, see the [LICENSE](./LICENSE) file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftristan852%2Fkite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftristan852%2Fkite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftristan852%2Fkite/lists"}