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.
90 lines • 5.12 kB
JavaScript
import { match } from 'ts-pattern';
import { always, head, map, pipe, sum } from 'ramda';
import { pickFrameFlow } from './board/frame';
import { GameCommandEnum, SettlementRound, GameStatusEnum, } from './types';
import { completeBuild, completeCommit, completeConvert, completeCutPeat, completeFellTrees, completeSettle, completeUse, completeWorkContract, completeWithLaybrother, completeWithPrior, completeBuyPlot, completeBuyDistrict, } from './commands';
import { costPoints } from './board/resource';
import { allBuildingPoints, allDwellingPoints } from './board/landscape';
import { introduceBuildings, roundBuildings } from './board/buildings';
import { introduceGrapeToken, introduceStoneToken } from './board/rondel';
const computeFlow = (state) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
const frameFlow = pickFrameFlow(state.config);
const flow = [];
let limit = 200;
let frameIndex;
let playerIndex = state.frame.activePlayerIndex;
let { frame } = state;
let { round } = frame;
let lastSeenSettlementRound = SettlementRound.S;
do {
round = (_a = frame.round) !== null && _a !== void 0 ? _a : round;
playerIndex = (_b = frame.currentPlayerIndex) !== null && _b !== void 0 ? _b : playerIndex;
const settle = !!((_c = frame.bonusActions) === null || _c === void 0 ? void 0 : _c.includes(GameCommandEnum.SETTLE)) || !!frame.neutralBuildingPhase;
lastSeenSettlementRound = (_d = frame.settlementRound) !== null && _d !== void 0 ? _d : lastSeenSettlementRound;
flow.push({
round,
player: (_f = (_e = state === null || state === void 0 ? void 0 : state.players) === null || _e === void 0 ? void 0 : _e[playerIndex]) === null || _f === void 0 ? void 0 : _f.color,
settle,
bonus: !!frame.bonusRoundPlacement,
introduced: [
...(((_g = frame.upkeep) === null || _g === void 0 ? void 0 : _g.includes(introduceBuildings)) ? roundBuildings(state.config, lastSeenSettlementRound) : []),
...(((_h = frame.upkeep) === null || _h === void 0 ? void 0 : _h.includes(introduceGrapeToken)) ? ['grape'] : []),
...(((_j = frame.upkeep) === null || _j === void 0 ? void 0 : _j.includes(introduceStoneToken)) ? ['stone'] : []),
],
});
frameIndex = frame.next;
frame = frameFlow[frame.next];
} while (frame && frameIndex < frame.next && limit--);
return flow;
};
export const control = (state, partial, player) => {
const completion = match(head(partial))
.with(undefined, () => state.status === GameStatusEnum.FINISHED
? []
: [
...completeUse(state)([]),
...completeBuild(state)([]),
...completeCutPeat(state)([]),
...completeFellTrees(state)([]),
...completeWorkContract(state)([]),
...completeBuyPlot(state)([]),
...completeBuyDistrict(state)([]),
...completeConvert(state)([]),
...completeSettle(state)([]),
...completeWithLaybrother(state)([]),
...completeWithPrior(state)([]),
...completeCommit(state)([]),
])
// this can be prettier with https://github.com/gvergnaud/ts-pattern/pull/139
// git blame this line and see how it used to be, but i really want something like
// .with([GameCommandEnum.Use, P.rest], complete.USE(state))
.with(GameCommandEnum.USE, () => completeUse(state)(partial))
.with(GameCommandEnum.BUILD, () => completeBuild(state)(partial))
.with(GameCommandEnum.CUT_PEAT, () => completeCutPeat(state)(partial))
.with(GameCommandEnum.FELL_TREES, () => completeFellTrees(state)(partial))
.with(GameCommandEnum.WORK_CONTRACT, () => completeWorkContract(state)(partial))
.with(GameCommandEnum.BUY_PLOT, () => completeBuyPlot(state)(partial))
.with(GameCommandEnum.BUY_DISTRICT, () => completeBuyDistrict(state)(partial))
.with(GameCommandEnum.CONVERT, () => completeConvert(state)(partial))
.with(GameCommandEnum.SETTLE, () => completeSettle(state)(partial))
.with(GameCommandEnum.WITH_LAYBROTHER, () => completeWithLaybrother(state)(partial))
.with(GameCommandEnum.WITH_PRIOR, () => completeWithPrior(state)(partial))
.with(GameCommandEnum.COMMIT, () => completeCommit(state)(partial))
.otherwise(always([]));
const score = map(pipe((player) => ({
goods: costPoints(player) + 30 * player.wonders,
economic: allBuildingPoints(player.landscape),
settlements: allDwellingPoints(player.landscape),
total: -Infinity,
}), (score) => (Object.assign(Object.assign({}, score), { total: sum([...score.settlements, score.goods, score.economic]) }))), state.players.slice(0, state.config.players) // this will remove neutral player
);
return {
active: player === state.frame.activePlayerIndex,
flow: computeFlow(state),
partial,
completion,
score,
};
};
//# sourceMappingURL=control.js.map