UNPKG

tsgammon-core

Version:

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

259 lines (242 loc) 10.1 kB
import { BGEventHandler, concatBGListeners } from './BGEventHandler' import { BGListener } from './BGListener' import { cubeGameDispatcher } from './CubeGameDispatcher' import { CBAction, CBEoG, CBInPlay, CBOpening, CBResponse, CBToRoll, } from './CubeGameState' import { eogEventHandler } from './EOGEventHandlers' import { RollListener, rollListener, withRL } from './RollDispatcher' import { singleGameDispatcher } from './SingleGameDispatcher' import { SGEoG, SGInPlay, SGOpening, SGToRoll } from './SingleGameState' /** * BGListenerを後付け可能なBGEventHandler */ export type BGEventHandlersExtensible = BGEventHandler & { addListener: (toAdd: Partial<BGListener>) => BGEventHandlersExtensible } /** * BGEventHandler(BGEventHandlerExtensible)オブジェクトを生成する * @param skipCubeAction キューブアクション要否の判定関数(不要の場合、trueを返す) * @param rListener ダイスのロール目を供給するRollListenerオブジェクト * @param listeners BGEventHandlerに追加するBGListenerの配列 * @returns */ export function buildBGEventHandler( skipCubeAction: (cbState: CBInPlay) => boolean, rListener: RollListener = rollListener(), ...listeners: Partial<BGListener>[] ): BGEventHandlersExtensible { return buildBGEventHandler_rec(skipCubeAction, rListener, {}, ...listeners) } function buildBGEventHandler_rec( skipCubeAction: (cbState: CBInPlay) => boolean, rListener: RollListener = rollListener(), _bgListeners: Partial<BGListener>, ...listeners: Partial<BGListener>[] ): BGEventHandlersExtensible { const bgListeners = listeners.reduce( (prev, cur) => concatBGListeners(prev, cur), _bgListeners ) const handlers = _buildBGEventHandler( skipCubeAction, rListener, bgListeners ) function addListeners(...toAdd: Partial<BGListener>[]) { return buildBGEventHandler_rec( skipCubeAction, rListener, bgListeners, ...toAdd ) } return { ...handlers, addListener: addListeners, } } // singleGameDispatcherとcubeGameDispatcherを組み合わせて、BGEventHandlerを実装している function _buildBGEventHandler( skipCubeAction: (cbState: CBInPlay) => boolean, rollListener: RollListener, bgListeners: Partial<BGListener> ): BGEventHandler { // チェッカープレイでの終局処理の定義 const { onEndOfBGGame: onEndOfCubeGame } = eogEventHandler(bgListeners) const sgDispatcher = withRL(singleGameDispatcher, rollListener) const bgHandler = { onRollOpening: (bgState: { cbState: CBOpening sgState: SGOpening }) => { const sgResult = sgDispatcher.doOpeningRoll(bgState.sgState) sgResult({ // オープニングロールがあった:キューブ側もチェッカープレイに遷移 onOpeningCheckerPlayStarted: (nextSGState: SGInPlay) => { const cbResult = cubeGameDispatcher.doStartOpeningCheckerPlay( bgState.cbState, nextSGState.isRed ) cbResult({ onAwaitCheckerPlay: (nextCBState: CBInPlay) => { bgListeners.onAwaitCheckerPlay?.({ cbState: nextCBState, sgState: nextSGState, }) }, }) }, // オープニングロールのリロール:Cube側には影響がないので、イベント通知のみ onRerollOpening: (nextSGState: SGOpening) => { bgListeners.onBGOpeningRerolled?.({ cbState: bgState.cbState, sgState: nextSGState, }) }, }) }, onCommit: (bgState: { cbState: CBInPlay; sgState: SGInPlay }) => { const sgResult = sgDispatcher.doCommitCheckerPlay(bgState.sgState) sgResult({ // チェッカープレイが終了した:単にイベントを通知するだけにし、 // キューブの状態管理は他のListener(onAwaitRoll, onEndOfCubeGame)に任せる onCheckerPlayCommitted: (committedState: SGInPlay) => { bgListeners.onCommitted?.({ cbState: bgState.cbState, sgState: committedState, }) }, // チェッカープレイが終了し、まだ終局でない: // キューブアクション状態またはロール待ち状態のいずれかへ遷移し、 // 各Listenerに通知する onAwaitRoll: (nextSGState: SGToRoll) => { const cbResult = cubeGameDispatcher.doStartCubeAction( bgState.cbState, skipCubeAction ) cbResult({ onAwaitCubeAction: ( nextCBState: CBAction | CBToRoll ) => { bgListeners.onAwaitCubeAction?.({ cbState: nextCBState, sgState: nextSGState, }) }, onCubeActionStarted: (nextCBState: CBAction) => { bgListeners.onCubeActionStarted?.({ cbState: nextCBState, sgState: nextSGState, }) }, onCubeActionSkipped: (nextCBState: CBToRoll) => { bgListeners.onCubeActionSkipped?.({ cbState: nextCBState, sgState: nextSGState, }) }, }) }, // ゲームが終了した:EoGを通知する onEndOfGame: (nextSGState: SGEoG) => { onEndOfCubeGame( { cbState: bgState.cbState, sgState: nextSGState }, nextSGState.result, nextSGState.eogStatus ) }, }) }, onRoll: (bgState: { cbState: CBToRoll | CBAction sgState: SGToRoll }) => { const sgResult = sgDispatcher.doRoll(bgState.sgState) sgResult({ // チェッカープレイ状態に遷移した:キューブ側でも状態遷移する onCheckerPlayStarted: (nextSGState: SGInPlay) => { const cbResult = cubeGameDispatcher.doStartCheckerPlay( bgState.cbState ) cbResult({ onAwaitCheckerPlay: (nextCBState: CBInPlay) => { bgListeners.onAwaitCheckerPlay?.({ cbState: nextCBState, sgState: nextSGState, }) }, }) }, }) }, // ゲームを開始した:キューブ側でもゲーム開始 onStartGame: () => { const sgResult = sgDispatcher.doStartGame() sgResult({ onGameStarted: () => { const cbResult = cubeGameDispatcher.doStartCubeGame() cbResult({ onCubeGameStarted: () => { bgListeners.onBGGameStarted?.() }, }) }, }) }, // ダブルされた:キューブ側でもダブル onDouble: (bgState: { cbState: CBAction; sgState: SGToRoll }) => { const result = cubeGameDispatcher.doDouble(bgState.cbState) result({ onDoubled: (nextState: CBResponse) => { bgListeners.onDoubled?.( { cbState: nextState, sgState: bgState.sgState, }, bgState.cbState ) }, }) }, // テイクされた:キューブ側でもテイク、さらに必要なら自動ロール onTake: (bgState: { cbState: CBResponse; sgState: SGToRoll }) => { const result = cubeGameDispatcher.doTake(bgState.cbState) result({ onDoubleAccepted: (nextState: CBToRoll) => { bgListeners.onDoubleAccepted?.( { cbState: nextState, sgState: bgState.sgState, }, bgState.cbState ) // Take後は自動ロール bgHandler.onRoll({ cbState: nextState, sgState: bgState.sgState, }) }, }) }, // パスされた:キューブ側でもパス(終局状態となる) onPass: (bgState: { cbState: CBResponse; sgState: SGToRoll }) => { const result = cubeGameDispatcher.doPass(bgState.cbState) result({ onEndOfCubeGame: (cbEoG: CBEoG) => { bgListeners.onEndOfBGGame?.({ cbState: cbEoG, sgState: bgState.sgState, }) }, }) }, } return bgHandler }