@nodots-llc/backgammon-ai
Version:
AI and integration for nodots-backgammon using gnubg as a backend engine.
123 lines (122 loc) • 5.56 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getGnubgMoveHint = getGnubgMoveHint;
exports.getBestMoveForStartingPosition = getBestMoveForStartingPosition;
exports.selectMoveFromList = selectMoveFromList;
// Entry point for nodots-backgammon-ai
const child_process_1 = require("child_process");
const moveAnalyzers_1 = require("./moveAnalyzers");
/**
* Parses the output from gnubg 'hint' command to extract the best move string.
* Assumes the best move is on the line starting with '1. ' and contains a move like '8/4 6/4'.
* @param hintOutput The raw output from gnubg.
* @returns The best move string, or null if not found.
*/
function parseBestMoveFromHint(hintOutput) {
const lines = hintOutput.split('\n');
for (const line of lines) {
const match = line.match(/^\s*1\.\s+[^\s]+\s+[^\s]+\s+((?:[a-zA-Z0-9*]+\/[a-zA-Z0-9*]+(?:\*|)?\s*)+)/);
if (match) {
return match[1].trim();
}
}
// Fallback: look for "gnubg moves ..." line
for (const line of lines) {
const match = line.match(/gnubg moves ([\w/* ]+)\./i);
if (match) {
return match[1].trim();
}
}
return null;
}
/**
* Calls GNU Backgammon to get the best move for a given position ID.
*
* @param positionId The GNU Backgammon Position ID.
* @returns A promise that resolves with the best move string (e.g., '8/4 6/4').
* @throws Error if gnubg execution fails or if the best move cannot be parsed.
*/
function getGnubgMoveHint(positionId) {
return __awaiter(this, void 0, void 0, function* () {
const commands = `new game\nset board ${positionId}\nhint`;
return new Promise((resolve, reject) => {
const gnubgCommand = process.platform === 'win32' ? 'gnubg-cli.exe' : 'gnubg -t';
// Pipe commands to gnubg via stdin
(0, child_process_1.exec)(`echo "${commands}" | ${gnubgCommand}`, (error, stdout, stderr) => {
if (error) {
console.error(`Error executing gnubg: ${error.message}`);
console.error(`gnubg stderr: ${stderr}`);
reject(new Error(`Failed to execute gnubg. ${stderr || error.message}`));
return;
}
// Log the actual gnubg output for debugging
console.log('\n--- GNU BG Output ---\n' +
stdout +
'\n--- End of GNU BG Output ---\n');
const bestMove = parseBestMoveFromHint(stdout);
if (!bestMove) {
reject(new Error('Could not parse best move from gnubg output.'));
return;
}
console.log('Best move:', bestMove);
resolve(bestMove);
});
});
});
}
/**
* Gets the best move for the standard starting position.
* @returns A promise that resolves with the best move string.
*/
function getBestMoveForStartingPosition() {
return __awaiter(this, void 0, void 0, function* () {
const startingPositionId = '4HPwATDgc/ABMA'; // Standard starting position
return getGnubgMoveHint(startingPositionId);
});
}
/**
* Selects a move from a list using the specified analyzer (defaults to random).
* @param moves List of BackgammonMoveBase
* @param analyzer Optional MoveAnalyzer (defaults to RandomMoveAnalyzer)
* @returns The selected move, or null if the list is empty
*/
function selectMoveFromList(moves, analyzer) {
return __awaiter(this, void 0, void 0, function* () {
const moveAnalyzer = analyzer || new moveAnalyzers_1.RandomMoveAnalyzer();
return moveAnalyzer.selectMove(moves);
});
}
// Example usage (assuming you have a Position ID from nodots-backgammon-core):
// async function main() {
// // Replace with a real Position ID from your game
// const examplePositionId = "4HPwATDgc/ABMA"; // Standard starting position
// console.log(\`Requesting hint for position: \${examplePositionId}\`);
//
// try {
// const hintOutput = await getGnubgMoveHint(examplePositionId);
// console.log("\\n--- GNU BG Hint Output ---");
// console.log(hintOutput);
// console.log("--- End of GNU BG Hint Output ---");
//
// // TODO: Implement parsing for hintOutput to extract the top-ranked move.
// // The output format for the 'hint' command needs to be understood to parse it correctly.
// // It typically lists possible moves with their equities and other stats.
// // For example, you might look for the line starting with "1. Cubeful 0-ply ..."
// // and extract the move description (e.g., "8/4 6/4").
// console.log("\\nNext step: Parse the output above to find the best move.");
//
// } catch (error) {
// console.error("Error getting GNU BG hint:", error);
// }
// }
//
// main();