From d11d992fe73536873f6741c1b931870402646db0 Mon Sep 17 00:00:00 2001 From: Christopher Field Date: Sun, 8 Mar 2026 11:35:46 -0400 Subject: [PATCH 1/4] Updating Konane to support 11x8 and 15x12 --- src/games/konane.ts | 117 ++++++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 43 deletions(-) diff --git a/src/games/konane.ts b/src/games/konane.ts index 5dd1e8f3..4a3e3df2 100644 --- a/src/games/konane.ts +++ b/src/games/konane.ts @@ -27,7 +27,7 @@ export class KonaneGame extends GameBase { name: "Konane", uid: "konane", playercounts: [2], - version: "20241029", + version: "20260308", dateAdded: "2024-11-01", // i18next.t("apgames:descriptions.konane") description: "apgames:descriptions.konane", @@ -48,10 +48,19 @@ export class KonaneGame extends GameBase { { uid: "size-8", group: "board" + }, + { + uid: "size-11", + group: "board" + }, + { + uid: "size-15", + group: "board" } ] }; + public version = parseInt(KonaneGame.gameinfo.version, 10); public numplayers = 2; public currplayer: PlayerId = 1; public board!: Map; @@ -61,7 +70,8 @@ export class KonaneGame extends GameBase { public variants: string[] = []; public stack!: Array; public results: Array = []; - private boardSize = 0; + private boardWidth = 0; + private boardHeight = 0; private _points: [number, number][] = []; private _highlight: string | undefined; @@ -71,12 +81,15 @@ export class KonaneGame extends GameBase { if (variants !== undefined) { this.variants = [...variants]; } - this.boardSize = this.getBoardSize(); + this.setBoardSize(); const board: Map = new Map(); let color = 2 as PlayerId; - for (let x = 0; x < this.boardSize; x++) { - for (let y = 0; y < this.boardSize; y++) { - board.set(GameBase.coords2algebraic(x, y, this.boardSize), color); + for (let x = 0; x < this.boardWidth; x++) { + for (let y = 0; y < this.boardHeight; y++) { + if ((this.boardWidth !== 11 || (y !== 3 && y !== 4) || x !== 5) && + (this.boardWidth !== 15 || (y !== 5 && y !== 6) || x !== 7)) { + board.set(GameBase.coords2algebraic(x, y, this.boardHeight), color); + } color = (color === 1) ? 2 : 1; } color = (color === 1) ? 2 : 1; @@ -102,7 +115,7 @@ export class KonaneGame extends GameBase { this.variants = state.variants; this.stack = [...state.stack]; } - this.boardSize = this.getBoardSize(); + this.setBoardSize(); this.load(); this.buildGraph(); } @@ -116,8 +129,8 @@ export class KonaneGame extends GameBase { } const state = this.stack[idx]; + this.version = parseInt(state._version, 10); this.currplayer = state.currplayer; - this.board = deepclone(state.board) as Map; this.lastmove = state.lastmove; this.results = [...state._results]; @@ -125,15 +138,15 @@ export class KonaneGame extends GameBase { } private buildGraph(): SquareOrthGraph { - this.graph = new SquareOrthGraph(this.boardSize, this.boardSize); + this.graph = new SquareOrthGraph(this.boardWidth, this.boardHeight); return this.graph; } - private getGraph(boardSize?: number): SquareOrthGraph { - if (boardSize === undefined) { + private getGraph(boardWidth?: number, boardHeight?: number): SquareOrthGraph { + if (boardWidth === undefined || boardHeight === undefined) { return (this.graph === undefined) ? this.buildGraph() : this.graph; } else { - return new SquareOrthGraph(boardSize, boardSize); + return new SquareOrthGraph(boardWidth, boardHeight); } } @@ -151,19 +164,31 @@ export class KonaneGame extends GameBase { } } - private getBoardSize(): number { + private setBoardSize(): KonaneGame { + this.boardWidth = 6; + this.boardHeight = 6; // Get board 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("size")) + const sizeVariants = this.variants.filter(v => v.includes("size")); if (sizeVariants.length > 0) { - const size = sizeVariants[0].match(/\d+/); - return parseInt(size![0], 10); + const sizeString = sizeVariants[0].match(/\d+/); + const size = parseInt(sizeString![0], 10); + if (size <= 8) { + this.boardWidth = size; + this.boardHeight = size; + } else if (size === 11) { + this.boardWidth = 11; + this.boardHeight = 8; + } else if (size === 15) { + this.boardWidth = 15; + this.boardHeight = 12; + } } - if (isNaN(this.boardSize)) { + if (isNaN(this.boardWidth) || isNaN(this.boardHeight)) { throw new Error(`Could not determine the board size from variant "${this.variants[0]}"`); } } - return 6; + return this; } public moves(player?: PlayerId): string[] { @@ -173,20 +198,22 @@ export class KonaneGame extends GameBase { } const moves: string[] = []; - if (this.stack.length === 1) { - moves.push("a1"); - if (this.boardSize === 6) { - moves.push("c3"); - moves.push("d4"); - moves.push("f6"); - } else { - moves.push("d4"); - moves.push("e5"); - moves.push("h8"); - } - } else if (this.stack.length === 2) { - for (const m of this.getGraph().neighbours(this.stack[1].lastmove!)) { - moves.push(m); + if (this.boardWidth <= 8 && this.stack.length < 3) { + if (this.stack.length === 1) { + moves.push("a1"); + if (this.boardWidth === 6) { + moves.push("c3"); + moves.push("d4"); + moves.push("f6"); + } else if (this.boardWidth === 8) { + moves.push("d4"); + moves.push("e5"); + moves.push("h8"); + } + } else if (this.stack.length === 2) { + for (const m of this.getGraph().neighbours(this.stack[1].lastmove!)) { + moves.push(m); + } } } else { for (const cell of (this.listCells() as string[]).filter(c => this.board.has(c) && this.board.get(c) === this.currplayer)) { @@ -251,10 +278,12 @@ export class KonaneGame extends GameBase { if (m.length === 0) { result.valid = true; - if (this.stack.length === 1) { - result.message = i18next.t("apgames:validation.konane.FIRST_MOVE"); - } else if (this.stack.length === 2) { - result.message = i18next.t("apgames:validation.konane.SECOND_MOVE"); + if (this.boardWidth <= 8 && this.stack.length < 3) { + if (this.stack.length === 1) { + result.message = i18next.t("apgames:validation.konane.FIRST_MOVE"); + } else if (this.stack.length === 2) { + result.message = i18next.t("apgames:validation.konane.SECOND_MOVE"); + } } else { result.message = i18next.t("apgames:validation.konane.NORMAL_MOVE"); } @@ -263,10 +292,12 @@ export class KonaneGame extends GameBase { const moves = this.moves(); if (!moves.includes(m)) { - if (this.stack.length === 1) { - result.message = i18next.t("apgames:validation.konane.FIRST_MOVE"); - } else if (this.stack.length === 2) { - result.message = i18next.t("apgames:validation.konane.SECOND_MOVE"); + if (this.boardWidth <= 8 && this.stack.length < 3) { + if (this.stack.length === 1) { + result.message = i18next.t("apgames:validation.konane.FIRST_MOVE"); + } else if (this.stack.length === 2) { + result.message = i18next.t("apgames:validation.konane.SECOND_MOVE"); + } } else if (m.length > 0 && moves.filter(move => move.startsWith(m)).length > 0) { result.valid = true; result.canrender = true; @@ -381,7 +412,7 @@ export class KonaneGame extends GameBase { public moveState(): IMoveState { return { - _version: KonaneGame.gameinfo.version, + _version: `${this.version}`, _results: [...this.results], _timestamp: new Date(), currplayer: this.currplayer, @@ -426,8 +457,8 @@ export class KonaneGame extends GameBase { const rep: APRenderRep = { board: { style: "squares", - width: this.boardSize, - height: this.boardSize + width: this.boardWidth, + height: this.boardHeight }, legend: { A: { name: "piece", colour: 1 }, From bac29dfcf6fae6e11b2d1f6895b5478782096187 Mon Sep 17 00:00:00 2001 From: Christopher Field Date: Sun, 8 Mar 2026 11:43:13 -0400 Subject: [PATCH 2/4] Text for Konane changes. --- locales/en/apgames.json | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/locales/en/apgames.json b/locales/en/apgames.json index b304927e..ed7ff8d6 100644 --- a/locales/en/apgames.json +++ b/locales/en/apgames.json @@ -109,7 +109,7 @@ "irensei": "Irensei is a mixture of Go and Gomoku. Get a seven in a row inside the middle 15x15 region of the Go board to win. To reduce the first player's advantage, the first player loses if they make an overline (even if it extends beyond the 15x15 region), but the second player may win by overline.", "jacynth": "A Decktet card game where opponents vye for control of the city of Jacynth. Place cards, exert influence, and control the most area to win.", "kachit": "A chess-like game played on a 4x4 board where you try to eliminate the opposing king or promote your own. Pieces can change their orientation after moving.", - "konane": "A traditional Hawaiian game that was almost completely lost to time but for the memory of a single woman. Played on a square board, each player captures by jumping over their opponent's pieces. Last to move wins.", + "konane": "A traditional Hawaiian game that was almost completely lost to time but for the memory of a single woman, Kaʻahaʻaina Naihe of Kailua-Kona. Played on a rectangular board, each player captures by jumping over their opponent's pieces. Last to move wins.", "krypte": "A 4-in-a-row game where pieces enter from active sides that rotate after each turn, and placing a piece flips all adjacent pieces.", "lasca": "A Draughts variant where captured pieces remain on the board and can be freed later. Immobilize your opponent to win.", "lielow": "A wartime tale of Machiavellian usurpership. You win if your piece lands on your opponent's king. You also win if your opponent chooses to move their king off the board.", @@ -255,7 +255,7 @@ "gyges": "The goal squares are adjacent to all the cells in the back row. The renderer cannot currently handle \"floating\" cells.", "homeworlds": "The win condition is what's called \"Sinister Homeworlds.\" You only win by defeating the opponent to your left. If someone else does that, the game continues, but your left-hand opponent now shifts clockwise. For example, in a four-player game, if I'm South, then I win if I eliminate West. But if the North player ends up eliminating West, the game continues, but now my left-hand opponent is North.", "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 described in the BGG reference and is what is implemented here.", + "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.", "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 suit(s) 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 and/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.", @@ -1484,10 +1484,20 @@ }, "konane": { "#board": { - "name": "Size 6 board" + "name": "6x6 board", + "description": "Opening: Players will remove stones from the center square or the corners to start." }, "size-8": { - "name": "Size 8 board" + "name": "8x8 board", + "description": "Opening: Players will remove stones from the center square or the corners to start." + }, + "size-11": { + "name": "11x8 board", + "description": "Opening: The center two spaces will start empty." + }, + "size-15": { + "name": "15x12 board", + "description": "Opening: The center two spaces will start empty." } }, "lasca": { From 2ca04386d6351d37a019d2b684274459107591f9 Mon Sep 17 00:00:00 2001 From: Aaron Dalton Date: Sun, 8 Mar 2026 09:45:00 -0600 Subject: [PATCH 3/4] Wunchunk balance changes --- locales/en/apgames.json | 6 ++-- package.json | 2 +- src/games/wunchunk.ts | 58 +++++++++++++++++++++++++------------ test/games/wunchunk.test.ts | 48 ++++++++++++++++++++++++++++-- 4 files changed, 91 insertions(+), 23 deletions(-) diff --git a/locales/en/apgames.json b/locales/en/apgames.json index ed7ff8d6..8a0f3f20 100644 --- a/locales/en/apgames.json +++ b/locales/en/apgames.json @@ -2894,11 +2894,12 @@ "name": "Hexhex 5 (61 spaces)" }, "hex7": { - "name": "Hexhex 7 (127 spaces)" + "name": "Hexhex 7 (127 spaces)", + "description": "Can be selected for 3 and 4 player games." }, "hex8": { "name": "Hexhex 8 (169 spaces)", - "description": "For 3 and 4 player games, the hex8 board will be selected automatically." + "description": "Can be selected for 3 and 4 player games." }, "open": { "name": "Open start", @@ -5993,6 +5994,7 @@ "UNBROKEN": "Lines of pieces must be unbroken by empty cells." }, "wunchunk": { + "BALANCE": "Player 1 may not create two friendly chunks with their second placement.", "BOARD_FULL": "The board is full.", "INITIAL_INSTRUCTIONS_choose": "Use the buttons to choose whether to play first or second.", "INITIAL_INSTRUCTIONS_play_one": "You may place up to {{count}} piece this turn. Click the piece at the side of the board and then an empty cell to place it. Or just click an empty cell to place one of your own pieces.", diff --git a/package.json b/package.json index f8a214c0..726e247b 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "scripts": { "build": "npm run json2ts && npm run build-ts && npm run lint", "build-ts": "tsc && npm pack", - "test0": "mocha -r ts-node/register test/common/graphs/recttri.test.ts", + "test0": "mocha -r ts-node/register test/games/wunchunk.test.ts", "test": "mocha -r ts-node/register test/**/*.test.ts", "lint": "npx eslint .", "dist-dev": "rimraf dist && webpack", diff --git a/src/games/wunchunk.ts b/src/games/wunchunk.ts index b9a68649..3575f7be 100644 --- a/src/games/wunchunk.ts +++ b/src/games/wunchunk.ts @@ -69,7 +69,7 @@ export class WunchunkGame extends GameBase { { uid: "open" }, ], categories: ["goal>score>eog", "mechanic>place", "mechanic>share", "board>shape>hex", "board>connect>hex", "components>simple>1per", "other>2+players"], - flags: ["no-moves", "custom-randomization", "custom-buttons", "scores", "pie", "custom-colours"] + flags: ["no-moves", "custom-randomization", "custom-buttons", "scores", "custom-colours"] }; public numplayers = 2; @@ -92,14 +92,22 @@ export class WunchunkGame extends GameBase { let board: Map; if (this.numplayers === 3) { - this.variants = ["hex8"]; + if (this.variants.includes("hex7")) { + this.variants = ["hex7"]; + } else { + this.variants = ["hex8"]; + } board = new Map([ ["i7", 1], ["h7", 1], ["g7", 2], ["g8", 2], ["h9", 3], ["i8", 3], ]); } else if (this.numplayers === 4) { - this.variants = ["hex8"]; + if (this.variants.includes("hex7")) { + this.variants = ["hex7"]; + } else { + this.variants = ["hex8"]; + } board = new Map([ ["j8", 1], ["i9", 1], ["j6", 2], ["i6", 2], @@ -192,6 +200,24 @@ export class WunchunkGame extends GameBase { return new HexTriGraph(this.boardsize, (this.boardsize * 2) - 1); } + public get checkForBalance(): boolean { + let target = 3; + if (this.variants.includes("open")) { + if (this.swapped) { + target = 6; + } else { + target = 5; + } + } else { + if (this.numplayers === 3) { + target = 4; + } else if (this.numplayers === 4) { + target = 5; + } + } + return this.stack.length === target; + } + public getPlayerColour(p: playerid): number|string { if (this.swapped) { return p === 1 ? 2 : 1; @@ -228,21 +254,6 @@ export class WunchunkGame extends GameBase { return [{label: "pass", move: "pass"}]; } - public isPieTurn(): boolean { - if (this.numplayers === 2 && !this.variants.includes("open") && this.stack.length === 2) { - return true; - } - return false; - } - - public shouldOfferPie(): boolean { - if (this.numplayers === 2 && !this.variants.includes("open")) { - return true; - } - return false; - } - - public handleClick(move: string, row: number, col: number, piece?: string): IClickResult { const g = this.graph; try { @@ -430,6 +441,17 @@ export class WunchunkGame extends GameBase { cloned.move(steps.slice(0, i + 1).join(","), {partial: true, trusted: true}) } + // balance check + // The red player may not create two friendly chunks with their second placement. + if (cloned.checkForBalance) { + const num = cloned.countChunks(); + if (num > 1) { + result.valid = false; + result.message = i18next.t("apgames:validation.wunchunk.BALANCE"); + return result; + } + } + // if we get here, we're good // first handle initial setup if (this.variants.includes("open") && this.stack.length === 1) { diff --git a/test/games/wunchunk.test.ts b/test/games/wunchunk.test.ts index d65b536d..70997c98 100644 --- a/test/games/wunchunk.test.ts +++ b/test/games/wunchunk.test.ts @@ -35,9 +35,9 @@ describe("Wunchunk", () => { // "pass", "1f7", "2g4", - "1f8", + "1d8", "2h3", - "1d3,1e7", + "1e8", "pass", "pass", ]; @@ -47,4 +47,48 @@ describe("Wunchunk", () => { expect(g.gameover).to.be.true; expect(g.winner).to.deep.equal([1]); }); + it ("Balance check", () => { + // 2 player standard + let g = new WunchunkGame(2); + g.move("1a4"); + g.move("2k4"); + expect(g.checkForBalance).to.be.true; + let result = g.validateMove("1b4"); + expect(result.valid).to.be.false; + // 2 player open - pass + g = new WunchunkGame(2, ["open"]); + g.move("1e4,1f4,2f5,2f6"); + g.move("pass"); + g.move("1f7") + g.move("2g4") + expect(g.checkForBalance).to.be.true; + result = g.validateMove("1f8"); + expect(result.valid).to.be.false; + // 2 player open - swap + g = new WunchunkGame(2, ["open"]); + g.move("1e4,1f4,2f5,2f6"); + g.move("swap"); + g.move("1f7") + g.move("2g4") + expect(g.checkForBalance).to.be.true; + result = g.validateMove("1f8"); + expect(result.valid).to.be.false; + // 3 player + g = new WunchunkGame(3); + g.move("1l3"); + g.move("2k8"); + g.move("3f10"); + expect(g.checkForBalance).to.be.true; + result = g.validateMove("1m2"); + expect(result.valid).to.be.false; + // 4 player + g = new WunchunkGame(4); + g.move("1l3"); + g.move("2k8"); + g.move("3f10"); + g.move("4d3"); + expect(g.checkForBalance).to.be.true; + result = g.validateMove("1m2"); + expect(result.valid).to.be.false; + }); }); From 92b7c2a44cf43d5740c276d47e1ab23fe46866d5 Mon Sep 17 00:00:00 2001 From: Christopher Field Date: Sun, 8 Mar 2026 12:35:53 -0400 Subject: [PATCH 4/4] Adding the missing sizes to konane --- locales/en/apgames.json | 8 ++++++++ src/games/konane.ts | 37 ++++++++++++++++++++++++------------- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/locales/en/apgames.json b/locales/en/apgames.json index ed7ff8d6..64dc00b4 100644 --- a/locales/en/apgames.json +++ b/locales/en/apgames.json @@ -1487,10 +1487,18 @@ "name": "6x6 board", "description": "Opening: Players will remove stones from the center square or the corners to start." }, + "size-7": { + "name": "7x6 board", + "description": "Opening: The center two spaces will start empty." + }, "size-8": { "name": "8x8 board", "description": "Opening: Players will remove stones from the center square or the corners to start." }, + "size-9": { + "name": "11x8 board", + "description": "Opening: The center two spaces will start empty." + }, "size-11": { "name": "11x8 board", "description": "Opening: The center two spaces will start empty." diff --git a/src/games/konane.ts b/src/games/konane.ts index 4a3e3df2..646ded01 100644 --- a/src/games/konane.ts +++ b/src/games/konane.ts @@ -45,10 +45,18 @@ export class KonaneGame extends GameBase { categories: ["goal>immobilize", "mechanic>capture", "other>traditional", "board>shape>rect"], flags: ["automove"], variants: [ + { + uid: "size-7", + group: "board" + }, { uid: "size-8", group: "board" }, + { + uid: "size-9", + group: "board" + }, { uid: "size-11", group: "board" @@ -86,7 +94,9 @@ export class KonaneGame extends GameBase { let color = 2 as PlayerId; for (let x = 0; x < this.boardWidth; x++) { for (let y = 0; y < this.boardHeight; y++) { - if ((this.boardWidth !== 11 || (y !== 3 && y !== 4) || x !== 5) && + if ((this.boardWidth !== 7 || (y !== 2 && y !== 3) || x !== 3) && + (this.boardWidth !== 9 || (y !== 3 && y !== 4) || x !== 4) && + (this.boardWidth !== 11 || (y !== 3 && y !== 4) || x !== 5) && (this.boardWidth !== 15 || (y !== 5 && y !== 6) || x !== 7)) { board.set(GameBase.coords2algebraic(x, y, this.boardHeight), color); } @@ -167,20 +177,21 @@ export class KonaneGame extends GameBase { private setBoardSize(): KonaneGame { this.boardWidth = 6; this.boardHeight = 6; - // Get board 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("size")); if (sizeVariants.length > 0) { const sizeString = sizeVariants[0].match(/\d+/); - const size = parseInt(sizeString![0], 10); - if (size <= 8) { - this.boardWidth = size; - this.boardHeight = size; - } else if (size === 11) { - this.boardWidth = 11; + this.boardWidth = parseInt(sizeString![0], 10); + if (this.boardWidth === 6 || this.boardWidth === 8) { + this.boardHeight = this.boardWidth; + } else if (this.boardWidth === 7) { + this.boardHeight = 6; + } else if (this.boardWidth === 9) { + this.boardHeight = 8; + } else if (this.boardWidth === 11) { this.boardHeight = 8; - } else if (size === 15) { - this.boardWidth = 15; + } else if (this.boardWidth === 15) { this.boardHeight = 12; } } @@ -198,7 +209,7 @@ export class KonaneGame extends GameBase { } const moves: string[] = []; - if (this.boardWidth <= 8 && this.stack.length < 3) { + if ((this.boardWidth === 6 || this.boardWidth === 8) && this.stack.length < 3) { if (this.stack.length === 1) { moves.push("a1"); if (this.boardWidth === 6) { @@ -278,7 +289,7 @@ export class KonaneGame extends GameBase { if (m.length === 0) { result.valid = true; - if (this.boardWidth <= 8 && this.stack.length < 3) { + if ((this.boardWidth === 6 || this.boardWidth === 8) && this.stack.length < 3) { if (this.stack.length === 1) { result.message = i18next.t("apgames:validation.konane.FIRST_MOVE"); } else if (this.stack.length === 2) { @@ -292,7 +303,7 @@ export class KonaneGame extends GameBase { const moves = this.moves(); if (!moves.includes(m)) { - if (this.boardWidth <= 8 && this.stack.length < 3) { + if ((this.boardWidth === 6 || this.boardWidth === 8) && this.stack.length < 3) { if (this.stack.length === 1) { result.message = i18next.t("apgames:validation.konane.FIRST_MOVE"); } else if (this.stack.length === 2) {