1 | package genius.core.protocol;
|
---|
2 |
|
---|
3 | import java.util.ArrayList;
|
---|
4 | import java.util.HashMap;
|
---|
5 | import java.util.List;
|
---|
6 | import java.util.Map;
|
---|
7 | import java.util.concurrent.ExecutionException;
|
---|
8 |
|
---|
9 | import genius.core.AgentID;
|
---|
10 | import genius.core.Bid;
|
---|
11 | import genius.core.actions.Accept;
|
---|
12 | import genius.core.actions.Action;
|
---|
13 | import genius.core.actions.EndNegotiation;
|
---|
14 | import genius.core.actions.Inform;
|
---|
15 | import genius.core.actions.Offer;
|
---|
16 | import genius.core.exceptions.NegotiationPartyTimeoutException;
|
---|
17 | import genius.core.parties.NegotiationParty;
|
---|
18 | import genius.core.session.Round;
|
---|
19 | import genius.core.session.Session;
|
---|
20 | import genius.core.session.Turn;
|
---|
21 |
|
---|
22 | /**
|
---|
23 | * Implementation of an alternating offer protocol using offer/counter-offer.
|
---|
24 | * <p/>
|
---|
25 | * Protocol:
|
---|
26 | *
|
---|
27 | * <pre>
|
---|
28 | * The first agent makes an offer
|
---|
29 | * Other agents can accept or make a counter-offer
|
---|
30 | *
|
---|
31 | * If no agent makes a counter-offer, the negotiation end with this offer.
|
---|
32 | * Otherwise, the process continues until reaching deadline or agreement.
|
---|
33 | * </pre>
|
---|
34 | *
|
---|
35 | * @author David Festen
|
---|
36 | * @author Reyhan Aydogan
|
---|
37 | * @author Catholijn Jonker
|
---|
38 | */
|
---|
39 | public class StackedAlternatingOffersProtocol extends DefaultMultilateralProtocol {
|
---|
40 |
|
---|
41 | static final AgentID protocolID = new AgentID("StackedAlternatingOffersProtocol");
|
---|
42 |
|
---|
43 | /**
|
---|
44 | * Get all possible actions for given party in a given session.
|
---|
45 | *
|
---|
46 | * @param parties
|
---|
47 | * @param session
|
---|
48 | * the current state of this session
|
---|
49 | * @return A list of possible actions
|
---|
50 | */
|
---|
51 |
|
---|
52 | /**
|
---|
53 | * Defines the round structure.
|
---|
54 | *
|
---|
55 | * <pre>
|
---|
56 | * The first agent makes an offer
|
---|
57 | * Other agents can accept or make a counter-offer
|
---|
58 | * </pre>
|
---|
59 | *
|
---|
60 | * @param parties
|
---|
61 | * The parties currently participating
|
---|
62 | * @param session
|
---|
63 | * The complete session history
|
---|
64 | * @return A list of possible actions
|
---|
65 | */
|
---|
66 | @Override
|
---|
67 | public Round getRoundStructure(List<NegotiationParty> parties, Session session) {
|
---|
68 | Round round = new Round();
|
---|
69 | // we will create the very first action if this is the first round
|
---|
70 | boolean isVeryFirstAction = session.getRoundNumber() == 0;
|
---|
71 |
|
---|
72 | for (NegotiationParty party : parties) {
|
---|
73 | if (isVeryFirstAction) {
|
---|
74 | // If this is the first party in the first round, it can not
|
---|
75 | // accept.
|
---|
76 | round.addTurn(new Turn(party, Offer.class, EndNegotiation.class));
|
---|
77 | } else {
|
---|
78 | // Each party can either accept the outstanding offer, or
|
---|
79 | // propose a counteroffer.
|
---|
80 | round.addTurn(new Turn(party, Accept.class, Offer.class, EndNegotiation.class));
|
---|
81 | }
|
---|
82 | isVeryFirstAction = false;
|
---|
83 | }
|
---|
84 |
|
---|
85 | // return round structure
|
---|
86 | return round;
|
---|
87 | }
|
---|
88 |
|
---|
89 | /**
|
---|
90 | * Will return the current agreement.
|
---|
91 | *
|
---|
92 | * @param session
|
---|
93 | * The complete session history up to this point
|
---|
94 | * @return The agreed upon bid or null if no agreement
|
---|
95 | */
|
---|
96 | @Override
|
---|
97 | public Bid getCurrentAgreement(Session session, List<NegotiationParty> parties) {
|
---|
98 |
|
---|
99 | // if not all parties agree, we did not find an agreement
|
---|
100 | if (getNumberOfAgreeingParties(session, parties) < parties.size())
|
---|
101 | return null;
|
---|
102 |
|
---|
103 | // all parties agreed, return most recent offer
|
---|
104 | return getMostRecentBid(session);
|
---|
105 | }
|
---|
106 |
|
---|
107 | @Override
|
---|
108 | public int getNumberOfAgreeingParties(Session session, List<NegotiationParty> parties) {
|
---|
109 | int nAccepts = 0;
|
---|
110 | ArrayList<Action> actions = getMostRecentTwoRounds(session);
|
---|
111 | for (int i = actions.size() - 1; i >= 0; i--) {
|
---|
112 | if (actions.get(i) instanceof Accept) {
|
---|
113 | nAccepts++;
|
---|
114 | } else {
|
---|
115 | if (actions.get(i) instanceof Offer) {
|
---|
116 | // voting party also counts towards agreeing parties
|
---|
117 | nAccepts++;
|
---|
118 | }
|
---|
119 | // we found at least one not accepting party (offering/ending
|
---|
120 | // negotiation) so stop counting
|
---|
121 | break;
|
---|
122 | }
|
---|
123 | }
|
---|
124 | return nAccepts;
|
---|
125 | }
|
---|
126 |
|
---|
127 | /**
|
---|
128 | * Get a list of all actions of the most recent two rounds
|
---|
129 | *
|
---|
130 | * @param session
|
---|
131 | * Session to extract the most recent two rounds out of
|
---|
132 | * @return A list of actions done in the most recent two rounds.
|
---|
133 | */
|
---|
134 | private ArrayList<Action> getMostRecentTwoRounds(Session session) {
|
---|
135 |
|
---|
136 | // holds actions
|
---|
137 | ArrayList<Action> actions = new ArrayList<Action>();
|
---|
138 |
|
---|
139 | // add previous round if exists
|
---|
140 | if (session.getRoundNumber() >= 2) {
|
---|
141 | Round round = session.getRounds().get(session.getRoundNumber() - 2);
|
---|
142 | for (Action action : round.getActions())
|
---|
143 | actions.add(action);
|
---|
144 | }
|
---|
145 |
|
---|
146 | // add current round if exists (does not exists before any offer is
|
---|
147 | // made)
|
---|
148 | if (session.getRoundNumber() >= 1) {
|
---|
149 | Round round = session.getRounds().get(session.getRoundNumber() - 1);
|
---|
150 | for (Action action : round.getActions())
|
---|
151 | actions.add(action);
|
---|
152 | }
|
---|
153 |
|
---|
154 | // return aggregated actions
|
---|
155 | return actions;
|
---|
156 | }
|
---|
157 |
|
---|
158 | private Bid getMostRecentBid(Session session) {
|
---|
159 |
|
---|
160 | // reverse rounds/actions until offer is found or return null
|
---|
161 | for (int roundIndex = session.getRoundNumber() - 1; roundIndex >= 0; roundIndex--) {
|
---|
162 | for (int actionIndex = session.getRounds().get(roundIndex).getActions().size()
|
---|
163 | - 1; actionIndex >= 0; actionIndex--) {
|
---|
164 | Action action = session.getRounds().get(roundIndex).getActions().get(actionIndex);
|
---|
165 | if (action instanceof Offer)
|
---|
166 | return ((Offer) action).getBid();
|
---|
167 | }
|
---|
168 | }
|
---|
169 |
|
---|
170 | // No offer found, so return null (no most recent bid exists)
|
---|
171 | // since this is only possible when first party quits negotiation, it is
|
---|
172 | // probably a bug when this happens
|
---|
173 | System.err.println("Possible bug: No Offer was placed during negotiation");
|
---|
174 | return null;
|
---|
175 | }
|
---|
176 |
|
---|
177 | /**
|
---|
178 | * If all agents accept the most recent offer, then this negotiation ends.
|
---|
179 | * Also, when any agent ends the negotiation (EndNegotiationAction) the
|
---|
180 | * negotiation ends
|
---|
181 | *
|
---|
182 | * @param session
|
---|
183 | * the current state of this session
|
---|
184 | * @return true if the protocol is finished
|
---|
185 | */
|
---|
186 | @Override
|
---|
187 | public boolean isFinished(Session session, List<NegotiationParty> parties) {
|
---|
188 | return getCurrentAgreement(session, parties) != null || session.getMostRecentAction() instanceof EndNegotiation;
|
---|
189 | }
|
---|
190 |
|
---|
191 | /**
|
---|
192 | * Get a map of parties that are listening to each other.
|
---|
193 | *
|
---|
194 | * @return who is listening to who
|
---|
195 | */
|
---|
196 | @Override
|
---|
197 | public Map<NegotiationParty, List<NegotiationParty>> getActionListeners(List<NegotiationParty> parties) {
|
---|
198 |
|
---|
199 | return listenToAll(parties);
|
---|
200 | }
|
---|
201 |
|
---|
202 | @Override
|
---|
203 | public Map<NegotiationParty, List<Action>> beforeSession(Session session, final List<NegotiationParty> parties)
|
---|
204 | throws InterruptedException, ExecutionException, NegotiationPartyTimeoutException {
|
---|
205 | HashMap<NegotiationParty, List<Action>> map = new HashMap<NegotiationParty, List<Action>>();
|
---|
206 |
|
---|
207 | for (NegotiationParty party : parties) {
|
---|
208 | ArrayList<Action> actions = new ArrayList<Action>();
|
---|
209 | actions.add(new Inform(protocolID, "NumberOfAgents", parties.size()));
|
---|
210 | map.put(party, actions);
|
---|
211 | }
|
---|
212 |
|
---|
213 | return map;
|
---|
214 | }
|
---|
215 | }
|
---|