diff --git a/locales/en/apgames.json b/locales/en/apgames.json index 4d53637a..03d73563 100644 --- a/locales/en/apgames.json +++ b/locales/en/apgames.json @@ -5142,10 +5142,12 @@ }, "forms": { "INITIAL_INSTRUCTIONS": "Click a friendly piece.", - "INSTRUCTIONS": "Click on an orthogonally opposing piece to capture by replacement, or move to an path-adjacent empty cell.", + "INSTRUCTIONS": "Click on an orthogonally opposing piece to capture by replacement, or move to an path-adjacent empty cell that makes 2+ groups.", + "INSTRUCTIONS_ORIGINAL": "Click on any opponent piece to capture it.", "INVALID_SELECTION": "Click on a friendly piece!", "NO_MOVES": "This piece does not have valid moves!", - "INVALID_MOVE": "The piece must either: (a) capture an orthogonally adjacent enemy piece, (b) move to an empty cell which is connected by an orthogonal sequence of steps over empty cells!", + "INVALID_MOVE": "The piece must either: (a) capture an orthogonally adjacent enemy piece, (b) move to an empty cell which is connected by an orthogonal sequence of steps over empty cells, and makes 2+ groups!", + "INVALID_MOVE_ORIGINAL": "The piece must capture by replacement any opponent piece!", "INVALID_NUM_GROUPS": "A move must create, at least, two separate groups of pieces!" }, "frogger": { @@ -5434,7 +5436,7 @@ "INCURSION": "The cell at {{cell}} has no path to a friendly group." }, "linage": { - "INITIAL_SETUP": "Choose a number of points to add to the second player's score, and the next player will choose sides. (0.5 will be added to Komi to prevent draws.)", + "INITIAL_SETUP": "Choose a number of points to add to the second player's score, and the next player will choose sides. Afterwards, one player can take the button, which earns half-point by being the first one to pass.", "INSTRUCTIONS": "Place a piece on a free region.", "TABOO": "It is invalid to: (a) place on an owned region, (b) make a region without a horizontal or vertical line.", "INVALID_KOMI": "You must choose an integer number of points to add to the second player's score.", @@ -6806,7 +6808,7 @@ }, "viruswar": { "INITIAL_INSTRUCTIONS": "Place {{count}} pieces, each must be adjacent to the group of friendly pieces including your home-base. Pieces can be active viruses (circles), or defensive walls (squares). Either place a virus on an empty cell, or replace an enemy virus with a friendly defensive wall.", - "CANNOT_GROW": "Square {{where}} cannot receive a new virus/wall!" + "CANNOT_GROW": "Square {{where}} cannot receive a new virus/wall! It must be adjacent to the home-base group." }, "volcano": { "BAD_FROM": "{{from}} doesn't look like a position on the board, or a piece in your stash.", diff --git a/src/games/forms.ts b/src/games/forms.ts index 18ec34e2..9291114d 100644 --- a/src/games/forms.ts +++ b/src/games/forms.ts @@ -73,6 +73,7 @@ export class FormsGame extends GameBase { private ruleset: "default" | "original"; private dots: [number, number][] = []; // if there are points here, the renderer will show them + private highlight: string | undefined; // highlight moving piece constructor(state?: IFormsState | string, variants?: string[]) { super(); @@ -313,7 +314,11 @@ export class FormsGame extends GameBase { result.valid = true; result.complete = -1; // player still needs to move the piece result.canrender = true; - result.message = i18next.t("apgames:validation.forms.INSTRUCTIONS"); + if (this.ruleset === 'original' ) { + result.message = i18next.t("apgames:validation.forms.INSTRUCTIONS_ORIGINAL"); + } else { + result.message = i18next.t("apgames:validation.forms.INSTRUCTIONS"); + } return result; } @@ -322,7 +327,11 @@ export class FormsGame extends GameBase { if (! this.validMoves(from).includes(to) ) { result.valid = false; - result.message = i18next.t("apgames:validation.forms.INVALID_MOVE"); + if (this.ruleset === 'original' ) { + result.message = i18next.t("apgames:validation.forms.INVALID_MOVE_ORIGINAL"); + } else { + result.message = i18next.t("apgames:validation.forms.INVALID_MOVE"); + } return result; } @@ -348,9 +357,12 @@ export class FormsGame extends GameBase { this.results = []; this.dots = []; + this.highlight = undefined; + if ( m.length === 0 ) { return this; } if ( partial && !m.includes('-') ) { + this.highlight = m; if (this.ruleset !== 'original' ) { this.dots = this.validMoves(m).map(c => this.algebraic2coords(c)); } @@ -426,9 +438,9 @@ export class FormsGame extends GameBase { if (this.board.has(cell)) { const contents = this.board.get(cell)!; if (contents === 1) { - pieces.push("A"); + pieces.push(this.highlight === cell ? "C" : "A"); } else { - pieces.push("B"); + pieces.push(this.highlight === cell ? "D" : "B"); } } else { pieces.push("-"); @@ -448,6 +460,8 @@ export class FormsGame extends GameBase { legend: { A: { name: "piece", colour: 1 }, B: { name: "piece", colour: 2 }, + C: { name: "piece-horse", colour: 1 }, + D: { name: "piece-horse", colour: 2 } }, pieces: pstr, }; diff --git a/src/games/posit.ts b/src/games/posit.ts index 796d56b6..6c4862f6 100644 --- a/src/games/posit.ts +++ b/src/games/posit.ts @@ -399,7 +399,9 @@ export class PositGame extends GameBase { const neutralColour: Colourfuncs = { func: "custom", - default: "#999", + // default: "#778899", // slate gray + // default: "#44d7a8", // eucalyptus + default: "#ba55d3", // medium orchid palette: 3 }; diff --git a/src/games/viruswar.ts b/src/games/viruswar.ts index d7698db7..80c9be60 100644 --- a/src/games/viruswar.ts +++ b/src/games/viruswar.ts @@ -52,7 +52,7 @@ export class VirusWarGame extends GameBase { { uid: "size-25", group: "board" }, // 5 moves { uid: "#board", }, // 30x30, 6 moves ], - categories: ["goal>immobilize", "other>traditional", "mechanic>place", "mechanic>capture", "board>shape>rect", "board>connect>rect", "components>simple>2per"], + categories: ["goal>immobilize", "other>traditional", "mechanic>place", "mechanic>capture", "board>shape>rect", "board>connect>rect", "components>simple>pnp"], flags: ["no-moves", "experimental"] }; @@ -225,6 +225,7 @@ export class VirusWarGame extends GameBase { if (m.length === 0) { result.valid = true; result.complete = -1; + result.canrender = true; result.message = i18next.t("apgames:validation.viruswar.INITIAL_INSTRUCTIONS", {count: this.numMoves}) return result; } @@ -280,6 +281,13 @@ export class VirusWarGame extends GameBase { if (this.gameover) { throw new UserFacingError("MOVES_GAMEOVER", i18next.t("apgames:MOVES_GAMEOVER")); } + + if (m.length === 0) { // show all available moves even before selecting anything + this.results = []; + this.dots = this.getAdjacentMoves(this.currplayer, this.board); + return this; + } + m = m.toLowerCase(); m = m.replace(/\s+/g, ""); if (! trusted) { @@ -287,8 +295,6 @@ export class VirusWarGame extends GameBase { if (! result.valid) { throw new UserFacingError("VALIDATION_GENERAL", result.message) } } - if (m === "") { return this; } - const prevplayer = this.currplayer % 2 + 1 as playerid; for (const move of m.split(',')) { if (! this.board.has(move) ) {