#!/usr/bin/env node /** * Convert TGN files to move sequences (JSON format) * * Output format for C++ consumption: * { * "boardShape": { "x": 5, "y": 5, "z": 1 }, * "moves": [ * { "x": 1, "y": 2, "z": 0, "player": 1 }, // BLACK = 1 * { "x": 2, "y": 2, "z": 0, "player": 2 }, // WHITE = 2 * null, // pass move * ... * ] * } */ import { TrigoGame, StoneType, StepType } from "@inc/trigo/game.js"; import { calculateTerritory } from "@inc/trigo/gameUtils.js"; import { initializeParsers } from "@inc/trigo/parserInit.js"; import * as fs from "fs"; import * as path from "path"; interface MoveSequenceOutput { tgnFile: string; boardShape: { x: number; y: number; z: number }; moves: Array<{ x: number; y: number; z: number; player: number } | null>; finalBoard: number[][][]; territory: { black: number; white: number; neutral: number; }; error?: string; } function convertTgnToMoveSequence(tgnFilePath: string): MoveSequenceOutput { try { const tgnContent = fs.readFileSync(tgnFilePath, "utf-8"); // Parse TGN const game = TrigoGame.fromTGN(tgnContent); const shape = game.getShape(); const history = game.getHistory(); // Convert steps to simple move format const moveSequence = history .filter(step => step.type === StepType.DROP || step.type === StepType.PASS) .map(step => { if (step.type === StepType.PASS) { return null; // Pass move } if (step.type === StepType.DROP && step.position) { return { x: step.position.x, y: step.position.y, z: step.position.z, player: step.player === StoneType.BLACK ? 1 : 2 // BLACK = 1, WHITE = 2 }; } return null; }); // Get final board state and territory const board = game.getBoard(); const territory = calculateTerritory(board, shape); return { tgnFile: path.basename(tgnFilePath), boardShape: shape, moves: moveSequence, finalBoard: board, territory: { black: territory.black, white: territory.white, neutral: territory.neutral } }; } catch (error) { return { tgnFile: path.basename(tgnFilePath), boardShape: { x: 0, y: 0, z: 0 }, moves: [], finalBoard: [], territory: { black: 0, white: 0, neutral: 0 }, error: error instanceof Error ? error.message : String(error) }; } } function main() { const args = process.argv.slice(2); if (args.length === 0) { console.error("Usage: tgnToMoveSequence.ts [ ...]"); console.error(""); console.error("Converts TGN files to move sequences for C++ validation."); console.error("Outputs JSON to stdout."); process.exit(1); } // Initialize parsers first initializeParsers().then(() => { const results: MoveSequenceOutput[] = []; for (const tgnFile of args) { if (!fs.existsSync(tgnFile)) { console.error(`File not found: ${tgnFile}`); results.push({ tgnFile: path.basename(tgnFile), boardShape: { x: 0, y: 0, z: 0 }, moves: [], finalBoard: [], territory: { black: 0, white: 0, neutral: 0 }, error: "File not found" }); continue; } const result = convertTgnToMoveSequence(tgnFile); results.push(result); } // Output as JSON console.log(JSON.stringify(results, null, 2)); }).catch(error => { console.error("Error initializing parsers:", error); process.exit(1); }); } main();