Skip to content

Commit 00520ca

Browse files
authored
Merge pull request #26 from Xwilarg/game-types
2 parents 98f2c1d + baecc66 commit 00520ca

File tree

5 files changed

+143
-112
lines changed

5 files changed

+143
-112
lines changed

src/Game.js renamed to src/Algs.js

+25-109
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { INVALID_MOVE } from "boardgame.io/core";
21
import { Random } from "./Random";
32

43
function isValid(data, size, x, y) {
@@ -12,17 +11,17 @@ function isValid(data, size, x, y) {
1211
}
1312

1413
function parseMove(dx, dy, length, constraints, data, size, x, y) {
15-
let moves = [];
16-
let orientation = [];
14+
const moves = [];
15+
const orientation = [];
1716
const directions = [
1817
[-dx, -dy], // Forward
1918
[dx, dy], // Backward
2019
[-dy, dx], // Right
2120
[dy, -dx], // Left
2221
];
23-
for (let d of [-1, 1]) {
22+
for (const d of [-1, 1]) {
2423
// For pieces like knights, we need to reverse the X for each direction
25-
for (let rd in directions) {
24+
for (const rd in directions) {
2625
if ((constraints & (2 ** rd)) === 0) {
2726
continue;
2827
}
@@ -31,7 +30,7 @@ function parseMove(dx, dy, length, constraints, data, size, x, y) {
3130
orientation.push(nrd);
3231
}
3332
}
34-
for (let [yi, xi] of orientation) {
33+
for (const [yi, xi] of orientation) {
3534
for (let i = 1; i <= length; i++) {
3635
if (isValid(data, size, x + i * xi, y + i * yi))
3736
moves.push((y + i * yi) * size + (x + i * xi));
@@ -84,7 +83,7 @@ function parseNotation(notation, data, size, x, y) {
8483
let length = 1; // Length we are doing
8584
let moves = [];
8685
let constraints = 15;
87-
for (let s of notation) {
86+
for (const s of notation) {
8887
if (s === s.toLowerCase()) {
8988
if (dir !== null) {
9089
moves = moves.concat(
@@ -165,14 +164,14 @@ const pieceMovesCheck = {
165164
};
166165

167166
export function fillPositions(data) {
168-
let size = Math.sqrt(data.length); // Boards are always squared
167+
const size = Math.sqrt(data.length); // Boards are always squared
169168

170169
for (let y = 0; y < size; y++) {
171170
for (let x = 0; x < size; x++) {
172171
const value = data[y * size + x];
173172
if (!Number.isInteger(value)) {
174-
let moves = parseNotation(pieceMovesCheck[value], data, size, x, y);
175-
for (let move of moves) {
173+
const moves = parseNotation(pieceMovesCheck[value], data, size, x, y);
174+
for (const move of moves) {
176175
data[move]++;
177176
}
178177
}
@@ -183,18 +182,18 @@ export function fillPositions(data) {
183182
}
184183

185184
export function generateBoard(random, id, pieces, size, count) {
186-
let piecesMdf = {};
187-
for (let key in pieces) {
185+
const piecesMdf = {};
186+
for (const key in pieces) {
188187
piecesMdf[key] = pieces[key];
189188
}
190189

191-
let data = Array(size * size).fill(0);
190+
const data = Array(size * size).fill(0);
192191
let i = count;
193192
while (i > 0) {
194193
const rand = Math.floor(random.next() * (size * size));
195194
if (rand !== id && Number.isInteger(data[rand])) {
196195
const value = Math.floor(random.next() * Object.keys(piecesMdf).length);
197-
let piece = Object.keys(piecesMdf)[value];
196+
const piece = Object.keys(piecesMdf)[value];
198197

199198
if (piecesMdf[piece] === 0) {
200199
// We reached the amount of time we could spawn that piece
@@ -222,7 +221,7 @@ export function generateBoard(random, id, pieces, size, count) {
222221
}
223222

224223
function validateBoard(data, discovered, pieces, size) {
225-
let thinkData = Array(size * size).fill(0);
224+
const thinkData = Array(size * size).fill(0);
226225

227226
// For each tile...
228227
for (let i = 0; i < data.length; i++) {
@@ -232,10 +231,10 @@ function validateBoard(data, discovered, pieces, size) {
232231
}
233232

234233
let str = "";
235-
for (let piece of Object.keys(pieces)) {
234+
for (const piece of Object.keys(pieces)) {
236235
// Check all pieces
237236
// List of all moves for the current piece
238-
let moves = parseNotation(
237+
const moves = parseNotation(
239238
pieceMovesCheck[piece],
240239
thinkData,
241240
size,
@@ -245,7 +244,7 @@ function validateBoard(data, discovered, pieces, size) {
245244

246245
// If the piece have a move that is impossible, it means it can't be this one
247246
let isValid = true;
248-
for (let move of moves) {
247+
for (const move of moves) {
249248
if (discovered[move] && data[move] === 0) {
250249
isValid = false;
251250
break;
@@ -299,8 +298,8 @@ export function generatePuzzleBoard(seed, pieces, size, count, difficulty) {
299298
let giveup = false;
300299
while (!isSolved && !giveup) {
301300
// Get a random position that is not a piece and wasn't already taken
302-
let possibilities = [];
303-
for (let i in data) {
301+
const possibilities = [];
302+
for (const i in data) {
304303
if (
305304
!discovered[i] &&
306305
Number.isInteger(data[i]) &&
@@ -310,14 +309,14 @@ export function generatePuzzleBoard(seed, pieces, size, count, difficulty) {
310309
}
311310
}
312311
if (possibilities.length > 0) {
313-
let randPos = Math.floor(random.next() * possibilities.length);
312+
const randPos = Math.floor(random.next() * possibilities.length);
314313
discovered[possibilities[randPos]] = true;
315314
} else {
316315
giveup = true; // Algorithm failed with this generation, we give up
317316
continue;
318317
}
319318

320-
let validation = validateBoard(data, discovered, pieces, size);
319+
const validation = validateBoard(data, discovered, pieces, size);
321320
isSolved = validation["isSolved"];
322321
thinkData = validation["thinkData"];
323322
}
@@ -331,21 +330,21 @@ export function generatePuzzleBoard(seed, pieces, size, count, difficulty) {
331330
}
332331

333332
discovered[i] = false;
334-
let validation = validateBoard(data, discovered, pieces, size);
333+
const validation = validateBoard(data, discovered, pieces, size);
335334
if (!validation["isSolved"]) {
336335
discovered[i] = true;
337336
}
338337
}
339338

340-
let emptyCasesAfter = discovered.filter((x) => x === false).length;
339+
const emptyCasesAfter = discovered.filter((x) => x === false).length;
341340

342341
if (difficulty !== -1 && difficulty > emptyCasesAfter) {
343342
console.log(`Skipping puzzle with ${emptyCasesAfter} empty tiles`);
344343
} else {
345344
if (difficulty !== -1) {
346345
// Set tiles to adjust difficulty
347346

348-
let possibleTarget = [];
347+
const possibleTarget = [];
349348
for (let i = 0; i < data.length; i++) {
350349
if (!discovered[i] && Number.isInteger(data[i])) {
351350
possibleTarget.push(i);
@@ -372,7 +371,7 @@ export function generatePuzzleBoard(seed, pieces, size, count, difficulty) {
372371
error = "Failed to generate puzzle";
373372
} else {
374373
knownCells = Array(size * size).fill(false);
375-
for (let i in discovered) {
374+
for (const i in discovered) {
376375
if (discovered[i]) {
377376
knownCells[i] = true;
378377
}
@@ -381,86 +380,3 @@ export function generatePuzzleBoard(seed, pieces, size, count, difficulty) {
381380

382381
return { cells: data, knownCells, error };
383382
}
384-
385-
function generateClassicBoard(G, id) {
386-
const random = new Random(G.seed);
387-
G.cells = fillPositions(generateBoard(random, id, G.pieces, G.size, G.count));
388-
G.knownCells = Array(G.size * G.size).fill(false);
389-
}
390-
391-
function isWinCondition(G, id) {
392-
if (G.cells === null) {
393-
return false;
394-
}
395-
396-
for (let i = 0; i < G.size * G.size; i++) {
397-
if (!Number.isInteger(G.cells[i])) {
398-
if (G.cells[i] !== G.knownCells[i] && G.cells[i] !== id) {
399-
return false;
400-
}
401-
} else if (G.knownCells[i] !== true && G.knownCells[i] !== false) {
402-
return false;
403-
}
404-
}
405-
406-
return true;
407-
}
408-
409-
export const Game = (setupData) => ({
410-
setup: () => ({
411-
...setupData,
412-
knownCells: setupData.knownCells ?? null,
413-
cells: setupData.cells ?? null,
414-
}),
415-
416-
moves: {
417-
discoverPiece: ({ G, events }, id) => {
418-
if (G.cells === null) {
419-
generateClassicBoard(G, id);
420-
}
421-
422-
if (G.knownCells[id] !== false || G.gamemode === "p") {
423-
return INVALID_MOVE;
424-
}
425-
426-
if (Number.isInteger(G.cells[id])) {
427-
G.knownCells[id] = true;
428-
} else {
429-
events.endGame({ isWin: false });
430-
}
431-
},
432-
433-
placeHint: ({ G, events }, id, action) => {
434-
if (G.cells === null) {
435-
generateClassicBoard(G, id);
436-
}
437-
438-
if (G.knownCells[id] === true) {
439-
return INVALID_MOVE;
440-
}
441-
442-
G.knownCells[id] = action;
443-
444-
if (isWinCondition(G, id)) {
445-
events.endGame({ isWin: true });
446-
}
447-
},
448-
449-
removeHint: ({ G, events }, id) => {
450-
if (G.knownCells[id] === true) {
451-
return INVALID_MOVE;
452-
}
453-
454-
G.knownCells[id] = false;
455-
456-
if (isWinCondition(G, id)) {
457-
events.endGame({ isWin: true });
458-
}
459-
},
460-
},
461-
462-
turn: {
463-
minMoves: 1,
464-
maxMoves: 1,
465-
},
466-
});

src/App.jsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Client as BgioClient } from "boardgame.io/react";
2-
import { Game, generatePuzzleBoard } from "./Game";
2+
import { Game } from "./Game";
3+
import { generatePuzzleBoard } from "./Algs";
34
import { BoardWrapper } from "./components/BoardWrapper";
45
import { parseUrl } from "./Parsing";
56
import { Footer } from "./components/Footer";

src/Game.ts

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { Game as BgioGame } from "boardgame.io";
2+
import { INVALID_MOVE } from "boardgame.io/core";
3+
import { fillPositions, generateBoard } from "./Algs";
4+
import { Random } from "./Random";
5+
6+
type Cell = boolean | number | string;
7+
// todo: replace string with a union type for pieces?
8+
9+
interface SetupData {
10+
seed: string | null;
11+
pieces: Record<string, number>;
12+
size: number;
13+
count: number;
14+
gamemode: "c" | "p";
15+
difficulty: number;
16+
cells?: Cell[] | null;
17+
knownCells?: Cell[] | null;
18+
}
19+
20+
type GameState = Required<SetupData>;
21+
22+
function generateClassicBoard(G: GameState, id: number) {
23+
const random = new Random(G.seed);
24+
G.cells = fillPositions(generateBoard(random, id, G.pieces, G.size, G.count));
25+
G.knownCells = Array(G.size * G.size).fill(false);
26+
}
27+
28+
function isWinCondition(G: GameState, id: number) {
29+
if (G.cells === null) {
30+
return false;
31+
}
32+
33+
for (let i = 0; i < G.size * G.size; i++) {
34+
if (!Number.isInteger(G.cells[i])) {
35+
if (G.cells[i] !== G.knownCells?.[i] && G.cells[i] !== id) {
36+
return false;
37+
}
38+
} else if (G.knownCells?.[i] !== true && G.knownCells?.[i] !== false) {
39+
return false;
40+
}
41+
}
42+
43+
return true;
44+
}
45+
46+
export const Game = (setupData: SetupData): BgioGame<GameState> => ({
47+
setup: () => ({
48+
...setupData,
49+
knownCells: setupData.knownCells ?? null,
50+
cells: setupData.cells ?? null,
51+
}),
52+
53+
moves: {
54+
discoverPiece: ({ G, events }, id: number) => {
55+
if (G.cells === null) {
56+
generateClassicBoard(G, id);
57+
}
58+
59+
// cells and knownCells will be already set or set in generateClassicBoard
60+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
61+
if (G.knownCells![id] !== false || G.gamemode === "p") {
62+
return INVALID_MOVE;
63+
}
64+
65+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
66+
if (Number.isInteger(G.cells![id])) {
67+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
68+
G.knownCells![id] = true;
69+
} else {
70+
events.endGame({ isWin: false });
71+
}
72+
},
73+
74+
placeHint: ({ G, events }, id: number, action: string) => {
75+
if (G.cells === null) {
76+
generateClassicBoard(G, id);
77+
}
78+
79+
// cells and knownCells will be already set or set in generateClassicBoard
80+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
81+
if (G.knownCells![id] === true) {
82+
return INVALID_MOVE;
83+
}
84+
85+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
86+
G.knownCells![id] = action;
87+
88+
if (isWinCondition(G, id)) {
89+
events.endGame({ isWin: true });
90+
}
91+
},
92+
93+
removeHint: ({ G, events }, id: number) => {
94+
if (
95+
G.knownCells?.[id] === true ||
96+
G.cells === null ||
97+
G.knownCells === null
98+
) {
99+
return INVALID_MOVE;
100+
}
101+
102+
G.knownCells[id] = false;
103+
104+
if (isWinCondition(G, id)) {
105+
events.endGame({ isWin: true });
106+
}
107+
},
108+
},
109+
110+
turn: {
111+
minMoves: 1,
112+
maxMoves: 1,
113+
},
114+
});

src/PuzzleGenWorker.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { generatePuzzleBoard } from "./Game";
1+
import { generatePuzzleBoard } from "./Algs";
22

33
onmessage = (e) => {
44
const { seed, pieces, size, count, difficulty } = e.data;

0 commit comments

Comments
 (0)