tsgammon-core
Version:
A Backgammon library for Typescript, formerly developed as a part of tsgammon-ui
141 lines (140 loc) • 6.01 kB
JavaScript
;
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();