Game Theory Simulation: Reciprocal Altruism Main Thread

The Partridge Family were neither partridges nor a family. Discuss.
Post Reply
Zedtho_
Posts: 9
Joined: May 28th, 2020, 9:27 am

Game Theory Simulation: Reciprocal Altruism Main Thread

Post by Zedtho_ » May 30th, 2020, 2:52 pm

Hey everyone! I've learnt this project interests me, so I decided to make a main thread so that I don't start a new thread for every issue.

I've been working on a game theory simulation for a bio class.
The way it works is the following:
Spoiler:
The simulation is based on a book I've been reading called "The Evolution of Cooperation"by Robert Axelrod. The simulation will go as follows:

There are a number of agents (read: organisms) with different strategies.
Every round two randomly chosen agents meet, they can choose to either cooperate or defect. Points are dealt out like this:
2020-05-30 16_26_12-Book1 - Excel.png
2020-05-30 16_26_12-Book1 - Excel.png (4.5 KiB) Viewed 2457 times
(As you see, it is like the prisoner's dilemma)
A user-defined amount of rounds are played, and finally the scores are tallied. The scores of the varying Agents are then outputted.
As soon as this part of the program works, I will then add an evolutionary twist to it, where the more successful Agents will be represented more in successive generations, less successful ones will be represented less. It would be interesting to see what equilibrium the program tends towards.

The reason this simulation is so interesting to me is that it is expected to show that cooperating is actually a useful survival strategy. Thus, kindness could develop! However, no such simulations or programs exist on the web, most were done by mathematical biologists who sadly did not release their programs, so I wanted to recreate it!
I made a crappy draft on github before (see https://github.com/Zedtho/Simulation-Re ... ree/master), but now want to make it more general. More info will follow in edits!
Last edited by Zedtho_ on May 30th, 2020, 2:54 pm, edited 1 time in total.

Zedtho_
Posts: 9
Joined: May 28th, 2020, 9:27 am

Re: Game Theory Simulation: Reciprocal Altruism Main Thread

Post by Zedtho_ » May 30th, 2020, 2:52 pm

NOTE: I just got some extremely useful advice from Albinopapa, so this whole thing will probably be completely rewritten! If you guys have some additional comments though, feel free! :D
To contain the new strategies I want to use in my more generalized version, I thought inheriting classes would be a good idea. The base class would look something like this:

Code: Select all

class Agent
{
	unsigned int Score; //starts at 0
	std::vector<...> Memory; //(I need it to remember every meeting it has had: Both with whom (so a pointer to an Agent) and also what the other // opposing Agent decided to do in this
	
	public:
	bool Decision(Agent& agent); //This is where the specific strategies will come into play: Depending on the strategy, it will use its past //knowledge and a reference to its opponent to figure out what decision it will take. True = cooperate, false = Defect
	void Update(Agent& agent, and the choice of opposing agent); //This will update Memory with new information after the meeting has //happened)
}
And then I would inherit a bunch of classes from there and redefine Decision and Update in each:

Code: Select all

class AllD : public Agent
{
	bool Decision(Agent& agent) { return false}; //shitty example as it doesn't care what his opponent is, but you get the deal
	void Update etc.
}


There's a few open questions I have (if you have any ideas in general though, please share! I'm very interested in code structure)
  • 1. What type will the vector Memory be? (Would defining a nested class Event which contains a pointer to an Agent and a bool with its choice work?)
    2. Concerning the general layout of the program, I now have some problems. I don't know how to store all my agents currently. In my trials, I tried an std::vector<Agent>, but this would not accept my subclass types (perhaps in this case it would as only the functions changed?)
    Do you guys know by chance how to make that work?
    There's one work around I thought of, but there may be more pretty ways of going about:
    Spoiler:
    My idea was to have an std::vector of every subclass. Then, I would have an std::vector<Agent*> which contains a pointer to every instance of the subclasses. As far as I know, the std::vector<Agent*> won't get mad at having pointers to subclasses in it.
    3. As far as I am aware, there are also some pretty nifty C++ things I can do with virtual functions and stuff, does Chili have a video on those?

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Game Theory Simulation: Reciprocal Altruism Main Thread

Post by albinopapa » May 30th, 2020, 5:37 pm

  1. This is going to depend on what information each agent needs to make it's decision. If it only needs to know what it's last decision was with the previous encounter with an agent, then a simple struct with Agent* and bool might work. It doesn't have to be a nested class. If each agent needs different data though, things get more difficult as you'll have to figure out the Agent's derived type.
  2. std::vector requires that all elements be of the same type. There are a few ways to get around this.
    • Runtime polymorphism

      Code: Select all

      class TFT : public Agent{
      };
      class Agent{
      };
      std::vector<std::unique_ptr<Agent>> agents;
      agents.push_back( std::make_unique<TFT>() );
      
    • Using std::variant like I showed you in the PM:

      Code: Select all

      class Agent{
      public:
      using Strategy = std::variant<Coop, Defect, TFT>;
      template<typename Strat> Agent( Strat&& strat );
      };
      std::vector<Agent> agents;
      agents.emplace_back( TFT{} );
      
    • You could also just stick with the enums and just have a single Agent class and no others.
  3. Virtual functions aren't really anything special. They just declare the interface that derived classes must define.

    Code: Select all

    class Agent {
    public:
        // The = 0 at the end simply means that the Agent class doesn't have a default behavior, 
        // so child classes ( derived classes ) are required to define it.
        virtual void Update( Agent* other_agent ) = 0; 
    };
    class TFT : public Agent{
    public:
        // Here, override at the end tells the compiler that TFT has defined a function that 
        // Agent declared as pure virtual ( the = 0 thing ).
        void Update( Agent* other_agent )override{
            MakeDecision( other_agent );
        }
    
    private:
        void MakeDecision( Agent const* other_agent );
    };
    
    Chili does cover polymorphism in a couple of his videos in the intermediate series perhaps, it's been quite some time so I can't remember which ones. Luckily, his reboot series ( the ones he started in 2016 ) have descriptive titles so it shouldn't be too difficult to find them.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Slidy
Posts: 80
Joined: September 9th, 2017, 1:19 pm

Re: Game Theory Simulation: Reciprocal Altruism Main Thread

Post by Slidy » May 30th, 2020, 5:41 pm

1. Sure, that works. I wouldn't stress about it too much since you can always come back and revisit the class if you realise something is missing.

2. You do need to use std::vector<Agent*>, the pointer is necessary to leverage polymorphism. You don't need to store the subclasses in separate vectors, you can just do dynamic allocations to instantiate an object of the class. Here's an example:

Code: Select all

std::vector<Agent*> agents;
agents.push_back(new AllD);
agents.push_back(new Defector);
agents.push_back(new Something(5));
Something to note is if you don't want to be leaky you need to call "delete" on every pointer you get using "new" to free the memory you allocated:

Code: Select all

for (size_t i = 0; i < agents.size(); i++)
{
  delete agents[i];
}
There are some sexy ways to handle deleting allocations automagically by using smart pointers (unique_ptr, shared_ptr, etc.) but I'd consider that an unnecessary complication for you at this point.

3. Yes, Chili covers all of this in the intermediate. He also covers dynamic allocation and smart pointers which I mentioned above. Highly recommend you go through entire intermediate series, it has tons of essential C++ info in there. However if you just want to focus on the things relevant here then I think intermediate videos 1-6, and 17-21 are what you should watch.

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Game Theory Simulation: Reciprocal Altruism Main Thread

Post by albinopapa » May 30th, 2020, 5:52 pm

There is an idiom for determining a derived class's type without using enums and switch statements. It's the visitor pattern.

Code: Select all

// Forward declarations
class TFT;
class Defect;
class Coop;

class Agent {
public:
    virtual void Meet( Agent* other ) = 0;
    virtual void Meet( TFT* other ) = 0;
    virtual void Meet( Defect* other ) = 0;
    virtual void Meet( Coop* other ) = 0;
private:
};

class TFT : public Agent{
public:
    void Meet( Agent* other )override {
        // This will call the Agent::Meet( TFT* other ) virtual function
        // What ever child class 'other' is, will have to define this encounter as well   
        other->Meet( this );  
    }
    void Meet( TFT* other )override{
        // TFT meets another TFT
    }
    void Meet( Defect* other ) override{
        // TFT meets a Defect
    }
    void Meet( Coop* other ) override {
        // TFT meets a Coop
    }
};
You just override the virtual Meet() functions for each of the child classes ( Defect, Coop, etc... ). Since all the classes need to know about each other, you'll have to forward declare each child class in the header files for each, then only include each of their headers in the cpp files.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Zedtho_
Posts: 9
Joined: May 28th, 2020, 9:27 am

Re: Game Theory Simulation: Reciprocal Altruism Main Thread

Post by Zedtho_ » June 1st, 2020, 8:46 am

Thanks a bunch for the answers! This is very useful! I'll mess with the program and report back.

Post Reply