Skip to content

Commit f0dd1e2

Browse files
committed
Omny: Add size-4 and -5 boards and tweak random distribution
1 parent 1b3160e commit f0dd1e2

File tree

2 files changed

+75
-21
lines changed

2 files changed

+75
-21
lines changed

locales/en/apgames.json

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,13 @@
10841084
}
10851085
},
10861086
"omny": {
1087+
"size-4": {
1088+
"name": "Hexhex 4 (37 spaces)",
1089+
"description": "Random distribution on size-4 boards will not always be ideal"
1090+
},
1091+
"size-5": {
1092+
"name": "Hexhex 5 (61 spaces)"
1093+
},
10871094
"size-6": {
10881095
"name": "Hexhex 6 (91 spaces)"
10891096
},
@@ -1102,7 +1109,7 @@
11021109
},
11031110
"gyre": {
11041111
"name": "Gyre",
1105-
"description": "Stars around the perimeter in in the centre"
1112+
"description": "Stars around the perimeter and in the centre (proposed by Mark Steere)"
11061113
},
11071114
"yex": {
11081115
"name": "Yex",
@@ -1126,7 +1133,7 @@
11261133
},
11271134
"captures": {
11281135
"name": "Enable Adere-style captures",
1129-
"description": "Pieces can move between stacks of the same height."
1136+
"description": "Pieces can move onto an opposing stack of the same height. Increases game length. Consider using smaller boards."
11301137
}
11311138
},
11321139
"onyx": {
@@ -4070,7 +4077,8 @@
40704077
"INITIAL_INSTRUCTIONS": "Select an empty cell to place a piece of your colour.",
40714078
"NO_DUPES": "Your move may not include duplicate cells.",
40724079
"PARTIAL_MOVE": "Select the destination.",
4073-
"PARTIAL_SETUP": "Select a cell to place a star point (you must place at least one), or select a star point you placed to remove it. Click \"Complete move\" when you're done.",
4080+
"PARTIAL_SETUP_p1": "Select a cell to place a star point (you must place at least one), or select a star point you placed to remove it. Click \"Complete move\" when you're done.",
4081+
"PARTIAL_SETUP_p2": "Select a cell to place a star point, or select a star point you placed to remove it. You don't have to place any stars, but you typically want to. Click \"Complete move\" when you're done.",
40744082
"SAME_HEIGHT":"Pieces may only move between stacks of the same height."
40754083
},
40764084
"onager": {

src/games/omny.ts

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,22 @@ const starsValid = (g: HexTriGraph, stars: string[]): boolean => {
3333
const [sx, sy] = g.algebraic2coords(star);
3434
// immediate neighbours
3535
const n1 = new Set<string>(g.neighbours(star));
36-
// neighbours of neighbours
36+
// neighbours of neighbours (doesn't apply to size-4 boards)
3737
const n2 = new Set<string>();
38-
[...n1].forEach(cell => g.neighbours(cell).forEach(n => n2.add(n)));
38+
if (g.minwidth > 4) {
39+
[...n1].forEach(cell => g.neighbours(cell).forEach(n => n2.add(n)));
40+
}
3941
// combined
4042
const alln = new Set<string>([...n1, ...n2]);
4143
// delete starting cell
4244
alln.delete(star);
43-
// delete each cell at straight-line distance 2 away
44-
for (const dir of HexTriGraph.directions) {
45-
const ray = g.ray(sx, sy, dir).map(c => g.coords2algebraic(...c));
46-
if (ray.length >= 2) {
47-
alln.delete(ray[1]);
45+
// delete each cell at straight-line distance 2 away (doesn't apply to size-4 boards)
46+
if (g.minwidth > 4) {
47+
for (const dir of HexTriGraph.directions) {
48+
const ray = g.ray(sx, sy, dir).map(c => g.coords2algebraic(...c));
49+
if (ray.length >= 2) {
50+
alln.delete(ray[1]);
51+
}
4852
}
4953
}
5054
// if any of the stars appear in this set, it's invalid
@@ -74,22 +78,24 @@ export class OmnyGame extends GameBase {
7478
}
7579
],
7680
variants: [
81+
{uid: "size-4", group: "board"},
82+
{uid: "size-5", group: "board"},
7783
{uid: "size-6", group: "board"},
7884
{uid: "size-7", group: "board"},
7985
{uid: "size-9", group: "board"},
8086
{uid: "size-10", group: "board"},
8187
{uid: "constellation", group: "stars"},
8288
{uid: "gyre", group: "stars"},
8389
{uid: "yex", group: "stars"},
84-
{uid: "free", group: "stars"},
8590
{uid: "random-3", group: "stars"},
8691
{uid: "random-5", group: "stars"},
8792
{uid: "random-7", group: "stars"},
8893
{uid: "random-9", group: "stars"},
94+
{uid: "free", group: "stars"},
8995
{uid: "captures"},
9096
],
91-
categories: ["goal>connect", "mechanic>place", "mechanic>stack", "mechanic>move", "mechanic>coopt", "board>shape>hex", "board>connect>hex", "components>simple>1per"],
92-
flags: ["experimental", "pie", "automove"]
97+
categories: ["goal>connect", "mechanic>place", "mechanic>stack", "mechanic>move", "mechanic>coopt", "mechanic>random>setup", "board>shape>hex", "board>connect>hex", "components>simple>1per"],
98+
flags: ["experimental", "pie", "automove", "random-start"]
9399
};
94100

95101
public numplayers = 2;
@@ -129,11 +135,21 @@ export class OmnyGame extends GameBase {
129135
const [,numStr] = found.split("-");
130136
const num = parseInt(numStr, 10);
131137
const g = this.graph;
132-
let shuffled = shuffle(g.graph.nodes()) as string[];
133-
let stars = shuffled.slice(0, num);
134-
while (!starsValid(g, stars)) {
135-
shuffled = shuffle(g.graph.nodes()) as string[];
136-
stars = shuffled.slice(0, num);
138+
const shuffled = shuffle(g.graph.nodes()) as string[];
139+
const stars = [shuffled.pop()!];
140+
while (stars.length < num) {
141+
// if we need the rest of the queue to end the loop,
142+
// don't do any testing; just take them
143+
if (shuffled.length === num - stars.length) {
144+
stars.push(...shuffled);
145+
}
146+
// otherwise test the next one
147+
else {
148+
const next = shuffled.pop()!;
149+
if (starsValid(g, [...stars, next])) {
150+
stars.push(next);
151+
}
152+
}
137153
}
138154
this.startStars = [...stars];
139155
}
@@ -244,6 +260,10 @@ export class OmnyGame extends GameBase {
244260
player = this.currplayer;
245261
}
246262

263+
if (this.variants.includes("free") && this.stack.length <= 2) {
264+
return [];
265+
}
266+
247267
const moves: string[] = [];
248268
const graph = this.graph.graph;
249269

@@ -403,11 +423,30 @@ export class OmnyGame extends GameBase {
403423
return result;
404424
}
405425

426+
let complete: 1|0|-1 = -1;
427+
// P1 restrictions
428+
if (this.stack.length === 1) {
429+
if (cells.length > 2 && cells.length - 1 < graph.nodes().length) {
430+
complete = 0
431+
} else if (cells.length - 1 === graph.nodes().length) {
432+
complete = 1;
433+
}
434+
}
435+
// P2 restrictions
436+
else {
437+
if (cells.length > 0) {
438+
complete = 0;
439+
if (this.stars.size + cells.length - 1 === graph.nodes().length) {
440+
complete = 1;
441+
}
442+
}
443+
}
444+
406445
// we're good, but never complete
407446
result.valid = true;
408-
result.complete = cells.length >= 2 ? 0 : -1;
447+
result.complete = complete;
409448
result.canrender = true;
410-
result.message = i18next.t("apgames:validation.omny.PARTIAL_SETUP");
449+
result.message = i18next.t("apgames:validation.omny.PARTIAL_SETUP", {context: this.stack.length === 1 ? "p1" : "p2"});
411450
return result;
412451

413452
}
@@ -689,7 +728,7 @@ export class OmnyGame extends GameBase {
689728
} else if (this.board.has(cell)) {
690729
const contents = this.board.get(cell)! as number[];
691730
// if on a star point, change the glyph name
692-
if (stars.has(cell)) {
731+
if (stars.has(cell) || this.tmpstars.has(cell)) {
693732
contents[contents.length - 1] = contents[contents.length - 1] === 1 ? 3 : 4;
694733
}
695734
node.push(contents.join("").replace(/1/g, "A").replace(/2/g, "B").replace(/3/g, "Y").replace(/4/g, "Z"));
@@ -819,6 +858,13 @@ export class OmnyGame extends GameBase {
819858
// return resolved;
820859
// }
821860

861+
public getStartingPosition(): string {
862+
if (this.startStars !== undefined && this.startStars.length > 0) {
863+
return this.startStars.join(",");
864+
}
865+
return "";
866+
}
867+
822868
public clone(): OmnyGame {
823869
return Object.assign(new OmnyGame(), deepclone(this) as OmnyGame);
824870
// return new OmnyGame(this.serialize());

0 commit comments

Comments
 (0)