Files
hpr-knowledge-base/hpr_transcripts/hpr2828.txt

625 lines
29 KiB
Plaintext
Raw Normal View History

Episode: 2828
Title: HPR2828: Writing Web Game in Haskell - Science, part 2
Source: https://hub.hackerpublicradio.org/ccdn.php?filename=/eps/hpr2828/hpr2828.mp3
Transcribed: 2025-10-19 17:25:34
---
This in HP are episode 2008-128 entitled, writing web game in Hakell, science, part 2, and
in part of the series, Hakell.
It is hosted by Tuku Toroto and in about 46 minutes long, and Karina Cleanflag.
The summary is, Tuku Toroto continues their explanation on simulating science in a game
written in Hakell.
This episode of HBR is brought to you by an honesthost.com.
At 15% discount on all shared hosting with the offer code HBR15, that's HBR15.
Better web hosting that's honest and fair, at An Honesthost.com.
Hello, you are listening to Hacker Public Radio.
This is Tuku Toroto, and today's episode is about more science.
Last time we looked how to model technology and research in that forex game I'm working
on, and this time we actually do some research, but first I'll quick-click up, so there's
a technology that is something that allows usage of buildings, components, such that research
that is done in order to paint technology, and the research has an antecedent, so you
have to do some research in order to be able to do some more research, and there's
three main categories and several categories.
There's a research cost, that is the cost of research in research points, and these points
are generated by the buildings, at least at done being, and in the database there's three
tables, available research, current research, and complete research, and a note about this
thing is that there's a couple spots where I'm using NAT to store key value of theirs,
and later on I realized that this is a, when I wrote that it sounds like a good idea
but later on I realized that it's not a great idea, so I'm going to switch it to the
plane function later on.
There's a main reason is that if I store data in the NAT, I always get a native value
back from the, if I store it, if I use the function that has a, that is total, so it returns
value to every input you can give it, every possible input you can give it, I get a value
back always, so I, I get rid of one, one layer of maybe, so it's clear to write and the
compiler will help me a bit more there.
So, let's look at the implementation, most of the functions here are interacting with the
value of another.
The very top level, there's a server API, there's a three resources, there's a API slash
research, it's less available, that's your board's get, that is used to retry even for
the materials, it's currently available to choose from.
There's a API slash research slash current, that reports get post delete, you can retrieve
to find what is being currently researched, you can change your current research and you
can delete a research from the current list.
And then there's the production API slash research, slash production, that reports get, and
gives you an info about how much research points you are producing currently.
So, I'm not going through the implementation of those three methods, they are fairly uninteresting,
but it's maybe interesting to know that those are, those are how the client corrects to the
server and handles things related to the research.
And let's look at the simulation, then.
I'll try to avoid reading, I'll allow all of the code and concentrating only on the
interesting parts, I'll be leaving out the types mostly, but the idea is that for each
action, we are calculating the research point production, then we are loading the current
research, we are updating the progress of the research that we loaded from the database,
and then all the completed, all the research that is deemed to be completed are removed and
handled differently, and the research that is not wasn't completed are written back
to the database.
That's the very high level overview of this thing.
There's a, so, if you look at the research point production, these points are produced by
buildings, so, first thing is to load all types and buildings from the database.
For this, I have a function called Section Buildings, that is a very written in the
SQL.
That's a lot better for doing SQL manipulation, and it's a fairly, well, I don't know if
it's a complicated, it's a fairly complicated, because SQL.
SQL.
The better syntax is somewhat common rule in my opinion, but that's the reason I'm
grabbing that behind the function, so I don't have to deal with that all the time.
I can make a function that I know that will reach into the database, and it will return a list
of planets, and all of those planets list of buildings, so there's a couple where
the first is entity planet, and the second element is a list of entity buildings, and that
faction buildings will return a list of those.
Then we have to, from that list, extract buildings, so we ended up with that join,
dollar, FMOP, SMD, and then nail of the list.
That means that we are, first we are running the FMOP, we are running the function SMD to
each element on the list, and SMD is a function that takes the second element of the
couple.
The first element is SSSD, terrible name, but that's how they have been named, and that
returns a list of lists, so we use join to remove one layer of lists, so we get
a list of buildings.
And to count the research output, there's a, we have a function, once research output,
that takes a building, and returns the research score of total research score of the building,
telling how much points it's producing.
So, we are running that function to every building on our list, so that is done with
research output dot entity while dollar diamond building.
So, research output dot entity while, that means that we are combining two functions,
we are taking the entity while and research output functions, and making a new function
out of them.
So, whatever is set to the entity while produces a value that is set to the research output,
and so we, that function composition is pretty common thing to do.
And then we use the dollar diamond to run this new function to our buildings.
So, now we have a list of research outputs.
And then, because research output is in our dollar research score, and in the previous
episode, we made a monoid instance for it, we can use mconquette function to combine
all these research, total research score objects into one.
Basically, we have just summing them to get that, that gives you a total of the research
output.
So, mconquette dollar research output dot entity while dollar diamond buildings.
This will give you just one total research score that is the sum of all the total
research scores of all those buildings.
I wrote that research output function using pattern matching, meaning that on a
type level, that research output function takes a building and produces a total
research score.
And then I have multiple definitions for that function, each with different pattern
to match.
So, for example, for the research complex buildings and writing a function,
the signature reads research output, uppercase building, curl prices,
building type e equals uppercase research complex, complex close curl prices,
e equals.
And then, whatever that function returns.
This means that this version of the function matches the, I mean, you have to give
it the building in any case, but it matches to buildings that have building type
of research complex.
If you call this with a say farming complex, it would match to something else,
it would not execute this version.
And then, then I have similar definitions for each and every building that produces
research points.
And then, during the compilation, the compiler will figure out which one should be.
I'm sorry, runtime, of course, the system figures out which implementation will be called.
And then, if I only put different buildings that actually produce research points,
as soon as I would call this function with a building that doesn't produce research points,
we would get a crash because there would be an implementation to call.
There would be a matching implementation.
In other words, the research complex would be a total function.
Total function is a function that will always return a value that will always produce a value
for every possible input it is given.
So, for this reason, we have a final definition, research output,
underscore equals m empty.
This means that if no other function definition matches,
when we are calling this function, then the underscore one will be matched.
It's a default value.
It means anything goes, anything goes.
And since we are not interested for the value at that point, we can use the underscore.
If we would put the, for example, what the building,
a lowercase building, then it would be a variable name.
Then it still would match anything, but we would have a building, a local variable available inside of the function.
But since we are not interested in the building at this point anymore, we use the underscore.
And m empty means that we are using our monode definitions, the unit.
So, that's the, that's the our zero value that we defined for the total research goal.
And because we are summing total research goals in our monodefinition, then the unit is a total research goal of zero.
Meaning whenever we call total for total production function with a building that doesn't match,
we get a total research goal of zero value.
Okay, so now that we have calculated the amount of research points that we are producing,
and we have loaded our current research from the database,
we need to update the progress, so we need to move the research forward.
And there's two cases, but start from the same data.
So, first update the progress of all research, and then the research that was not finished,
will be written back to the database, and the research that was finished will be moved into the completed research table,
and the news article will be written about it.
So, that whoever is interested on the news, we'll see that now this research has been completed,
and then they can go and check what is currently available and community research.
So, the function that does that, the main function is update progress,
and it takes a total research goal, and then it takes the entity current research and produces entity current research.
So, entity current research only means that it's a current research data that has a database primary key associated with it,
so it sort of maps into the database.
Basically, we are first, we have to look up for the...
Well, we have a production and current, and then we have to not current into the research,
like current research, we have to find the details of the research,
because the current research doesn't have that much of data in it.
It doesn't, for example, that doesn't tell how many research points you are still regard to,
how many research points you still need to spend in order to complete that,
for that we need the research data.
So, we are fetching that from the data map,
and then we are taking that research that we found apart and finding the research goal,
and then we are going to update the research.
So, let's see, the interesting part here is that we are using,
for example, when we are updating the engineering research,
because there's three cases for three different categories.
So, we have calculated an engineering research component from the research,
production, and we want to add that to the research progress of the current research.
And then we have a two-level hierarchy, we have a entity,
current research, meaning that the entity has two values.
It has the primary, it has the data-based key, and it has the actual data.
And in certain current research, there's a record type,
in that there's a field called current research progress,
meaning that this tells how long we have progressed,
and we want to add to this value our research point production.
And because in Haskell data is immutable,
you cannot change anything, you can only create new objects or new data.
That would mean that we would have to first take our different layers apart,
so we would have first have to get our current research,
then as to that current research progress value,
which will produce a new current research,
and then we would have to construct a new entity current research
that has the same primary key as before,
but that has the current research and data is the one that we just calculated.
And with two layers, it's still a tuple.
I mean, it's always a tuple, but with two layers,
it's not too much to write, but I wanted to try something different
and use lenses here.
And lenses are really an interesting topic,
that I don't understand very enough to talk a lot,
or do a very enough to do a proper tutorial,
but the idea is here that we can write a special kind of functions
that can be used both to retrieving the values from some structure
and also to construct that structure,
and we can combine those together
and use some help of functions to use those,
to read from very deep structure,
or change a value in a deep structure
while constructing the whole structure automatically.
So that part is, we have written as an entity while L,
that's the current research progress L,
plus the yield,
and research the current limit.
So what does this mean?
The left side, the entity while L dot current research progress L,
that's the lens path, those,
I wrote those entity while L and current research progress L by hand,
the lens life I could make those automatically,
but I didn't want to switch completely over using lenses,
I only want to use them in some places,
and I wanted to get a bit practice of writing stuff by hand,
so that I sort of understood what's going under the hood.
So we combine these two functions,
and then we are, so this is a ten of functions,
that I can use, I think it was called DU,
I could, if this would be called,
well, I could use DU and this lens,
and then give it to the entity current research,
and it would give me the current research progress of that,
so it would go through layers and give me that value.
But then we are using the plus delta,
which means that we are taking that value,
we are calculating a new value,
and then the lens will build the whole structure for us.
So maybe that was convoluted and long-winded explanation,
but the main idea is that I don't want to take a lot of taking
a fluxes apart and building them just to update a value,
so I am using a lens to do that thing automatically.
It still takes the fluxes apart to,
and then changes the value and builds the whole thing back up,
but it's automatic.
So now that we have an entity current research,
that has been updated with the progress.
We have to write them back to the database.
So for that, we are first filtering them.
I mean, we have a list of these things,
so we are first filtering the ones that are not finished,
and then we are using the replace function to write them back to the database.
So update finished function,
that takes us just one.
And one entity current research data.
So filtering is done with the fill the function,
so we have a let unfinished equals filter.
Open turned node.researchready.entity well,
close burn updated.
So again, we are using the dot operator to combine functions.
So you can read that dot as a off,
because then it reached node of research ready of entity well,
is the what our function does.
So first we use the entity well to get the data from the entity,
then we use the research ready function to check if the research has been completed,
and then we use the not function to invert the result.
So if it was true, it was false, and if it was false, it's true.
And now we have a list of unfinished research, entity research.
No, sorry, entity current research.
And then we use replace function to write those back down.
So map M and then the open pattern slash X arrow replace open patterns,
entity key X, close burn, open pattern entity well,
X, close burn, close burn, so unfinished.
So what we are here doing is we are using running the anonymous function
that calls replace and gives it a in primary key.
And the data to replace in the database.
So that updates the database.
I think I tried to find that couldn't find,
but I hope I should again, because this is running a one query for each data to update.
So I'm wondering if them would be a way to touch them up.
So I would give it a list of data and it would just run one query to update all the data.
Not sure if it's possible, but that's something that I should look into.
Okay, then the ones that has been finished, it's again, find finished.
Then we move the entries to the complete research and add new entries.
And then we remove the data from the current research,
because we don't want it to hang around the anymore.
This has been completed.
It doesn't have to be anymore.
And then we also clear the available research for the research type,
meaning that now that the one research has been completed,
for example engineering category, we clear all the engineering category
because then in the later stop we can write new available research there.
Because this research type was just completed,
might unlock some additional researches.
And I'll try not to read all our code.
So that's handled in the handle complete function.
Again, we are filtering it.
And then we are turning it out.
It's basically the same filter that default, but without enough.
And then we are, that we have a finished current research,
then we are finding the research platform there,
of the data technology.
And then we are using insert many
to move to insert to the completed research.
And there's a function current to complete it,
which does the, that does the,
when given a current research,
it will give you back a corresponding completed research data.
So we can just use that to create our,
our completed research data based on the,
what has been, what current research are given to them.
And then just use insert many to write a whole bunch of those,
in case there's a whole bunch of those into the database.
And then there's a research completed function
that will, when given, are current started,
the action ID, and the research type,
it will, it will write our, these entries.
And again, we can use the insert many to write a whole bunch of these.
Then the last two steps are to delete from the current research,
all the, all the finished ones.
In the code history, delete, delete the,
open bracket current research ID,
arrow to the left dot fmap entity key finished, close bracket.
So here we are.
This creates a delete,
I don't know, I don't know.
Now I'm drawing a blank how the SQL actually works.
Delete from a current underscore of research,
where ID in the list of IDs.
I think it's that, that's what it will generate.
But that arrow, arrow to the left dot is a,
it creates a in, in the SQL.
So when given a list of something,
ID is in this, in this case,
it will inset them to the SQL that will be executed.
And then we also want to delete the,
arrow for research.
So that's again, I delete the open bracket,
arrow to the left dot finish text,
comma available research equals equals dot FID.
And here there are two, two are current frames.
So the research path has to match of that values in that list
and that the, uh,
faction ID has to be that current FID.
And that's because we don't accidentally want to delete
the available research of some other faction only,
only this that we are currently handling.
And, uh,
last step is to get, uh,
create a new available research,
if there has been research complete,
or if this function is,
the process has been run on the first time,
we need to,
we need to insert into the,
the arrow for research,
data-based table,
the research is the current available.
I'll save the random number for another time,
there's plenty to talk about anyway.
But, uh, yeah.
Okay, so first we load the arrow for,
and complete the research,
we was both unheated,
and then we create a new,
random number generator.
It's done with, uh,
G arrow to the left,
lift I-O,
get SDD chain.
So this means that it's using the I-O
to get, uh,
access to the,
uh, input output.
So it's getting access to the SAS system,
and then it's creating a new,
standard random number generator,
and that is actually assigned to the G.
Uh,
this is because the
randomness depends on
some state of the system.
So usually it is done,
that it's, uh,
current time that is used for that.
Because this is a
self-random number generator that we are getting.
Yeah.
If, if we are,
if we construct,
like when we construct a
self-random generator
with a feed value,
it will always produce the same sequence
of the numbers,
no matter how many times you work.
So we have to varie the feed,
and that is done with the
current system date time.
And, uh,
then I throw the helper function,
get R,
that is, uh,
using a given random number generator,
it picks some amount of
entries from a given list.
And this picking is completely,
uh,
even it's not weighed in any way.
I'm not thinking that I should
do some weighing there,
but I had gotten around to that,
and I don't really,
I don't really have a good
plan for that yet.
How I would rate it.
But, uh,
the list for,
or, um,
for list,
to pick,
the,
the, the, the research,
uh, new, new,
novel research comes from the new
novel report research function.
And,
that, that is, uh, takes,
um, and then different categories
are handled separately.
So,
they are engineering
and social sciences,
and natural sciences.
And,
they are sort of separate,
in the system,
so they are handled separately.
We are using the same random number
generation here,
random number generator here.
So,
they,
while we are getting the same sequence,
for all three cases,
it's too long matter.
And,
if, if I later,
I read that it,
causes problems,
I can change that still.
And, then,
we are writing the,
database by,
rewrite,
rewrite,
analytical research.
So, for example,
if we were to,
figure out what,
research is available for the engineering,
we will write this as a,
let,
n,
dot,
equals,
get,
h,
g,
g,
our random number generation.
Open bracket,
unresearch limit,
max available,
close bracket.
Search limit,
how many research,
how many researches we can,
keep going at the same time.
And,
dola,
new available research,
this function that does the,
calculation of,
bio-research is available,
and that takes parameter,
is engineering,
max available,
available and completed.
So,
is engineering means that
it will only handle
engineering research.
Max available is the research limit,
how many researches we can,
have currently,
this is,
currently,
hard-coded to three,
later on,
there will be a,
a system that,
when you unlock some,
some higher research,
you get a higher research limit,
research limit,
research limit,
value,
meaning that you can,
you are not so,
you can,
you can,
better guide your research,
to which side,
which direction you,
you can go,
you have more options to forward from.
And,
available and completed,
are,
loaded from the database,
so,
this,
available research,
and complete research,
because,
a,
new available research,
needs to,
know what has been,
completed.
And,
what is currently available,
In order to, well, the strictly speaking, it only needs the complete research to figure
out what research is available, but passing in the available we can do some optimizations.
So the new available research has a quite a bit logic in that of it.
The main idea is that first we check that if the research limit is higher or equal.
No, sorry.
First, yeah, first we check if the amount of currently available research in that specific category
is in an engineering in our case.
In this example is higher or equal to the limit.
Second, if it if you add the limit or beyond it, we are just returning the empty list
because there's no no new available research.
First, we have to get our available research below that research limit.
Otherwise, we return candidates.
And the candidates are counted by the multiple steps.
So candidates are all the research categories of specific types, engineering.
In our case, that have been unlocked and are under researched.
So unlocked and under researched are the ones in unlocked research that are not known.
So we have unlocked research and unlocked and under researched are the ones that are not known
and are not in research.
And the unlocked research are again are from the category of all the research
that have all the antecedents available.
And note that is the all the completed, this is given outside, completed,
all the research types of the completed research.
So that's a, I don't want to read that aloud.
The whole implementation of that because that probably will be very interesting
in the show notes as usual.
But the idea is that based on the, what has been, what is available
and what has been completed, we filter that data and get to the list of specific categories.
It's not a specific category and this is the research categories that we are getting from that.
Okay, so from that, we have a loop of research function.
We get the, in our example, engineering candidates.
And then you want to update the database with this, with this information.
We, we figured out the new available research for the engineering natural sciences
and social sciences separately.
But then we use the, then we just combine the list together because it's easy to handle.
The reason we keep them separated in this state is that the search limit is pure category.
We have to figure out that the category.
But after that we can just combine them.
Now we have a list of, list of these categories that we can, sorry, not categories.
The list of available research is that we need to write into the database.
And this is done with the rewrite available research function.
And here we, first we have to find our dot categories of all this information.
So we have to find if we have some research that we want to write available,
we want to find the dot category of that.
And we need to find that for all the researches and we want to make a list of unique items.
So we, we use mk-unique to get a list, list of top categories.
I'm sorry, research categories, no categories.
We get a list of categories that are unique.
And then if the list is not empty, we do two things.
First we delete all the available research that are of this faction, are the these factions available research.
And that are in the list of the categories.
And then we use insert many to create available researches.
So this will mean that if some, if our faction complete this complete research in the engineering category,
for example, we manage to research a space sub-hull.
Caramel health, for example, which is in the engineering materials, if I remember correctly.
This will, this function will find out that that engineering material for research belongs to the top category of engineering.
And then it will wipe all the engineering available researches for this faction.
There should be two left if everything has been gone, gone as planned.
And then it will take all those new engineering researches and insert those into the database.
And this is why I tried explaining earlier.
This is done because completing a research might unlock some other researches.
So we want to learn figuring out how to research to do next.
Or letting the player option to figure out what to research to do next.
And taking those new, new possible new researches into account.
Again, we are still taking a limit subset of the demo.
There might be that there are, for example, 10 different researches that could be available for the only giving player three to two from.
Because the research technology is limited.
They can only see three options.
And when they get more advanced research technology, they get, for example, different engines and analytical engines.
Then they have research limit four.
They can data guide their research to towards the direction they want to take.
They can see more research options.
Okay, now everything should be ready for the next month of simulation.
So to recap.
So to recap, we find out what is the current production.
Then we find out what's the current research in the database, what's the algorithm research in the database.
And sorry, we actually find only what's the current research in the database.
And then we update the research of these current ones.
And those that wasn't completed are written back to the database.
Those that were completed are removed from the current research and moved into the complete research.
While at the same time creating a new generation is letting the faculty members know that this research has been completed.
That you should do something about it.
And then we are creating a new algorithm research is if there's a, if there's a situation balance.
That's it.
I think this was a bit, a bit convoluted explanation.
I'll try a bit better next time.
And in any case, if you have any questions, comments or feedback.
You can reach me by email or you can read me at the status where I'm to do or to add mustard on that social.
Or even better, write your, record your own episode.
And next time.
Well, I don't know yet.
But what I will look into next time.
Something related to the Haskell that that's pretty much given, I think.
Jalice, do sort of.
Thanks for listening.
All right.
You've been listening to Hacker Public Radio at HackerPublicRadio.org.
We are a community podcast network that releases shows every weekday, Monday through Friday.
Today's show, like all our shows, was contributed by an HPR listener like yourself.
If you ever thought of recording a podcast, then click on our contributing to find out how easy it really is.
Hacker Public Radio was founded by the Digital Dove Pound and the Infonomicon Computer Club.
And it's part of the binary revolution at binrev.com.
If you have comments on today's show, please email the host directly.
Leave a comment on the website or record a follow-up episode yourself.
Unless otherwise stated, today's show is released on the creative comments,
attribution, share a life, 3.0 license.