UNPKG

hathora-et-labora-game

Version:

Game logic reducer for Uwe Rosenberg's Ora et Labora

151 lines 5.62 kB
import { any, filter, flatten, lensPath, map, set, view } from 'ramda'; import { match } from 'ts-pattern'; import { Clergy, PlayerColor, } from '../types'; export const activeLens = (state) => lensPath(['players', view(lensPath(['frame', 'activePlayerIndex']), state)]); const withPlayer = (lens) => (func) => (state) => { if (state === undefined) return state; const oldPlayer = view(lens, state); if (oldPlayer === undefined) return undefined; const newPlayer = func(oldPlayer); if (newPlayer === oldPlayer) return state; // dont create another state if nothing changes if (newPlayer === undefined) return undefined; // dont just set the one player to undefined return set(lens, newPlayer, state); }; export const withActivePlayer = (func) => (state) => withPlayer(activeLens(state))(func)(state); export const indexLens = (playerIndex) => lensPath(['players', playerIndex]); export const withPlayerIndex = (playerIndex) => (func) => (state) => withPlayer(indexLens(playerIndex))(func)(state); export const withEachPlayer = (func) => (state) => { if (state === undefined) return state; let dirty = false; const players = map((p) => { const q = func(p); if (p !== q) dirty = true; return q; }, state.players); if (!dirty) return state; if (any((p) => p === undefined, players)) return undefined; return { ...state, players: players, }; }; export const clergyForColor = (config) => { if ((config?.players === 3 || config?.players === 4) && config?.length === 'short') { return (color) => match(color) .with(PlayerColor.Red, () => [Clergy.LayBrother1R, Clergy.PriorR]) .with(PlayerColor.Green, () => [Clergy.LayBrother1G, Clergy.PriorG]) .with(PlayerColor.Blue, () => [Clergy.LayBrother1B, Clergy.PriorB]) .with(PlayerColor.White, () => [Clergy.LayBrother1W, Clergy.PriorW]) .exhaustive(); } return (color) => match(color) .with(PlayerColor.Red, () => [Clergy.LayBrother1R, Clergy.LayBrother2R, Clergy.PriorR]) .with(PlayerColor.Green, () => [Clergy.LayBrother1G, Clergy.LayBrother2G, Clergy.PriorG]) .with(PlayerColor.Blue, () => [Clergy.LayBrother1B, Clergy.LayBrother2B, Clergy.PriorB]) .with(PlayerColor.White, () => [Clergy.LayBrother1W, Clergy.LayBrother2W, Clergy.PriorW]) .exhaustive(); }; export const isPrior = (clergy) => !!(clergy && [Clergy.PriorR, Clergy.PriorG, Clergy.PriorB, Clergy.PriorW].includes(clergy)); export const isLayBrother = (clergy) => !!(clergy && !isPrior(clergy)); export const priors = (state) => { if (state === undefined) return []; const a = map(({ color }) => color, state.players); const b = map(clergyForColor(state.config), a); const c = flatten(b); const d = filter(isPrior, c); return d; }; export const payCost = (cost) => (player) => { if (player === undefined) return undefined; let dirty = false; const newPlayer = { ...player }; const fields = Object.entries(cost); // TODO: do better for (let i = 0; i < fields.length; i++) { const [type, amount] = fields[i]; if (amount) { dirty = true; let newValue = newPlayer[type] - (amount ?? 0); if (newValue < 0) { if (type === 'straw') { newPlayer.grain += newValue; newValue = 0; if (newPlayer.grain < 0) return undefined; } else if (type !== 'penny') { return undefined; } } newPlayer[type] = newValue; } } if (!dirty) return player; while (newPlayer.penny < 0 && newPlayer.nickel > 0) { newPlayer.penny += 5; newPlayer.nickel -= 1; } while (newPlayer.penny < 0 && newPlayer.whiskey > 0) { newPlayer.penny += 2; newPlayer.whiskey -= 1; } while (newPlayer.penny < 0 && newPlayer.wine > 0) { newPlayer.wine -= 1; newPlayer.penny += 1; } if (newPlayer.penny < 0) return undefined; return newPlayer; }; export const getCost = (cost) => (player) => player && Object.entries(cost).reduce((player, [type, amount]) => { // TODO: write test to ensure this works with amount === undefined if (!amount) return player; return { ...player, [type]: player[type] + amount, }; }, player); export const subtractCoins = (amount) => (player) => { if (player === undefined) return undefined; // first clone p const p = { ...player }; // Taken from Player.subtractCoins... feels gross // https://github.com/philihp/weblabora/blob/737717fd59c1301da6584a6874a20420eba4e71e/src/main/java/com/philihp/weblabora/model/Player.java#L542 p.penny -= amount; while (p.penny < 0 && p.nickel > 0) { p.penny += 5; p.nickel -= 1; } if (p.penny < 0 && p.penny + p.wine >= 0) { p.wine += p.penny; p.penny = 0; } while (p.penny < 0 && p.whiskey > 0) { p.penny += 2; p.whiskey -= 1; } if (p.penny < 0 || p.nickel < 0 || p.wine < 0 || p.whiskey < 0) return undefined; return p; }; export const getWonder = (amount = 1) => (player) => { return (player && { ...player, wonders: player.wonders + amount, }); }; //# sourceMappingURL=player.js.map