UNPKG

tsgammon-core

Version:

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

411 lines (369 loc) 10.8 kB
import { AbsoluteBoardState, redViewAbsoluteBoard, whiteViewAbsoluteBoard, } from '../AbsoluteBoardState' import { AbsoluteMove, makeMoveAbsoluteAsRed, makeMoveAbsoluteAsWhite, } from '../AbsoluteMove' import { BoardState } from '../BoardState' import { BoardStateNode, boardStateNode } from '../BoardStateNode' import { Dice, DicePip, DiceRoll } from '../Dices' import { EOGStatus } from '../EOGStatus' import { Move } from '../Move' import { Ply } from '../Ply' import { SGResult } from '../records/SGResult' import { score, Score, scoreAsRed, scoreAsWhite } from '../Score' /** * キューブレスのバックギャモンの局面の状態を定義した型 */ export type SGState = SGOpening | SGInPlay | SGToRoll | SGEoG export type SGInPlay = SGInPlayRed | SGInPlayWhite export type SGToRoll = SGToRollRed | SGToRollWhite export type SGEoG = SGEoGRedWon | SGEoGWhiteWon | SGEoGNoGame type _SGState = { absBoard: AbsoluteBoardState boardState: BoardState } export type SGOpening = Omit<_SGState, 'lastPly'> & { tag: 'SGOpening' dicePip?: DicePip doOpening: (openingRoll: DiceRoll) => SGInPlay | SGOpening } type _SGInPlay = _SGState & { tag: 'SGInPlay' dices: Dice[] curPly: Ply boardStateNode: BoardStateNode revertTo: BoardStateNode toPly: (boardStateNode: BoardStateNode) => Ply toAbsBoard: (boardState: BoardState) => AbsoluteBoardState toPos: (absPos: number) => number } export type SGInPlayRed = _SGInPlay & { isRed: true doCheckerPlayCommit: ( boardStateNode: BoardStateNode, revertTo: BoardStateNode ) => SGToRollWhite | SGEoGRedWon withNode: (boardStateNode: BoardStateNode) => SGInPlayRed } export type SGInPlayWhite = _SGInPlay & { isRed: false doCheckerPlayCommit: ( boardStateNode: BoardStateNode, revertTo: BoardStateNode ) => SGToRollRed | SGEoGWhiteWon withNode: (boardStateNode: BoardStateNode) => SGInPlayWhite } type _SGToRoll = _SGState & { tag: 'SGToRoll' lastPly: Ply } export type SGToRollRed = _SGToRoll & { isRed: true doRoll: (dices: DiceRoll) => SGInPlayRed } export type SGToRollWhite = _SGToRoll & { isRed: false doRoll: (dices: DiceRoll) => SGInPlayWhite } export type _SGEoG = _SGState & { tag: 'SGEoG' dices: Dice[] stake: Score eogStatus: EOGStatus } export type SGEoGRedWon = _SGEoG & { result: SGResult.REDWON isRed: true } export type SGEoGWhiteWon = _SGEoG & { result: SGResult.WHITEWON isRed: false } export type SGEoGNoGame = _SGEoG & { result: SGResult.NOGAME } export function openingState( boardState: BoardState, dicePip: DicePip | undefined, movesForDouble = 4 ): SGOpening { const absBoard = whiteViewAbsoluteBoard(boardState) return { tag: 'SGOpening', dicePip, absBoard, boardState, doOpening: (openingRoll: DiceRoll) => { if (openingRoll.dice1 === openingRoll.dice2) { return openingState( boardState, openingRoll.dice1, movesForDouble ) } else { const isRedPlayerFirst = openingRoll.dice1 > openingRoll.dice2 const boardToPlay = isRedPlayerFirst ? boardState.revert() : boardState const inPlayState = isRedPlayerFirst ? inPlayStateRed : inPlayStateWhite return inPlayState(boardToPlay, openingRoll, movesForDouble) } }, } } export function inPlayStateRed( boardState: BoardState, dices: DiceRoll, movesForDouble = 4 ): SGInPlayRed { const node = boardStateNode(boardState, dices, movesForDouble) return inPlayStateRedFromNode( node, { dices: node.dices.map((dice) => dice.pip), moves: [], isRed: true, }, node, movesForDouble ) } export function inPlayStateRedFromNode( boardStateNode: BoardStateNode, curPly: Ply, revertTo: BoardStateNode, movesForDouble: number ): SGInPlayRed { const dices = boardStateNode.dices const absBoard = redViewAbsoluteBoard(boardStateNode.board) const toPly = buildToPly(boardStateNode.dices, makeMoveAbsoluteAsRed, true) const doCheckerPlayCommit = buildDoCheckerCommit( toPly, (boardState: BoardState, lastPly: Ply) => toRollStateWhite(boardState, lastPly, movesForDouble), (stakeValue: number, eog: EOGStatus, committed: BoardStateNode) => eogStateRed( eog.calcStake(stakeValue), eog, redViewAbsoluteBoard(committed.board), committed.board ) ) return { curPly, tag: 'SGInPlay', boardStateNode, boardState: boardStateNode.board, dices, absBoard, revertTo, toPly, toPos: (n: number) => boardStateNode.board.invertPos(n), toAbsBoard: redViewAbsoluteBoard, isRed: true, doCheckerPlayCommit, withNode: (node: BoardStateNode) => { return inPlayStateRedFromNode( node, toPly(node), revertTo, movesForDouble ) }, } } export function inPlayStateWhite( boardState: BoardState, dices: DiceRoll, movesForDouble = 4 ): SGInPlayWhite { const node = boardStateNode(boardState, dices, movesForDouble) return inPlayStateWhiteFromNode( node, { dices: node.dices.map((dice) => dice.pip), moves: [], isRed: false, }, node, movesForDouble ) } function inPlayStateWhiteFromNode( boardStateNode: BoardStateNode, curPly: Ply, revertTo: BoardStateNode, movesForDouble: number ): SGInPlayWhite { const dices = boardStateNode.dices const absBoard = whiteViewAbsoluteBoard(boardStateNode.board) const toPly = buildToPly( boardStateNode.dices, makeMoveAbsoluteAsWhite, false ) const doCheckerPlayCommit = buildDoCheckerCommit( toPly, (boardState: BoardState, lastPly: Ply) => toRollStateRed(boardState, lastPly, movesForDouble), (stakeValue: number, eog: EOGStatus, committed: BoardStateNode) => eogStateWhite( eog.calcStake(stakeValue), eog, whiteViewAbsoluteBoard(committed.board), committed.board ) ) return { curPly, tag: 'SGInPlay', boardStateNode, boardState: boardStateNode.board, dices, absBoard, revertTo, toPly, toPos: (n: number) => n, toAbsBoard: whiteViewAbsoluteBoard, isRed: false, doCheckerPlayCommit, withNode: (node: BoardStateNode) => { return inPlayStateWhiteFromNode( node, toPly(node), revertTo, movesForDouble ) }, } } function buildDoCheckerCommit<TOROLL extends SGToRoll, EOG extends SGEoG>( toPly: (board: BoardStateNode) => Ply, toRollState: (boardState: BoardState, lastPly: Ply) => TOROLL, toEoGState: ( stakeValue: number, eog: EOGStatus, committed: BoardStateNode ) => EOG ): (committed: BoardStateNode) => EOG | TOROLL { return (committed: BoardStateNode) => { const ply = toPly(committed) const boardState = committed.board const eogStatus = boardState.eogStatus() if (eogStatus.isEndOfGame) { return toEoGState(1, committed.board.eogStatus(), committed) } else { // 手番プレイヤーと相対表記の盤面とをそれぞれ入れ替える const nextBoardState = boardState.revert() return toRollState(nextBoardState, ply) } } } export function toRollStateRed( boardState: BoardState, lastPly: Ply = { moves: [], dices: [], isRed: true }, movesForDouble = 4 ): SGToRollRed { const absBoard = redViewAbsoluteBoard(boardState) return { tag: 'SGToRoll', boardState, absBoard, isRed: true, lastPly, doRoll: (dices: DiceRoll): SGInPlayRed => { return inPlayStateRed(boardState, dices, movesForDouble) }, } } export function toRollStateWhite( boardState: BoardState, lastPly: Ply = { moves: [], dices: [], isRed: false }, movesForDouble = 4 ): SGToRollWhite { const absBoard = whiteViewAbsoluteBoard(boardState) return { tag: 'SGToRoll', boardState, absBoard, isRed: false, lastPly, doRoll: (dices: DiceRoll): SGInPlayWhite => { return inPlayStateWhite(boardState, dices, movesForDouble) }, } } export function eogStateRed( stakeValue: number, eogStatus: EOGStatus, absBoard: AbsoluteBoardState, boardState: BoardState ): SGEoGRedWon { const stake = scoreAsRed(stakeValue) return { ...eogState(eogStatus, stake, absBoard, boardState), result: SGResult.REDWON, isRed: true, } } export function eogStateWhite( stakeValue: number, eogStatus: EOGStatus, absBoard: AbsoluteBoardState, boardState: BoardState ): SGEoGWhiteWon { const stake = scoreAsWhite(stakeValue) return { ...eogState(eogStatus, stake, absBoard, boardState), result: SGResult.WHITEWON, isRed: false, } } export function eogStateNogame( eogStatus: EOGStatus, absBoard: AbsoluteBoardState, boardState: BoardState ): SGEoG { return { ...eogState(eogStatus, score(), absBoard, boardState), result: SGResult.NOGAME, } } function eogState( eogStatus: EOGStatus, stake: Score, absBoard: AbsoluteBoardState, boardState: BoardState ): _SGEoG { return { tag: 'SGEoG', absBoard, boardState, dices: [], stake, eogStatus, } } function buildToPly( dices: Dice[], toAbsMove: (move: Move, invertPos: (pos: number) => number) => AbsoluteMove, isRed: boolean ): (boardStateNode: BoardStateNode) => Ply { return (boardStateNode: BoardStateNode) => { return { moves: boardStateNode .lastMoves() .map((m) => toAbsMove(m, boardStateNode.board.invertPos)), dices: dices.map((dice) => dice.pip), isRed: isRed, isWhite: !isRed, } } }