Info
This post was imported from a personal note. It may contain inside jokes, streams of consciousness, errors, and other nonsense.
Class inheritance is private by default #
TIL Class inheritance is private by default. Got a message like:
conversion from Boid * to ICollidable & exists but is inaccessible
This was because I was adding IAgent as a Boid interface.
class Boid : public IAgent, ICollidable {
And I only had one public
keyword for IAgent
. The solution:
class Boid : public IAgent, public ICollidable {
Looking at my plans here: I wanna create a new class BraitenBoid to replace Boid. I’m gonna have both of them implement the interface IAgent. For my purposes, Agents are entities that can take action in the World. Boids are agents. Food sources are not. Where are Boids used?
- BoidRenderer
- CollisionDetection
- IBoidListener & SimRenderer (boidCreated() and boidDeleted())
- Simulation (destructor, stepBoids(), handleCollisions(), addBoid(), boidCreated(), boidDeleted())
- MainEvaluate & MainVisualize (reportGenerationFitness(), logGeneration(), selectAndMutate(), fitnessFunction())
Phew. I think I need at least one more interface. Updating this as I go through the code that refers to Boid:
IAgent. Extends IEntity, ICollidable
- unsigned int getId()
- enum getEntityType()
- enum getAgentType()
- Vector2f &position()
- void handleCollision(ICollidable &)
BoidRenderer. I can probably just create a new renderer, BraitenBoidRenderer. But then I’ll need to have an interface for that. IBoidRenderer
. BoidRenderer is used in SimRenderer, when a boidCreated()
event is received it creates a renderer and on boidDeleted()
destroys it. Other than that it just iterates on all renderers and calls draw()
though the order of these calls could become important at some point for z-indexing… worry about that later.
For now, should I have an IEntityListener
that defines entityCreated()
and entityDeleted()
and then it’ll need to determine the entity type and instantiate the correct renderer. IDs will need to be unique across _all_ entities, not just within boids and within food sources. Otherwise I’ll see problems when deleting entities.
CollisionDetection. My OOP is shaky here. Can I define detect(Boid &, FoodSource� &)
and detect(BraitenBoid &, FooSource &)
and then just pass an IAgent
as the first argument and let the compiler figure out which detect function to call? If not, I guess I’ll receive an IAgent &
as the first argument and implement a switch myself.
IAgent should have a getter indicating the type of agent. For that matter, IEntity should have a getter indicating the type of entity.
IBoidListener & SimRenderer. All SimRenderer actually does is iterate all entities, regardless of whether Boid or FoodSource, and call draw()
. So I feel pretty safe combining the IBoidListener and IFoodSourceListener into a single IEntityListener.
Simulation. Store agents instead of boids. Can change stepBoids()
to stepAgents()
. Done. Except stepBoids()
also handles the world edge boundary conditions. Can I go ahead and give IEntity
a position? Or more to the point, give IAgent
a position?
handleCollisions()
should iterate through all IAgents and check each against all FoodSources for now. Ah. Looks like IAgent will need to implement ICollidable then. Are all entities collidable? I guess not. It would be good to have the option to not collide with decoration entities if that ever happens. Or more importantly, for noise entities that don’t do anything but provide false positives for agent sensors.
boidCreated()
and boidDeleted()
will change to entityCreated()
and entityDeleted()
so we good there.
addBoid()
is tricky. Right now I’ve got BoidProps
which track the boid ID, generationIndex, numFoodsEaten, and weights for the neural network. All for loading from / saving to file. I can have a separate props object for BraitenBoids, reckon. They don’t need to be interchangeable. The number of weights is going to be different for sure. And if I start evolving other aspects of the agents, such as the range of their sensors, that’s going to depend on the species. So that means create a new BraitenBoidProps
.
Forgot one more:
BoidMarshaller. Rename to AgentMarshaller
because it’ll be loading and saving all kinds of agents. Should each agent class define its own serialization? Probably? And random initialization? Or at least its Props class should do. Why do I have a Props class in the first place?
Get rid of Props class? I should probably just add serialize()
and deserialize()
methods to Simulation
and IAgent
or even IEntity
(okay, later for IEntity
).
#
Serialization #
Wow, great resource for my exact issue: https://isocpp.org/wiki/faq/serialization#serialize-no-inherit-no-ptrs
So looks like instead of BoidMarshaller, I’ll have Simulation.serialize(std::ostream)
and…
Lots of love for Cereal library. https://uscilab.github.io/cereal/
It’ll save me some time with the details so looks like I’ll be using it.
So instead of serialize(std::ostream &)
I’ll have serialize(Archive &archive)
. Then I can drop the BoidProps
class altogether.
Actually I can drop BoidMarshaller
, too. That’s great. I’ll need a way to add random boids to the simulation but that never belonged in BoidMarshaller
anyway.
What if down the road I _do_ want to save agent positions? Or like, be able to choose between saving them or not. Or loading them or not.
To-do list #
I’m adding these to Todoist so no need to use checkboxes here but I do have a few things to code up.
- Cerealize Boid (add IAgent members)
- Cerealize Simulation
- Get rid of BoidMarshaller and BoidProps
- Change IBoidListener and IFoodSourceListener to IEntityListener
- Add
getEntityType()
to IEntity. - Add
getAgentType()
to IAgent. - Create an IBoidRenderer interface, have BoidRenderer implement it.
- Create a BraitenBoidRenderer.
- Change references to Boid class to use IAgent interface.
Cereal #
Getting linker errors now. StackOverflow hooked me up. Problem was that I only had the template function _definition_ in the header file. My _implementation_ was in the cpp file. Sure, you do that with other methods but not with templates.
The reason? The compiler needs to have the implementation of the function to generate the templates. It needs access to the implementation while it’s processing the header file. It doesn’t have access to the source file at this point in compilation.
Another solution offered is to define a template file .tpp
and put the implementation in there. Then at the bottom of the header file put:
#include myclass.tpp
Neat.
I’ll have to wait to try it out tomorrow.