source: protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPState.java

Last change on this file was 52, checked in by ruud, 13 months ago

Fixed small issues in domaineditor.

File size: 11.9 KB
Line 
1package geniusweb.protocol.session.shaop;
2
3import java.util.Collections;
4import java.util.HashMap;
5import java.util.LinkedList;
6import java.util.List;
7import java.util.Map;
8import java.util.Set;
9import java.util.stream.Collectors;
10
11import com.fasterxml.jackson.annotation.JsonCreator;
12import com.fasterxml.jackson.annotation.JsonProperty;
13
14import geniusweb.actions.Accept;
15import geniusweb.actions.Action;
16import geniusweb.actions.Comparison;
17import geniusweb.actions.ElicitComparison;
18import geniusweb.actions.EndNegotiation;
19import geniusweb.actions.Offer;
20import geniusweb.actions.PartyId;
21import geniusweb.inform.Agreements;
22import geniusweb.issuevalue.Bid;
23import geniusweb.progress.Progress;
24import geniusweb.progress.ProgressRounds;
25import geniusweb.protocol.ProtocolException;
26import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
27import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
28import geniusweb.references.PartyWithProfile;
29
30/**
31 * Immutable.
32 */
33public class SHAOPState extends BareSHAOPState {
34
35 public static final double DEFAULT_ELICITATATION_COST = 0.01d;
36
37 /**
38 * see {@link BareSHAOPState}.
39 */
40 /**
41 *
42 * @param actions see
43 * {@link BareSHAOPState#BareSHAOPState(List, ProtocolToPartyConnections, Progress, SHAOPSettings, ProtocolException, int, Map, Map)}.
44 * @param conns see
45 * {@link BareSHAOPState#BareSHAOPState(List, ProtocolToPartyConnections, Progress, SHAOPSettings, ProtocolException, int, Map, Map)}.
46 * @param progress see
47 * {@link BareSHAOPState#BareSHAOPState(List, ProtocolToPartyConnections, Progress, SHAOPSettings, ProtocolException, int, Map, Map)}.
48 * @param settings see
49 * {@link BareSHAOPState#BareSHAOPState(List, ProtocolToPartyConnections, Progress, SHAOPSettings, ProtocolException, int, Map, Map)}.
50 * @param e see
51 * {@link BareSHAOPState#BareSHAOPState(List, ProtocolToPartyConnections, Progress, SHAOPSettings, ProtocolException, int, Map, Map)}.
52 * @param teamNr see
53 * {@link BareSHAOPState#BareSHAOPState(List, ProtocolToPartyConnections, Progress, SHAOPSettings, ProtocolException, int, Map, Map)}.
54 * @param partytNumbers see
55 * {@link BareSHAOPState#BareSHAOPState(List, ProtocolToPartyConnections, Progress, SHAOPSettings, ProtocolException, int, Map, Map)}.
56 * @param spent see
57 * {@link BareSHAOPState#BareSHAOPState(List, ProtocolToPartyConnections, Progress, SHAOPSettings, ProtocolException, int, Map, Map)}.
58 */
59 @JsonCreator
60 public SHAOPState(@JsonProperty("actions") List<Action> actions,
61 @JsonProperty("connections") ProtocolToPartyConnections conns,
62 @JsonProperty("progress") Progress progress,
63 @JsonProperty("settings") SHAOPSettings settings,
64 @JsonProperty("error") ProtocolException e,
65 @JsonProperty("teamNr") int teamNr,
66 @JsonProperty("partyNumbers") Map<PartyId, Integer> partytNumbers,
67 @JsonProperty("totalSpent") Map<PartyId, Double> spent) {
68 super(actions, conns, progress, settings, e, teamNr, partytNumbers,
69 spent);
70 }
71
72 /**
73 * Creates the initial state from the given settings
74 *
75 * @param settings the {@link SHAOPSettings}
76 */
77 public SHAOPState(SHAOPSettings settings) {
78 this(Collections.emptyList(), null, null, settings, null, 0, null,
79 null);
80 }
81
82 /**
83 *
84 * @param id the {@link PartyId}
85 * @return the {@link PartyWithProfile} for that party
86 */
87 public PartyWithProfile getSettings(PartyId id) {
88 return settings.getAllParties().get(partyNumbers.get(id));
89 }
90
91 /**
92 *
93 * @param party the {@link PartyId} to test
94 * @return true iff party is a SHAOP party (not a COB party).
95 */
96 public boolean isShaopParty(PartyId party) {
97 return (partyNumbers.get(party) & 1) == 0;
98 }
99
100 /**
101 *
102 * @return the team leader {@link PartyId}s which are the SHAOP parties.
103 */
104 private Set<PartyId> getLeaders() {
105 return partyNumbers.keySet().stream().filter(id -> isShaopParty(id))
106 .collect(Collectors.toSet());
107 }
108
109 /**
110 *
111 * @param party a Party Id
112 * @return the PartyId of the party (COB and SHAOP are partners)
113 */
114 public PartyId getPartner(PartyId party) {
115 return connections.get(partyNumbers.get(party) ^ 1).getParty();
116 }
117
118 @Override
119 public boolean isFinal(long currentTimeMs) {
120 List<Action> acts = getActions();
121 return super.isFinal(currentTimeMs)
122 || !getAgreements().getMap().isEmpty() || (!acts.isEmpty()
123 && acts.get(acts.size() - 1) instanceof EndNegotiation);
124 }
125
126 @Override
127 public SHAOPSettings getSettings() {
128 return settings;
129 }
130
131 /**
132 * search back through the actions for an offer. We can not smarly limit the
133 * search size because there can be an unlimited number of cob Comparison
134 * actions anywhere in the list.
135 *
136 * @return Bid from the most recent offer, or null if no such offer
137 */
138 private Bid getLastBid() {
139 for (int n = actions.size() - 1; n >= 0; n--) {
140 Action action = actions.get(n);
141 if (action instanceof Offer) {
142 return ((Offer) action).getBid();
143 }
144 }
145 return null;
146 }
147
148 /**
149 *
150 * @param connection the new {@link ProtocolToPartyConn} to a party
151 * @return new SessionState with the new connection added. This call ignores
152 * the progress (does not check isFinal) because SAOP uses this
153 * during the setup where the deadline is not yet relevant. The
154 * connection should be informed about the partyprofile but the
155 * state currently indicate if that already happened or not.
156 * IMPORTANT. The connections MUST be made in the same order as
157 * {@link SHAOPSettings#getTeams()}.
158 */
159 public SHAOPState with(ProtocolToPartyConn connection) {
160 // Only called from the SHAOP initialization phase.
161 ProtocolToPartyConnections newconns = connections.with(connection);
162 Map<PartyId, Integer> newNumbers = new HashMap<>(partyNumbers);
163 newNumbers.put(connection.getParty(), connections.size());
164 return new SHAOPState(actions, newconns, progress, settings, null,
165 teamNr, newNumbers, totalSpent);
166 }
167
168 /**
169 *
170 * @param e the error that occured
171 * @return a new state with the error set.
172 */
173 public SHAOPState with(ProtocolException e) {
174 return new SHAOPState(actions, connections, progress, settings, e,
175 teamNr, partyNumbers, totalSpent);
176 }
177
178 /**
179 * Sets the progress for this session. call is ignored if progress already
180 * set.
181 *
182 * @param newprogress the new progress
183 * @return new SAOPState with the progress set
184 */
185 public SHAOPState with(Progress newprogress) {
186 if (newprogress == null) {
187 throw new IllegalArgumentException("newprogress must be not null");
188 }
189 if (progress != null) {
190 return this;
191 }
192 return new SHAOPState(actions, connections, newprogress, settings,
193 error, teamNr, partyNumbers, totalSpent);
194 }
195
196 @Override
197 public Agreements getAgreements() {
198 Agreements agrees = new Agreements();
199 List<Action> acts = getActions();
200 if (acts.isEmpty())
201 return agrees;
202
203 int requiredaccepts = getSettings().getTeams().size() - 1;
204
205 // check that there are requiredaccepts and find the offer before that
206 for (int n = acts.size() - 1; n >= 0; n--) {
207 Action act = acts.get(n);
208 if (act instanceof Comparison || act instanceof ElicitComparison)
209 continue;
210 if (requiredaccepts == 0 && act instanceof Offer)
211 return agrees.with(
212 new Agreements(((Offer) act).getBid(), getLeaders()));
213 if (!(act instanceof Accept))
214 return agrees;
215 requiredaccepts--;
216 }
217 return agrees;
218 }
219
220 /**
221 * @param actor the actor that did this action. Can be used to check if
222 * action is valid. NOTICE caller has to make sure the current
223 * state is not final.
224 * @param action the action that was proposed by actor.
225 * @return new SessionState with the action added as last action.
226 */
227
228 public SHAOPState with(PartyId actor, Action action)
229 throws IllegalArgumentException {
230
231 if (actor == null) { // this is a bug
232 throw new IllegalArgumentException("actor must not be null");
233 }
234 if (action == null) {
235 throw new IllegalArgumentException("action is null");
236 }
237 if (!actor.equals(action.getActor())) {
238 throw new IllegalArgumentException(
239 "act contains wrong credentials: " + action);
240 }
241
242 if (action instanceof ElicitComparison) {
243 return withShaopInternalAction(actor, (ElicitComparison) action);
244 }
245 if (action instanceof Comparison) {
246 return withCobInternalAction(actor, (Comparison) action);
247 }
248
249 return withShaopRealAction(actor, action);
250
251 }
252
253 private SHAOPState withCobInternalAction(PartyId actor, Comparison action)
254 throws IllegalArgumentException {
255 if (isShaopParty(actor))
256 throw new IllegalArgumentException(
257 "Illegal action, actor is not a cob party:" + action);
258 return withAction(action);
259 }
260
261 /**
262 *
263 * @param actor actor, supposedly SHAOP, did Elicit action
264 * @param action an elicit action
265 * @return new state
266 * @throws ProtocolException
267 */
268 private SHAOPState withShaopInternalAction(PartyId actor,
269 ElicitComparison action) throws IllegalArgumentException {
270 if (!isShaopParty(actor))
271 throw new IllegalArgumentException(
272 "Illegal action, actor is not a shaop party:" + action);
273 return withAction(action);
274 }
275
276 /**
277 * update action, actorNr, and progress
278 *
279 * @param action the action that was done. Elicit actions do not update
280 * progress
281 * @return
282 */
283 private SHAOPState withAction(Action action) {
284 Progress newprogress = progress;
285 Map<PartyId, Double> newSpent = new HashMap<>(totalSpent);
286 PartyId partyid = action.getActor();
287
288 List<Action> newactions = new LinkedList<>(getActions());
289 newactions.add(action);
290 int newTeam = teamNr;
291 if (action instanceof ElicitComparison) {
292 Object cost = getPartyProfile(partyid).getParty().getParameters()
293 .get("elicitationcost");
294 if (!(cost instanceof Double)) {
295 cost = DEFAULT_ELICITATATION_COST;
296 }
297 Double oldSpent = totalSpent.get(partyid);
298 if (oldSpent == null) {
299 oldSpent = 0d;
300 }
301 newSpent.put(partyid, oldSpent + (Double) cost);
302
303 } else if (action instanceof Comparison) {
304 // nothing, turn remains with current SHAOP party
305 } else {
306 // shaop party does real action, move to next SHAOP party.
307 newTeam = (teamNr + 1) % settings.getTeams().size();
308 // check if we completed a round
309 if (newprogress instanceof ProgressRounds && newTeam == 0) {
310 newprogress = ((ProgressRounds) newprogress).advance();
311 System.out.println("Progressed rounds " + newprogress
312 + " after action " + action + "");
313 }
314 }
315 return new SHAOPState(newactions, connections, newprogress, settings,
316 null, newTeam, partyNumbers, newSpent);
317
318 }
319
320 /**
321 * party that did a real action
322 *
323 * @param actor
324 * @param action
325 * @return new state.
326 * @throws ProtocolException
327 */
328 private SHAOPState withShaopRealAction(PartyId actor, Action action)
329 throws IllegalArgumentException {
330 if (!isShaopParty(actor))
331 throw new IllegalArgumentException(
332 "Only SHAOP party can execute " + action);
333 // real action only allowed if party has the turn.
334 if (!actor.equals(connections.get(2 * teamNr).getParty())) {
335 throw new IllegalArgumentException("Party does not have the turn ");
336 }
337 // check protocol is followed for specific actions
338 if (action instanceof Accept) {
339 Bid bid = getLastBid();
340 if (bid == null) {
341 throw new IllegalArgumentException(
342 "Accept without a recent offer");
343 }
344 if (!bid.equals(((Accept) action).getBid())) {
345 throw new IllegalArgumentException(
346 "Party accepts a bid differing from the last offer ="
347 + bid + ", action=" + action + ")");
348 }
349 } else if (action instanceof Offer) {
350 // offer is always fine. We don't check if bid is actually in domain
351 // or complete etc. We would need domain for that
352 } else if (action instanceof EndNegotiation) {
353 // just act, we will get into the final state then.
354 } else {
355 throw new IllegalArgumentException(
356 "Action " + action + " is not allowed in SHAOP");
357 }
358 return withAction(action);
359 }
360
361}
Note: See TracBrowser for help on using the repository browser.