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
JavaScript
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