UNPKG

tsgammon-core

Version:

A Backgammon library for Typescript, formerly developed as a part of tsgammon-ui

141 lines (140 loc) 6.01 kB
#!/usr/bin/env node "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const AbsoluteMove_1 = require("../AbsoluteMove"); const BoardState_1 = require("../BoardState"); const BoardStateNode_1 = require("../BoardStateNode"); const MatchState_1 = require("../MatchState"); const SimpleNNGammon_1 = require("../engines/SimpleNNGammon"); const GameConf_1 = require("../GameConf"); const MatchRecord_1 = require("../records/MatchRecord"); const PlyRecord_1 = require("../records/PlyRecord"); const SGResult_1 = require("../records/SGResult"); const formatMatchRecord_1 = require("../records/utils/formatMatchRecord"); const Score_1 = require("../Score"); const DiceSource_1 = require("../utils/DiceSource"); const formatAbsMove_1 = require("../utils/formatAbsMove"); const formatBoard_1 = require("../utils/formatBoard"); const formatPly_1 = require("../utils/formatPly"); const formatStake_1 = require("../utils/formatStake"); const toPositionID_1 = require("../utils/toPositionID"); const engine = SimpleNNGammon_1.simpleNNEngine; const conf = Object.assign(Object.assign({}, GameConf_1.standardConf), { jacobyRule: false }); function* runAutoMatch(gameScoreBefore = (0, Score_1.score)(), diceSource = DiceSource_1.randomDiceSource) { const initialBoardState = (0, BoardState_1.boardState)(conf.initialPos); const roll = diceSource.openingRoll(); let { boardStateNode: node, lastPly: ply } = doCheckerPlay(initialBoardState, roll, roll.dice1 > roll.dice2); yield { tag: 'play', before: initialBoardState, after: node.board, ply }; while (!isEoG(node)) { const roll = diceSource.roll(); const { boardStateNode: nextNode, lastPly } = doCheckerPlay(node.board.revert(), roll, !ply.isRed); yield { tag: 'play', before: node.board, after: nextNode.board, ply: lastPly, }; node = nextNode; ply = lastPly; } const eogStatus = node.board.eogStatus(); const stake = (ply.isRed ? Score_1.scoreAsRed : Score_1.scoreAsWhite)(eogStatus.calcStake(1)); const gameScore = gameScoreBefore.add(stake); yield { tag: 'eog', stake, eogStatus, sgResult: ply.isRed ? SGResult_1.SGResult.REDWON : SGResult_1.SGResult.WHITEWON, }; return gameScore; } function doCheckerPlay(boardState, roll, isRed) { const node = (0, BoardStateNode_1.boardStateNode)(boardState, roll); const nextNode = engine.checkerPlay(node); const amoves = (isRed ? AbsoluteMove_1.absoluteMovesRed : AbsoluteMove_1.absoluteMovesWhite)(nextNode.lastMoves()); const lastPly = { moves: amoves, dices: nextNode.dices.map((dice) => dice.pip), isRed, }; return { boardStateNode: nextNode, lastPly }; } function isEoG(boardStateNode) { return boardStateNode.board.eogStatus().isEndOfGame; } const defaultDriver = { doPlay: (evt) => console.log((0, formatPly_1.formatPly)(evt.ply)), doEoG: (evt) => console.log((0, formatStake_1.formatStake)(evt.stake, evt.eogStatus)), doResult: (evt) => console.log(`Result: red ${evt.gameScore.redScore} - white ${evt.gameScore.whiteScore}`), }; function runDriver(driver = defaultDriver) { const evtIt = runAutoMatch(); let e; while (!(e = evtIt.next()).done) { const ev = e.value; switch (ev.tag) { case 'play': driver.doPlay(ev); break; case 'eog': driver.doEoG(ev); } } const gameScore = e.value; driver.doResult({ gameScore }); } function xgDriver(conf) { const matchState = (0, MatchState_1.matchStateForUnlimitedMatch)((0, Score_1.score)(), conf.jacobyRule); const record = { match: (0, MatchRecord_1.matchRecordInPlay)(conf, matchState), }; return Object.assign(Object.assign({}, defaultDriver), { doPlay: (evt) => { record.match = (0, MatchRecord_1.addPlyRecord)(record.match, (0, PlyRecord_1.plyRecordForCheckerPlay)(evt.ply), undefined); }, doEoG: (evt) => { record.eog = (0, MatchRecord_1.eogRecord)(record.match, (0, PlyRecord_1.plyRecordForEoG)(evt.stake, evt.sgResult, evt.eogStatus)); }, doResult: () => { var _a; (0, formatMatchRecord_1.formatMatchRecord)((_a = record.eog) !== null && _a !== void 0 ? _a : record.match) .split('\n') .forEach((l) => console.log(l)); } }); } function boardDriver() { return Object.assign(Object.assign({}, defaultDriver), { doPlay: (evt) => { const { after, ply } = evt; // 移動後の盤面を、X=white 側の視点で出力 const whiteBoard = ply.isRed ? after.revert() : after; // 移動後の盤面から、次の手番プレイヤーの視点でPosIDを生成 const posID = (0, toPositionID_1.toPositionID)(after.revert()); // Moveも画面での座標に合わせて出力(redは1->24, whiteは24->1) const plyStr = (0, formatPly_1.formatPly)(ply, formatAbsMove_1.MoveFormatDirection.ABSOLUTE_INV); console.log(''); console.log((ply.isRed ? 'red: ' : 'white: ') + plyStr); console.log(''); console.log(` tsgammon Position ID: ${posID}`); console.log(''); (0, formatBoard_1.formatBoard)(whiteBoard).forEach((s) => console.log(s)); } }); } function main() { if (process.argv.length < 3) { runDriver(); return; } const arg = process.argv[2]; if (arg === '-?' || arg === '--help') { console.log('usage:'); console.log(' -x: output as text log (.mat format) '); console.log(' -b: output as visual board'); } else if (arg.startsWith('-x')) { runDriver(xgDriver(GameConf_1.standardConf)); } else if (arg.startsWith('-b')) { runDriver(boardDriver()); } else { runDriver(); } } main();