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.
110 lines (101 loc) • 3.57 kB
text/typescript
import { always, lift, map, pipe, range, reverse, view, without } from 'ramda'
import { P, match } from 'ts-pattern'
import { activeLens, subtractCoins, withActivePlayer } from '../board/player'
import { GameCommandConvertParams, GameCommandEnum, GameStatePlaying, ResourceEnum, Tableau } from '../types'
import { stringRepeater } from '../board/resource'
const convertGrain =
(amount = 0) =>
(player: Tableau | undefined) =>
player && {
...player,
grain: (player.grain ?? 0) - amount,
straw: (player.straw ?? 0) + amount,
}
const convertWine =
(amount = 0) =>
(player: Tableau | undefined) =>
player && {
...player,
wine: (player.wine ?? 0) - amount,
penny: (player.penny ?? 0) + amount,
}
const convertNickel =
(amount = 0) =>
(player: Tableau | undefined) =>
player && {
...player,
nickel: (player.nickel ?? 0) - amount,
penny: (player.penny ?? 0) + amount * 5,
}
const convertWhiskey =
(amount = 0) =>
(player: Tableau | undefined) =>
player && {
...player,
whiskey: (player.whiskey ?? 0) - amount,
penny: (player.penny ?? 0) + amount * 2,
}
const convertPenny =
(amount = 0) =>
(player: Tableau | undefined) => {
const subtracted = subtractCoins(amount)(player)
if (subtracted === undefined) return undefined
const p = { ...subtracted }
// janice, then do the thing
p.nickel += amount / 5
p.penny += amount % 5
return p
}
export const convert = ({ grain, wine, nickel, whiskey, penny }: GameCommandConvertParams) => {
if ((penny ?? 0) % 5 !== 0) return () => undefined
return withActivePlayer(
pipe(
//
convertGrain(grain),
convertWine(wine),
convertNickel(nickel),
convertWhiskey(whiskey),
convertPenny(penny)
)
)
}
export const complete =
(state: GameStatePlaying) =>
(partial: string[]): string[] =>
match<string[], string[]>(partial)
.with([], () => {
if (
state.frame.currentPlayerIndex === state.frame.activePlayerIndex &&
withActivePlayer((player) => {
if (!player) return player
const { penny, grain, wine, nickel, whiskey } = player
if (penny >= 5 || grain || wine || nickel || whiskey) return player
return undefined
})(state)
) {
return [GameCommandEnum.CONVERT]
}
return []
})
.with([GameCommandEnum.CONVERT], () => {
const { penny = 0, grain = 0, wine = 0, nickel = 0, whiskey = 0 } = view(activeLens(state), state)
const convertMoney = [
...map((s) => stringRepeater(ResourceEnum.Penny, 5 * s), reverse(range(1, 1 + Math.floor(penny / 5)))),
...map((s) => stringRepeater(ResourceEnum.Nickel, s), reverse(range(1, 1 + nickel))),
'',
]
const convertGrain = map((s) => stringRepeater(ResourceEnum.Grain, s), reverse(range(0, 1 + grain)))
const convertWhiskey = map((s) => stringRepeater(ResourceEnum.Whiskey, s), reverse(range(0, 1 + whiskey)))
const convertWine = map((s) => stringRepeater(ResourceEnum.Wine, s), reverse(range(0, 1 + wine)))
return without<string>(
[''],
lift<(a: string, b: string, c: string, d: string) => string>((a, b, c, d) => a + b + c + d)(
convertMoney,
convertGrain,
convertWhiskey,
convertWine
)
)
})
.with([GameCommandEnum.CONVERT, P._], always(['']))
.otherwise(always([]))