UNPKG

tsgammon-core

Version:

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

182 lines (169 loc) 5.11 kB
import { BGListener } from '../dispatchers/BGListener' import { BGState } from '../dispatchers/BGState' import { CBAction, CBEoG, CBInPlay, CBResponse, CBToRoll, } from '../dispatchers/CubeGameState' import { SingleGameListener } from '../dispatchers/SingleGameListener' import { SGEoG, SGInPlay, SGState, SGToRoll, } from '../dispatchers/SingleGameState' import { GameConf } from '../GameConf' import { addPlyRecord, discardCurrentGame, eogRecord, MatchRecord, recordFinishedGame, trimPlyRecords, } from './MatchRecord' import { PlyRecordEoG, plyRecordForCheckerPlay, plyRecordForDouble, plyRecordForEoG, plyRecordForPass, plyRecordForTake, PlyRecordInPlay, } from './PlyRecord' import { PlyStateRecord } from './PlyStateRecord' import { SGResult } from './SGResult' export type MatchRecorder<T> = { recordPly: (plyRecord: PlyRecordInPlay, lastState: T) => void recordEoG: (plyRecord: PlyRecordEoG) => void resetCurGame: () => void resumeTo: (index: number) => PlyStateRecord<T> } /** * MatchRecorderをラップして、SingleGameListnerとして返す * @param matchRecorder ラップされるMatchRecorder * @returns */ export function matchRecorderAsSG( matchRecorder: MatchRecorder<SGState> ): Partial<SingleGameListener> { return { onCheckerPlayCommitted: (committedState: SGInPlay) => { matchRecorder.recordPly( plyRecordForCheckerPlay(committedState.curPly), committedState ) }, onGameStarted: () => { matchRecorder.resetCurGame() }, onEndOfGame: (sgEoG: SGEoG) => { const { stake, result, eogStatus } = sgEoG matchRecorder.recordEoG(plyRecordForEoG(stake, result, eogStatus)) }, } } /** * MatchRecorderをラップして、BGListnerとして返す * @param matchRecorder ラップされるMatchRecorder * @returns */ export function matchRecorderAsBG( gameConf: GameConf, matchRecorder: MatchRecorder<BGState> ): Partial<BGListener> { return { onDoubled: ( bgState: { cbState: CBResponse; sgState: SGToRoll }, lastState: CBAction ) => { const plyRecord = plyRecordForDouble( lastState.cubeState, lastState.isRed ) matchRecorder.recordPly(plyRecord, { cbState: lastState, sgState: bgState.sgState, }) }, onDoubleAccepted: ( bgState: { cbState: CBToRoll; sgState: SGToRoll }, lastState: CBResponse ) => { const plyRecord = plyRecordForTake(lastState.isRed) matchRecorder.recordPly(plyRecord, { cbState: lastState, sgState: bgState.sgState, }) }, onPassed: ( bgState: { cbState: CBResponse; sgState: SGToRoll }, isRedWon: boolean ) => { const plyRecord = plyRecordForPass( isRedWon ? SGResult.REDWON : SGResult.WHITEWON ) matchRecorder.recordPly(plyRecord, bgState) }, onBGGameStarted: () => { matchRecorder.resetCurGame() }, onCommitted: (bgState: { cbState: CBInPlay; sgState: SGInPlay }) => { const committedState = bgState.sgState matchRecorder.recordPly( plyRecordForCheckerPlay(committedState.curPly), bgState ) }, onEndOfBGGame: (bgState: { cbState: CBEoG; sgState: SGState }) => { const { stake, eogStatus } = bgState.cbState.calcStake(gameConf) const plyRecordEoG = plyRecordForEoG( stake, bgState.cbState.result, eogStatus ) matchRecorder.recordEoG(plyRecordEoG) }, } } /** * MatchRecorderオブジェクトを生成する * * @param matchRecord * @param setMatchRecord * @returns */ export function buildMatchRecorder<T>( matchRecord: MatchRecord<T>, setMatchRecord: (f: (prev: MatchRecord<T>) => MatchRecord<T>) => void ): MatchRecorder<T> { function recordPly(plyRecord: PlyRecordInPlay, state: T) { setMatchRecord((prev) => prev.isEoG ? prev : addPlyRecord(prev, plyRecord, state) ) } function recordEoG(eogPlyRecord: PlyRecordEoG) { setMatchRecord((prev) => { if (prev.isEoG) { return prev } return eogRecord(prev, eogPlyRecord) }) } function resetCurGame() { setMatchRecord((prev) => prev.isEoG ? recordFinishedGame(prev) : discardCurrentGame(prev) ) } function resumeTo(index: number): PlyStateRecord<T> { setMatchRecord((prev) => trimPlyRecords(prev, index)) return matchRecord.curGameRecord.plyRecords[index] } return { recordEoG, resetCurGame, recordPly, resumeTo, } }