Skip to content

Commit 5fc58e8

Browse files
authored
Merge pull request #289 from AbstractPlay/develop
Develop
2 parents 20c8a3c + f4c862d commit 5fc58e8

File tree

9 files changed

+810
-556
lines changed

9 files changed

+810
-556
lines changed

locales/en/apgames.json

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@
158158
"queensland": "Create long open corridors between your pieces to score points. Easy, right?",
159159
"query": "A connection game on an Alquerque board. Place a piece on an eight-way intersection, or two pieces on four-way intersections.",
160160
"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.",
161+
"rampart": "An annihilation game where players grow from a predetermined set of groups.",
161162
"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.",
162163
"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.",
163164
"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.",
@@ -1929,6 +1930,24 @@
19291930
"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."
19301931
}
19311932
},
1933+
"rampart": {
1934+
"#setup": {
1935+
"name": "Standard setup"
1936+
},
1937+
"custom": {
1938+
"name": "Custom setup (unrated)",
1939+
"description": "The players can create arbitrary setups. Should only be done with good communication. These games cannot be rated."
1940+
},
1941+
"#board": {
1942+
"name": "11x11 board"
1943+
},
1944+
"13x13": {
1945+
"name": "13x13 board"
1946+
},
1947+
"hex7": {
1948+
"name": "Hexagonal board (base-7)"
1949+
}
1950+
},
19321951
"realm": {
19331952
"capturedBases": {
19341953
"description": "The number of captured bases becomes a second tie breaker.",
@@ -4857,6 +4876,13 @@
48574876
"INITIAL_INSTRUCTIONS": "Select a card from your hand to place on the board.",
48584877
"PARTIAL": "Provide the destination."
48594878
},
4879+
"rampart": {
4880+
"BAD_PLACEMENT": "One of the cells you're trying to place a piece on is either nonexistent or occupied.",
4881+
"INITIAL_INSTRUCTIONS_play": "Select an empty cell to place a piece or a dead enemy piece to remove the groups.",
4882+
"INITIAL_INSTRUCTIONS_setup": "Select any empty cell to place a piece or an existing friendly piece to remove it.",
4883+
"PARTIAL_PLACE": "Continue adding and removing pieces until you're done, then click the Complete Move button.",
4884+
"PLACE_ONE": "You must place at least one piece."
4885+
},
48604886
"razzle": {
48614887
"BAD_DIRECTION": "You can only move the ball orthogonally or diagonally. The move from {{from}} to {{to}} is invalid.",
48624888
"INCOMPLETE_BALL_MOVE": "You must keep moving the ball, because this position repeats an earlier position and would therefore not be valid.",
@@ -5005,13 +5031,13 @@
50055031
"INVALID_MOVE": "You may only place a piece where it will flip at least one of your opponent's pieces."
50065032
},
50075033
"rootbound": {
5008-
"BAD_SECOND_MOVE": "You must start by making a second group as soon as possible.",
5009-
"CLAIMED_CELL": "You may not place a piece into a claimed territory.",
50105034
"FIRST_MOVE_INSTRUCTIONS": "Place a piece anywhere on the board.",
5035+
"SECOND_MOVE_INSTRUCTIONS": "Place two pieces on empty spaces. You must make a second group as soon as possible.",
5036+
"THIRD_MOVE_INSTRUCTIONS": "Place one or two pieces on empty spaces. You must make a second group as soon as possible.",
50115037
"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.",
5038+
"BAD_SECOND_MOVE": "You must start by making a second group as soon as possible.",
5039+
"CLAIMED_CELL": "You may not place a piece into a claimed territory.",
50125040
"RAPID_GROWTH": "You cannot grow a group in a straight line from an existing piece in a single turn.",
5013-
"SECOND_MOVE_INSTRUCTIONS": "Place one or two pieces on empty spaces. You must make a second group as soon as possible.",
5014-
"TOO_FEW_GROUPS": "You cannot end a turn with only one group unless it claims territory.",
50155041
"TOO_MANY_NEIGHBORS": "Your pieces cannot form a small triangle."
50165042
},
50175043
"saltire": {

package-lock.json

Lines changed: 1 addition & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/games/arimaa.ts

Lines changed: 51 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -490,11 +490,12 @@ export class ArimaaGame extends GameBase {
490490
// clicking an occupied cell after selecting a piece to place
491491
if (lastmove.length === 0) {
492492
const idx = steps.findIndex(([pc,,f,]) => pc === piece![0] && f === cell);
493-
if (idx === -1) {
494-
throw new Error("This should never happen");
493+
if (idx >= 0) {
494+
steps.splice(idx, 1);
495+
newmove = steps.map(([pc, p, f,]) => `${p === 1 ? pc : pc.toLowerCase()}${f}`).join(",");
496+
} else {
497+
newmove = stub;
495498
}
496-
steps.splice(idx, 1);
497-
newmove = steps.map(([pc, p, f,]) => `${p === 1 ? pc : pc.toLowerCase()}${f}`).join(",");
498499
} else {
499500
newmove = stub;
500501
}
@@ -524,7 +525,7 @@ export class ArimaaGame extends GameBase {
524525
}
525526
}
526527

527-
// console.log(`About to validate ${newmove}`);
528+
// console.log(`About to validate '${newmove}'`);
528529
let result = this.validateMove(newmove) as IClickResult;
529530
if (! result.valid) {
530531
result.move = move;
@@ -556,6 +557,11 @@ export class ArimaaGame extends GameBase {
556557
if (m.length === 0) {
557558
result.valid = true;
558559
result.complete = -1;
560+
// if in the setup phase, we need canrender, otherwise don't
561+
result.canrender = false;
562+
if (!this.variants.includes("eee") && this.stack.length <= 2) {
563+
result.canrender = true;
564+
}
559565
result.canrender = true;
560566
result.message = i18next.t("apgames:validation.arimaa.INITIAL_INSTRUCTIONS", {context: (this.hands !== undefined && this.hands[this.currplayer - 1].length > 0) ? "place" : "play"});
561567
return result;
@@ -896,49 +902,51 @@ export class ArimaaGame extends GameBase {
896902
}
897903
}
898904

899-
// because we don't have a move list to fall back on,
900-
// we do some basic validation as we go and throw on errors
901-
// but we don't go so far as to validate pushes and pulls here
902-
this.results = [];
903905
const initial = this.clone(); // used to triple check that the board state changes
904906
const lastmove: string[] = [];
905-
const steps = m.split(",").filter(Boolean).map(mv => ArimaaGame.baseMove(mv));
906-
for (let i = 0; i < steps.length; i++) {
907-
const [pc, owner, from, to] = steps[i];
908-
// placement
909-
if (this.hands !== undefined && this.hands[this.currplayer - 1].length > 0) {
910-
if (from !== undefined) {
911-
this.board.set(from, [pc, this.currplayer]);
912-
this.results.push({type: "place", what: pc, where: from});
913-
// update hand
914-
if (!this.variants.includes("free")) {
915-
this.hands![this.currplayer - 1].splice(this.hands![this.currplayer - 1].indexOf(pc), 1);
907+
if (m.length > 0) {
908+
// because we don't have a move list to fall back on,
909+
// we do some basic validation as we go and throw on errors
910+
// but we don't go so far as to validate pushes and pulls here
911+
this.results = [];
912+
const steps = m.split(",").filter(Boolean).map(mv => ArimaaGame.baseMove(mv));
913+
for (let i = 0; i < steps.length; i++) {
914+
const [pc, owner, from, to] = steps[i];
915+
// placement
916+
if (this.hands !== undefined && this.hands[this.currplayer - 1].length > 0) {
917+
if (from !== undefined) {
918+
this.board.set(from, [pc, this.currplayer]);
919+
this.results.push({type: "place", what: pc, where: from});
920+
// update hand
921+
if (!this.variants.includes("free")) {
922+
this.hands![this.currplayer - 1].splice(this.hands![this.currplayer - 1].indexOf(pc), 1);
923+
}
924+
lastmove.push(`${this.currplayer === 1 ? pc : pc.toLowerCase()}${from}`);
925+
} else if (i !== steps.length - 1) {
926+
throw new Error("Invalid placement detected in the middle of the move.");
916927
}
917-
lastmove.push(`${this.currplayer === 1 ? pc : pc.toLowerCase()}${from}`);
918-
} else if (i !== steps.length - 1) {
919-
throw new Error("Invalid placement detected in the middle of the move.");
920928
}
921-
}
922-
// movement
923-
else {
924-
if (from !== undefined && to !== undefined) {
925-
const moved = this.board.get(from)!;
926-
this.board.set(to, moved);
927-
this.board.delete(from);
928-
this.results.push({type: "move", from, to});
929-
// check traps
930-
let parenthetical = "";
931-
for (const trap of traps) {
932-
if (this.board.has(trap) && this.isAlone(trap)) {
933-
const [trapPc, trapOwner] = this.board.get(trap)!;
934-
this.board.delete(trap);
935-
this.results.push({type: "destroy", what: trapOwner === 1 ? trapPc : trapPc.toLowerCase(), where: trap});
936-
parenthetical = `(x${trapOwner === 1 ? trapPc : trapPc.toLowerCase()}${trap})`;
929+
// movement
930+
else {
931+
if (from !== undefined && to !== undefined) {
932+
const moved = this.board.get(from)!;
933+
this.board.set(to, moved);
934+
this.board.delete(from);
935+
this.results.push({type: "move", from, to});
936+
// check traps
937+
let parenthetical = "";
938+
for (const trap of traps) {
939+
if (this.board.has(trap) && this.isAlone(trap)) {
940+
const [trapPc, trapOwner] = this.board.get(trap)!;
941+
this.board.delete(trap);
942+
this.results.push({type: "destroy", what: trapOwner === 1 ? trapPc : trapPc.toLowerCase(), where: trap});
943+
parenthetical = `(x${trapOwner === 1 ? trapPc : trapPc.toLowerCase()}${trap})`;
944+
}
937945
}
946+
lastmove.push(`${owner === 1 ? pc : pc.toLowerCase()}${from}${to}${parenthetical}`);
947+
} else if (i !== steps.length - 1) {
948+
throw new Error("Invalid move detected in the middle of the move.");
938949
}
939-
lastmove.push(`${owner === 1 ? pc : pc.toLowerCase()}${from}${to}${parenthetical}`);
940-
} else if (i !== steps.length - 1) {
941-
throw new Error("Invalid move detected in the middle of the move.");
942950
}
943951
}
944952
}
@@ -970,7 +978,7 @@ export class ArimaaGame extends GameBase {
970978
// clear hands when both are empty
971979
if (
972980
(this.hands !== undefined && this.hands[0].length === 0 && this.hands[1].length === 0) ||
973-
(this.variants.includes("free") && this.stack.length ===2)
981+
(this.variants.includes("free") && this.stack.length === 2)
974982
) {
975983
this.hands = undefined;
976984
}

0 commit comments

Comments
 (0)