Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions locales/en/apgames.json
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
"queensland": "Create long open corridors between your pieces to score points. Easy, right?",
"query": "A connection game on an Alquerque board. Place a piece on an eight-way intersection, or two pieces on four-way intersections.",
"quincunx": "A Decktet tableau-building game where the grid gets built as you go. Each play scores points (sometimes negative points). A game consists of as many rounds as there are players. Highest accumulated score wins.",
"rampart": "An annihilation game where players grow from a predetermined set of groups.",
"razzle": "Razzle Dazzle, also called Knight Moves, is a fast-paced game, designed to recreate as much of the experience of Ultimate Frisbee as possible while keeping the rules and gameplay simple and elegant. Two players try to move their ball to the opposite side of the board, using their 5 pieces as passing instruments. A player may either pass the ball or move a piece like a knight in a game of Chess.",
"realm": "Realm is played on a unique 12x12 board with only two types of mobile pieces. The goal is to capture more territory than your opponent by blocking and immobilizing their pieces. A few simple rules let you generate more pieces, which leads to ever-evolving tactics.",
"renju": "An asymmetric five-in-a-row game where the first player is not allowed to create double open threes, double fours, or overlines. Competitive openings like Taraguchi-10 and Soosõrv-8 are implemented here.",
Expand Down Expand Up @@ -1929,6 +1930,24 @@
"description": "When present, if the card you played creates or extends a continuous line (including diagonal) of four or more cards that share the same suit, or that forms a 2x2 square of cards that share a suit, score 10 points for each line and square."
}
},
"rampart": {
"#setup": {
"name": "Standard setup"
},
"custom": {
"name": "Custom setup (unrated)",
"description": "The players can create arbitrary setups. Should only be done with good communication. These games cannot be rated."
},
"#board": {
"name": "11x11 board"
},
"13x13": {
"name": "13x13 board"
},
"hex7": {
"name": "Hexagonal board (base-7)"
}
},
"realm": {
"capturedBases": {
"description": "The number of captured bases becomes a second tie breaker.",
Expand Down Expand Up @@ -4857,6 +4876,13 @@
"INITIAL_INSTRUCTIONS": "Select a card from your hand to place on the board.",
"PARTIAL": "Provide the destination."
},
"rampart": {
"BAD_PLACEMENT": "One of the cells you're trying to place a piece on is either nonexistent or occupied.",
"INITIAL_INSTRUCTIONS_play": "Select an empty cell to place a piece or a dead enemy piece to remove the groups.",
"INITIAL_INSTRUCTIONS_setup": "Select any empty cell to place a piece or an existing friendly piece to remove it.",
"PARTIAL_PLACE": "Continue adding and removing pieces until you're done, then click the Complete Move button.",
"PLACE_ONE": "You must place at least one piece."
},
"razzle": {
"BAD_DIRECTION": "You can only move the ball orthogonally or diagonally. The move from {{from}} to {{to}} is invalid.",
"INCOMPLETE_BALL_MOVE": "You must keep moving the ball, because this position repeats an earlier position and would therefore not be valid.",
Expand Down Expand Up @@ -5005,13 +5031,13 @@
"INVALID_MOVE": "You may only place a piece where it will flip at least one of your opponent's pieces."
},
"rootbound": {
"BAD_SECOND_MOVE": "You must start by making a second group as soon as possible.",
"CLAIMED_CELL": "You may not place a piece into a claimed territory.",
"FIRST_MOVE_INSTRUCTIONS": "Place a piece anywhere on the board.",
"SECOND_MOVE_INSTRUCTIONS": "Place two pieces on empty spaces. You must make a second group as soon as possible.",
"THIRD_MOVE_INSTRUCTIONS": "Place one or two pieces on empty spaces. You must make a second group as soon as possible.",
"INITIAL_INSTRUCTIONS": "Place one or two pieces on an unclaimed empty space. Your pieces may not form a small triangle nor can you grow a group in a straight line from an existing piece.",
"BAD_SECOND_MOVE": "You must start by making a second group as soon as possible.",
"CLAIMED_CELL": "You may not place a piece into a claimed territory.",
"RAPID_GROWTH": "You cannot grow a group in a straight line from an existing piece in a single turn.",
"SECOND_MOVE_INSTRUCTIONS": "Place one or two pieces on empty spaces. You must make a second group as soon as possible.",
"TOO_FEW_GROUPS": "You cannot end a turn with only one group unless it claims territory.",
"TOO_MANY_NEIGHBORS": "Your pieces cannot form a small triangle."
},
"saltire": {
Expand Down
17 changes: 1 addition & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

94 changes: 51 additions & 43 deletions src/games/arimaa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,11 +490,12 @@ export class ArimaaGame extends GameBase {
// clicking an occupied cell after selecting a piece to place
if (lastmove.length === 0) {
const idx = steps.findIndex(([pc,,f,]) => pc === piece![0] && f === cell);
if (idx === -1) {
throw new Error("This should never happen");
if (idx >= 0) {
steps.splice(idx, 1);
newmove = steps.map(([pc, p, f,]) => `${p === 1 ? pc : pc.toLowerCase()}${f}`).join(",");
} else {
newmove = stub;
}
steps.splice(idx, 1);
newmove = steps.map(([pc, p, f,]) => `${p === 1 ? pc : pc.toLowerCase()}${f}`).join(",");
} else {
newmove = stub;
}
Expand Down Expand Up @@ -524,7 +525,7 @@ export class ArimaaGame extends GameBase {
}
}

// console.log(`About to validate ${newmove}`);
// console.log(`About to validate '${newmove}'`);
let result = this.validateMove(newmove) as IClickResult;
if (! result.valid) {
result.move = move;
Expand Down Expand Up @@ -556,6 +557,11 @@ export class ArimaaGame extends GameBase {
if (m.length === 0) {
result.valid = true;
result.complete = -1;
// if in the setup phase, we need canrender, otherwise don't
result.canrender = false;
if (!this.variants.includes("eee") && this.stack.length <= 2) {
result.canrender = true;
}
result.canrender = true;
result.message = i18next.t("apgames:validation.arimaa.INITIAL_INSTRUCTIONS", {context: (this.hands !== undefined && this.hands[this.currplayer - 1].length > 0) ? "place" : "play"});
return result;
Expand Down Expand Up @@ -896,49 +902,51 @@ export class ArimaaGame extends GameBase {
}
}

// because we don't have a move list to fall back on,
// we do some basic validation as we go and throw on errors
// but we don't go so far as to validate pushes and pulls here
this.results = [];
const initial = this.clone(); // used to triple check that the board state changes
const lastmove: string[] = [];
const steps = m.split(",").filter(Boolean).map(mv => ArimaaGame.baseMove(mv));
for (let i = 0; i < steps.length; i++) {
const [pc, owner, from, to] = steps[i];
// placement
if (this.hands !== undefined && this.hands[this.currplayer - 1].length > 0) {
if (from !== undefined) {
this.board.set(from, [pc, this.currplayer]);
this.results.push({type: "place", what: pc, where: from});
// update hand
if (!this.variants.includes("free")) {
this.hands![this.currplayer - 1].splice(this.hands![this.currplayer - 1].indexOf(pc), 1);
if (m.length > 0) {
// because we don't have a move list to fall back on,
// we do some basic validation as we go and throw on errors
// but we don't go so far as to validate pushes and pulls here
this.results = [];
const steps = m.split(",").filter(Boolean).map(mv => ArimaaGame.baseMove(mv));
for (let i = 0; i < steps.length; i++) {
const [pc, owner, from, to] = steps[i];
// placement
if (this.hands !== undefined && this.hands[this.currplayer - 1].length > 0) {
if (from !== undefined) {
this.board.set(from, [pc, this.currplayer]);
this.results.push({type: "place", what: pc, where: from});
// update hand
if (!this.variants.includes("free")) {
this.hands![this.currplayer - 1].splice(this.hands![this.currplayer - 1].indexOf(pc), 1);
}
lastmove.push(`${this.currplayer === 1 ? pc : pc.toLowerCase()}${from}`);
} else if (i !== steps.length - 1) {
throw new Error("Invalid placement detected in the middle of the move.");
}
lastmove.push(`${this.currplayer === 1 ? pc : pc.toLowerCase()}${from}`);
} else if (i !== steps.length - 1) {
throw new Error("Invalid placement detected in the middle of the move.");
}
}
// movement
else {
if (from !== undefined && to !== undefined) {
const moved = this.board.get(from)!;
this.board.set(to, moved);
this.board.delete(from);
this.results.push({type: "move", from, to});
// check traps
let parenthetical = "";
for (const trap of traps) {
if (this.board.has(trap) && this.isAlone(trap)) {
const [trapPc, trapOwner] = this.board.get(trap)!;
this.board.delete(trap);
this.results.push({type: "destroy", what: trapOwner === 1 ? trapPc : trapPc.toLowerCase(), where: trap});
parenthetical = `(x${trapOwner === 1 ? trapPc : trapPc.toLowerCase()}${trap})`;
// movement
else {
if (from !== undefined && to !== undefined) {
const moved = this.board.get(from)!;
this.board.set(to, moved);
this.board.delete(from);
this.results.push({type: "move", from, to});
// check traps
let parenthetical = "";
for (const trap of traps) {
if (this.board.has(trap) && this.isAlone(trap)) {
const [trapPc, trapOwner] = this.board.get(trap)!;
this.board.delete(trap);
this.results.push({type: "destroy", what: trapOwner === 1 ? trapPc : trapPc.toLowerCase(), where: trap});
parenthetical = `(x${trapOwner === 1 ? trapPc : trapPc.toLowerCase()}${trap})`;
}
}
lastmove.push(`${owner === 1 ? pc : pc.toLowerCase()}${from}${to}${parenthetical}`);
} else if (i !== steps.length - 1) {
throw new Error("Invalid move detected in the middle of the move.");
}
lastmove.push(`${owner === 1 ? pc : pc.toLowerCase()}${from}${to}${parenthetical}`);
} else if (i !== steps.length - 1) {
throw new Error("Invalid move detected in the middle of the move.");
}
}
}
Expand Down Expand Up @@ -970,7 +978,7 @@ export class ArimaaGame extends GameBase {
// clear hands when both are empty
if (
(this.hands !== undefined && this.hands[0].length === 0 && this.hands[1].length === 0) ||
(this.variants.includes("free") && this.stack.length ===2)
(this.variants.includes("free") && this.stack.length === 2)
) {
this.hands = undefined;
}
Expand Down
Loading