{"id":36419691,"url":"https://github.com/lunalobos/chess4kt","last_synced_at":"2026-05-26T00:04:57.093Z","repository":{"id":331906192,"uuid":"1110246451","full_name":"lunalobos/chess4kt","owner":"lunalobos","description":"A kotlin multiplatform library for related chess operations","archived":false,"fork":false,"pushed_at":"2026-04-29T04:03:37.000Z","size":976,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-29T04:11:26.243Z","etag":null,"topics":["chess","kotlin","kotlin-library","kotlin-multiplatform","legal-moves","pgn-parser"],"latest_commit_sha":null,"homepage":"https://chess4kt.pages.dev/","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lunalobos.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-12-04T23:15:45.000Z","updated_at":"2026-04-29T04:03:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/lunalobos/chess4kt","commit_stats":null,"previous_names":["lunalobos/chess4kt"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/lunalobos/chess4kt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lunalobos%2Fchess4kt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lunalobos%2Fchess4kt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lunalobos%2Fchess4kt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lunalobos%2Fchess4kt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lunalobos","download_url":"https://codeload.github.com/lunalobos/chess4kt/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lunalobos%2Fchess4kt/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32482460,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["chess","kotlin","kotlin-library","kotlin-multiplatform","legal-moves","pgn-parser"],"created_at":"2026-01-11T17:05:15.397Z","updated_at":"2026-05-26T00:04:57.081Z","avatar_url":"https://github.com/lunalobos.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"![License](https://img.shields.io/github/license/lunalobos/chess4kt)\n[![Maven Central](https://img.shields.io/maven-central/v/io.github.lunalobos/chess4kt)](https://central.sonatype.com/artifact/io.github.lunalobos/chess4kt)\n[![KDoc](https://img.shields.io/badge/kdoc-1.0.0--beta.9j-a97bff)](https://chess4kt.pages.dev/)\n[![npm](https://img.shields.io/npm/v/chess4js?logo=npm)](https://www.npmjs.com/package/chess4js)\n\n# Chess4kt\n\n---\n\n## Overview\n\nThis library provides the necessary components to perform and represent **chess-related operations**, such as position management, legal move calculation, move and game representation, and PGN file parsing.\n\nThe types and functions are designed for **intuitive and fluid use**. The library is currently in beta version.\n\n***\n\n## Guide\n\nThe following is an overview of the library's most important types and functions. For more details, see the [documentation](https://chess4kt.pages.dev/).\n\n### Square\n\n[`Square`](https://chess4kt.pages.dev/core/io.github.lunalobos.chess4kt/-square/) class is an **enum that represents a square on the chessboard**. The **ordinal** property of each instance is highly relevant, as the library uses its integer ordinal value (Int) as the internal equivalent of a square. The standard index order is **file-major**, where A1 is 0, B1 is 1, up to H8 which is 63.\n\n```kotlin\nval a1 = Square.A1.ordinal // A1 -\u003e 0\nval b1 = Square.B1.ordinal // B1 -\u003e 1\n// and so on\nval h8 = Square.H8.ordinal // H8 -\u003e 63\n````\n\n### Piece\n\n[`Piece`](https://chess4kt.pages.dev/core/io.github.lunalobos.chess4kt/-piece/) class is an **enum that represents a chess piece** on the board. The **ordinal** property of each instance is relevant, since the library uses this ordinal value (Int) as the internal piece identifier. The order starts with `EMPTY` (0), followed by White pieces, then Black pieces:\n\n```kotlin\nval empty = Piece.EMPTY.ordinal // empty -\u003e 0\nval wp = Piece.WP.ordinal // white pawn -\u003e 1\nval wn = Piece.WN.ordinal // white knight -\u003e 2\nval wb = Piece.WB.ordinal // white bishop -\u003e 3\nval wr = Piece.WR.ordinal // white rook -\u003e 4\nval wq = Piece.WQ.ordinal // white queen -\u003e 5\nval wk = Piece.WK.ordinal // white king -\u003e 6\nval bp = Piece.BP.ordinal // black pawn -\u003e 7\nval bn = Piece.BN.ordinal // black knight -\u003e 8\nval bb = Piece.BB.ordinal // black bishop -\u003e 9\nval br = Piece.BR.ordinal // black rook -\u003e 10\nval bq = Piece.BQ.ordinal // black queen -\u003e 11\nval bk = Piece.BK.ordinal // black king -\u003e 12\n```\n\n### Position\n\nThe [`Position`](https://chess4kt.pages.dev/core/io.github.lunalobos.chess4kt/-position/) \nclass allows you to represent a specific chess position. It is **immutable**, which simplifies state management. To create a position, you can use the factory function [`positionOf`](https://chess4kt.pages.dev/core/io.github.lunalobos.chess4kt/position-of) with or without argument.\n\n```kotlin\nval startpos = positionOf() // without argument, the function returns the initial position\n\n// but if you provide a FEN string, the indicated position is generated\nval spanishOpening = positionOf(\"r1b2rk1/2q1bppp/p2p1n2/npp1p3/3PP3/2P2N1P/PPBN1PP1/R1BQR1K1 b - - 2 12\")\n```\n\n`Position` constructors are marked **internal** and are not accessible to external users. When a FEN string is provided, the factory function `positionOf` verifies that it is valid and results in a legal position.\n\n`Position` objects have several properties that allow you to work with the position internal state, such as **bitboards**, en passant status, and turn:\n\n```kotlin\nval position = positionOf() // startpos\nval bitboards = position.bitboards // an array of long integers; each element is a bitboard\nval whiteQueenBitboard = bitboards[Piece.WQ.ordinal - 1] // white queen's bitboard\nval enPassant = position.enPassant // the square (as an integer index) of the en passant target, or -1 if none exists\nval whiteMove = position.whiteMove // a boolean value: true if it's White's turn to move; false if it's Black's turn.\n```\n\nA very important feature of this class is the extension property **`children`**. It is a list of [tuples](https://chess4kt.pages.dev/core/io.github.lunalobos.chess4kt/-tuple/) (`Position`, `Move`) that encompasses all **legal moves** derived from the position.\n\n```kotlin\nval position = positionOf()\nval children = position.children // list of all legal moves and resulting positions from the starting position\n```\n\nIf a legal move is known, it can be executed from the position, and a new `Position` object derived from this move can be obtained.\n\n```kotlin\nval position = positionOf()\n// The move is provided in the modified long algebraic notation used in the UCI protocol\nval afterE4 = position.move(\"e2e4\") \n```\n\nThe move above was executed using the **UCI notation** (Universal Chess Interface, e.g., `e2e4`), which is the default for the `move` function. This notation only needs the origin and destination squares and the promotion piece, if any.\n\nHowever, you can also provide the move in **Standard Algebraic Notation (SAN)**. This is possible because the `move` extension function in `Position` has a second parameter, `notation` (of type [`Notation`](https://chess4kt.pages.dev/core/io.github.lunalobos.chess4kt/-notation/)), which can be set to `Notation.SAN`.\n\n```kotlin\nval afterE4 = position.move(\"e4\", Notation.SAN) // the move is provided in SAN notation\n```\n\nSee the [documentation](https://chess4kt.pages.dev/core/io.github.lunalobos.chess4kt/-position/) for more features of this class,\n\n### Move\n\nThe [`Move`](https://chess4kt.pages.dev/core/io.github.lunalobos.chess4kt/-move/) class represents **a single player's action**. It contains the origin, target, and promotion piece properties (all as integers) and is **immutable**. There are many ways to create an instance of this class using the factory function `moveOf`:\n\n```kotlin\nval move = moveOf(\"e2e4\") // Must be provided in UCI notation; SAN notation is not possible due to ambiguity\nval otherMove = moveOf(origin = Square.A1, target = Square.B2) // Integer indices or Square enums can be used\nval promotionMove = moveOf(Square.G2, Square.G1, Piece.BQ) // Promotion to Black Queen\n```\n\n### Game\n\nThe [`Game`](https://chess4kt.pages.dev/core/io.github.lunalobos.chess4kt/-game/) class is used to **represent entire games**, whether they are for serious competitive matches or for analysis (with variations, ratings, and comments).\n\n* **Game Mode:** `Game` instances have a game mode represented by the internal enum [`GameMode`](https://chess4kt.pages.dev/core/io.github.lunalobos.chess4kt/-game/-game-mode/), which has two values: `ANALYSIS` and `MATCH`.\n* **Structure:** Games are composed of **metadata** (PGN tags) and a series of connected **nodes** (of type [`Node`](https://chess4kt.pages.dev/core/io.github.lunalobos.chess4kt/-game/-node/)) that form a game tree.\n\nIn **Match** mode, these nodes form a **simple linked list** (variations are typically not allowed). In **Analysis** mode, the nodes form a **tree**, enabling the storage of Recursive Annotation Variations (RAVs).\n\n```mermaid\n\n---\nconfig:\n  theme: neo-dark\n  layout: dagre\n  look: classic\n---\nflowchart LR\nsubgraph subGraph0[\"Game Tree\"]\nB[\"MoveNode\n(1. e4 - First move)\"]\nA[\"Node\n(Start position of the game)\"]\nC@{ label: \"Node\\n        (1... c5 - Black's response)\" }\nD[\"Node\n(2. Nf3 - Next main move)\"]\nE[\"Node\n(2. d4 - Alternative move by White)\"]\nF@{ label: \"Node\\n        (2... cxd4 - Black's response in variation)\" }\nend\nA -- children 0: Main line --\u003e B\nB -- children 0: Main line --\u003e C\nC -- children 0: Main line --\u003e D\nC -- children 1: Variation --\u003e E\nE -- children 0: Main line of the variation --\u003e F\n\nC@{ shape: rect}\nF@{ shape: rect}\nstyle B fill:#424242,stroke:#333\nstyle A fill:#424242,stroke:#424242\nstyle C fill:#424242,stroke:#333\nstyle D fill:#424242,stroke:#333\nstyle E fill:#424242,stroke:#333\nstyle F fill:#424242,stroke:#333\n\n```\n\nThe `\u003cGame\u003e` instance contains the **root node**; all other nodes can be traced back to this root using the `parent` property. The root node does not have an associated `Move` object.\n\nLet's look at an example of using the `Game` class in **Match mode**:\n\n```kotlin\n// first create the game instance\nval game = strictMatch() // primary factory function for match games with strict autoconfiguration\n\n// set some metadata\nwith(game) {\n    tags[\"event\"] = \"chess4kt demo\" \n    tags[\"site\"] = \"github.com/lunalobos/chess4kt\"\n    tags[\"date\"] = \"2025.12.6\"\n    tags[\"round\"] = \"game demo\"\n    tags[\"white\"] = \"smart player\"\n    tags[\"black\"] = \"fool player\"\n}\n\n// then add some moves using the fluid API\ngame.root\n    .appendMove(\"e4\")\n    .appendMove(\"e5\")\n    .appendMove(\"Bc4\")\n    .appendMove(\"d6\")\n    .appendMove(\"Qh5\")\n    .appendMove(\"Nf6\")\n    .appendMove(\"Qxf7#\") // the fool's mate\n\ngame.result // Game.Result.WHITE_WIN\ngame.toString() // outputs the game in PGN format\n\n/*\n[Event \"chess4kt demo\"]\n[Site \"github.com/lunalobos/chess4kt\"]\n[Date \"2025.12.6\"]\n[Round \"game demo\"]\n[White \"smart player\"]\n[Black \"fool player\"]\n[Result \"1-0\"]\n[ECO \"C23\"]\n[Id \"null\"]\n\n1.e4 e5 2.Bc4 d6 3.Qh5 Nf6 4.Qxf7# 1-0\n*/\n```\n\nThis class has many configuration possibilities and features. For more information see the [documentation](https://chess4kt.pages.dev/core/io.github.lunalobos.chess4kt/-game/).\n\n### PGN parsing\nUsing the [`parseGames`](https://chess4kt.pages.dev/core/io.github.lunalobos.chess4kt/parse-games) function, you can convert a string containing an arbitrary quantity of games in PGN format into a list of Game objects.\n```kotlin\nval petrovDefense = \"\"\"\n        [Event \"Jerusalem Masters 2025\"]\n        [Site \"Jerusalem, Occupied Country\"]\n        [Date \"2025.12.01\"]\n        [Round \"5.5\"]\n        [White \"Nepomniachtchi,I\"]\n        [Black \"Gorshtein,Ido\"]\n        [Result \"1-0\"]\n        [WhiteElo \"2732\"]\n        [BlackElo \"2554\"]\n        [ECO \"C42\"]\n        [EventDate \"2025.11.30\"]\n        [WhiteTitle \"GM\"]\n        [BlackTitle \"GM\"]\n        [Opening \"Petrov\"]\n        [Variation \"classical attack\"]\n        [WhiteFideId \"4168119\"]\n        [BlackFideId \"2815532\"]\n\n        1.e4 e5 2.Nf3 Nf6 3.Nxe5 d6 4.Nf3 Nxe4 5.d4 d5 6.Bd3 Be7 7.O-O Nc6 8.Nbd2 \n        Nxd2 9.Qxd2 $5 \n            ( 9.Bxd2 Bg4 10.c3 O-O 11.h3 Bh5 )\n            ( 9.Bxd2 Bg4 10.c3 O-O 11.h3 Bh5 12.g3 Qd7 13.Kg2 Bg6 14.Bf4 Be4 15.\n            Re1 f5 16.Kh2 Bd6 17.Ne5 Qe7 18.Qd2 Bxe5 19.Bxe5 Nxe5 20.dxe5 Rad8 {\n            1/2-1/2(26) Petrov,N (2576) - Gorshtein,Ido (2537) / 25th European \n            Teams (3.12), Batumi GEO 2025} )\n        9...O-O 10.c3 Bg4 11.Ne1 Qd7 12.Qc2 h6 13.h3 Be6 14.f4 f5 15.Nf3 Bd6 16.\n        Bd2 a6 17.Rae1 Rae8 18.Nh4 Ne7 19.Re2 g6 20.g4 Kh8 $6 {Blacks blunder a \n        pawn.} \n            ( 20...Qc8 21.Rfe1 Bd7 22.gxf5 Nxf5 23.Nxg6 $16 \n                ( 23.Nxf5 Rxe2 24.Rxe2 Bxf5 25.Bxf5 Rxf5 26.Qd3 $17 )\n            23...Rxe2 24.Rxe2 Rf6 25.Rg2 Ng7 26.f5 )\n            ( 20...Rf6 $1 21.g5 hxg5 22.fxg5 Rff8 23.Bf4 c5 $10 )\n        21.Rfe1 Bf7 22.gxf5 Nxf5 23.Nxf5 Rxe2 24.Rxe2 gxf5 25.Bxf5 Rg8+ 26.Rg2 \n        Rxg2+ 27.Kxg2 Be6 28.Bxe6 Qxe6 29.Qd3 Kg7 30.f5 Qe4+ 31.Qxe4 dxe4 32.Kf2 \n        Kf6 33.Bxh6 Kxf5 34.h4 Be7 35.h5 Kg4 36.Ke3 Kxh5 37.Bf4 c6 38.Kxe4 Kg6 39.\n        Ke5 Kf7 40.Kf5 b5 41.Ke5 Ke8 42.Ke6 a5 43.Bd6 Bd8 44.Bb8 Be7 45.Ba7 Bg5 \n        46.Kd6 Bc1 47.b3 Bb2 48.c4 1-0\n        \"\"\".trimIndent()\n\nval games = parseGames(petrovDefense) // a list containing one game\nval nepomniachtchiVsGorshtein = games[0] // the specific game in question\n```\n\n### Game Serialization\n\nThe `Game` class is not directly serializable (to avoid adding the Kotlin serialization module as a dependency), but it exposes all the data needed to reconstruct a full game tree using only primitive values and plain objects.\n\n#### Intermediate Data Classes\n\nYou can define your own lightweight, serializable data classes outside the library to act as the bridge between a `Game` instance and any serialization format (JSON, binary, etc.):\n\n```kotlin\n// These classes belong to YOUR code, not to chess4kt\n\ndata class SerializableNode(\n    val id: Int,\n    val move: String?,          // result of move.toString() (UCI notation), null for root\n    val childrenIds: List\u003cInt\u003e,\n    val parentId: Int?          // null for root\n)\n\ndata class SerializableGame(\n    val initialFen: String,     // FEN of the root position\n    val nodes: List\u003cSerializableNode\u003e,\n    val tags: Map\u003cString, String\u003e,\n    val gameMode: String,                    // \"MATCH\" or \"ANALYSIS\"\n    val threeRepetitionsMode: String,        // \"IGNORE\", \"STRICT\" or \"AWARE\"\n    val fiftyMovesRuleMode: String,          // \"IGNORE\", \"STRICT\" or \"AWARE\"\n    val result: String?,                     // \"1-0\", \"0-1\", \"1/2-1/2\" or null\n    val finalComment: String?,\n    val finalEndLineComment: String?,\n    val threeRepetitions: Boolean,\n    val fiveRepetitions: Boolean,\n    val fiftyMoves: Boolean,\n    val seventyFiveMoves: Boolean\n)\n```\n\n#### Serialization Example\n\nThe following example shows how to walk the game tree and build a `SerializableGame` that can then be converted to JSON (here using `kotlinx.serialization` as an illustration, but any JSON library works):\n\n```kotlin\nfun Game.toSerializable(): SerializableGame {\n    // Collect all nodes with a BFS/DFS traversal\n    val allNodes = mutableListOf\u003cGame.Node\u003e()\n    fun collect(node: Game.Node) {\n        allNodes.add(node)\n        node.children.forEach { collect(it) }\n    }\n    collect(root)\n\n    val serializableNodes = allNodes.map { node -\u003e\n        SerializableNode(\n            id          = node.id,\n            move        = node.move?.toString(),   // UCI string, e.g. \"e2e4\" or \"a7a8q\"\n            childrenIds = node.children.map { it.id },\n            parentId    = node.parent?.id\n        )\n    }\n\n    return SerializableGame(\n        initialFen            = root.position.fen,\n        nodes                 = serializableNodes,\n        tags                  = tags.toMap(),\n        gameMode              = if (threeRepetitionsMode == Game.ThreeRepetitionsMode.IGNORE\n                                    \u0026\u0026 fiftyMovesRuleMode == Game.FiftyMovesRuleMode.IGNORE)\n                                    \"MATCH\" else \"ANALYSIS\",\n        threeRepetitionsMode  = threeRepetitionsMode.name,\n        fiftyMovesRuleMode    = fiftyMovesRuleMode.name,\n        result                = result?.str,\n        finalComment          = finalComment,\n        finalEndLineComment   = finalEndLineComment,\n        threeRepetitions      = threeRepetitions,\n        fiveRepetitions       = fiveRepetitions,\n        fiftyMoves            = fiftyMoves,\n        seventyFiveMoves      = seventyFiveMoves\n    )\n}\n\n// --- JSON serialization (using any JSON library, e.g. Gson or kotlinx.serialization) ---\n\n// With Gson:\n// val json: String = Gson().toJson(game.toSerializable())\n\n// With kotlinx.serialization (add @Serializable to your data classes):\n// val json: String = Json.encodeToString(game.toSerializable())\n```\n\n#### Deserialization Example\n\nTo reconstruct a `Game` from a `SerializableGame`, create a new `Game` instance from the stored FEN, then replay every stored node in order, using the parent–child relationships encoded in the node list:\n\n```kotlin\nfun SerializableGame.toGame(): Game {\n    // 1. Recreate the game with the original configuration\n    val game = gameOf(\n        tags                 = tags.toMutableMap(),\n        gameMode             = Game.GameMode.valueOf(gameMode),\n        threeRepetitionsMode = Game.ThreeRepetitionsMode.valueOf(threeRepetitionsMode),\n        fiftyMovesRuleMode   = Game.FiftyMovesRuleMode.valueOf(fiftyMovesRuleMode)\n    )\n\n    // 2. Build an id -\u003e Node index as we create nodes\n    val nodeIndex = mutableMapOf\u003cInt, Game.Node\u003e()\n    nodeIndex[nodes.first().id] = game.root   // root is already created\n\n    // 3. Replay nodes in order (they were stored breadth/depth-first, so parents come first)\n    for (serialNode in nodes.drop(1)) {       // skip root\n        val uciMove = serialNode.move ?: continue\n        val parentNode = nodeIndex[serialNode.parentId]\n            ?: error(\"Parent node ${serialNode.parentId} not found\")\n\n        // appendMove returns the newly created node\n        val createdNode = parentNode.appendMove(uciMove, notation = Notation.UCI)\n        nodeIndex[serialNode.id] = createdNode\n    }\n\n    // 4. Restore the remaining game metadata\n    game.finalComment          = finalComment\n    game.finalEndLineComment   = finalEndLineComment\n    game.threeRepetitions      = threeRepetitions\n    game.fiveRepetitions       = fiveRepetitions\n    game.fiftyMoves            = fiftyMoves\n    game.seventyFiveMoves      = seventyFiveMoves\n    result?.let { game.result  = Game.Result.entries.first { r -\u003e r.str == it } }\n\n    return game\n}\n```\n\n\u003e **Note:** Node comments (`comment`, `initialComment`, `endLineComment`, `suffixAnnotations`) and other per-node metadata are not shown above for brevity, but they can be added to `SerializableNode` and restored in the same way, since all those properties are publicly accessible on `Game.Node`.\n\n\u003e **ByteArray serialization:** If you need a binary representation instead of JSON, serialize your `SerializableGame` to a `ByteArray` using any binary encoder (e.g., Protocol Buffers, Avro, or Kotlin's `java.io.ObjectOutputStream` on the JVM). The approach is identical — the intermediate data classes act as the serialization boundary\n\n### Tournament Management (since 1.0.0-beta.8)\nThe library provides a suite of classes, interfaces, and factories for tournament management. The purpose of these \nutilities is to handle all logic related to tournament administration. Post-tournament database storage is not \nexplicitly handled, as the `Game` class (being exportable to PGN) provides all necessary features for this purpose.\n\n#### Score\nThe `Score` class is a way to store scores that are exact multiples of 0.5, avoiding the use of Java's `BigDecimal` \n(which is unavailable in KMP). It implements the `Comparable` interface, making it ideal for storing a player's primary \nscore as well as other tie-breaking metrics (Buchholz, Sonneborn-Berger, etc.).\n\nThis class features a straightforward factory:\n\n```kotlin\nval highScore = scoreOf(\"4.5\")\n\nval lowScore = scoreOf(\"1.0\")\n\nhighScore \u003e lowScore // true\n```\n\n#### Player\nThe `Player` class represents a participant in a tournament. It includes several properties beyond the obvious ones, \nsuch as `score` (the player's tournament points), `name` (which should be a unique identifier), `initialElo` (rating at \nthe start of the tournament), and `currentElo` (the player's live rating). Refer to the documentation to see \nall available properties.\n\n```kotlin\nval player = Player(\"name\", 1600).apply {\n    score = scoreOf(\"1.5\")\n}\n```\n\n#### EloCalculator\nThe `EloCalculator` class determines the new ratings for two players after a ranked match. It features three properties \nthat modify rating variations:\n\n* **impactFactor** (`Double`): The K-factor that determines how much a single match affects the rating. A higher value \nleads to faster rating changes. Default is `32.0`.\n\n* **rangeFactor** (`Double`): The scale factor used to determine win probability. Default is `400.0`.\n\n* **logisticBase** (`Double`): The base of the exponent in the logistic function. Default is `10.0`.\n\n```kotlin\nval defaultEloCalculator = EloCalculator()\n\nval customEloCalculator = EloCalculator(\n    impactFactor = 16.0,\n    rangeFactor = 300.0,\n    logisticBase = 2\n)\n```\n\n#### Match\nThe `Match` interface represents a game within a tournament. It contains the basic information for the encounter: the \nplayer playing White, the player playing Black, and the result. The result is an instance of the `Outcome` enum.\nThe most significant implementation of `Match` is `RatedMatch`, which can be instantiated as follows:\n\n```kotlin\nval white = Player(\"foo\", 1600)\nval black = Player(\"bar\", 1600)\nval eloCalculator = EloCalculator()\nval match: Match = RatedMatch(white, black, eloCalculator)\n```\n\nWhen the `outcome` of a `RatedMatch` instance is set, the `eloCalculator` is triggered to automatically determine the \nplayers new Elo ratings.\n\n```kotlin\nmatch.outcome = Outcome.DRAW // eloCalculator determinate new Elo ratings\n```\n\n#### Tiebreaker\nThe `Tiebreaker` interface is a contract for defining tie-breaking strategies. These strategies are common in Swiss \ntournaments, such as Buchholz, Sonneborn-Berger, Progressive, etc. The goal is to calculate a secondary score based \non a player's specific statistics to break ties between players with equal primary points.\n\nThey can be instantiated using the `tiebreakerOf` factory:\n```kotlin\nval player = Player(\"foo\", 1600)\n/* some tournament logic for this player*/\nval fidePerformance = tiebreakerOf(\"fidePerformance\")\nval buchholzScore = fidePerformance.getValue(player) // the FIDE Performance score for this player\n```\nEach `Tiebreaker` implementation includes a `Comparator\u003cPlayer\u003e` instance, allowing for player comparisons based on that \nspecific logic. These comparators sort players from highest to lowest score.\n\nThe `tiebreakerComparatorOf` factory function allows you to create a composite comparator that executes multiple \ntie-breaking criteria in a defined order. For example, to break ties first by FIDE Performance, then by \nSonneborn-Berger, and finally by Buchholz:\n\n```kotlin\nval customComparator = tiebreakerComparatorOf(\"fidePerformance\", \"sonnebornBerger\", \"buchholz\")\n```\n\n#### Tournament interface, ArenaTournament, SwissTournament\nFinally, the library features a `Tournament` interface. Implementations of this interface manage different tournament \nformats, handling pairings automatically. You can obtain implementations using the `tournament` factory function:\n\n```kotlin\nval swissTournament = tournament(type = \"swiss\")\n```\n\nThese instances create `Match` objects without requiring you to manually provide an `EloCalculator`. You can pass a \ncalculator directly to the factory, along with a custom player comparator:\n\n```kotlin\nval eloCalculator = EloCalculator(impactFactor = 16.0)\nval comparator = tiebreakerComparatorOf(\"fidePerformance\", \"sonnebornBerger\", \"buchholz\")\nval swissTournament = tournament(\n    type = \"swiss\", \n    eloCalculator =  eloCalculator,\n    comparator = comparator\n)\n```\n\nThe example above returns an instance of the `SwissTournament` class, which implements Swiss-system logic. There is also \nan `ArenaTournament` class for arena-style logic; to get an instance of this class, pass the \"arena\" literal as the type \nparameter.\n\nOnce the tournament is instantiated, you can add players using `addPlayer` and generate the first round with \n`nextRound`. Given a list of n players named players:\n\n```kotlin\nplayers.forEach { swissTournament.addPlayer(it) }\nval firstRound = swissTournament.nextRound() // List\u003cMatch\u003e\n```\n\nYou can then set the outcome for each match. In a Swiss tournament, once outcomes are set, you can request the next \nround:\n\n```kotlin\nval secondRound = swissTournament.nextRound()\n```\n\nThis continues until the tournament ends. `SwissTournament` includes a `numberOfRounds` property (Int), which is \nautomatically calculated at the start of the first round using a logarithmic formula to determine the efficient \nnumber of rounds needed to find a winner.\n\nYou can check if a tournament is over via the `completed` flag. In `SwissTournament`, this becomes true automatically \nafter the final round. In `ArenaTournament`, this flag must be set manually, as arena logic is more arbitrary and \nleaves the end-condition criteria to the developer's discretion. For more details, please visit the documentation.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flunalobos%2Fchess4kt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flunalobos%2Fchess4kt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flunalobos%2Fchess4kt/lists"}