hathora-et-labora-game
Version:
Plays Uwe Rosenberg's Ora et Labora for the Hathora engine. It reduces a list of moves into a board game state.
78 lines (73 loc) • 3.15 kB
text/typescript
import { always, any, pipe, view } from 'ramda'
import { P, match } from 'ts-pattern'
import { activeLens, withActivePlayer } from '../board/player'
import { GameCommandCutPeatParams, Tile, BuildingEnum, GameCommandEnum, StateReducer, GameStatePlaying } from '../types'
import { standardSesourceGatheringAction, updateToken, withRondel } from '../board/rondel'
import { checkNotBonusRound, oncePerFrame } from '../board/frame'
import { moorLocations, moorLocationsForCol } from '../board/landscape'
import { shortGameBonusProduction } from '../board/resource'
const removePeatAt = (row: number, col: number) =>
withActivePlayer((player) => {
const tile = player.landscape[row + player.landscapeOffset][col + 2]
const [land, building] = tile
if (building !== BuildingEnum.Moor) return undefined
const landscape = [
...player.landscape.slice(0, row + player.landscapeOffset),
[
...player.landscape[row + player.landscapeOffset].slice(0, col + 2),
// the tile in question
[land] as Tile,
...player.landscape[row + player.landscapeOffset].slice(col + 2 + 1),
],
...player.landscape.slice(row + player.landscapeOffset + 1),
]
return {
...player,
landscape,
}
})
const hasAMoor = (landscape: Tile[][]): boolean =>
any((landRow: Tile[]) => {
return any((tile: Tile): boolean => {
return tile?.[1] === BuildingEnum.Moor
}, landRow)
}, landscape)
export const cutPeat = ({ row, col, useJoker }: GameCommandCutPeatParams): StateReducer =>
pipe(
//
oncePerFrame(GameCommandEnum.CUT_PEAT),
checkNotBonusRound,
standardSesourceGatheringAction('peat', useJoker),
removePeatAt(row, col),
withRondel(updateToken('peat', useJoker)),
shortGameBonusProduction({ peat: 1 })
)
export const complete =
(state: GameStatePlaying) =>
(partial: string[]): string[] => {
const player = view(activeLens(state), state)
return (
match<string[], string[]>(partial)
.with([], () => {
if (checkNotBonusRound(state) === undefined) return []
if (!hasAMoor(player.landscape)) return []
if (oncePerFrame(GameCommandEnum.CUT_PEAT)(state) === undefined) return []
return [GameCommandEnum.CUT_PEAT]
})
.with([GameCommandEnum.CUT_PEAT], () => moorLocations(player))
// shouldnt actually ever see this, but i think its important for completeness
.with([GameCommandEnum.CUT_PEAT, P._], (_, [, c]) => moorLocationsForCol(c, player))
.with([GameCommandEnum.CUT_PEAT, P._, P._], (_, [, c, r]) => {
const row = Number.parseInt(r, 10) + player.landscapeOffset
const col = Number.parseInt(c, 10) + 2
const tile = player.landscape?.[row]?.[col]
if (tile?.[1] !== BuildingEnum.Moor) return []
const options = []
if (state?.rondel?.peat !== undefined) options.push('')
if (state?.rondel?.joker !== undefined) options.push('Jo')
return options
})
.with([GameCommandEnum.CUT_PEAT, P._, P._, 'Jo'], always(['']))
.otherwise(always([]))
)
}