From dc952cdd4026ca92f972915a282ad3d31a4f765e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Pedro=20Neto?= <886455+jpneto@users.noreply.github.com> Date: Tue, 7 Jul 2026 22:17:36 +0100 Subject: [PATCH] General line size of Linage --- locales/en/apgames.json | 14 +++++++++++++- src/games/linage.ts | 40 ++++++++++++++++++++++++++++++++++------ 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/locales/en/apgames.json b/locales/en/apgames.json index cbcbdaef..97e806ea 100644 --- a/locales/en/apgames.json +++ b/locales/en/apgames.json @@ -308,7 +308,7 @@ "jacynth": "More information on the Decktet system can be found on the [official Decktet website](https://www.decktet.com). Cards in players' hands are hidden from observers and opponents.", "konane": "Several competing opening protocols exist, but the most common ruleset is the Naihe Ruleset, used by tournaments at the Bishop Museum in Hawaii and described in the BGG reference. This is what is implemented here.", "lasca": "Maneuverability is a measure of how close your pieces are to promoting. Your maximum maneuverability is the board size times the number of stacks you control.\n\nMaterial is calculated by giving you a point for every friendly piece in a stack you control, plus an extra point if you have an officer on top of that stack.", - "linage" : "Linage is a 2016 game designed by Luis Bolaños Mures. The game is based on the following definitions: A **region** is a maximal set of orthogonally contiguous empty points. A **line** is a set of three orthogonally contiguous empty points on the same row or column; and a region is **owned** by Vertical if it contains no horizontal lines, and by Horizontal if it contains no vertical lines. A region is free if it is owned by neither player. Regions must always have at least one line.\n\nOn his turn, the player passes or places a stone in a free region. The game ends when both players pass in succession, and wins the one with highest score (i.e., the number of points in the regions the player owns, plus komi).", + "linage" : "Linage is a 2016 game designed by Luis Bolaños Mures. The game is based on the following definitions: A **region** is a maximal set of orthogonally contiguous empty points. A **line** is a set of N orthogonally contiguous empty points on the same row or column (default is N=3); and a region is **owned** by Vertical if it contains no horizontal lines, and by Horizontal if it contains no vertical lines. A region is free if it is owned by neither player. Regions must always have at least one line.\n\nOn his turn, the player passes or places a stone in a free region. The game ends when both players pass in succession, and wins the one with highest score (i.e., the number of points in the regions the player owns, plus komi).", "loa": "In the centre of the 9x9 board is a \"black hole.\". Landing on the black hole means the piece is removed from the game. Simultaneous connections are scored as a draw.", "magnate": "The terminology of some Magnate actions has been altered for clarity and brevity. Completely developing a new property is called the \"Buy\" action; purchasing a deed for a new property is called \"Deed\", developing deeds (that is, adding tokens to a deeded property, whether it results in the deed becoming fully developed or not) is called \"Add\". (Selling a card and trading suit tokens 3 for 1 are unchanged.)\n\nIn order to speed up the process of rolling for resources, there are two additional actions:\n* \"Prefer\" is for setting your preference of which suit token to take when a deed pays out on your opponent's roll. If you do not set an explicit preference, the code will choose the rarer token for you based on your non-crown suits and current supply of tokens. The currently preferred token is circled in the UI, but your personal preference is never visible to the other player.\n* \"Choose\" is a mandatory first action for collecting suit tokens when a deed pays out on your own roll. (In all cases where you need to choose a suit token that is not already among your tokens, you still click on the appropriate token pile.)\n\nBecause you can perform several actions during a ply in any order, there is also an \"Undo\" action to back out your most recent action, whether or not it was complete.\n\nNote that only the final resource die result is displayed, but the distribution of expected outcomes is still that of rolling 2d10 and taking the higher value. Taxation happens when the lower of 2d10 comes up 1; a suit die is rolled (or two, in the double taxation variant), and the suits will be displayed underneath the resource result. The roll is logged at the end of a player's turn, and is attributed to the next player (who would have rolled in the physical game). Except for a \"Choose\", no user action is required; resources are added or removed automatically by the server in between turns.\n\nWhen a player is ahead in a district, the Pawn or Excuse for that district is outlined in that player's color. The first tiebreaker score (total property value) is displayed in parentheses after the district score. The second tiebreaker is total number of tokens remaining.", "mchess": "If there have been seven consecutive turns without a capture, someone can \"call the clock\" by adding an asterisk (*) to the end of their move. This can only be done by selecting the move from the drop-down list. After another seven turns with no capture, the game will end and be scored.", @@ -1877,6 +1877,18 @@ }, "size-19": { "name": "19x19 board" + }, + "line-2": { + "name": "Lines of 2" + }, + "#line": { + "name": "Lines of 3" + }, + "line-4": { + "name": "Lines of 4" + }, + "line-5": { + "name": "Lines of 5" } }, "loa": { diff --git a/src/games/linage.ts b/src/games/linage.ts index 80fa9427..bd293010 100644 --- a/src/games/linage.ts +++ b/src/games/linage.ts @@ -82,6 +82,10 @@ export class LinageGame extends GameBase { { uid: "#board", }, // 15x15 { uid: "size-17", group: "board" }, { uid: "size-19", group: "board" }, + { uid: "line-2", group: "line" }, + { uid: "#line", group: "line" }, + { uid: "line-4", group: "line" }, + { uid: "line-5", group: "line" }, ], flags: ["custom-buttons", "custom-colours", "no-moves", "scores"] }; @@ -100,6 +104,7 @@ export class LinageGame extends GameBase { public winner: playerid[] = []; public variants: string[] = []; public boardSize = 13; + public lineSize = 3; public stack!: Array; public results: Array = []; public komi?: number; @@ -150,6 +155,7 @@ export class LinageGame extends GameBase { this.board = new Map(state.board); this.lastmove = state.lastmove; this.boardSize = this.getBoardSize(); + this.lineSize = this.getLineSize(); this.komi = state.komi; this.buttontaker = state.buttontaker; this.swapped = false; @@ -177,6 +183,21 @@ export class LinageGame extends GameBase { return 15; } + private getLineSize(): number { + // Get line size from variants. + if ( (this.variants !== undefined) && (this.variants.length > 0) && (this.variants[0] !== undefined) && (this.variants[0].length > 0) ) { + const sizeVariants = this.variants.filter(v => v.includes("line")); + if (sizeVariants.length > 0) { + const size = sizeVariants[0].match(/\d+/); + return parseInt(size![0], 10); + } + if (isNaN(this.lineSize)) { + throw new Error(`Could not determine the line size from variant "${this.variants[0]}"`); + } + } + return 3; + } + public isKomiTurn(): boolean { return this.stack.length === 1; } @@ -200,14 +221,21 @@ export class LinageGame extends GameBase { } for (const cell of area) { const [x,y] = this.algebraic2coords(cell); - // check right for 3 in-a-row - if (cells.has(`${x+1},${y}`) && cells.has(`${x+2},${y}`) ) { - hLines += 1; + let addLine = true; + for (let i=1; i