hathora-et-labora-game
Version:
Game logic reducer for Uwe Rosenberg's Ora et Labora
151 lines • 5.62 kB
JavaScript
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