1 | package agents.ai2014.group9;
|
---|
2 |
|
---|
3 | import java.util.ArrayList;
|
---|
4 | import java.util.HashMap;
|
---|
5 | import java.util.List;
|
---|
6 |
|
---|
7 | import genius.core.AgentID;
|
---|
8 | import genius.core.Bid;
|
---|
9 | import genius.core.actions.Accept;
|
---|
10 | import genius.core.actions.Action;
|
---|
11 | import genius.core.actions.Offer;
|
---|
12 | import genius.core.bidding.BidDetails;
|
---|
13 | import genius.core.boaframework.SortedOutcomeSpace;
|
---|
14 | import genius.core.issue.Issue;
|
---|
15 | import genius.core.issue.IssueDiscrete;
|
---|
16 | import genius.core.issue.IssueInteger;
|
---|
17 | import genius.core.issue.IssueReal;
|
---|
18 | import genius.core.issue.ValueDiscrete;
|
---|
19 | import genius.core.issue.ValueInteger;
|
---|
20 | import genius.core.issue.ValueReal;
|
---|
21 | import genius.core.misc.Range;
|
---|
22 | import genius.core.parties.AbstractNegotiationParty;
|
---|
23 | import genius.core.parties.NegotiationInfo;
|
---|
24 |
|
---|
25 | public class Group9 extends AbstractNegotiationParty {
|
---|
26 | private SortedOutcomeSpace outcomeSpace;
|
---|
27 | private Bid lastBid = null;
|
---|
28 | private double lastBidUtility = 0;
|
---|
29 | private Bid myLastBid = null;
|
---|
30 | private double myLastUtility = 1;
|
---|
31 |
|
---|
32 | private HashMap<Object, ArrayList<Bid>> partyBids;
|
---|
33 | private HashMap<Object, float[]> partyIssueWeights;
|
---|
34 | private HashMap<Object, HashMap<Integer, int[]>> partyIssueCounter;
|
---|
35 |
|
---|
36 | @Override
|
---|
37 | public void init(NegotiationInfo info) {
|
---|
38 | super.init(info);
|
---|
39 | partyBids = new HashMap<Object, ArrayList<Bid>>();
|
---|
40 | partyIssueWeights = new HashMap<Object, float[]>();
|
---|
41 | partyIssueCounter = new HashMap<Object, HashMap<Integer, int[]>>();
|
---|
42 | outcomeSpace = new SortedOutcomeSpace(utilitySpace);
|
---|
43 | }
|
---|
44 |
|
---|
45 | /**
|
---|
46 | * Each round this method gets called and ask you to accept or offer. The
|
---|
47 | * first party in the first round is a bit different, it can only propose an
|
---|
48 | * offer.
|
---|
49 | *
|
---|
50 | * @param validActions
|
---|
51 | * Either a list containing both accept and offer or only offer.
|
---|
52 | * @return The chosen action.
|
---|
53 | */
|
---|
54 | @Override
|
---|
55 | // Bidding and acceptance strategy
|
---|
56 | public Action chooseAction(List<Class<? extends Action>> validActions) {
|
---|
57 | // If a negotiating party makes an offer with >0.9 utility, we accept
|
---|
58 | // it.
|
---|
59 | if (lastBidUtility > 0.9) {
|
---|
60 | return new Accept(getPartyId(), lastBid);
|
---|
61 | }
|
---|
62 |
|
---|
63 | Bid newBid = null;
|
---|
64 | double newBidUtility = 0;
|
---|
65 | // if I have never offered a bid before, bid my max utility bid
|
---|
66 | try {
|
---|
67 | if (!validActions.contains(Accept.class) || myLastBid == null) {
|
---|
68 | newBid = utilitySpace.getMaxUtilityBid();
|
---|
69 | newBidUtility = utilitySpace.getUtility(newBid);
|
---|
70 | myLastBid = newBid;
|
---|
71 | myLastUtility = newBidUtility;
|
---|
72 | return new Offer(getPartyId(), newBid);
|
---|
73 | }
|
---|
74 | } catch (Exception e) {
|
---|
75 | }
|
---|
76 |
|
---|
77 | // I am a conceder, so calculate a utility threshold
|
---|
78 | double concederThreshold = 1;
|
---|
79 | if (timeline.getTime() > 0.75) {
|
---|
80 | double progress = (timeline.getTime() - 0.75) / (1.0 - 0.75);
|
---|
81 | concederThreshold = Math.sqrt(Math.sqrt(1.0 - progress)); // fourth
|
---|
82 | // root
|
---|
83 | }
|
---|
84 |
|
---|
85 | // concede until I think I've conceded more than necessary for an equal
|
---|
86 | // gain outcome.
|
---|
87 | myLastUtility = getBidUtility(myLastBid);
|
---|
88 | boolean KS = true; // Kalai-Smorodinsky point
|
---|
89 | for (Object key : partyBids.keySet()) {
|
---|
90 | double approximate = approximateUtility(key, myLastBid);
|
---|
91 | double diff = Math.abs(myLastUtility - approximate);
|
---|
92 | if (diff > 0.2) {
|
---|
93 | KS = false;
|
---|
94 | }
|
---|
95 | }
|
---|
96 | // if the bid I made earlier qualifies as an estimated Kalai-Smorodinsky
|
---|
97 | // point, I'll offer that bid again.
|
---|
98 | if (KS && myLastUtility <= concederThreshold) {
|
---|
99 | return new Offer(getPartyId(), myLastBid);
|
---|
100 | }
|
---|
101 |
|
---|
102 | // explore the bids with a personal utility slightly lower than my
|
---|
103 | // previous bid
|
---|
104 | List<BidDetails> myPossibleBids = outcomeSpace.getBidsinRange(new Range(myLastUtility, myLastUtility));
|
---|
105 | double i = 0.02;
|
---|
106 | if (myLastUtility <= concederThreshold) { // if the threshold is not yet
|
---|
107 | // relevant
|
---|
108 | while ((myPossibleBids.size() < 3) && (i < 1)) {
|
---|
109 | double lw = myLastUtility - i;
|
---|
110 | double up = myLastUtility;
|
---|
111 | myPossibleBids = outcomeSpace.getBidsinRange(new Range(lw, up));
|
---|
112 | i += 0.02;
|
---|
113 | }
|
---|
114 | } else {
|
---|
115 | while ((myPossibleBids.size() < 3) && (i < 1)) {
|
---|
116 | double lw = concederThreshold - i;
|
---|
117 | double up = concederThreshold;
|
---|
118 | myPossibleBids = outcomeSpace.getBidsinRange(new Range(lw, up));
|
---|
119 | i += 0.02;
|
---|
120 | }
|
---|
121 | }
|
---|
122 |
|
---|
123 | // Check for each selected bid which one has the highest mutual utility
|
---|
124 | int bestBidIndex = 0;
|
---|
125 | double maxCommonUtility = 0;
|
---|
126 | for (int j = 1; j < myPossibleBids.size(); j++) {
|
---|
127 | Bid currentBid = myPossibleBids.get(j).getBid();
|
---|
128 | double commonUtility = 1;
|
---|
129 | try {
|
---|
130 | commonUtility = utilitySpace.getUtility(currentBid);
|
---|
131 | if (commonUtility == myLastUtility)
|
---|
132 | continue;
|
---|
133 | } catch (Exception e) {
|
---|
134 | }
|
---|
135 |
|
---|
136 | for (Object key : partyBids.keySet()) {
|
---|
137 | commonUtility *= approximateUtility(key, currentBid);
|
---|
138 | }
|
---|
139 |
|
---|
140 | if (commonUtility > maxCommonUtility) {
|
---|
141 | maxCommonUtility = commonUtility;
|
---|
142 | bestBidIndex = j;
|
---|
143 | }
|
---|
144 | }
|
---|
145 | newBid = myPossibleBids.get(bestBidIndex).getBid();
|
---|
146 | newBidUtility = getBidUtility(newBid);
|
---|
147 | // Bid the selected bid, unless its utility is lower than what we were
|
---|
148 | // offered.
|
---|
149 | if (newBidUtility < lastBidUtility) {
|
---|
150 | return new Accept(getPartyId(), lastBid);
|
---|
151 | } else {
|
---|
152 | myLastBid = newBid;
|
---|
153 | lastBid = myLastBid;
|
---|
154 | try {
|
---|
155 | lastBidUtility = utilitySpace.getUtility(lastBid);
|
---|
156 | } catch (Exception e) {
|
---|
157 | }
|
---|
158 | return new Offer(getPartyId(), newBid);
|
---|
159 | }
|
---|
160 | }
|
---|
161 |
|
---|
162 | /**
|
---|
163 | * All offers proposed by the other parties will be received as a message.
|
---|
164 | * You can use this information to your advantage, for example to predict
|
---|
165 | * their utility.
|
---|
166 | *
|
---|
167 | * @param sender
|
---|
168 | * The party that did the action.
|
---|
169 | * @param action
|
---|
170 | * The action that party did.
|
---|
171 | */
|
---|
172 | @Override
|
---|
173 | public void receiveMessage(AgentID sender, Action action) {
|
---|
174 | // Here you can listen to other parties' messages
|
---|
175 | if (!partyBids.containsKey(sender) && sender != null) {
|
---|
176 | partyBids.put(sender, new ArrayList<Bid>());
|
---|
177 | }
|
---|
178 |
|
---|
179 | // if another player sends an offer
|
---|
180 | if ((action instanceof Offer)) {
|
---|
181 | lastBid = ((Offer) action).getBid();
|
---|
182 | partyBids.get(sender).add(lastBid);
|
---|
183 | try {
|
---|
184 | lastBidUtility = utilitySpace.getUtility(lastBid);
|
---|
185 | } catch (Exception e) {
|
---|
186 | }
|
---|
187 | } else if (action instanceof Accept) {
|
---|
188 | // if another player accepts a bid, they like this bid.
|
---|
189 | if (lastBid != null)
|
---|
190 | partyBids.get(sender).add(lastBid);
|
---|
191 | }
|
---|
192 |
|
---|
193 | CalculateOpponentUtilityParameters(sender);
|
---|
194 | }
|
---|
195 |
|
---|
196 | // simple try catch prevention
|
---|
197 | private double getBidUtility(Bid b) {
|
---|
198 | if (b == null)
|
---|
199 | return 0;
|
---|
200 |
|
---|
201 | try {
|
---|
202 | return utilitySpace.getUtility(b);
|
---|
203 | } catch (Exception e) {
|
---|
204 | }
|
---|
205 | return 0;
|
---|
206 | }
|
---|
207 |
|
---|
208 | // calculate the parameters that we use to predict the opponents utilities
|
---|
209 | private void CalculateOpponentUtilityParameters(Object sender) {
|
---|
210 | float n = 0.1f;
|
---|
211 |
|
---|
212 | if (!partyBids.containsKey(sender))
|
---|
213 | return;
|
---|
214 |
|
---|
215 | ArrayList<Bid> bidList = partyBids.get(sender);
|
---|
216 | if (bidList.size() == 1) { // the first time
|
---|
217 | Bid first = bidList.get(0);
|
---|
218 |
|
---|
219 | // initialize the arrays
|
---|
220 | List<Issue> issues = first.getIssues();
|
---|
221 | int issueSize = issues.size();
|
---|
222 | partyIssueWeights.put(sender, new float[issueSize]);
|
---|
223 | partyIssueCounter.put(sender, new HashMap<Integer, int[]>());
|
---|
224 |
|
---|
225 | // go over each issue
|
---|
226 | for (int i = 0; i < issueSize; i++) {
|
---|
227 | Issue iss = first.getIssues().get(i);
|
---|
228 | int isn = iss.getNumber();
|
---|
229 | partyIssueWeights.get(sender)[i] = 1f / issueSize;
|
---|
230 |
|
---|
231 | int numValues = 1;
|
---|
232 | int issueInd = 0;
|
---|
233 | try {
|
---|
234 | // get the correct issue type
|
---|
235 | if (iss instanceof IssueDiscrete) {
|
---|
236 | numValues = ((IssueDiscrete) iss).getValues().size();
|
---|
237 | ValueDiscrete val = (ValueDiscrete) first.getValue(isn);
|
---|
238 | issueInd = ((IssueDiscrete) iss).getValueIndex(val);
|
---|
239 | } else if (iss instanceof IssueInteger) {
|
---|
240 | numValues = ((IssueInteger) iss).getUpperBound() - ((IssueInteger) iss).getLowerBound() + 1;
|
---|
241 | ValueInteger val = (ValueInteger) first.getValue(isn);
|
---|
242 | issueInd = val.getValue() - ((IssueInteger) iss).getLowerBound();
|
---|
243 | } else if (iss instanceof IssueReal) {
|
---|
244 | // put them in 5 bins
|
---|
245 | numValues = 5;
|
---|
246 | ValueReal val = (ValueReal) first.getValue(isn);
|
---|
247 | double normalized = (((IssueReal) iss).getUpperBound() - val.getValue())
|
---|
248 | / (((IssueReal) iss).getUpperBound() - ((IssueReal) iss).getLowerBound());
|
---|
249 | issueInd = (int) Math.floor(normalized * 5);
|
---|
250 | if (issueInd >= 5)
|
---|
251 | issueInd = 4;
|
---|
252 | }
|
---|
253 | } catch (Exception e) {
|
---|
254 | }
|
---|
255 |
|
---|
256 | partyIssueCounter.get(sender).put(isn, new int[numValues]);
|
---|
257 | partyIssueCounter.get(sender).get(isn)[issueInd] = 10;
|
---|
258 | // set counter to 10, we assume they start with their most
|
---|
259 | // important
|
---|
260 | }
|
---|
261 | } else if (bidList.size() >= 2) { // not the first time
|
---|
262 | int issueSize = bidList.get(0).getIssues().size();
|
---|
263 | int lastBidIndex = bidList.size() - 1;
|
---|
264 | float totalWeight = 0;
|
---|
265 |
|
---|
266 | // go over each issue
|
---|
267 | for (int i = 0; i < issueSize; i++) {
|
---|
268 | Issue iss = bidList.get(lastBidIndex).getIssues().get(i);
|
---|
269 | int isn = iss.getNumber();
|
---|
270 |
|
---|
271 | // add a small weight if the value changed
|
---|
272 | if (bidList.get(lastBidIndex).getIssues().get(i) != bidList.get(lastBidIndex - 1).getIssues().get(i)) {
|
---|
273 | partyIssueWeights.get(sender)[i] += n;
|
---|
274 | totalWeight += partyIssueWeights.get(sender)[i];
|
---|
275 | }
|
---|
276 |
|
---|
277 | int issueInd = 0;
|
---|
278 | try {
|
---|
279 | // find which of the values this is
|
---|
280 | if (iss instanceof IssueDiscrete) {
|
---|
281 | issueInd = ((IssueDiscrete) iss)
|
---|
282 | .getValueIndex((ValueDiscrete) bidList.get(lastBidIndex).getValue(isn));
|
---|
283 | } else if (iss instanceof IssueInteger) {
|
---|
284 | issueInd = ((ValueInteger) bidList.get(lastBidIndex).getValue(isn)).getValue()
|
---|
285 | - ((IssueInteger) iss).getLowerBound();
|
---|
286 | } else if (iss instanceof IssueReal) {
|
---|
287 | double value = ((ValueReal) bidList.get(lastBidIndex).getValue(isn)).getValue();
|
---|
288 | double normalized = (((IssueReal) iss).getUpperBound() - value)
|
---|
289 | / (((IssueReal) iss).getUpperBound() - ((IssueReal) iss).getLowerBound());
|
---|
290 | issueInd = (int) Math.floor(normalized * 5);
|
---|
291 | if (issueInd >= 5)
|
---|
292 | issueInd = 4;
|
---|
293 | }
|
---|
294 | } catch (Exception e) {
|
---|
295 | }
|
---|
296 | // add 1 to the issue chosen
|
---|
297 | partyIssueCounter.get(sender).get(isn)[issueInd]++;
|
---|
298 | }
|
---|
299 | if (totalWeight == 0)
|
---|
300 | return;
|
---|
301 |
|
---|
302 | // normalize
|
---|
303 | for (int i = 0; i < issueSize; i++) {
|
---|
304 | partyIssueWeights.get(sender)[i] /= totalWeight;
|
---|
305 | }
|
---|
306 | }
|
---|
307 | }
|
---|
308 |
|
---|
309 | // given a bid, predict the utility for the opponent
|
---|
310 | private double approximateUtility(Object party, Bid bid) {
|
---|
311 | if (partyIssueCounter == null || party == null || bid == null)
|
---|
312 | return 0.5;
|
---|
313 |
|
---|
314 | double totalUtility = 0;
|
---|
315 | // go over each issue
|
---|
316 | for (int i = 0; i < bid.getIssues().size(); i++) {
|
---|
317 | Issue iss = bid.getIssues().get(i);
|
---|
318 | if (iss == null)
|
---|
319 | continue;
|
---|
320 |
|
---|
321 | int isn = iss.getNumber();
|
---|
322 |
|
---|
323 | int issueInd = 0;
|
---|
324 | try {
|
---|
325 | // get the correct issue type
|
---|
326 | if (iss instanceof IssueDiscrete) {
|
---|
327 | issueInd = ((IssueDiscrete) iss).getValueIndex((ValueDiscrete) bid.getValue(isn));
|
---|
328 | } else if (iss instanceof IssueInteger) {
|
---|
329 | issueInd = ((ValueInteger) bid.getValue(isn)).getValue() - ((IssueInteger) iss).getLowerBound();
|
---|
330 | } else if (iss instanceof IssueReal) {
|
---|
331 | double value = ((ValueReal) bid.getValue(isn)).getValue();
|
---|
332 | double divider = ((IssueReal) iss).getUpperBound() - ((IssueReal) iss).getLowerBound();
|
---|
333 | if (divider == 0)
|
---|
334 | continue;
|
---|
335 | double normalized = (((IssueReal) iss).getUpperBound() - value) / divider;
|
---|
336 | issueInd = (int) Math.floor(normalized * 5);
|
---|
337 | if (issueInd >= 5)
|
---|
338 | issueInd = 4;
|
---|
339 | }
|
---|
340 | } catch (Exception e) {
|
---|
341 | }
|
---|
342 |
|
---|
343 | if (!partyIssueCounter.containsKey(party))
|
---|
344 | continue;
|
---|
345 | if (!partyIssueCounter.get(party).containsKey(isn))
|
---|
346 | continue;
|
---|
347 | if (partyIssueCounter.get(party).get(isn) == null)
|
---|
348 | continue;
|
---|
349 |
|
---|
350 | // get the max value
|
---|
351 | int maxCount = 1;
|
---|
352 | int numOptions = 0;
|
---|
353 | for (int j = 0; j < partyIssueCounter.get(party).get(isn).length; j++) {
|
---|
354 | maxCount = Math.max(maxCount, partyIssueCounter.get(party).get(isn)[j]);
|
---|
355 | if (partyIssueCounter.get(party).get(isn)[j] != 0)
|
---|
356 | numOptions++;
|
---|
357 | }
|
---|
358 |
|
---|
359 | // get the weight and the value for this issue
|
---|
360 | double value = partyIssueCounter.get(party).get(isn)[issueInd] / (double) maxCount;
|
---|
361 |
|
---|
362 | if (numOptions == 1 && value == 0.0) // for hardball players who
|
---|
363 | // only select 1 option
|
---|
364 | value = 0.5; // we dont know how you feel about this subject, so
|
---|
365 | // we assume 0.5
|
---|
366 | else if (value == 0.0)
|
---|
367 | value = 0.1; // you probably dont hate it, even though you
|
---|
368 | // didn't choose it
|
---|
369 |
|
---|
370 | float weight = partyIssueWeights.get(party)[i];
|
---|
371 | totalUtility += value * weight;
|
---|
372 | }
|
---|
373 |
|
---|
374 | if (totalUtility == 0) // something went wrong
|
---|
375 | return 0.5;
|
---|
376 | else
|
---|
377 | return totalUtility;
|
---|
378 | }
|
---|
379 |
|
---|
380 | protected AgentID partyId = new AgentID("Group 9");
|
---|
381 |
|
---|
382 | @Override
|
---|
383 | public String getDescription() {
|
---|
384 | return "ai2014 group9";
|
---|
385 | }
|
---|
386 |
|
---|
387 | } |
---|