// Plays seeded random games with the TypeScript engine (packages/mancala.js) and writes every move and its outcome, // so EngineParityTest can check that the Java engine plays exactly the same. Run it again after changing the rules: // // yarn --cwd ../../packages/mancala.js build && node scripts/record-engine-games.js // // Moves include illegal ones (the wrong player, an empty pit, a store, out of range) and moves after the end. const fs = require("fs"); const path = require("path"); const { CommonMancalaGame } = require("../../../packages/mancala.js"); const GAME_COUNT = 15; const MAX_MOVES = 400; const OUTPUT = path.join(__dirname, "../src/test/resources/engine/recorded-games.json"); // mulberry32 function random(seed) { return () => { seed |= 0; seed = (seed + 0x6d2b79f5) | 0; let t = Math.imul(seed ^ (seed >>> 15), 1 | seed); t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; } function recordGame(number) { const next = random(number + 1); const pick = (items) => items[Math.floor(next() * items.length)]; const game = new CommonMancalaGame(`game-${number}`, `kc:player1-${number}`, `kc:player2-${number}`); game.turnPlayerId = next() < 0.5 ? game.player1Id : game.player2Id; const recorded = { id: game.id, player1Id: game.player1Id, player2Id: game.player2Id, turnPlayerId: game.turnPlayerId, moves: [], }; let movesAfterEnd = 0; for (let i = 0; i < MAX_MOVES && movesAfterEnd < 2; i++) { const roll = next(); const opponent = game.turnPlayerId === game.player1Id ? game.player2Id : game.player1Id; const playerId = roll < 0.93 ? game.turnPlayerId : opponent; const pitsWithStones = [0, 1, 2, 3, 4, 5].filter((pit) => game.board.pits[game.getBoardIndexByPlayerId(playerId, pit)].stoneCount > 0); const pitIndex = roll < 0.85 && pitsWithStones.length > 0 ? pick(pitsWithStones) : roll < 0.97 ? Math.floor(next() * 6) : pick([-1, 6, 7, 13, 14]); const move = { playerId, pitIndex }; try { game.moveByPlayerPit(playerId, pitIndex); } catch (err) { move.error = err.message; } Object.assign(move, { board: game.board.getStoneArray(), turnPlayerId: game.turnPlayerId, state: game.state }); recorded.moves.push(move); if (game.state === "ended") movesAfterEnd++; } recorded.wonPlayerId = game.getWonPlayerId() ?? null; recorded.game = JSON.parse(JSON.stringify(game)); return recorded; } const games = Array.from({ length: GAME_COUNT }, (_, number) => recordGame(number)); fs.writeFileSync(OUTPUT, JSON.stringify({ games }) + "\n"); const moves = games.reduce((sum, game) => sum + game.moves.length, 0); console.log(`Recorded ${games.length} games, ${moves} moves, to ${path.relative(process.cwd(), OUTPUT)}`);