UNPKG

tsgammon-core

Version:

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

105 lines (104 loc) 5.55 kB
"use strict"; 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, ]; }; }