UNPKG

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.

146 lines 6.77 kB
import { always, equals, findIndex, head, map, pipe, range, reject, remove, toString, view } from 'ramda'; import { P, match } from 'ts-pattern'; import { costMoney } from '../board/resource'; import { activeLens, subtractCoins, withActivePlayer } from '../board/player'; import { LandEnum, BuildingEnum, GameCommandEnum } from '../types'; const checkCanGetLandscape = (state) => { if (state === undefined) return undefined; if (state.frame.currentPlayerIndex !== state.frame.activePlayerIndex) return undefined; if (state.frame.canBuyLandscape) return state; if (state.frame.bonusActions.includes(GameCommandEnum.BUY_DISTRICT)) return state; return undefined; }; const checkForConnection = (y) => // new district must have an existing withActivePlayer((player) => { var _a, _b, _c, _d, _e, _f, _g, _h; const { landscape, landscapeOffset } = player; const north = (_b = (_a = landscape[y + landscapeOffset - 1]) === null || _a === void 0 ? void 0 : _a[4]) === null || _b === void 0 ? void 0 : _b[0]; const east = (_d = (_c = landscape[y + landscapeOffset]) === null || _c === void 0 ? void 0 : _c[5]) === null || _d === void 0 ? void 0 : _d[0]; const west = (_f = (_e = landscape[y + landscapeOffset]) === null || _e === void 0 ? void 0 : _e[1]) === null || _f === void 0 ? void 0 : _f[0]; const south = (_h = (_g = landscape[y + landscapeOffset + 1]) === null || _g === void 0 ? void 0 : _g[4]) === null || _h === void 0 ? void 0 : _h[0]; if (north || east || west || south) return player; return undefined; }); const checkForOverlap = (y) => withActivePlayer((player) => { var _a, _b; const { landscape, landscapeOffset } = player; if (((_b = (_a = landscape[y + landscapeOffset]) === null || _a === void 0 ? void 0 : _a[3]) === null || _b === void 0 ? void 0 : _b[0]) !== undefined) return undefined; return player; }); const checkCanAfford = (state) => { var _a; if ((_a = state === null || state === void 0 ? void 0 : state.frame.bonusActions) === null || _a === void 0 ? void 0 : _a.includes(GameCommandEnum.BUY_DISTRICT)) return state; const cost = state === null || state === void 0 ? void 0 : state.districtPurchasePrices[0]; return withActivePlayer((player) => { if (cost === undefined) return undefined; if (costMoney(player) < cost) return undefined; return player; })(state); }; const payForDistrict = (state) => { var _a; if (state === undefined) return undefined; if ((_a = state.frame.bonusActions) === null || _a === void 0 ? void 0 : _a.includes(GameCommandEnum.BUY_DISTRICT)) return state; const cost = state.districtPurchasePrices[0]; return withActivePlayer(subtractCoins(cost))(state); }; const removeDistrictFromPool = (state) => { var _a; if (state === undefined) return undefined; if ((_a = state.frame.bonusActions) === null || _a === void 0 ? void 0 : _a.includes(GameCommandEnum.BUY_DISTRICT)) return state; return Object.assign(Object.assign({}, state), { districtPurchasePrices: state.districtPurchasePrices.slice(1) }); }; const denyBuyingAnyMoreLandscape = (state) => { if (state === undefined) return undefined; const atIndex = findIndex(equals(GameCommandEnum.BUY_DISTRICT), state.frame.bonusActions); return Object.assign(Object.assign({}, state), { frame: Object.assign(Object.assign({}, state.frame), (atIndex !== -1 ? { // leave canBuyLandscape as it was bonusActions: remove(atIndex, 1, state.frame.bonusActions), } : { // otherwise set canBuyLandscape to false canBuyLandscape: false, })) }); }; const NEW_ROW = [[], [], [], [], [], [], [], [], []]; const expandLandscape = (y) => withActivePlayer((player) => { const landscape = [...player.landscape]; let { landscapeOffset } = player; if (y + player.landscapeOffset < 0) { landscape.unshift(NEW_ROW); landscapeOffset++; } else if (y + player.landscapeOffset >= landscape.length) { landscape.push(NEW_ROW); } return Object.assign(Object.assign({}, player), { landscape, landscapeOffset }); }); const newDistrict = (side) => match(side) .with('HILLS', () => [ [LandEnum.Plains, BuildingEnum.Moor], [LandEnum.Plains, BuildingEnum.Forest], [LandEnum.Plains, BuildingEnum.Forest], [LandEnum.Hillside], [LandEnum.Hillside], ]) .with('PLAINS', () => [ [LandEnum.Plains, BuildingEnum.Forest], [LandEnum.Plains], [LandEnum.Plains], [LandEnum.Plains], [LandEnum.Hillside], ]) .exhaustive(); const addNewDistrict = (y, side) => withActivePlayer((player) => { const newTiles = newDistrict(side); const rowsBefore = player.landscape.slice(0, y + player.landscapeOffset); const newRow = [ ...player.landscape[y + player.landscapeOffset].slice(0, 2), ...newTiles, ...player.landscape[y + player.landscapeOffset].slice(7, 9), ]; const rowsAfter = player.landscape.slice(y + player.landscapeOffset + 1); const landscape = [...rowsBefore, newRow, ...rowsAfter]; return Object.assign(Object.assign({}, player), { landscape }); }); export const buyDistrict = ({ side, y }) => pipe( // checkCanGetLandscape, checkForOverlap(y), checkForConnection(y), checkCanAfford, payForDistrict, removeDistrictFromPool, denyBuyingAnyMoreLandscape, expandLandscape(y), addNewDistrict(y, side)); export const complete = (state) => (partial) => { const player = view(activeLens(state), state); return match(partial) .with([], () => { var _a; if (state.frame.bonusActions.includes(GameCommandEnum.BUY_DISTRICT) && head(state.districtPurchasePrices)) return [GameCommandEnum.BUY_DISTRICT]; if (checkCanGetLandscape(state) === undefined) return []; const playerWealth = costMoney(player); const nextDistrictCost = (_a = head(state.districtPurchasePrices)) !== null && _a !== void 0 ? _a : Infinity; if (playerWealth < nextDistrictCost) return []; return [GameCommandEnum.BUY_DISTRICT]; }) .with([GameCommandEnum.BUY_DISTRICT], () => map(toString, reject((y) => !checkForConnection(y)(state) || !checkForOverlap(y)(state), range(-1 - player.landscapeOffset, 1 + player.landscape.length - player.landscapeOffset)))) .with([GameCommandEnum.BUY_DISTRICT, P._], always(['PLAINS', 'HILLS'])) .with([GameCommandEnum.BUY_DISTRICT, P._, P._], always([''])) .otherwise(always([])); }; //# sourceMappingURL=buyDistrict.js.map