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