tsgammon-core
Version:
A Backgammon library for Typescript, formerly developed as a part of tsgammon-ui
182 lines (169 loc) • 5.11 kB
text/typescript
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,
}
}