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.

500 lines (492 loc) 18.6 kB
import { filter, union } from 'ramda' import { match } from 'ts-pattern' import { BuildingEnum, Cost, ErectionEnum, GameCommandConfigParams, SettlementEnum, SettlementRound, StateReducer, } from '../types' export const costForBuilding = (building: BuildingEnum): Cost => match<BuildingEnum, Cost>(building) .with(BuildingEnum.Bakery, () => ({ clay: 2, straw: 1 })) .with(BuildingEnum.CloisterCourtyard, () => ({ wood: 2 })) .with(BuildingEnum.HarborPromenade, () => ({ wood: 1, stone: 1 })) .with(BuildingEnum.Market, () => ({ stone: 2 })) .with(BuildingEnum.PeatCoalKiln, () => ({ clay: 1 })) .with(BuildingEnum.StoneMerchant, () => ({ wood: 1 })) .with(BuildingEnum.Windmill, () => ({ wood: 3, clay: 2 })) .with(BuildingEnum.CloisterGarden, () => ({ penny: 3 })) .with(BuildingEnum.FuelMerchant, () => ({ clay: 1, straw: 1 })) .with(BuildingEnum.Priory, () => ({ wood: 1, clay: 1 })) .with(BuildingEnum.BuildersMarket, () => ({ clay: 2 })) .with(BuildingEnum.Carpentry, () => ({ wood: 2, clay: 1 })) .with(BuildingEnum.GrainStorage, () => ({ wood: 1, straw: 1 })) .with(BuildingEnum.CloisterLibrary, () => ({ stone: 2, straw: 1 })) .with(BuildingEnum.CloisterWorkshop, () => ({ wood: 3 })) .with(BuildingEnum.GrapevineA, () => ({ wood: 1 })) .with(BuildingEnum.Slaughterhouse, () => ({ wood: 2, clay: 2 })) .with(BuildingEnum.CloisterChapterHouse, () => ({ clay: 3, straw: 1 })) .with(BuildingEnum.FinancedEstate, () => ({ clay: 1, stone: 1 })) .with(BuildingEnum.CloisterChurch, () => ({ clay: 5, stone: 3 })) .with(BuildingEnum.QuarryA, () => ({ penny: 5 })) .with(BuildingEnum.Shipyard, () => ({ clay: 4, stone: 1 })) .with(BuildingEnum.Winery, () => ({ clay: 2, straw: 2 })) .with(BuildingEnum.Inn, () => ({ wood: 2, straw: 2 })) .with(BuildingEnum.Bathhouse, () => ({ stone: 1, straw: 1 })) .with(BuildingEnum.ChamberOfWonders, () => ({ wood: 1, clay: 1 })) .with(BuildingEnum.Castle, () => ({ wood: 6, stone: 5 })) .with(BuildingEnum.Palace, () => ({ penny: 25 })) .with(BuildingEnum.ShippingCompany, () => ({ wood: 3, clay: 3 })) .with(BuildingEnum.TownEstate, () => ({ stone: 2, straw: 2 })) .with(BuildingEnum.Calefactory, () => ({ stone: 1 })) .with(BuildingEnum.QuarryB, () => ({ penny: 5 })) .with(BuildingEnum.GrapevineB, () => ({ wood: 1 })) .with(BuildingEnum.Dormitory, () => ({ clay: 3 })) .with(BuildingEnum.ForgersWorkshop, () => ({ clay: 2, straw: 1 })) .with(BuildingEnum.HouseOfTheBrotherhood, () => ({ clay: 1, stone: 1 })) .with(BuildingEnum.PrintingOffice, () => ({ wood: 1, stone: 2 })) .with(BuildingEnum.Sacristy, () => ({ stone: 3, straw: 2 })) .with(BuildingEnum.Hospice, () => ({ wood: 3, straw: 1 })) .with(BuildingEnum.PilgrimageSite, () => ({ penny: 6 })) .with(BuildingEnum.Estate, () => ({ wood: 2, stone: 2 })) .with(BuildingEnum.Granary, () => ({ wood: 1 })) .with(BuildingEnum.Malthouse, () => ({ clay: 2 })) .with(BuildingEnum.Brewery, () => ({ stone: 2, straw: 1 })) .with(BuildingEnum.FalseLighthouse, () => ({ wood: 2, clay: 1 })) .with(BuildingEnum.SpinningMill, () => ({ wood: 1, straw: 1 })) .with(BuildingEnum.Cottage, () => ({ stone: 1, straw: 1 })) .with(BuildingEnum.Houseboat, () => ({ wood: 1 })) .with(BuildingEnum.SacredSite, () => ({ stone: 1 })) .with(BuildingEnum.DruidsHouse, () => ({ clay: 1, stone: 1 })) .with(BuildingEnum.Scriptorium, () => ({ wood: 1, straw: 1 })) .with(BuildingEnum.Alehouse, () => ({ wood: 1, stone: 1 })) .with(BuildingEnum.Locutory, () => ({ wood: 3, clay: 2 })) .with(BuildingEnum.Chapel, () => ({ clay: 3, stone: 3 })) .with(BuildingEnum.Portico, () => ({ clay: 2 })) .with(BuildingEnum.GrandManor, () => ({ penny: 20 })) .with(BuildingEnum.ForestHut, () => ({ clay: 1, straw: 1 })) .with(BuildingEnum.Refectory, () => ({ wood: 1, clay: 2 })) .with(BuildingEnum.CoalHarbor, () => ({ clay: 1, stone: 2 })) .with(BuildingEnum.FilialChurch, () => ({ wood: 3, clay: 4 })) .with(BuildingEnum.Cooperage, () => ({ clay: 3, straw: 1 })) .with(BuildingEnum.RoundTower, () => ({ stone: 4 })) .with(BuildingEnum.Camera, () => ({ clay: 2 })) .with(BuildingEnum.Bulwark, () => ({ wood: 2, clay: 4 })) .with(BuildingEnum.FestivalGround, () => ({ penny: 10 })) .with(BuildingEnum.Guesthouse, () => ({ wood: 3, straw: 1 })) .otherwise(() => ({})) const SETTLEMENTS = Object.values(SettlementEnum) export const isSettlement = (building?: ErectionEnum): boolean => SETTLEMENTS.includes(building as unknown as SettlementEnum) export const isCloisterBuilding = (building?: ErectionEnum): boolean => { if (building === undefined) return false return ( [ BuildingEnum.CloisterOfficeR, BuildingEnum.CloisterOfficeG, BuildingEnum.CloisterOfficeB, BuildingEnum.CloisterOfficeW, BuildingEnum.CloisterCourtyard, BuildingEnum.CloisterGarden, BuildingEnum.Priory, BuildingEnum.CloisterLibrary, BuildingEnum.CloisterWorkshop, BuildingEnum.CloisterChapterHouse, BuildingEnum.CloisterChurch, BuildingEnum.Bathhouse, BuildingEnum.Calefactory, BuildingEnum.Dormitory, BuildingEnum.HouseOfTheBrotherhood, BuildingEnum.Sacristy, BuildingEnum.Hospice, BuildingEnum.Camera, BuildingEnum.FilialChurch, BuildingEnum.Refectory, BuildingEnum.Portico, BuildingEnum.Chapel, BuildingEnum.Locutory, BuildingEnum.Scriptorium, BuildingEnum.Granary, ] as ErectionEnum[] ).includes(building) } export const roundBuildings = (config: GameCommandConfigParams, round: SettlementRound): BuildingEnum[] => { return match<[GameCommandConfigParams, SettlementRound], BuildingEnum[]>([config, round]) .with([{ country: 'france', players: 4 }, SettlementRound.S], () => [ BuildingEnum.Priory, BuildingEnum.CloisterCourtyard, BuildingEnum.GrainStorage, BuildingEnum.Windmill, BuildingEnum.Bakery, BuildingEnum.FuelMerchant, BuildingEnum.PeatCoalKiln, BuildingEnum.Market, BuildingEnum.CloisterGarden, BuildingEnum.Carpentry, BuildingEnum.HarborPromenade, BuildingEnum.StoneMerchant, BuildingEnum.BuildersMarket, ]) .with([{ country: 'france', players: 3 }, SettlementRound.S], () => [ BuildingEnum.Priory, BuildingEnum.CloisterCourtyard, BuildingEnum.Windmill, BuildingEnum.Bakery, BuildingEnum.FuelMerchant, BuildingEnum.PeatCoalKiln, BuildingEnum.Market, BuildingEnum.CloisterGarden, BuildingEnum.HarborPromenade, BuildingEnum.StoneMerchant, ]) .with([{ country: 'france', players: 2 }, SettlementRound.S], () => [ BuildingEnum.CloisterCourtyard, BuildingEnum.Windmill, BuildingEnum.Bakery, BuildingEnum.PeatCoalKiln, BuildingEnum.Market, BuildingEnum.HarborPromenade, BuildingEnum.StoneMerchant, ]) .with([{ country: 'france', players: 1 }, SettlementRound.S], () => [ BuildingEnum.Priory, BuildingEnum.CloisterCourtyard, BuildingEnum.GrainStorage, BuildingEnum.Windmill, BuildingEnum.Bakery, BuildingEnum.FuelMerchant, BuildingEnum.PeatCoalKiln, BuildingEnum.Market, BuildingEnum.CloisterGarden, BuildingEnum.HarborPromenade, BuildingEnum.StoneMerchant, // BuildingEnum.BuildersMarket, // this automatically gets spawned on neutral player ]) .with([{ country: 'ireland', players: 4 }, SettlementRound.S], () => [ BuildingEnum.Priory, BuildingEnum.CloisterCourtyard, BuildingEnum.Granary, BuildingEnum.Malthouse, BuildingEnum.Brewery, BuildingEnum.FuelMerchant, BuildingEnum.PeatCoalKiln, BuildingEnum.FalseLighthouse, BuildingEnum.SpinningMill, BuildingEnum.Cottage, BuildingEnum.Houseboat, BuildingEnum.StoneMerchant, BuildingEnum.BuildersMarket, ]) .with([{ country: 'ireland', players: 3 }, SettlementRound.S], () => [ BuildingEnum.Priory, BuildingEnum.CloisterCourtyard, BuildingEnum.Malthouse, BuildingEnum.Brewery, BuildingEnum.FuelMerchant, BuildingEnum.PeatCoalKiln, BuildingEnum.FalseLighthouse, BuildingEnum.SpinningMill, BuildingEnum.Houseboat, BuildingEnum.StoneMerchant, ]) .with([{ country: 'ireland', players: 2 }, SettlementRound.S], () => [ BuildingEnum.CloisterCourtyard, BuildingEnum.Malthouse, BuildingEnum.Brewery, BuildingEnum.PeatCoalKiln, BuildingEnum.FalseLighthouse, BuildingEnum.Houseboat, BuildingEnum.StoneMerchant, ]) .with([{ country: 'ireland', players: 1 }, SettlementRound.S], () => [ BuildingEnum.Priory, BuildingEnum.CloisterCourtyard, BuildingEnum.Granary, BuildingEnum.Malthouse, BuildingEnum.Brewery, BuildingEnum.FuelMerchant, BuildingEnum.PeatCoalKiln, BuildingEnum.FalseLighthouse, BuildingEnum.SpinningMill, BuildingEnum.Cottage, BuildingEnum.Houseboat, BuildingEnum.StoneMerchant, // BuildingEnum.BuildersMarket, // this gets automatically spawned on neutral player ]) .with([{ country: 'france', players: 4 }, SettlementRound.A], () => [ BuildingEnum.GrapevineA, BuildingEnum.FinancedEstate, BuildingEnum.CloisterChapterHouse, BuildingEnum.CloisterLibrary, BuildingEnum.CloisterWorkshop, BuildingEnum.Slaughterhouse, ]) .with([{ country: 'france', players: 3 }, SettlementRound.A], () => [ BuildingEnum.GrapevineA, BuildingEnum.CloisterChapterHouse, BuildingEnum.CloisterLibrary, BuildingEnum.CloisterWorkshop, BuildingEnum.Slaughterhouse, ]) .with([{ country: 'france', players: 2 }, SettlementRound.A], () => [ BuildingEnum.GrapevineA, BuildingEnum.CloisterLibrary, BuildingEnum.CloisterWorkshop, BuildingEnum.Slaughterhouse, ]) .with([{ country: 'france', players: 1 }, SettlementRound.A], () => [ BuildingEnum.FinancedEstate, BuildingEnum.CloisterChapterHouse, BuildingEnum.CloisterLibrary, BuildingEnum.CloisterWorkshop, BuildingEnum.Slaughterhouse, ]) .with([{ country: 'ireland', players: 4 }, SettlementRound.A], () => [ BuildingEnum.SacredSite, BuildingEnum.DruidsHouse, BuildingEnum.CloisterChapterHouse, BuildingEnum.Scriptorium, BuildingEnum.CloisterWorkshop, BuildingEnum.Slaughterhouse, ]) .with([{ country: 'ireland', players: 3 }, SettlementRound.A], () => [ BuildingEnum.SacredSite, BuildingEnum.CloisterChapterHouse, BuildingEnum.Scriptorium, BuildingEnum.CloisterWorkshop, BuildingEnum.Slaughterhouse, ]) .with([{ country: 'ireland', players: 2 }, SettlementRound.A], () => [ BuildingEnum.SacredSite, BuildingEnum.Scriptorium, BuildingEnum.CloisterWorkshop, BuildingEnum.Slaughterhouse, ]) .with([{ country: 'ireland', players: 1 }, SettlementRound.A], () => [ BuildingEnum.SacredSite, BuildingEnum.DruidsHouse, BuildingEnum.CloisterChapterHouse, BuildingEnum.Scriptorium, BuildingEnum.CloisterWorkshop, BuildingEnum.Slaughterhouse, ]) .with([{ country: 'france', players: 4 }, SettlementRound.B], () => [ BuildingEnum.Inn, BuildingEnum.Winery, BuildingEnum.QuarryA, BuildingEnum.Bathhouse, BuildingEnum.CloisterChurch, BuildingEnum.ChamberOfWonders, BuildingEnum.Shipyard, ]) .with([{ country: 'france', players: 3 }, SettlementRound.B], () => [ BuildingEnum.Inn, BuildingEnum.Winery, BuildingEnum.QuarryA, BuildingEnum.CloisterChurch, BuildingEnum.Shipyard, ]) .with([{ country: 'france', players: 2 }, SettlementRound.B], () => [ BuildingEnum.Winery, BuildingEnum.QuarryA, BuildingEnum.CloisterChurch, BuildingEnum.Shipyard, ]) .with([{ country: 'france', players: 1 }, SettlementRound.B], () => [ BuildingEnum.Inn, BuildingEnum.Winery, BuildingEnum.QuarryA, BuildingEnum.Bathhouse, BuildingEnum.CloisterChurch, BuildingEnum.ChamberOfWonders, BuildingEnum.Shipyard, ]) .with([{ country: 'ireland', players: 4 }, SettlementRound.B], () => [ BuildingEnum.Alehouse, BuildingEnum.WhiskeyDistillery, BuildingEnum.QuarryA, BuildingEnum.Locutory, BuildingEnum.Chapel, BuildingEnum.Portico, BuildingEnum.Shipyard, ]) .with([{ country: 'ireland', players: 3 }, SettlementRound.B], () => [ BuildingEnum.Alehouse, BuildingEnum.WhiskeyDistillery, BuildingEnum.QuarryA, BuildingEnum.Chapel, BuildingEnum.Shipyard, ]) .with([{ country: 'ireland', players: 2 }, SettlementRound.B], () => [ BuildingEnum.WhiskeyDistillery, BuildingEnum.QuarryA, BuildingEnum.Chapel, BuildingEnum.Shipyard, ]) .with([{ country: 'ireland', players: 1 }, SettlementRound.B], () => [ BuildingEnum.Alehouse, BuildingEnum.WhiskeyDistillery, BuildingEnum.QuarryA, BuildingEnum.Locutory, BuildingEnum.Chapel, BuildingEnum.Portico, BuildingEnum.Shipyard, ]) .with([{ country: 'france', players: 4 }, SettlementRound.C], () => [ BuildingEnum.Palace, BuildingEnum.Castle, BuildingEnum.QuarryB, BuildingEnum.TownEstate, BuildingEnum.GrapevineB, BuildingEnum.Calefactory, BuildingEnum.ShippingCompany, ]) .with([{ country: 'france', players: 3 }, SettlementRound.C], () => [ BuildingEnum.Palace, BuildingEnum.Castle, BuildingEnum.QuarryB, BuildingEnum.TownEstate, BuildingEnum.Calefactory, BuildingEnum.ShippingCompany, ]) .with([{ country: 'france', players: 2 }, SettlementRound.C], () => [ BuildingEnum.Palace, BuildingEnum.Castle, BuildingEnum.TownEstate, BuildingEnum.ShippingCompany, ]) .with([{ country: 'france', players: 1 }, SettlementRound.C], () => [ BuildingEnum.Palace, BuildingEnum.Castle, BuildingEnum.TownEstate, BuildingEnum.Calefactory, BuildingEnum.ShippingCompany, ]) .with([{ country: 'ireland', players: 4 }, SettlementRound.C], () => [ BuildingEnum.GrandManor, BuildingEnum.Castle, BuildingEnum.ForestHut, BuildingEnum.Refectory, BuildingEnum.CoalHarbor, BuildingEnum.Cooperage, BuildingEnum.FilialChurch, ]) .with([{ country: 'ireland', players: 3 }, SettlementRound.C], () => [ BuildingEnum.GrandManor, BuildingEnum.Castle, BuildingEnum.ForestHut, BuildingEnum.Refectory, BuildingEnum.Cooperage, BuildingEnum.FilialChurch, ]) .with([{ country: 'ireland', players: 2 }, SettlementRound.C], () => [ BuildingEnum.GrandManor, BuildingEnum.Castle, BuildingEnum.Refectory, BuildingEnum.Cooperage, ]) .with([{ country: 'ireland', players: 1 }, SettlementRound.C], () => [ BuildingEnum.GrandManor, BuildingEnum.Castle, BuildingEnum.ForestHut, BuildingEnum.Refectory, BuildingEnum.CoalHarbor, BuildingEnum.Cooperage, BuildingEnum.FilialChurch, ]) .with([{ country: 'france', players: 4 }, SettlementRound.D], () => [ BuildingEnum.Sacristy, BuildingEnum.ForgersWorkshop, BuildingEnum.PilgrimageSite, BuildingEnum.Dormitory, BuildingEnum.PrintingOffice, BuildingEnum.Estate, BuildingEnum.Hospice, BuildingEnum.HouseOfTheBrotherhood, ]) .with([{ country: 'france', players: 3 }, SettlementRound.D], () => [ BuildingEnum.Sacristy, BuildingEnum.ForgersWorkshop, BuildingEnum.PilgrimageSite, BuildingEnum.Dormitory, BuildingEnum.PrintingOffice, BuildingEnum.Hospice, BuildingEnum.HouseOfTheBrotherhood, ]) .with([{ country: 'france', players: 2 }, SettlementRound.D], () => [ BuildingEnum.Sacristy, BuildingEnum.ForgersWorkshop, BuildingEnum.Dormitory, BuildingEnum.PrintingOffice, BuildingEnum.HouseOfTheBrotherhood, ]) .with([{ country: 'france', players: 1 }, SettlementRound.D], () => [ BuildingEnum.Sacristy, BuildingEnum.ForgersWorkshop, BuildingEnum.PilgrimageSite, BuildingEnum.Dormitory, BuildingEnum.PrintingOffice, BuildingEnum.Estate, BuildingEnum.Hospice, BuildingEnum.HouseOfTheBrotherhood, ]) .with([{ country: 'ireland', players: 4 }, SettlementRound.D], () => [ BuildingEnum.Sacristy, BuildingEnum.RoundTower, BuildingEnum.Camera, BuildingEnum.Bulwark, BuildingEnum.FestivalGround, BuildingEnum.Estate, BuildingEnum.Guesthouse, BuildingEnum.HouseOfTheBrotherhood, ]) .with([{ country: 'ireland', players: 3 }, SettlementRound.D], () => [ BuildingEnum.Sacristy, BuildingEnum.RoundTower, BuildingEnum.Camera, BuildingEnum.Bulwark, BuildingEnum.FestivalGround, BuildingEnum.Guesthouse, BuildingEnum.HouseOfTheBrotherhood, ]) .with([{ country: 'ireland', players: 2 }, SettlementRound.D], () => [ BuildingEnum.Sacristy, BuildingEnum.RoundTower, BuildingEnum.Bulwark, BuildingEnum.FestivalGround, BuildingEnum.HouseOfTheBrotherhood, ]) .with([{ country: 'ireland', players: 1 }, SettlementRound.D], () => [ BuildingEnum.Sacristy, BuildingEnum.RoundTower, BuildingEnum.Camera, BuildingEnum.Bulwark, BuildingEnum.FestivalGround, BuildingEnum.Estate, BuildingEnum.Guesthouse, BuildingEnum.HouseOfTheBrotherhood, ]) .otherwise(() => []) } export const introduceBuildings: StateReducer = (state) => { if (state === undefined) return undefined return { ...state, buildings: union(state.buildings, roundBuildings(state.config, state.frame.settlementRound)), } } export const removeBuildingFromUnbuilt = (building: BuildingEnum): StateReducer => (state) => { if (state === undefined) return undefined if (!state.buildings.includes(building)) return undefined return ( state && { ...state, buildings: filter((b) => b !== building, state?.buildings), } ) }