package ise.mace.agents;
import ise.mace.actions.Proposal.ProposalType;
import ise.mace.actions.Vote.VoteType;
import ise.mace.inputs.Proposition;
import ise.mace.models.Food;
import ise.mace.models.HuntingTeam;
import ise.mace.participants.AbstractAgent;
import ise.mace.participants.PublicGroupDataModel;
import ise.mace.models.GroupDataInitialiser;
import static ise.mace.models.ScaledDouble.scale;
import ise.mace.tokens.AgentType;
import ise.mace.models.History;
import ise.mace.models.Tuple;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import ise.mace.participants.AbstractGroupAgent;
import java.util.Collections;
import java.util.Comparator;
import java.util.TreeSet;
import java.util.HashMap;
import java.util.ListIterator;
import java.util.Iterator;
import java.util.Random;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
*/
public class PoliticalAgent extends AbstractAgent
{
private static final long serialVersionUID = 1L;
private String invitationToGroup = null;
private final static TreeSet<String> invitationHolders = new TreeSet<String>();
private final static HashMap<String, String> groupFounders = new HashMap<String, String>();
private final static TreeSet<String> membersToKickOut = new TreeSet<String>();
private final static TreeSet<String> freeToGroup = new TreeSet<String>();
private History<Double> satisfaction = new History<Double>(1);
// private static List<String> special_agents = new LinkedList<String>();
private static String previousAdvisor = null;
@Deprecated
public PoliticalAgent()
{
super();
}
public PoliticalAgent(double initialFood, double consumption, AgentType type,
double socialBelief, double economicBelief)
{
super("<hunter>", 0, initialFood, consumption, type, socialBelief,
economicBelief);
}
//ADDED The0
public PoliticalAgent(double initialFood, double consumption, AgentType type,
double socialBelief, double economicBelief, String name)
{
super("<hunter>", 0, initialFood, consumption, type, socialBelief,
economicBelief, name);
}
@Override
protected void onActivate()
{
freeToGroup.add(this.getId());
}
@Override
protected void beforeNewRound()
{
TreeSet<String> theLiving = new TreeSet<String>();
for (String activeAgent : getConn().getAgents())
{
theLiving.add(activeAgent);
}
if (!theLiving.isEmpty())
{
TreeSet<String> theDead = new TreeSet<String>();
for (String agent : freeToGroup)
{
if (!theLiving.contains(agent))
{
theDead.add(agent);
}
}
for (String agent : theDead)
{
if (freeToGroup.contains(agent))
{
freeToGroup.remove(agent);
}
}
}
else
{
freeToGroup.clear();
}
}
/**
* This method assesses an agent's satisfaction in the group. If the agent is satisfied remains in the group
* otherwise it will request to leave the group.
* @param none
* @return Satisfied or not?
*/
protected boolean SatisfiedInGroup()
{
if (satisfaction.isEmpty())
satisfaction.newEntry(0.0);
//get the previous satisfaction value
Double previousSatisfaction = satisfaction.getValue();
if (getConn().getGroupById(getDataModel().getGroupId()) == null)
return false;
//compute current satisfaction, based on your socio economic vector distance with the group
double myEconomic = getDataModel().getEconomicBelief();
double mySocial = getDataModel().getSocialBelief();
double groupEconomic = getConn().getGroupById(getDataModel().getGroupId()).getCurrentEconomicPoisition();
double groupSocial = getConn().getGroupById(getDataModel().getGroupId()).getEstimatedSocialLocation();
double deltaEconomic = groupEconomic - myEconomic;//change in X
double deltaSocial = groupSocial - mySocial;//change in Y
Double currentSatisfaction = Math.sqrt(Math.pow(deltaEconomic, 2) + Math.pow(
deltaSocial, 2));
//store it in the history
satisfaction.newEntry(currentSatisfaction);
//compare with previous satisfaction and find out if agent should stay in the group
Double deltaSatisfaction = currentSatisfaction - previousSatisfaction;
if (deltaSatisfaction >= 0)//you're growing more satisfied, so stay
{
return true;
}
else if (currentSatisfaction > 0.6)//if strictly greater than the weighting of 'esFaction' in the grouping heuristic
{
//you're very far apart on the political compass and you're not satisfied, so give up and leave
return false;
}
else if (deltaSatisfaction < -0.2)
{
//you may be quite close on the political compass, but you were very unsatisfied in the last turn,
//so give up and leave
return false;
}
else
{
//you're close on the political compass and you were unsatisfied in the last turn, but, you're
//willing to give the group another shot
return true;
}
}
/**
* This procedure is primarily used right before an agent issues the choice to leave the group it
* is currently in. If the group it is leaving had only one other member then that member is stored
* in a dedicated tree structure 'membersToKickOut' so that it is known that this member will have
* to leave in the next turn. This is necessary as you cannot have a group characterised by only one member.
* @param none
* @return none
*/
protected void checkToEvict()
{
PublicGroupDataModel myGroup = getConn().getGroupById(
getDataModel().getGroupId());
//Used when agent is about to issue the command to leave the group of only himself and another,
//it searches for the other agent and tells it leave the group as well
if (myGroup == null) return;
if (myGroup.getMemberList().size() == 2)
{
for (String member : myGroup.getMemberList())
{
//look for the other member of this group to kick out into free agent mode
if (!member.equals(getDataModel().getId()))
membersToKickOut.add(member);
}
}
}
/**
* This method enables agents to form groups. It uses a heuristic based on mutual trust and
* the socio-economic beliefs. The agent can either try the heuristic with another free agent or
* with an existing group. The priority is to find an existing group to join. If that fails then
* we check compatibility between two free agents.
* @param none
* @return The group ID that this agent has chosen to join. If null no group is chosen.
* If leaveGroup is returned the agent requested to leave the group
*/
@Override
protected String chooseGroup()
{
String chosenGroup = "";
//If you're not available to group and you are part of a group already
//Note: an agent is allowed to not be free to group and not be part of a group,
// this agent would be waiting to receive an invitation or wants to return one
if (!freeToGroup.contains(this.getId()) && getDataModel().getGroupId() != null)
{
//If you have been told to leave this group then do so
if (membersToKickOut.contains(this.getId()))
{
membersToKickOut.remove(this.getId());
return leaveGroup;
}
//If you're satisfied in the group, nothing changes for you, you remain not free to group with others
if (SatisfiedInGroup())
{
return null;
}
else//Otherwise, you need to leave the group and tell the other guy to leave if its just you and him in the group
{
checkToEvict();
return leaveGroup;
}
}
else//Otherwise, you are not yet part of a group:
{
//If you have a pending invitation to a group then return the invitation
if (invitationToGroup != null && invitationHolders.contains(this.getId()))
{
return invitationToGroup;
}
else//Otherwise, you have to look for a grouping:
{
//If you're here then you're still a free agent, so, firstly try to find an optimal group to join with
if (freeToGroup.contains(this.getId()) && !getConn().getGroups().isEmpty())
{
chosenGroup = agentGroupGrouping();//returns either "" or a new String (which is the group)
}
//Secondly, if the above fails, try to find an optimal free agent to form a group with
if (freeToGroup.contains(this.getId()) && chosenGroup.equals(""))
{
chosenGroup = freeAgentsGrouping();//returns either null or a new String (which is the group)
}
}
}
return chosenGroup;//can either be null or a String (which is the group)
}
/**
* This method enables agents to check their compatibility to already existing groups
* @param none
* @return The group ID that this agent has chosen to join. If null no group is chosen.
*/
private String agentGroupGrouping()
{
String chosenGroup = "";
double maxDistance = Math.sqrt(2);
List< Tuple<String, Double>> partnershipCandidates = new LinkedList< Tuple<String, Double>>();
//Assess each group in turn
for (String groupID : getConn().getGroups())
{
//proceed, only if, this is a group with two members or more
if (getConn().getGroupById(groupID).getMemberList().size() >= 2 && !PoliticalAgentGroup.special.equals(
groupID)) //ADDED THE0
{
int numKnownTrustValues = 0;
double trustSum = 0;
for (String trustee : getConn().getGroupById(groupID).getMemberList())
{
//Obtain how much trust there is between this agent and the members of the group
if (getDataModel().getTrust(trustee) != null)
{
trustSum += getDataModel().getTrust(trustee);
numKnownTrustValues++;
}
}
//calculates the vector distance between agent's and group's beliefs
double economic = getConn().getGroupById(groupID).getCurrentEconomicPoisition() - getDataModel().getEconomicBelief();//change in X
double social = getConn().getGroupById(groupID).getEstimatedSocialLocation() - getDataModel().getSocialBelief();//change in Y
double vectorDistance = Math.sqrt(Math.pow(economic, 2) + Math.pow(
social, 2));
//The longer the distance the lower the esFaction is. Therefore, agents close to group's beliefs have
//higher probability of joining this group
double esFaction = 1 - (vectorDistance / maxDistance);
Tuple<String, Double> tuple;
double heuristicValue;
if (numKnownTrustValues != 0)
{
//The actual heuristic value is calculated. The politics is more important for compatibility than
//trust when a free agent tries to enter a group
heuristicValue = 0.3 * (trustSum / numKnownTrustValues) + 0.7 * esFaction;
}
else
{
heuristicValue = 0.7 * esFaction;
}
tuple = new Tuple<String, Double>(groupID, heuristicValue);
partnershipCandidates.add(tuple);
}
}
//We sort candidate groups in descending order based on the heuristic value
Collections.sort(partnershipCandidates, c);
//Then simply check if the top candidate has a heuristic evaluation above a certain threshold
//If top candidate has a value less than the threshold no need to check anyone else since
//they are in descending order.
if (!partnershipCandidates.isEmpty())
{
double topCandidateHeuristicValue = partnershipCandidates.get(0).getValue();
//If top candidate has evaluation above the threshold then choose that group
if (topCandidateHeuristicValue > 0.4)
{
chosenGroup = partnershipCandidates.get(0).getKey();
freeToGroup.remove(this.getId());
}
}
return chosenGroup;
}
/**
* This method enables agents to check their compatibility with other free agents
* @param none
* @return The group ID that this agent has chosen to join. If null no group is chosen.
*/
private String freeAgentsGrouping()
{
String chosenGroup = null;
double maxDistance = Math.sqrt(2);
List< Tuple<String, Double>> partnershipCandidates = new LinkedList< Tuple<String, Double>>();
List< Tuple<String, Double>> partnershipESFactions = new LinkedList< Tuple<String, Double>>();
//Iterate over the set of free agents
for (String trustee : getConn().getUngroupedAgents())
{
//if an agent is not comparing with itself and has not been invited or has not formed a group already
if ((!this.getId().equals(trustee)) && (!invitationHolders.contains(
trustee)) && (!groupFounders.containsKey(trustee))
&& (freeToGroup.contains(trustee)))
{
Double trustValue = this.getDataModel().getTrust(trustee);
//Calculate the vector distance between these two agents socio-economic beliefs
double economic = getConn().getAgentById(trustee).getEconomicBelief() - getDataModel().getEconomicBelief();//change in X
double social = getConn().getAgentById(trustee).getSocialBelief() - getDataModel().getSocialBelief();//change in Y
double vectorDistance = Math.sqrt(Math.pow(economic, 2) + Math.pow(
social, 2));
//The longer the distance the lower the esFaction is. Therefore, agents close to group's beliefs have
//higher probability of joining this group
double esFaction = 1 - (vectorDistance / maxDistance);
Tuple<String, Double> tuple;
double heuristicValue;
if (trustValue != null)
{
//The actual heuristic value is calculated. Trust is more important for compatibility than the politics
//when free agents try to group with each other
heuristicValue = 0.7 * trustValue + 0.3 * esFaction;
}
else
{
heuristicValue = 0.3 * esFaction;
}
tuple = new Tuple<String, Double>(trustee, heuristicValue);
partnershipCandidates.add(tuple);
Tuple<String, Double> esTuple = new Tuple<String, Double>(trustee,
esFaction);
partnershipESFactions.add(esTuple);
}
}
//We sort candidate groups in descending order based on the heuristic value
Collections.sort(partnershipCandidates, c);
//Then simply check if the top candidate has a heuristic evaluation above a certain threshold
//If top candidate has a value less than the threshold no need to check anyone else since
//they are in descending order.
if (!partnershipCandidates.isEmpty())
{
double topCandidateHeuristicValue = partnershipCandidates.get(0).getValue();
//If top candidate has evaluation above the threshold then choose that group
if (topCandidateHeuristicValue > 0.3)
{
String invitee = partnershipCandidates.get(0).getKey();
ListIterator<Tuple<String, Double>> itr = partnershipESFactions.listIterator();
Double topCandidateESFaction = null;
while (itr.hasNext())
{
Tuple<String, Double> listTuple = itr.next();
if (listTuple.getKey().equals(invitee))
topCandidateESFaction = listTuple.getValue();
}
if (inviteeAccepts(invitee, topCandidateESFaction))
{
//Create a new group and invite your partner to join it
GroupDataInitialiser myGroup = new GroupDataInitialiser(
this.uniformRandLong(),
(this.getDataModel().getEconomicBelief() + getConn().getAgentById(
invitee).getEconomicBelief()) / 2);
Class<? extends AbstractGroupAgent> gtype = getConn().getAllowedGroupTypes().get(
0);
chosenGroup = getConn().createGroup(gtype, myGroup, invitee);
createGroupAgent(chosenGroup); //Create the group agent
groupFounders.put(this.getId(), chosenGroup);
freeToGroup.remove(this.getId());
}
}
}
return chosenGroup;
}
/**
* Creates the agent that represents a group
* @param chosenGroup : the name of the agent is equal to the group id representing
*/
private void createGroupAgent(String chosenGroup)
{
//****** ADDED THEO
//GROUP INTO AGENTS
PoliticalAgentGroup.special_no++;
//Create special group
if (PoliticalAgentGroup.special_no == 1)
{
GroupDataInitialiser spGroup = new GroupDataInitialiser(
this.uniformRandLong(), 1.0);
Class<? extends AbstractGroupAgent> gtype = getConn().getAllowedGroupTypes().get(
1);
PoliticalAgentGroup.special = getConn().createGroup(gtype, spGroup);
}
//Creates a political Agent-group
getConn().createAgent(0,
getConn().getGroupById(PoliticalAgentGroup.special).getCurrentEconomicPoisition(),
0.5, chosenGroup); //CREATE a new AGENT-Group
//*********
}
/**
* This method enables agents who received an invitation to check if they want to accept that invitation
* @param none
* @return The group ID held in the invitation. If null, the agent has rejected the invitation.
*/
private boolean inviteeAccepts(String invitee, Double esFaction)
{
//Retieve the trust value between founder and invitee
Double trustValue = getConn().getAgentById(invitee).getTrust(
getDataModel().getId());
double heuristicValue;
if (trustValue != null)
{
//The actual heuristic value is calculated. Trust is more important for compatibility than the politics
//when free agents try to group with each other
heuristicValue = 0.7 * trustValue + 0.3 * esFaction;
}
else
{
heuristicValue = 0.3 * esFaction;
}
if (heuristicValue > 0.3)
{
//you accept the invitation and are no longer free to group with anyone else
freeToGroup.remove(invitee);
return true;
}
else
{
//you reject the invitation
return false;
}
}
@Override
protected void groupApplicationResponse(boolean accepted)
{
if (invitationHolders.contains(this.getId()))
{
invitationHolders.remove(this.getId());
invitationToGroup = null;
}
if (groupFounders.containsKey(this.getId()))
{
groupFounders.remove(this.getId());
}
if (!accepted)
{
freeToGroup.add(this.getId());
}
}
/**
* This method enables agents to pick their preferred choice of food
* The choice is based on several factors. First of all if we deal with a free agent
* its choice is based only on its type (TFT, AD, AC or R). Otherwise, the agent belongs
* to a group it can also ask for advice. If the advice is not good enough then the agent just
* follows its type.
* @param none
* @return The chosen food for this round.
*/
@Override
protected Food chooseFood()
{
if (getDataModel().getHuntingTeam() == null) return null;
List<String> members = this.getDataModel().getHuntingTeam().getMembers();
//We assume there will only be two food sources (stags/rabbits)
List<Food> foodArray = new LinkedList<Food>();
Food suggestedFood, cooperateFood, defectFood, choice;
//Distinguish between stag (cooperate) and rabbit (defect)
foodArray = this.getFoodTypes();
cooperateFood = foodArray.get(0);
defectFood = foodArray.get(1);
String groupID = this.getDataModel().getGroupId();
//If the agent belongs to a group then it can ask for advice
if (groupID != null && getConn().getGroupById(groupID).getMemberList().size() > 1)
{
suggestedFood = this.askAdvice(members);
if (suggestedFood != null)
{
return suggestedFood;
}
}
//If the agent is not in a group or advisor didn't give a definitive answer then hunt
//according to type
switch (this.getDataModel().getAgentType())
{
//The choice is always to hunt stags
case AC:
choice = cooperateFood;
break;
//The choice is always to hunt rabbits
case AD:
choice = defectFood;
break;
// Picks a random stratergy
case R:
choice = (uniformRandBoolean() ? cooperateFood : defectFood);
break;
//If first time cooperate else imitate what your partner (opponent?) choose the previous time
case TFT:
//Get last hunting choice of opponent and act accordingly
Food opponentPreviousChoice = cooperateFood;
// TFT makes no sense in a team of 1...
if (members.size() == 1)
{
choice = cooperateFood;
return choice;
}
//Get the previous choice of your pair. For this round imitate him.
//In the first round we have no hunting history therefore default choice is stag
if (members.get(0).equals(this.getId()))
{
if (getConn().getAgentById(members.get(1)) != null)
{
if (getConn().getAgentById(members.get(1)).getHuntingHistory().size() != 1)
{
opponentPreviousChoice = getConn().getAgentById(members.get(1)).getHuntingHistory().getValue(
1);
}
}
}
else
{
if (getConn().getAgentById(members.get(0)) != null)
{
if (getConn().getAgentById(members.get(0)).getHuntingHistory().size() != 1)
{
opponentPreviousChoice = getConn().getAgentById(members.get(0)).getHuntingHistory().getValue(
1);
}
}
}
choice = opponentPreviousChoice;
break;
default:
throw new IllegalStateException("Agent type was not recognised");
}
return choice;
}
/**
* This method allows an agent to make a proposal to change the economic belief of the group.
* Based on its own economic belief the agent decides the type of the proposal.
* @param none
* @return The type of the proposal. Three types {staySame, moveRight, moveLeft}
*/
@Override
protected ProposalType makeProposal()
{
//Note : No need to check if agent is in a group. This is done by doMakeProposal
String groupId = this.getDataModel().getGroupId();
if (getConn().getGroupById(groupId) == null) return ProposalType.staySame;
ProposalType proposal;
//Get the economic beliefs of the agent and the group
double groupEconomicPosition = this.getConn().getGroupById(groupId).getCurrentEconomicPoisition();
double agentEconomicBelief = this.getDataModel().getEconomicBelief();
//Three cases: -> Agent economic belief = Group economic belief -> agent proposes to stay there
// -> Agent economic belief > group economic belief -> agent prefers to move right (remember left is zero and right is one)
// -> Agent economic belief < group economic belief -> agent prefers to move left
if (agentEconomicBelief > groupEconomicPosition)
{
proposal = ProposalType.moveRight;
}
else if (agentEconomicBelief < groupEconomicPosition)
{
proposal = ProposalType.moveLeft;
}
else
{
proposal = ProposalType.staySame;
}
return proposal;
}
/**
* This method allows an agent to cast its vote about a proposal. The decision depends
* on the economic belief of this agent.
* @param p The proposition
* @return The type of the vote. Two types {For, Against}
*/
@Override
protected VoteType castVote(Proposition p)
{
String groupId = this.getDataModel().getGroupId();
String proposerGroup = p.getOwnerGroup();
ProposalType agentProposal;
VoteType vote = null;
if (groupId != null && getConn().getGroupById(groupId).getMemberList().size() > 1)//check if is in a group
{
if (groupId.equals(proposerGroup)) //check if agent is in the same group as the proposal
{
double groupEconomicPosition = this.getConn().getGroupById(groupId).getCurrentEconomicPoisition();
double agentEconomicBelief = this.getDataModel().getEconomicBelief();
//What this agent would propose...
if (agentEconomicBelief > groupEconomicPosition)
{
agentProposal = ProposalType.moveRight;
}
else if (agentEconomicBelief < groupEconomicPosition)
{
agentProposal = ProposalType.moveLeft;
}
else
{
agentProposal = ProposalType.staySame;
}
//Compare agent's possible proposal to the actual proposal.
//If they agree obviously the agent will vote for.
if (p.getType().equals(agentProposal))
{
vote = VoteType.For;
}
else
{
vote = VoteType.Against;
}
}
else
{ //must never happen!!
vote = VoteType.Abstain;
}
}
else //must never happen!!
{
vote = VoteType.Abstain;
}
return vote;
}
/**
* If this agent has been chosen to be an advisor then this method will return the advice
* @param agent The asking agent
* @param agentsTeam The hunting team the asking agent belongs to
* @return The advice in the form of suggested food type
*/
@Override
protected Food giveAdvice(String agent, HuntingTeam agentsTeam)
{
double MaxThreshold = 0.9;
double MinThreshold = 0.1;
String opponentID = null;
//find opponent
if (agentsTeam.getMembers().get(0).equals(agent))
{
if (getConn().getAgentById(agentsTeam.getMembers().get(1)) != null)
{
opponentID = agentsTeam.getMembers().get(1);
}
}
else
{
if (getConn().getAgentById(agentsTeam.getMembers().get(0)) != null)
{
opponentID = agentsTeam.getMembers().get(0);
}
}
//get opponent's trust value from "this" agent
double opponentTrust;
if ((opponentID != null) && (getDataModel().getTrust(opponentID) != null))
{
opponentTrust = getDataModel().getTrust(opponentID);
}
else
{
return null;
}
//We assume there will only be two food sources (stags/rabbits)
List<Food> foodArray = new LinkedList<Food>();
Food cooperateFood, defectFood, choice;
//Distinguish between stag (cooperate) and rabbit (defect)
foodArray = this.getFoodTypes();
cooperateFood = foodArray.get(0);
defectFood = foodArray.get(1);
//Check for threshold values. If this agent has high trust value for the opponent
// the advice is to cooperate. Otherwise the advice is to defect.
if (opponentTrust >= MaxThreshold)
{
choice = cooperateFood;
}
else if (opponentTrust <= MinThreshold)
{
choice = defectFood;
}
else //This agent cannot say for sure if the opponent will cooperate or not
{
choice = null;
}
return choice;
}
/**
* This method updates the agent's happiness after hunt.
* @param foodHunted The amount of food the agent returned from hunting.
* @param foodReceived The final amount of food the agent received after tax
* @return The new happiness value
*/
@Override
protected double updateHappinessAfterHunt(double foodHunted,
double foodReceived)
{
//NOTE: Free agents can update their happiness but not their loyalty (see next method)
//'entitelment' denotes the amount of food an agent wants to get, at the least
double entitlement = getDataModel().getEconomicBelief() * foodHunted;
double surplus = foodReceived - entitlement;
Double currentHappiness = getDataModel().getCurrentHappiness();
if (currentHappiness == null)
//By default we are all satisfied with the economic position
//we start off in, unless you are always happy or just hate life
currentHappiness = 0.5 * getDataModel().getEconomicBelief();
//If surplus is >0 you're overjoyed and increase happiness
//If surplus is <0 you are dissapointed and decrease your happiness
//If surplus is zero nothing really changed
/*
* NOTE: We tried using the below statement to update happiness but now
* feel that it is biased to move agents more Left on the plotical compass
*/
//currentHappiness = scale(currentHappiness, surplus, 0.1);
return currentHappiness;
}
/**
* This method updates the agent's loyalty after hunt. Note that loyalty is related to happiness
* @param foodHunted The amount of food the agent returned from hunting.
* @param foodReceived The final amount of food the agent received after tax
* @return The new loyalty value
*/
@Override
protected double updateLoyaltyAfterHunt(double foodHunted, double foodReceived)
{
//Loyalty after hunting refines from how much more happy you are after the hunt
//and from comparing your economic (sharing of food) belief with the group's belief.
String groupId = getDataModel().getGroupId();
if (groupId != null && getConn().getGroupById(groupId).getMemberList().size() > 1)
{
//get change in economic beliefs
double myEconomic = getDataModel().getEconomicBelief();
double myGroupEconomic = getConn().getGroupById(
getDataModel().getGroupId()).getCurrentEconomicPoisition();
//how close are you to the group's belief
double deltaEconomic = Math.abs(myGroupEconomic - myEconomic);
//get change in happiness
Double oneTurnAgoHappiness = this.getDataModel().getHappinessHistory().getValue(
1);
//if there is no entry for happiness initialise it
if (oneTurnAgoHappiness == null)
{
oneTurnAgoHappiness = 0.5 * myEconomic;
}
//Calculate difference in happiness between the current and the previous round
Double currentHappiness = getDataModel().getCurrentHappiness();
double deltaHappiness = currentHappiness - oneTurnAgoHappiness;//how much or less happy did you get
//get new loyalty
Double currentLoyalty = getDataModel().getCurrentLoyalty();
//As this if statement implies either entry to your first group or
//entry to a new (but not necessarily your first) group then you're
//loyal to the average sense (not too much and no too little)
if (currentLoyalty == null || currentLoyalty == 0)
{
currentLoyalty = 0.5 * (oneTurnAgoHappiness + deltaEconomic);
}
//If deltaHappiness is < 0 you lose loyalty to the group. Otherwise if deltaHappiness is >0
//you gain loyalty. If deltaHappiness is zero you don't change your loyalty
currentLoyalty = scale(currentLoyalty, deltaHappiness, deltaEconomic);
return currentLoyalty;
}
else //agent doesnt belong to a group and so is not loyal to anyone
{
return 0;
}
}
/**
* This method updates the agent's trust value for its current opponent after hunt.
* @param foodHunted The amount of food the agent returned from hunting.
* @param foodReceived The final amount of food the agent received after tax
* @return A map entry containing the opponent's ID and the new trust value
*/
@Override
protected Map<String, Double> updateTrustAfterHunt(double foodHunted,
double foodReceived)
{
String opponentID;
Map<String, Double> newTrustValue = new HashMap<String, Double>();
double trust;
//get what this agent has chosen to hunt in this round
Food lastHunted = this.getDataModel().getLastHunted();
//Get the members of the hunting team
if (this.getDataModel().getHuntingTeam() == null) return null;
List<String> members = this.getDataModel().getHuntingTeam().getMembers();
//If agent didn't go hunting or has no team pair then do nothing
if ((lastHunted == null) || (members.size() < 2)) return null;
//Find out agent's opponent ID
if (members.get(0).equals(this.getId()))
{
opponentID = members.get(1);
}
else
{
opponentID = members.get(0);
}
//Get agent's trust value for this particular opponent
//If there is no entry initialise it
if (this.getDataModel().getTrust(opponentID) != null)
{
trust = this.getDataModel().getTrust(opponentID);
}
else
{
trust = 0.1;
}
//If agent hunted stag then check what the opponent did. If betrayed decrease trust
// otherwise increase it. If the agent hunted rabbit no change in trust
if (lastHunted.getName().equals("Stag"))
{
if (foodHunted == 0) //Agent has been betrayed
{
trust = scale(trust, -1, this.uniformRand());
}
else //Opponent cooperated
{
trust = scale(trust, 1, this.uniformRand());
}
}
else //Agent hunted rabbit so no trust issues
{
trust = scale(trust, 0, 0.3);
}
newTrustValue.put(opponentID, trust);
//Agents must also increase or decrease their trusts for their advisors. If the advice was
//good then they increase trust.
if (previousAdvisor != null)
{
double advisorTrust;
if (getDataModel().getTrust(previousAdvisor) != null)
{
advisorTrust = getDataModel().getTrust(previousAdvisor);
}
else
{
advisorTrust = 0.1;
}
if (foodHunted > 0)
{
advisorTrust = scale(advisorTrust, 10, this.uniformRand());
}
else
{
advisorTrust = scale(advisorTrust, -10, this.uniformRand());
}
newTrustValue.put(previousAdvisor, advisorTrust);
}
return newTrustValue;
}
/**
* This method updates the agent's loyalty after the voting results are published.
* @param proposition The proposition
* @param votes How many votes this proposition got. If votes > 0 proposition passed otherwise has not.
* @param overallMovement The overall change in group's position after voting
* @return The new loyalty value
*/
@Override
protected double updateLoyaltyAfterVotes(Proposition proposition, int votes,
double overallMovement)
{
//Loyalty after voting refines from how much more happy you are after the vote
//and from comparing your economic (decision to deviate from your belief) belief
//with the group's belief.
String groupId = getDataModel().getGroupId();
if (groupId != null && getConn().getGroupById(groupId).getMemberList().size() > 1)
{
//get change in economic beliefs
double myEconomic = getDataModel().getEconomicBelief();
double myGroupEconomic = getConn().getGroupById(
getDataModel().getGroupId()).getCurrentEconomicPoisition();
double deltaEconomic = Math.abs(myGroupEconomic - myEconomic);//how close are you to the group's belief
//get change in happiness
Double oneTurnAgoHappiness = getDataModel().getHappinessHistory().getValue(
1);
if (oneTurnAgoHappiness == null)
{
oneTurnAgoHappiness = 0.5 * myEconomic;
}
double currentHappiness = getDataModel().getCurrentHappiness();
double deltaHappiness = currentHappiness - oneTurnAgoHappiness;//how much or less happy did you get
//get new loyalty
Double currentLoyalty = getDataModel().getCurrentLoyalty();
if (currentLoyalty == null || currentLoyalty == 0)
//As this if statement implies either entry to your first group or
//entry to a new (but not necessarily your first) group then you're
//loyal to the average sense (not too much and no too little)
currentLoyalty = 0.5 * (oneTurnAgoHappiness + deltaEconomic);
//If this concerns you...
if (this.getDataModel().getGroupId().equals(proposition.getOwnerGroup()))
{
//If deltaHappiness <0 you lose loyalty to your group
//If deltaHappiness >0 you increase your loyalty to your group
//If deltaHappiness = 0 you don't change at all
currentLoyalty = scale(currentLoyalty, deltaHappiness, deltaEconomic);
}
return currentLoyalty;
}
else
return 0;//agent doesnt belong to a group and so is not loyal to anyone
}
/**
* This method updates the agent's happiness after the voting results are published.
* @param proposition The proposition
* @param votes How many votes this proposition got. If votes > 0 proposition passed otherwise has not.
* @param overallMovement The overall change in group's position after voting
* @return The new happiness value
*/
@Override
protected double updateHappinessAfterVotes(Proposition proposition, int votes,
double overallMovement)
{
Double currentHappiness = getDataModel().getCurrentHappiness();
if (getDataModel().getGroupId() == null) return currentHappiness;
if (currentHappiness == null)
{
//By default we are all satisfied with the economic position
//we start off in, unless you are always happy or just hate life
currentHappiness = 0.5 * getDataModel().getEconomicBelief();
}
//If this concerns you...
if (getDataModel().getGroupId().equals(proposition.getOwnerGroup()))
{
//If votes > 0 you are happy your proposition was passed
//If votes < 0 you are dissapointed your proposition was not passed
currentHappiness = scale(currentHappiness, votes,
Math.abs(overallMovement));
}
return currentHappiness;
}
/**
* This method updates the agent's trust for the proposer after the voting results are published.
* @param proposition The proposition
* @param votes How many votes this proposition got. If votes > 0 proposition passed otherwise has not.
* @param overallMovement The overall change in group's position after voting
* @return The new loyalty value
*/
@Override
protected Map<String, Double> updateTrustAfterVotes(Proposition proposition,
int votes, double overallMovement)
{
Map<String, Double> newTrustValue = new HashMap<String, Double>();
String proposer = proposition.getProposer();
double proposerTrust;
//Check if proposer is not this agent. There is no point in increasing (or decreasing)
//the trust to yourself
if (!this.getDataModel().getId().equals(proposer))
{
if (this.getDataModel().getTrust(proposer) != null)
{
proposerTrust = this.getDataModel().getTrust(proposer); //get current trust of proposer
}
else
{
proposerTrust = 0.1;
}
//if votes > 0 we increase the trust for proposer
//if votes < 0 we decrease the trust for proposer
proposerTrust = scale(proposerTrust, votes, this.uniformRand());
newTrustValue.put(proposer, proposerTrust);
}
else
{
newTrustValue = null;
}
return newTrustValue;
}
/**
* This method updates the agent's social belief after the voting results are published.
* @param proposition The proposition
* @param votes How many votes this proposition got. If votes > 0 proposition passed otherwise has not.
* @param overallMovement The overall change in group's position after voting
* @return The new social belief
*/
@Override
protected double updateSocialBeliefAfterVotes(Proposition proposition,
int votes, double overallMovement)
{
double currentSocial = getDataModel().getSocialBelief();
//Your social belief refines from how much more/less trust there is in the group
//after the vote. Whether or not your proposition passed reflects how much you
//want to trust the group to make decisions or a single dictator to make decisions.
String groupId = getDataModel().getGroupId();
if ((groupId != null) && (getConn().getGroupById(groupId).getMemberList().size() > 1))
{
//If this concerns you...
if (this.getDataModel().getGroupId().equals(proposition.getOwnerGroup()))
{
double groupSocial = getConn().getGroupById(getDataModel().getGroupId()).getEstimatedSocialLocation();
double deltaSocial = groupSocial - currentSocial;//how close are you to the group's belief
if (votes > 0)
{ //you're social belief moves towards the group's social posistion
currentSocial = scale(currentSocial, deltaSocial * 10, Math.abs(
overallMovement));
}
else if (votes < 0)
{
//you're social belief moves away from the group's social posistion
currentSocial = scale(currentSocial, -deltaSocial * 10, Math.abs(
overallMovement));
}
//otherwise your social belief remains the same
}
return currentSocial;
}
else
{
return currentSocial;//agent doesnt belong to a group and does not vote
}
}
/**
* This method updates the agent's economic belief after the voting results are published.
* @param proposition The proposition
* @param votes How many votes this proposition got. If votes > 0 proposition passed otherwise has not.
* @param overallMovement The overall change in group's position after voting
* @return The new social belief
*/
@Override
protected double updateEconomicBeliefAfterVotes(Proposition proposition,
int votes, double overallMovement)
{
double currentEconomic = getDataModel().getEconomicBelief();
//Your economic belief refines from how much more/less happy you are after the vote
//and from how loyal you are after the group made their decision after the vote.
String groupId = getDataModel().getGroupId();
if ((groupId != null) && (getConn().getGroupById(groupId).getMemberList().size() > 1))
{
//If this concerns you...
if (this.getDataModel().getGroupId().equals(proposition.getOwnerGroup()))
{
double groupEconomic = getConn().getGroupById(
getDataModel().getGroupId()).getCurrentEconomicPoisition();
double deltaEconomic = groupEconomic - currentEconomic;//how close are you to the group's belief
if (moreLoyal() && moreHappy())
{
currentEconomic = scale(currentEconomic, deltaEconomic * 10, Math.abs(
overallMovement));
}
else
{
if (deltaEconomic != 0)//if your beliefs are NOT the same as the group's beliefs
{
currentEconomic = scale(currentEconomic, -deltaEconomic * 10,
Math.abs(overallMovement));
}
else //if your beliefs are exactly the same with the group's beliefs
{
//move in any direction, for now
boolean random = uniformRandBoolean();
if (random)
currentEconomic = scale(currentEconomic, 1, Math.abs(
overallMovement));
else
currentEconomic = scale(currentEconomic, -1, Math.abs(
overallMovement));
}
}
}
return currentEconomic;
}
else
return currentEconomic;//agent doesnt belong to a group and so is not loyal to anyone
}
/**
* An agent which has been invited to a group must be tagged in order to process the invitation later
* @param group The group this agent has been invited to
* @return none
*/
@Override
protected void onInvite(String group)
{
invitationHolders.add(this.getId());
this.invitationToGroup = group;
}
/**
* An agent which belongs to a group can consult another agent to choose what type of food to hunt
* given its current opponent
* @param none
* @return The suggested food type
*/
private Food askAdvice(List<String> members)
{
Food suggestedFood = null;
String opponentID = null;
//If the agent has no pair then no advice
if (members.size() == 1) return null;
//Find opponent's ID
if (members.get(0).equals(this.getId()))
{
if (getConn().getAgentById(members.get(1)) != null)
{
opponentID = members.get(1);
}
}
else
{
if (getConn().getAgentById(members.get(0)) != null)
{
opponentID = members.get(0);
}
}
//Get the hunting teams history of the opponent. Get the last hunting team of the opponent
//and find out which agent was its opponent at that time. This agent has the latest information
//about our opponent. Therefore this agent is the advisor.
if (opponentID != null)
{
HuntingTeam opponentPreviousTeam = getConn().getAgentById(opponentID).getTeamHistory().getValue(
1);
if (opponentPreviousTeam != null)
{
for (String agent : opponentPreviousTeam.getMembers())
{
if (!agent.equals(opponentID) && !agent.equals(this.getId()))
{
previousAdvisor = agent;
return suggestedFood = seekAvice(agent);
}
}
}
}
return suggestedFood;
}
/**
* This method checks if the agent has become more loyal since last round
* @param none
* @return True for becoming more loyal and false otherwise
*/
private boolean moreLoyal()
{
String groupId = getDataModel().getGroupId();
if (groupId != null && getConn().getGroupById(groupId).getMemberList().size() > 1)
{
//get change in economic beliefs
double myEconomic = getDataModel().getEconomicBelief();
double myGroupEconomic = getConn().getGroupById(
getDataModel().getGroupId()).getCurrentEconomicPoisition();
double deltaEconomic = Math.abs(myGroupEconomic - myEconomic);//how close are you to the group's belief
Double oneTurnAgoHappiness = getDataModel().getHappinessHistory().getValue(
1);
if (oneTurnAgoHappiness == null)
{
oneTurnAgoHappiness = 0.5 * myEconomic;
}
Double curretnHappiness = getDataModel().getCurrentHappiness();
if (curretnHappiness == null)
{
curretnHappiness = 0.5 * myEconomic;
}
//get your loyalty and loyalty history
Double oneTurnAgoLoyalty = getDataModel().getLoyaltyHistory().getValue(1);
if (oneTurnAgoLoyalty == null)
{
oneTurnAgoLoyalty = 0.5 * (oneTurnAgoHappiness * deltaEconomic);
}
Double currentLoyalty = getDataModel().getCurrentLoyalty();
if (currentLoyalty == null)
{
currentLoyalty = 0.5 * (curretnHappiness * deltaEconomic);
}
double deltaLoyalty = currentLoyalty - oneTurnAgoLoyalty;//how much or less loyal did you get
if (deltaLoyalty > 0)
{
//you became more loyal to the group
return true;
}
else if (deltaLoyalty < 0)
{
//you became less loyal to the group
return false;
}
else
//you just got in the group and for that you must be loyal to them, at the least
return true;
}
else
//not loyal to anyone
return false;
}
/**
* This method checks if the agent has become happier since last round
* @param none
* @return True for becoming happier and false otherwise
*/
private boolean moreHappy()
{
//get change in economic beliefs
double myEconomic = getDataModel().getEconomicBelief();
//get your loyalty and loyalty history
Double oneTurnAgoHappiness = getDataModel().getHappinessHistory().getValue(1);
if (oneTurnAgoHappiness == null)
{
oneTurnAgoHappiness = 0.5 * myEconomic;
}
Double currentHappiness = getDataModel().getCurrentHappiness();
if (currentHappiness == null)
{
currentHappiness = 0.5 * myEconomic;
}
double deltaHappiness = currentHappiness - oneTurnAgoHappiness;//how much or less loyal did you get
if (deltaHappiness > 0)
{
//you became more loyal to the group
return true;
}
else if (deltaHappiness < 0)
{
//you became less loyal to the group
return false;
}
else
//you're not overjoyed but you're satisfied
return true;
}
/**
* This is a helper method and distinguishes what is the food type for cooperation and defection
* @param none
* @return A list containing the food for cooperation and defection
*/
private List<Food> getFoodTypes()
{
List<Food> foodArray = new LinkedList<Food>();
List<Food> foodList = new LinkedList<Food>();
Food cooperateFood, defectFood;
//Stores the two sources in an array
for (Food noms : getConn().availableFoods())
{
foodArray.add(noms);
}
//Hunting a stag is equivalent to cooperation. Hunting rabbit is equivalent to defection
if (foodArray.get(0).getNutrition() > foodArray.get(1).getNutrition())
{
cooperateFood = foodArray.get(0);
defectFood = foodArray.get(1);
}
else
{
cooperateFood = foodArray.get(1);
defectFood = foodArray.get(0);
}
foodList.add(cooperateFood);
foodList.add(defectFood);
return foodList;
}
private Comparator< Tuple<String, Double>> c = new Comparator< Tuple<String, Double>>()
{
@Override
public int compare(Tuple<String, Double> o1, Tuple<String, Double> o2)
{
Double v1 = o1.getValue();
Double v2 = o2.getValue();
return (v1 > v2 ? -1 : 1);
}
};
/**
* This method allows followers to rate their leaders' decisions. The rating is based only on the external
* strategy followed by the group.
* @param none
* @return A map structure containing new trust values for every member of the current panel of the group that this agent belongs to
*/
@Override
protected Map<String, Double> updateTrustAfterLeadersHunt()
{
String groupID = getDataModel().getGroupId();
//If this is a free agent then no leader to rate
if (groupID == null) return null;
//Get the current panel of te group that this agent belongs to
List<String> currentPanel = getConn().getGroupById(groupID).getPanel();
//If there is nobobdy to rate or this agent is member of the current panel do nothing
if (currentPanel.isEmpty() || (currentPanel.contains(getDataModel().getId())))
return null;
//Get the preferred strategy for an agent i.e. its type and the strategy adopted by the panel
//Positive or negative rating depends on the similarity of these two strategies
AgentType groupStrategy = getConn().getGroupById(groupID).getGroupStrategy();
AgentType followerStrategy = getDataModel().getAgentType();
//The rating weighting is a simple function of the group's population
int population = getConn().getGroupById(groupID).getMemberList().size();
double rating = 1 / population;
Map<String, Double> newTrustValues = new HashMap<String, Double>();
//If the agent supports the group's strategy it will give a positive rating to every member of the panel
//The reward to a panel member is to increase its current trust value from this agent. Accordingly, the
//punishment for a bad decision is to decrease the trust value! Note that there is a threshold associated with a leader (panel member)
for (String panelMember : currentPanel)
{
if (getDataModel().getTrust(panelMember) != null)
{
if (followerStrategy == groupStrategy)
{
double currentTrustForPanelMember = getDataModel().getTrust(
panelMember);
currentTrustForPanelMember = scale(currentTrustForPanelMember, 100,
rating);
newTrustValues.put(panelMember, currentTrustForPanelMember);
}
else
{
double currentTrustForPanelMember = getDataModel().getTrust(
panelMember);
currentTrustForPanelMember = scale(currentTrustForPanelMember, -100,
rating);
newTrustValues.put(panelMember, currentTrustForPanelMember);
}
}
}
return newTrustValues;
}
}