tsgammon-core
Version:
A Backgammon library for Typescript, formerly developed as a part of tsgammon-ui
105 lines (104 loc) • 5.55 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildNodesForDoublet = void 0;
const BoardStateNode_1 = require("../BoardStateNode");
const Dices_1 = require("../Dices");
const applyDicePipToPoints_1 = require("./applyDicePipToPoints");
const NodeBuilder_1 = require("./NodeBuilder");
/**
* ゾロ目に対して、バックギャモンのルールに基づいて可能な手を列挙し、BoardStateNodeとして返す
* @param board 盤面
* @param dicePip ダイスの目
* @param count ダイスの目を使える回数(省略時は4)
* @returns
*/
function buildNodesForDoublet(board, dicePip, count) {
const unusedDices = (0, Dices_1.dices)(dicePip, count);
const [node] = buildNodesForDoubletRec(board, unusedDices, dicePip, [], [], false);
return node;
}
exports.buildNodesForDoublet = buildNodesForDoublet;
/**
* 未使用のダイスが残り2個になるまで再帰しつつ、可能な局面のツリーを構築する
*
* @param board 盤面
* @param unusedDices 未使用のダイス
* @param pip 適用しようとしている目
* @param usedDices 使用済みのダイス
* @param lastMoves この局面に至るまでの手
* @param isAlreadyRedundant すでに冗長かどうか。ゾロ目の場合、冗長なノードの下位のムーブは全て冗長となる。
* @returns
*/
function buildNodesForDoubletRec(board, unusedDices, pip, usedDices, lastMoves, isAlreadyRedundant) {
// 子ノードに格納する内容を整えて、子ノードのビルダーを準備する
// ダイスは1つ使用済みになる
const dicesUsedUp = usedDices.concat({ pip, used: true });
// 未使用ダイスが残り2個なら末端(とその親)ノードの生成、そうでなければ再帰
const nodeBuilder = unusedDices.length === 2
? leaveNodesAndParentBuilder(pip, dicesUsedUp, lastMoves, isAlreadyRedundant)
: recursiveNodeBuilder(unusedDices, pip, dicesUsedUp, lastMoves, isAlreadyRedundant);
// 各ポイントについて、子ノードを構築する
const [major, marked] = (0, applyDicePipToPoints_1.applyDicePipToPoints)(board, pip, nodeBuilder, unusedDices.length);
// 子ノードで使えなかったダイスの数=markedを、使用不可としてマークする
const markAfter = unusedDices.length - marked;
const dices = usedDices.concat(Array.from({ length: unusedDices.length }, (_, idx) => {
return idx < marked ? { pip, used: true } : { pip, used: false };
}));
return [
{
hasValue: true,
dices,
board: board,
majorFirst: major,
minorFirst: () => BoardStateNode_1.NO_MOVE,
lastMoves: () => lastMoves,
isRedundant: false,
isCommitable: markAfter === 0,
},
marked,
];
}
// 最後の二つになるまでは、未使用ダイスを1つず減らしながら再帰するだけ
function recursiveNodeBuilder(unusedDices, pip, usedDices, lastMoves, isAlreadyRedundant) {
return (board, move) => {
return buildNodesForDoubletRec(board, unusedDices.slice(1), pip, usedDices, lastMoves.concat(move), isAlreadyRedundant || lastMoveIsRedundant(lastMoves, move));
};
}
// 最後に適用した手が、それまでに適用した手より手前の駒を動かす場合は、冗長なムーブ
function lastMoveIsRedundant(lastMoves, move) {
return (lastMoves.length > 0 && move.from < lastMoves[lastMoves.length - 1].from);
}
// buildNodes()で構築するツリーの、最末端ノードを構築する
// すなわち、最後の一つのダイスを各ポイントに適用した結果を返す
function leaveNodesAndParentBuilder(pip, usedDices, lastMoves, isAlreadyRedundant) {
return (board, moveBeforeFinal) => {
const lastMovesForLeave = lastMoves.concat(moveBeforeFinal);
const isEog = board.eogStatus().isEndOfGame;
if (isEog) {
// ゾロ目の場合、ダイスの入れ替えを気にする必要はないので、
// marked=1とすることで、EoGに達して余ったダイスは使用済み扱いになる。
// (よって、ロール直後は最後の一つが使えない状態としてマークされる)
return (0, NodeBuilder_1.buildNodeForEoG)(board, usedDices.concat({ pip, used: true }), lastMovesForLeave, 1);
}
// 最後のpipを適用して末端ノードを生成する
const isAlreadyRedundantForLeave = isAlreadyRedundant ||
lastMoveIsRedundant(lastMoves, moveBeforeFinal);
const nodeBuilder = (0, NodeBuilder_1.leaveNodeBuilder)(usedDices, pip, (finalMove) => lastMovesForLeave.concat(finalMove), (finalMove) => isAlreadyRedundantForLeave ||
lastMoveIsRedundant(lastMovesForLeave, finalMove));
const [major, unusedDices] = (0, applyDicePipToPoints_1.applyDicePipToPoints)(board, pip, nodeBuilder, 1);
// 末端ノードの親ノードを生成して返す
return [
{
hasValue: true,
dices: usedDices.concat([{ pip, used: unusedDices === 1 }]),
board: board,
majorFirst: major,
minorFirst: () => BoardStateNode_1.NO_MOVE,
lastMoves: () => lastMovesForLeave,
isRedundant: false,
isCommitable: unusedDices === 1,
},
unusedDices,
];
};
}