1 | package parties.in4010.q12015.group20;
|
---|
2 |
|
---|
3 | import java.util.ArrayList;
|
---|
4 | import java.util.Collection;
|
---|
5 | import java.util.HashMap;
|
---|
6 | import java.util.Iterator;
|
---|
7 | import java.util.List;
|
---|
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.ActionWithBid;
|
---|
14 | import genius.core.actions.DefaultAction;
|
---|
15 | import genius.core.actions.Offer;
|
---|
16 | import genius.core.issue.Issue;
|
---|
17 | import genius.core.issue.IssueDiscrete;
|
---|
18 | import genius.core.issue.Value;
|
---|
19 | import genius.core.issue.ValueDiscrete;
|
---|
20 | import genius.core.parties.AbstractNegotiationParty;
|
---|
21 | import genius.core.parties.NegotiationInfo;
|
---|
22 | import genius.core.utility.AdditiveUtilitySpace;
|
---|
23 | import genius.core.utility.EvaluatorDiscrete;
|
---|
24 |
|
---|
25 | /**
|
---|
26 | * This is your negotiation party.
|
---|
27 | */
|
---|
28 | public class Group20 extends AbstractNegotiationParty {
|
---|
29 |
|
---|
30 | // Max > Min
|
---|
31 | private final static double MINUTILSCORE = 0.7;
|
---|
32 | private final static double MAXUTILSCORE = 0.8;
|
---|
33 |
|
---|
34 | private Action lastOpponentAction = null;
|
---|
35 | private Double[] ourWeights; // store weights of issues
|
---|
36 | private Bid lastOpOffer = null;
|
---|
37 | private HashMap<Integer, Double[]> choices = new HashMap<Integer, Double[]>(); // store
|
---|
38 | // evaluation
|
---|
39 | // values
|
---|
40 | // for
|
---|
41 | // every
|
---|
42 | // issue
|
---|
43 | private HashMap<Integer, Integer[]> agentIssues = new HashMap<Integer, Integer[]>(); // store
|
---|
44 | // evaluation
|
---|
45 | // values
|
---|
46 | // for
|
---|
47 | // every
|
---|
48 | // issue:
|
---|
49 | // <issueID,
|
---|
50 | // values>
|
---|
51 | private Double[] agentWeights = null; // save weights of opponents'
|
---|
52 | // preference
|
---|
53 |
|
---|
54 | /**
|
---|
55 | * Initialization function
|
---|
56 | */
|
---|
57 | @Override
|
---|
58 | public void init(NegotiationInfo info) {
|
---|
59 | super.init(info);
|
---|
60 | agentWeights = new Double[utilitySpace.getDomain().getIssues().size()];
|
---|
61 |
|
---|
62 | /*******
|
---|
63 | * reservation value is 0, I believe, read somewhere in the assignment
|
---|
64 | ******/
|
---|
65 | int numIssues = utilitySpace.getDomain().getIssues().size(); // total
|
---|
66 | // number
|
---|
67 | // of
|
---|
68 | // issues
|
---|
69 | ourWeights = new Double[numIssues];
|
---|
70 |
|
---|
71 | /********** Store evaluation value for each choice ************/
|
---|
72 | List<Issue> issues = utilitySpace.getDomain().getIssues();
|
---|
73 |
|
---|
74 | /**************
|
---|
75 | * initialize on the 1st round, every value equals to 1
|
---|
76 | ***************/
|
---|
77 | for (Issue lIssue : issues) {
|
---|
78 | int issueID = lIssue.getNumber();
|
---|
79 | IssueDiscrete lIssueDiscrete = (IssueDiscrete) lIssue; // TODO add
|
---|
80 | // non
|
---|
81 | // discrete
|
---|
82 | Integer[] evals = new Integer[lIssueDiscrete.getNumberOfValues()];
|
---|
83 | for (int i = 0; i < lIssueDiscrete.getNumberOfValues(); i++) {
|
---|
84 | evals[i] = 1; // initialize every value to 1
|
---|
85 | }
|
---|
86 | agentIssues.put(issueID, evals);
|
---|
87 | }
|
---|
88 |
|
---|
89 | for (Issue lIssue : issues) {
|
---|
90 | int issueID = lIssue.getNumber();
|
---|
91 | agentWeights[issueID - 1] = 1.0 / issues.size(); // initialize the
|
---|
92 | // opponents'
|
---|
93 | // weight
|
---|
94 | ourWeights[issueID - 1] = ((AdditiveUtilitySpace) utilitySpace).getWeight(issueID); // save
|
---|
95 | // weights
|
---|
96 | // of
|
---|
97 | // issues
|
---|
98 | // into
|
---|
99 | // weights[]
|
---|
100 |
|
---|
101 | IssueDiscrete lIssueDiscrete = (IssueDiscrete) lIssue;
|
---|
102 | Double[] evals = new Double[lIssueDiscrete.getNumberOfValues()];
|
---|
103 | EvaluatorDiscrete evaluator = (EvaluatorDiscrete) ((AdditiveUtilitySpace) utilitySpace)
|
---|
104 | .getEvaluator(issueID);
|
---|
105 |
|
---|
106 | for (int i = 0; i < lIssueDiscrete.getNumberOfValues(); i++) {
|
---|
107 | ValueDiscrete value = lIssueDiscrete.getValue(i);
|
---|
108 | Double eval = evaluator.getDoubleValue(value); // evaluation of
|
---|
109 | // each choice,
|
---|
110 | // such as
|
---|
111 | // "cocktail","beer
|
---|
112 | // only"
|
---|
113 | evals[i] = eval; // evals[i] is like 5,25,10,....
|
---|
114 | }
|
---|
115 |
|
---|
116 | choices.put(issueID, evals);
|
---|
117 | }
|
---|
118 | }
|
---|
119 |
|
---|
120 | /**
|
---|
121 | * Each round this method gets called and ask you to accept or offer. The
|
---|
122 | * first party in the first round is a bit different, it can only propose an
|
---|
123 | * offer.
|
---|
124 | *
|
---|
125 | * @param validActions
|
---|
126 | * Either a list containing both accept and offer or only offer.
|
---|
127 | * @return The chosen action.
|
---|
128 | */
|
---|
129 | public Action chooseAction(List<Class<? extends Action>> validActions) {
|
---|
130 | Action action = null;
|
---|
131 |
|
---|
132 | try {
|
---|
133 | if (lastOpponentAction == null) {
|
---|
134 | action = createBidAction();
|
---|
135 | }
|
---|
136 |
|
---|
137 | if (lastOpponentAction instanceof Offer) {
|
---|
138 | Bid partnerBid = ((Offer) lastOpponentAction).getBid();
|
---|
139 |
|
---|
140 | if (isAcceptable(partnerBid)) {
|
---|
141 | return new Accept(getPartyId(), partnerBid);
|
---|
142 | }
|
---|
143 | action = createBidAction();
|
---|
144 | }
|
---|
145 | } catch (Exception e) {
|
---|
146 | System.out.println("Exception in ChooseAction:" + e.getMessage());
|
---|
147 | // !!!!best guess if things go
|
---|
148 | // wrong.
|
---|
149 | return new Accept(getPartyId(), ((ActionWithBid) lastOpponentAction).getBid());
|
---|
150 | }
|
---|
151 | return action;
|
---|
152 | }
|
---|
153 |
|
---|
154 | /**
|
---|
155 | * All offers proposed by the other parties will be received as a message.
|
---|
156 | * These offers are used to build a predicted preference profile
|
---|
157 | *
|
---|
158 | * @param sender
|
---|
159 | * The party that did the action.
|
---|
160 | * @param action
|
---|
161 | * The action that party did.
|
---|
162 | */
|
---|
163 | @Override
|
---|
164 | public void receiveMessage(AgentID sender, Action action) {
|
---|
165 | super.receiveMessage(sender, action);
|
---|
166 | if (action != null && action instanceof Offer) {
|
---|
167 | lastOpponentAction = (Offer) action;
|
---|
168 |
|
---|
169 | Bid opponentBid = DefaultAction.getBidFromAction(action);
|
---|
170 | predictPreferences(opponentBid);
|
---|
171 | lastOpOffer = opponentBid; // save the offer of opponent
|
---|
172 | }
|
---|
173 |
|
---|
174 | }
|
---|
175 |
|
---|
176 | /**
|
---|
177 | * Predict opponents preferences using Frequency Analysis Heuristic Instead
|
---|
178 | * of two preferences, generate a preference profile for all the opponents
|
---|
179 | *
|
---|
180 | * @param agentID
|
---|
181 | * @param agentBid
|
---|
182 | */
|
---|
183 | private void predictPreferences(Bid agentBid) {
|
---|
184 | /********** save issues of the opponent ***********/
|
---|
185 | List<Issue> opponentIssues = agentBid.getIssues();
|
---|
186 |
|
---|
187 | /***************** update each round ********************/
|
---|
188 | for (Issue lIssue : opponentIssues) {
|
---|
189 | int issueID = lIssue.getNumber(); // issueID starts from 1
|
---|
190 | IssueDiscrete lIssueDiscrete = (IssueDiscrete) lIssue; // TODO add
|
---|
191 | // non
|
---|
192 | // discrete
|
---|
193 | lIssueDiscrete.getNumberOfValues();
|
---|
194 | int valIndex = 0; // valIndex starts from 0
|
---|
195 |
|
---|
196 | try {
|
---|
197 | valIndex = lIssueDiscrete.getValueIndex((ValueDiscrete) agentBid.getValue(issueID)); // find
|
---|
198 | // index
|
---|
199 | // of
|
---|
200 | // value
|
---|
201 | // in
|
---|
202 | // issue
|
---|
203 |
|
---|
204 | /*********** refresh weights *********/
|
---|
205 | if (lastOpOffer != null && lastOpOffer.getValue(issueID).equals(agentBid.getValue(issueID))) {
|
---|
206 | Double[] newWeights = refreshWeight(agentWeights, issueID, 0.1).clone();
|
---|
207 | agentWeights = newWeights.clone();
|
---|
208 | }
|
---|
209 | } catch (Exception e) {
|
---|
210 | e.printStackTrace();
|
---|
211 | }
|
---|
212 |
|
---|
213 | if (agentIssues.containsKey(issueID)) {
|
---|
214 |
|
---|
215 | Integer[] values = agentIssues.get(issueID).clone();
|
---|
216 | values[valIndex] = values[valIndex] + 1; // every time the bid
|
---|
217 | // appeared, give it
|
---|
218 | // plus 1 value
|
---|
219 | if (agentIssues.containsKey(issueID)) {
|
---|
220 | agentIssues.put(issueID, values.clone());
|
---|
221 | }
|
---|
222 | }
|
---|
223 | }
|
---|
224 |
|
---|
225 | }
|
---|
226 |
|
---|
227 | /**
|
---|
228 | * Create a bid or accept
|
---|
229 | *
|
---|
230 | * @return Created action can be offer or accept action
|
---|
231 | */
|
---|
232 | private Action createBidAction() {
|
---|
233 | Bid nextBid = null;
|
---|
234 |
|
---|
235 | try {
|
---|
236 | nextBid = getOurNewBid();
|
---|
237 | } catch (Exception e) {
|
---|
238 | System.out.println("Problem with received bid:" + e + ". cancelling bidding");
|
---|
239 | }
|
---|
240 |
|
---|
241 | if (nextBid == null) {
|
---|
242 | System.out.println("Should not happen.");
|
---|
243 | return (new Accept(getPartyId(), ((ActionWithBid) lastOpponentAction).getBid()));
|
---|
244 | }
|
---|
245 | return (new Offer(getPartyId(), nextBid));
|
---|
246 | }
|
---|
247 |
|
---|
248 | /**
|
---|
249 | * Checks if we want to accept a bid. Accepting is based on the given bid
|
---|
250 | * having a higher utility value than that we currently have as a minimum
|
---|
251 | *
|
---|
252 | * @param partnerBid
|
---|
253 | * the bid we want to check
|
---|
254 | * @return true or false
|
---|
255 | * @throws Exception
|
---|
256 | */
|
---|
257 | private boolean isAcceptable(Bid partnerBid) throws Exception {
|
---|
258 | boolean isAccept = false;
|
---|
259 | double acceptUtil = getMinUtilValue();
|
---|
260 | if (acceptUtil < utilitySpace.getUtility(partnerBid)) {
|
---|
261 | isAccept = true;
|
---|
262 | }
|
---|
263 | return isAccept;
|
---|
264 | }
|
---|
265 |
|
---|
266 | /**
|
---|
267 | * Get the minimum utility value based on the current negotiation time
|
---|
268 | *
|
---|
269 | * @return minimum utility
|
---|
270 | */
|
---|
271 | private double getMinUtilValue() {
|
---|
272 | return MINUTILSCORE + (timeline.getTime() * (MAXUTILSCORE - MINUTILSCORE));
|
---|
273 | }
|
---|
274 |
|
---|
275 | /**
|
---|
276 | * Get a new bid
|
---|
277 | *
|
---|
278 | * @return a new bid being a combination of opponets possible max utility
|
---|
279 | * adjusted to our min utility score.
|
---|
280 | * @throws Exception
|
---|
281 | */
|
---|
282 | private Bid getOurNewBid() throws Exception {
|
---|
283 | Bid maxOppBid = createMaxOpponentBid();
|
---|
284 | compromiseBid(maxOppBid);
|
---|
285 | return maxOppBid;
|
---|
286 |
|
---|
287 | }
|
---|
288 |
|
---|
289 | /**
|
---|
290 | * Create the maximum possible bid for the opponents based on the predicted
|
---|
291 | * preference profile that was formed of them.
|
---|
292 | *
|
---|
293 | * @return Bid with maximum utility score for all opponents
|
---|
294 | * @throws Exception
|
---|
295 | */
|
---|
296 | private Bid createMaxOpponentBid() throws Exception {
|
---|
297 | Bid bid = null;
|
---|
298 | Collection<Integer[]> valueList = agentIssues.values();
|
---|
299 | int i = 1;
|
---|
300 | HashMap<Integer, Value> tmpValues = new HashMap<Integer, Value>();
|
---|
301 | for (Iterator iterator = valueList.iterator(); iterator.hasNext(); i++) {
|
---|
302 | int maxIndex = getIndexMaxValue((Integer[]) iterator.next());
|
---|
303 | ValueDiscrete maxPreValue = ((IssueDiscrete) ((AdditiveUtilitySpace) utilitySpace).getIssue(i - 1))
|
---|
304 | .getValue(maxIndex);
|
---|
305 | tmpValues.put(i, maxPreValue);
|
---|
306 | }
|
---|
307 |
|
---|
308 | bid = new Bid(utilitySpace.getDomain(), tmpValues);
|
---|
309 | return bid;
|
---|
310 | }
|
---|
311 |
|
---|
312 | /**
|
---|
313 | * Get the index of the element with the maximum
|
---|
314 | *
|
---|
315 | * @param primArray
|
---|
316 | * @return index of maxvalue from primarray
|
---|
317 | */
|
---|
318 | private int getIndexMaxValue(Integer[] primArray) {
|
---|
319 | int max = -1;
|
---|
320 | int index = -1;
|
---|
321 | for (int i = 0; i < primArray.length; i++) {
|
---|
322 | if (primArray[i] > max) {
|
---|
323 | max = primArray[i];
|
---|
324 | index = i;
|
---|
325 | }
|
---|
326 | }
|
---|
327 | return index;
|
---|
328 | }
|
---|
329 |
|
---|
330 | /**
|
---|
331 | * Given a Bid, increase the bid until we get to a value that is acceptable
|
---|
332 | * for us. The issue to change is chosen where our weight is high while the
|
---|
333 | * predicted opponents weight is low. This makes it so that for us there is
|
---|
334 | * a high change in utility while for the opponent the change is minimal.
|
---|
335 | *
|
---|
336 | * @param maxOppBid
|
---|
337 | * Bid to adjust
|
---|
338 | * @throws Exception
|
---|
339 | */
|
---|
340 | private void compromiseBid(Bid maxOppBid) throws Exception {
|
---|
341 | ArrayList<Double> diffList = getDiffList();
|
---|
342 | int issueId;
|
---|
343 | while (utilitySpace.getUtility(maxOppBid) < getMinUtilValue()) {
|
---|
344 | issueId = getMaxID(diffList);
|
---|
345 | ValueDiscrete newValue = getMaxIssueValue(issueId);
|
---|
346 | maxOppBid = maxOppBid.putValue(issueId, newValue);
|
---|
347 | }
|
---|
348 | }
|
---|
349 |
|
---|
350 | /**
|
---|
351 | * Get the maximal value for a certain discrete issue
|
---|
352 | *
|
---|
353 | * @param issueId
|
---|
354 | * @return maximum vallue for issueID
|
---|
355 | */
|
---|
356 | private ValueDiscrete getMaxIssueValue(int issueId) {
|
---|
357 | Double max = Double.MIN_VALUE;
|
---|
358 | int idx = -1;
|
---|
359 | Double[] values = choices.get(issueId);
|
---|
360 | for (int i = 0; i < values.length; i++) {
|
---|
361 | if (values[i] > max) {
|
---|
362 | max = values[i];
|
---|
363 | idx = i;
|
---|
364 | }
|
---|
365 | }
|
---|
366 | return ((IssueDiscrete) ((AdditiveUtilitySpace) utilitySpace).getIssue(issueId - 1)).getValue(idx);
|
---|
367 | }
|
---|
368 |
|
---|
369 | /**
|
---|
370 | * Returns the ID of maximum difference, and sets that index to minimal
|
---|
371 | * value
|
---|
372 | *
|
---|
373 | * @param diffList
|
---|
374 | * @return ID of the issue (starts at 1)
|
---|
375 | */
|
---|
376 | private int getMaxID(ArrayList<Double> diffList) {
|
---|
377 | int id = -1;
|
---|
378 | double maxDiff = Double.MIN_VALUE;
|
---|
379 |
|
---|
380 | for (int i = 0; i < diffList.size(); i++) {
|
---|
381 | if (diffList.get(i) > maxDiff) {
|
---|
382 | id = i + 1;
|
---|
383 | maxDiff = diffList.get(i);
|
---|
384 | }
|
---|
385 | }
|
---|
386 |
|
---|
387 | diffList.set(id - 1, -1.0);
|
---|
388 |
|
---|
389 | return id;
|
---|
390 | }
|
---|
391 |
|
---|
392 | /**
|
---|
393 | * Creates a differences list of the differences between our domain weights
|
---|
394 | * and the predicted weights of the opponents.
|
---|
395 | *
|
---|
396 | * @return list of differences in weight
|
---|
397 | */
|
---|
398 | private ArrayList<Double> getDiffList() {
|
---|
399 | ArrayList<Double> diffList = new ArrayList<Double>();
|
---|
400 | for (int j = 0; j < agentWeights.length; j++) {
|
---|
401 | diffList.add(ourWeights[j] - agentWeights[j] + 1);
|
---|
402 | }
|
---|
403 | return diffList;
|
---|
404 | }
|
---|
405 |
|
---|
406 | /**
|
---|
407 | * Refresh and update opponents weights
|
---|
408 | *
|
---|
409 | * @param opWeights
|
---|
410 | * @param issueID
|
---|
411 | * @param n
|
---|
412 | * @return
|
---|
413 | */
|
---|
414 | public Double[] refreshWeight(Double[] opWeights, int issueID, double n) {
|
---|
415 | opWeights[issueID - 1] = opWeights[issueID - 1] + n; // issueID starts
|
---|
416 | // from 1
|
---|
417 | Double[] newWeights = opWeights.clone();
|
---|
418 | double sum = 0;
|
---|
419 |
|
---|
420 | for (int i = 0; i < newWeights.length; i++) {
|
---|
421 | sum += newWeights[i];
|
---|
422 | }
|
---|
423 |
|
---|
424 | for (int i = 0; i < newWeights.length; i++) {
|
---|
425 | newWeights[i] = newWeights[i] / sum;
|
---|
426 | }
|
---|
427 |
|
---|
428 | return newWeights;
|
---|
429 | }
|
---|
430 |
|
---|
431 | /**
|
---|
432 | * Returns the predicted utility function for a opponent
|
---|
433 | *
|
---|
434 | * @param agentID
|
---|
435 | * @param bid
|
---|
436 | * @return
|
---|
437 | * @throws Exception
|
---|
438 | */
|
---|
439 | public double predictUtility(AgentID agentID, Bid bid) throws Exception {
|
---|
440 | double opUtility = 0;
|
---|
441 | List<Issue> issueList = bid.getIssues();
|
---|
442 |
|
---|
443 | for (Issue lIssue : issueList) {
|
---|
444 | int issueID = lIssue.getNumber(); // issueID starts from 1
|
---|
445 | IssueDiscrete lIssueDiscrete = (IssueDiscrete) lIssue;
|
---|
446 | Integer[] values = agentIssues.get(issueID).clone();
|
---|
447 | double sumValues = 0;
|
---|
448 |
|
---|
449 | for (int i = 0; i < values.length; i++) {
|
---|
450 | if (values[i] == null)
|
---|
451 | values[i] = 1; // or other values, to escape
|
---|
452 | // nullpointerexception;
|
---|
453 | sumValues += values[i];
|
---|
454 | }
|
---|
455 |
|
---|
456 | int valIndex = lIssueDiscrete.getValueIndex((ValueDiscrete) bid.getValue(issueID));
|
---|
457 | opUtility += agentWeights[issueID - 1] * values[valIndex] / sumValues;
|
---|
458 |
|
---|
459 | }
|
---|
460 |
|
---|
461 | return opUtility;
|
---|
462 |
|
---|
463 | }
|
---|
464 |
|
---|
465 | @Override
|
---|
466 | public String getDescription() {
|
---|
467 | return "Party group 20";
|
---|
468 | }
|
---|
469 |
|
---|
470 | } |
---|