Now that our agents are positioned in space, it's reasonable to ask them to move around. This requires the following changes:
To create the move method in the agent class, we modify Agent.h:
/* Actions */
bool cooperate(); // Will indicate whether the agent cooperates or not; probability determined by = c / total
void play(repast::SharedContext<RepastHPCDemoAgent>* context); // Choose three other agents from the given context and see if they cooperate or not
void move(repast::SharedDiscreteSpace<RepastHPCDemoAgent, repast::WrapAroundBorders, repast::SimpleAdder<RepastHPCDemoAgent> >* space);
And Agent.cpp:
void RepastHPCDemoAgent::move(repast::SharedDiscreteSpace >* space){
std::vector agentLoc;
space->getLocation(id_, agentLoc);
std::vector agentNewLoc;
agentNewLoc.push_back(agentLoc[0] + (id_.id() < 7 ? -1 : 1));
agentNewLoc.push_back(agentLoc[1] + (id_.id() > 3 ? -1 : 1));
space->moveTo(id_,agentNewLoc);
}
Notice that the agent in this case must ask the space for its location. We could endow the agent with a record of its x,y coordinates, and in many cases this is perfectly fine. But we omit it here because it helps make clear that the projection, not the agent, is what determines the agent's relationship with other agents in that space.
We add a few lines to determine a new location; in this case we allow some agents (based on ID number) to move by either increasing or decreasing their x and y coordinates. We then use the 'moveTo' method to tell the space to move the agent to the new location.
This requires the following changes in the Model.cpp code. We will not add a special method to the model; we will instead simple add to the 'doSomething' method:
void RepastHPCDemoModel::doSomething(){
int whichRank = 0;
if(repast::RepastProcess::instance()->rank() == whichRank) std::cout << " TICK " << repast::RepastProcess::instance()->getScheduleRunner().currentTick() << std::endl;
if(repast::RepastProcess::instance()->rank() == whichRank){
std::cout << "LOCAL AGENTS:" << std::endl;
for(int r = 0; r < 4; r++){
for(int i = 0; i < 10; i++){
repast::AgentId toDisplay(i, r, 0);
RepastHPCDemoAgent* agent = context.getAgent(toDisplay);
if((agent != 0) && (agent->getId().currentRank() == whichRank)){
std::vector<int> agentLoc;
discreteSpace->getLocation(agent->getId(), agentLoc);
repast::Point<int> agentLocation(agentLoc);
std::cout << agent->getId() << " " << agent->getC() << " " << agent->getTotal() << " AT " << agentLocation << std::endl;
}
}
}
std::cout << "NON LOCAL AGENTS:" << std::endl;
for(int r = 0; r < 4; r++){
for(int i = 0; i < 10; i++){
repast::AgentId toDisplay(i, r, 0);
RepastHPCDemoAgent* agent = context.getAgent(toDisplay);
if((agent != 0) && (agent->getId().currentRank() != whichRank)){
std::vector<int> agentLoc;
discreteSpace->getLocation(agent->getId(), agentLoc);
repast::Point<int> agentLocation(agentLoc);
std::cout << agent->getId() << " " << agent->getC() << " " << agent->getTotal() << " AT " << agentLocation << std::endl;
}
}
}
}
std::vector<RepastHPCDemoAgent*> agents;
context.selectAgents(repast::SharedContext<RepastHPCDemoAgent>::LOCAL, countOfAgents, agents);
std::vector<RepastHPCDemoAgent*>::iterator it = agents.begin();
while(it != agents.end()){
(*it)->play(&context);
it++;
}
it = agents.begin();
while(it != agents.end()){
(*it)->move(discreteSpace);
it++;
}
discreteSpace->balance();
repast::RepastProcess::instance()->synchronizeAgentStatus<RepastHPCDemoAgent, RepastHPCDemoAgentPackage,
RepastHPCDemoAgentPackageProvider, RepastHPCDemoAgentPackageReceiver>(context, *provider, *receiver, *receiver);
repast::RepastProcess::instance()->synchronizeProjectionInfo<RepastHPCDemoAgent, RepastHPCDemoAgentPackage,
RepastHPCDemoAgentPackageProvider, RepastHPCDemoAgentPackageReceiver>(context, *provider, *receiver, *receiver);
repast::RepastProcess::instance()->synchronizeAgentStates<RepastHPCDemoAgentPackage,
RepastHPCDemoAgentPackageProvider, RepastHPCDemoAgentPackageReceiver>(*provider, *receiver);
}
The code creates the iterator through the local agents and asks them to 'play'. It then re-sets the iterator to the beginning of the (same) list, and iterates through, asking the agents to 'move'. After the agents move, the code performs four tasks:
IMPORTANT This sequence of four calls (balance, synchronizeAgentStatus, synchronizeProjectionInfo, and synchronizeAgentState) is subtle. The 'balance' method must be called before the 'synchronizeAgentStatus' method; these two are usually called one after the other. But you may find it useful to call 'synchronizeProjectionInfo' in a different place- e.g., before the 'play' method is called. In this simulation, 'synchronizeAgentStates' probably doesn't need to be called after 'synchronizeProjectionInfo'; however, there are some instances where 'synchronizeProjectionInfo' does not result in all non-local agents having updated copies of their local originals' states. Generally this has to do with network projections, not spatial projections, so this is probably not a concern here. However, it is a good idea to keep in mind that the synchronize routines do not necessarily leave the simulation in a complete and consistent state; only by using all of them together are you guaranteed to leave the simulation in a consistent state.