Intro
Last time we looked how to model technology and research. This time we’ll do some actual research. I’m skipping over some of the details as the episode is long enough as it is. Hopefully it’s still possible to follow with the show notes.
Main concepts that I’m mentioning: Technology allows usage of specific buildings, ship components and such. Research unlock technologies and may have antecedents that has to be completed before the research can be started. Research cost is measure of how expensive a research is in terms of research points, which are produced by different buildings.
Earlier I modeled tech tree as Map that had Technology as keys and Research as values. I realized that this is suboptimal and will replace it at some point in the future.
Server API
There’s three resources that client can connect to. First one is for retrieving list of available research, second one for manipulating current research and last one for retrieving info on how much research points is being produced.
/api/research/available ApiAvailableResearchR GET
/api/research/current ApiCurrentResearchR GET POST DELETE
/api/research/production ApiResearchProductionR GET
Simulation
Simulation of research is done by handleFactionResearch, which does simulation for one faction for a given date. After calculating current research point production and retrieving list of current research, function calculates progress of current researches. Unfinished ones are written back to database, while completed are moved into completed_research table. Final step is updating what research will be available in the next turn.
handleFactionResearch date faction = do
production <- totalProduction $ entityKey faction
current <- selectList [ CurrentResearchFactionId ==. entityKey faction ] []
let updated = updateProgress production <$> current
_ <- updateUnfinished updated
_ <- handleCompleted date updated $ entityKey faction
_ <- updateAvailableResearch $ entityKey faction
return ()
Research point production
Research points are produced by buildings. So first step is to load all planets owned by the faction and buildings on those planets. Applying researchOutput function to each building yields a list of TotalResearchScore, which is then summed up by mconcat. We can use mconcat as TotalResearchScore is a monoid (I talked about these couple episodes ago).
totalProduction fId = do
pnbs <- factionBuildings fId
let buildings = join $ fmap snd pnbs
return $ mconcat $ researchOutput . entityVal <$> buildings
researchOutput function below uses pattern matching. Instead of writing one function definition and case expression inside of it, we’re writing multiple definitions. Each of them matches building of different type. First example is definition that is used for ResearchComplex, while second one is for ParticleAccelerator. Final case uses underscore to match anything and indicate that we’re not even interested on the particular value being matched. mempty is again from our monoid definition. It is empty or unit value of monoid, which in case of TotalResearchScore is zero points in all research categories.
researchOutput Building { buildingType = ResearchComplex } =
TotalResearchScore
{ totalResearchScoreEngineering = ResearchScore 10
, totalResearchScoreNatural = ResearchScore 10
, totalResearchScoreSocial = ResearchScore 10
}
researchOutput Building { buildingType = ParticleAccelerator } =
TotalResearchScore