[339] | 1 | package bargainingchips.players;
|
---|
| 2 |
|
---|
| 3 | import static java.lang.Math.pow;
|
---|
| 4 |
|
---|
| 5 | import java.util.ArrayList;
|
---|
| 6 | import java.util.List;
|
---|
| 7 | import java.util.Random;
|
---|
| 8 | import java.util.concurrent.BlockingQueue;
|
---|
| 9 |
|
---|
| 10 | import bargainingchips.Bundle;
|
---|
| 11 | import bargainingchips.NegotiationContext;
|
---|
| 12 | import bargainingchips.OutcomeSpace;
|
---|
| 13 | import bargainingchips.actions.Bid;
|
---|
| 14 | import bargainingchips.actions.FinalAccept;
|
---|
| 15 | import bargainingchips.actions.Offer;
|
---|
| 16 | import bargainingchips.actions.OfferBy;
|
---|
| 17 | import bargainingchips.actions.OfferType;
|
---|
| 18 | import bargainingchips.actions.PreAccept;
|
---|
| 19 | import bargainingchips.analysis.BundleUtilityPoint;
|
---|
| 20 | import bargainingchips.analysis.BundleUtilitySpace;
|
---|
[340] | 21 | import bargainingchips.messaging.coordination.CoordinationMessage;
|
---|
| 22 | import bargainingchips.messaging.status.StatusMessage;
|
---|
[339] | 23 | import bargainingchips.outcomes.Outcome;
|
---|
| 24 | import bargainingchips.players.Agent;
|
---|
| 25 | import bargainingchips.utilityfunctions.UtilityFunction;
|
---|
| 26 | import bargainingchips.players.history.BidEvent;
|
---|
| 27 | import bargainingchips.players.history.BidEventHistory;
|
---|
| 28 | import bargainingchips.players.history.BidEventHistoryKeeper;
|
---|
| 29 |
|
---|
| 30 | //import agents.bayesianopponentmodel.BayesianOpponentModelScalable;
|
---|
| 31 | //import agents.bayesianopponentmodel.OpponentModelUtilSpace;
|
---|
| 32 |
|
---|
| 33 | //import bargainingchips.players.TitForTatNegotiationAgent.BayesianOpponentModelScalable;
|
---|
| 34 |
|
---|
| 35 | /**
|
---|
| 36 | * @version 1
|
---|
| 37 | *
|
---|
| 38 | * This TitForTatNegotiatingAgent adapts {@link /NegotiatorGUI/src/main/java/agents/anac/y2011/Nice_Tit_for_Tat} [1],
|
---|
| 39 | * according to Bargaining Chips definitions. Tit-for-Tat strategy has been successful in reaching Nash equilibrium,
|
---|
| 40 | * a pessimistic solution (not the optimistic Pareto solution) in repeated infinite Prisoners' Dilemma games assuming
|
---|
| 41 | * the definition given for `rationality'. Although, not all humans and also all rational humans behaves under that
|
---|
| 42 | * definition for rationality, but this class is as a test base skeleton for other agents which uses Tit-For-Tat.
|
---|
| 43 | *
|
---|
| 44 | * [1] T. Baarslag, K. Hindriks, C. Jonker, A tit for tat negotiation strategy for real-time bilateral negotiations,
|
---|
| 45 | * Complex Automated Negotiations, 229-233 (2013).
|
---|
| 46 | *
|
---|
| 47 | *
|
---|
| 48 | * @author Faria Nassiri-Mofakham
|
---|
| 49 | *
|
---|
| 50 | */
|
---|
| 51 |
|
---|
| 52 | public abstract class NiceTitForTatNegotiationAgent extends HistoryAgent
|
---|
| 53 | {
|
---|
| 54 | private static final double TIME_USED_TO_DETERMINE_OPPONENT_STARTING_POINT = 0.01;
|
---|
| 55 |
|
---|
| 56 | private Offer lastReceivedOffer = null;
|
---|
| 57 |
|
---|
| 58 | /**
|
---|
| 59 | * Indicates the number of times we have offered the best bid by the
|
---|
| 60 | * opponent so far (which means we are very close to the deadline and wanted
|
---|
| 61 | * to accept)
|
---|
| 62 | */
|
---|
| 63 | private int offeredOpponentBestBid = 0;
|
---|
| 64 |
|
---|
| 65 | private long DOMAINSIZE;
|
---|
| 66 | private List<Bundle> space;
|
---|
| 67 |
|
---|
| 68 | //private BayesianOpponentModelScalable opponentModel;
|
---|
| 69 |
|
---|
| 70 | private double myNashUtility;
|
---|
| 71 |
|
---|
| 72 | private double initialGap;
|
---|
| 73 | private final boolean TEST_EQUIVALENCE = false;
|
---|
| 74 | private Random random100;
|
---|
| 75 |
|
---|
| 76 |
|
---|
| 77 | public NiceTitForTatNegotiationAgent(String name, UtilityFunction u, NegotiationContext nc,
|
---|
| 78 | BlockingQueue<Offer> in,
|
---|
| 79 | BlockingQueue<OfferBy> out,
|
---|
| 80 | BlockingQueue<CoordinationMessage> cin,
|
---|
| 81 | BlockingQueue<StatusMessage> cout) {
|
---|
| 82 | super(name, u, nc, in, out, cin, cout);
|
---|
| 83 | }
|
---|
| 84 |
|
---|
| 85 | @Override
|
---|
| 86 | public void init() {
|
---|
| 87 | super.init();
|
---|
| 88 | space = outcomeSpace.getAllBids(); //getNumberOfPossibleBids();
|
---|
| 89 | //prepareOpponentModel();
|
---|
| 90 | DOMAINSIZE = space.size();
|
---|
| 91 | if (TEST_EQUIVALENCE) {
|
---|
| 92 | random100 = new Random(100);
|
---|
| 93 | } else {
|
---|
| 94 | random100 = new Random();
|
---|
| 95 | }
|
---|
| 96 | log("Domain size: " + DOMAINSIZE);
|
---|
| 97 | }
|
---|
| 98 |
|
---|
| 99 | //**
|
---|
| 100 | protected void prepareOpponentModel() {
|
---|
| 101 | // opponentModel = new BayesianOpponentModelScalable( // for OPPONENT MODEL
|
---|
| 102 | // (AdditiveUtilitySpace) utilitySpace);
|
---|
| 103 | }
|
---|
| 104 |
|
---|
| 105 | @Override
|
---|
| 106 | // public Bundle chooseCounterBid() {
|
---|
| 107 | protected Bundle getNextBid()
|
---|
| 108 | {
|
---|
| 109 | // OutcomeSpace outcomeSpace = negotiationContext.getOutcomeSpace();
|
---|
| 110 | // return outcomeSpace.getBidNearUtility(getTargetUtility(), u, this);
|
---|
| 111 | // Bundle opponentLastBid = lastReceivedOffer;
|
---|
| 112 |
|
---|
| 113 | Bundle opponentLastBid = getOpponentLastBid();
|
---|
| 114 |
|
---|
| 115 | double time = getTime();
|
---|
| 116 | log("---------- t = " + time + "----------\n");
|
---|
| 117 |
|
---|
| 118 | // // If we have time, we receiveMessage the opponent model
|
---|
| 119 | // if (canUpdateBeliefs(time)) { // for OPPONENT MODEL
|
---|
| 120 | // updateBeliefs(opponentLastBid);
|
---|
| 121 | // updateMyNashUtility();
|
---|
| 122 | // }
|
---|
| 123 |
|
---|
| 124 | double myUtilityOfOpponentLastBid = getUtility(opponentLastBid);
|
---|
| 125 | double maximumOfferedUtilityByOpponent = opponentHistory
|
---|
| 126 | .getMaximumUtility();
|
---|
| 127 | double minimumOfferedUtilityByOpponent = opponentHistory
|
---|
| 128 | .getMinimumUtility();
|
---|
| 129 |
|
---|
| 130 | double minUtilityOfOpponentFirstBids = getMinimumUtilityOfOpponentFirstBids(
|
---|
| 131 | myUtilityOfOpponentLastBid);
|
---|
| 132 |
|
---|
| 133 | double opponentConcession = maximumOfferedUtilityByOpponent
|
---|
| 134 | - minUtilityOfOpponentFirstBids;
|
---|
| 135 | /**
|
---|
| 136 | * Measures how far away the opponent is from myNashUtility. 0 =
|
---|
| 137 | * farthest, 1 = closest
|
---|
| 138 | */
|
---|
| 139 | double opponentConcedeFactor = Math.min(1, opponentConcession
|
---|
| 140 | / (myNashUtility - minUtilityOfOpponentFirstBids));
|
---|
| 141 | double myConcession = opponentConcedeFactor * (1 - myNashUtility);
|
---|
| 142 |
|
---|
| 143 | double myCurrentTargetUtility = 1 - myConcession;
|
---|
| 144 |
|
---|
| 145 | log("Min/Max utility offered by opponent was "
|
---|
| 146 | + round2(minimumOfferedUtilityByOpponent) + "/"
|
---|
| 147 | + round2(maximumOfferedUtilityByOpponent)
|
---|
| 148 | + ". (Right now, he offers me "
|
---|
| 149 | + round2(myUtilityOfOpponentLastBid) + ")\n"
|
---|
| 150 | + "The minimum of his first few bids is "
|
---|
| 151 | + round2(minUtilityOfOpponentFirstBids)
|
---|
| 152 | + " so his concession is " + round2(opponentConcession)
|
---|
| 153 | + ", which is at " + percentage(opponentConcedeFactor) + "%\n"
|
---|
| 154 | + "So I concede the same factor and my concession is "
|
---|
| 155 | + round2(myConcession)
|
---|
| 156 | + ". Therefore, my current target util is "
|
---|
| 157 | + round2(myCurrentTargetUtility));
|
---|
| 158 |
|
---|
| 159 | initialGap = 1 - minUtilityOfOpponentFirstBids;
|
---|
| 160 | double gapToNash = Math.max(0, myCurrentTargetUtility - myNashUtility);
|
---|
| 161 |
|
---|
| 162 | double bonus = getBonus();
|
---|
| 163 | double tit = bonus * gapToNash;
|
---|
| 164 | myCurrentTargetUtility -= tit;
|
---|
| 165 | log("The gap to Nash is then " + round2(gapToNash)
|
---|
| 166 | + ". I will add another bonus of " + round2(tit) + " (="
|
---|
| 167 | + percentage(bonus) + "%)" + " to have a target util of "
|
---|
| 168 | + myCurrentTargetUtility);
|
---|
| 169 |
|
---|
| 170 | List<Bundle> myBids = getBidsOfUtility(myCurrentTargetUtility);
|
---|
| 171 | Bundle myBid = getBestBidForOpponent(myBids);
|
---|
| 172 | myBid = makeAppropriate(myBid);
|
---|
| 173 | log(" so I choose a bid with util " + getUtility(myBid));
|
---|
| 174 | return myBid;
|
---|
| 175 | }
|
---|
| 176 |
|
---|
| 177 | /**
|
---|
| 178 | * Decides if we have enough time to receiveMessage opponent model and Nash
|
---|
| 179 | * point. On large domains, this may take 3 seconds.
|
---|
| 180 | *
|
---|
| 181 | * @param time
|
---|
| 182 | */
|
---|
| 183 | private boolean canUpdateBeliefs(double time) {
|
---|
| 184 | // in the last seconds we don't want to lose any time
|
---|
| 185 | if (time > 0.99)
|
---|
| 186 | return false;
|
---|
| 187 |
|
---|
| 188 | // in a big domain, we stop updating half-way
|
---|
| 189 | if (isDomainBig())
|
---|
| 190 | if (time > 0.5)
|
---|
| 191 | return false;
|
---|
| 192 |
|
---|
| 193 | return true;
|
---|
| 194 | }
|
---|
| 195 |
|
---|
| 196 | /**
|
---|
| 197 | * Returns a small bonus multiplier to our target utility if we have to be
|
---|
| 198 | * fast.
|
---|
| 199 | *
|
---|
| 200 | * discount = 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 bonus = 0.5 0.3
|
---|
| 201 | * 0.1
|
---|
| 202 | *
|
---|
| 203 | * Normal domains: time = 0.91 to 0.96 bonus = 0.0 to 1.0
|
---|
| 204 | *
|
---|
| 205 | * Big domains: time = 0.85 to 0.9 bonus = 0.0 to 1.0
|
---|
| 206 | */
|
---|
| 207 | private double getBonus() {
|
---|
| 208 | double discountFactor = outcomeSpace.getDiscountFactor();
|
---|
| 209 | double discountBonus = 0.5 - 0.4 * discountFactor;
|
---|
| 210 | if (discountFactor < 1)
|
---|
| 211 | log("Discount = " + discountFactor
|
---|
| 212 | + ", so we set discount bonus to "
|
---|
| 213 | + percentage(discountBonus) + "%.");
|
---|
| 214 |
|
---|
| 215 | boolean isBigDomain = DOMAINSIZE > 3000;
|
---|
| 216 | double timeBonus = 0;
|
---|
| 217 | double time = getTime();
|
---|
| 218 | double minTime = 0.91;
|
---|
| 219 | if (isBigDomain)
|
---|
| 220 | minTime = 0.85;
|
---|
| 221 | if (time > minTime) {
|
---|
| 222 | if (isBigDomain)
|
---|
| 223 | log("We have a big domain of size " + DOMAINSIZE
|
---|
| 224 | + ", so we start the time bonus from t = " + minTime);
|
---|
| 225 | timeBonus = Math.min(1, 20 * (time - minTime));
|
---|
| 226 | log("t = " + round2(time) + ", so we set time bonus to "
|
---|
| 227 | + percentage(timeBonus) + "%.");
|
---|
| 228 | }
|
---|
| 229 |
|
---|
| 230 | double bonus = Math.max(discountBonus, timeBonus);
|
---|
| 231 | if (bonus < 0)
|
---|
| 232 | bonus = 0;
|
---|
| 233 | if (bonus > 1)
|
---|
| 234 | bonus = 1;
|
---|
| 235 | return bonus;
|
---|
| 236 | }
|
---|
| 237 |
|
---|
| 238 | private void updateMyNashUtility() throws Exception {
|
---|
| 239 | BundleUtilitySpace bs=null;
|
---|
| 240 | try {
|
---|
| 241 | bs = new BundleUtilitySpace(outcomeSpace,outcomeSpace, true, false);
|
---|
| 242 | } catch (Exception e1) {
|
---|
| 243 | e1.printStackTrace();
|
---|
| 244 | }//null;
|
---|
| 245 | myNashUtility = 0.7;
|
---|
| 246 | try {
|
---|
| 247 | double nashMultiplier = getNashMultiplier(initialGap);
|
---|
| 248 | // if (DOMAINSIZE < 200000) { // by OPPONENT MODEL
|
---|
| 249 | // bs = new BundleUtilitySpace(outcomeSpace,
|
---|
| 250 | // new OpponentModelUtilSpace(opponentModel), true, false);
|
---|
| 251 | BundleUtilityPoint nash = bs.getNash();
|
---|
| 252 | if (nash != null && nash.getUtilityA() != null)
|
---|
| 253 | myNashUtility = nash.getUtilityA();
|
---|
| 254 |
|
---|
| 255 | myNashUtility *= nashMultiplier;
|
---|
| 256 |
|
---|
| 257 | // don't ask for too much or too little
|
---|
| 258 | if (myNashUtility > 1)
|
---|
| 259 | myNashUtility = 1;
|
---|
| 260 | if (myNashUtility < 0.5)
|
---|
| 261 | myNashUtility = 0.5;
|
---|
| 262 | } catch (Exception e) {
|
---|
| 263 | e.printStackTrace();
|
---|
| 264 | }
|
---|
| 265 | }
|
---|
| 266 |
|
---|
| 267 | /**
|
---|
| 268 | * If the gap is big (e.g. 0.8) the multiplier is smaller than 1 (e.g. 0.92)
|
---|
| 269 | * If the gap is small (e.g. 0.2) the multiplier is bigger than 1 (e.g.
|
---|
| 270 | * 1.28)
|
---|
| 271 | */
|
---|
| 272 | private double getNashMultiplier(double gap) {
|
---|
| 273 | double mult = 1.4 - 0.6 * gap;
|
---|
| 274 | if (mult < 0)
|
---|
| 275 | mult = 0;
|
---|
| 276 | return mult;
|
---|
| 277 | }
|
---|
| 278 |
|
---|
| 279 | private void updateBeliefs(Bundle opponentLastBid) {
|
---|
| 280 | // try { // by OPPONENT MODEL
|
---|
| 281 | // opponentModel.updateBeliefs(opponentLastBid);
|
---|
| 282 | // } catch (Exception e) {
|
---|
| 283 | // e.printStackTrace();
|
---|
| 284 | // }
|
---|
| 285 | }
|
---|
| 286 |
|
---|
| 287 | /**
|
---|
| 288 | * Gives us our utility of the starting point of our opponent. This is used
|
---|
| 289 | * as a reference whether the opponent concedes or not.
|
---|
| 290 | */
|
---|
| 291 | private double getMinimumUtilityOfOpponentFirstBids(
|
---|
| 292 | double myUtilityOfOpponentLastBid) {
|
---|
| 293 | BidEventHistory firstBids = opponentHistory.filterBetweenTime(0,
|
---|
| 294 | TIME_USED_TO_DETERMINE_OPPONENT_STARTING_POINT);
|
---|
| 295 | double firstBidsMinUtility;
|
---|
| 296 | if (firstBids.size() == 0)
|
---|
| 297 | firstBidsMinUtility = opponentHistory.getFirstBidEvent()
|
---|
| 298 | .getMyUtility();
|
---|
| 299 | else
|
---|
| 300 | firstBidsMinUtility = firstBids.getMinimumUtility();
|
---|
| 301 | return firstBidsMinUtility;
|
---|
| 302 | }
|
---|
| 303 |
|
---|
| 304 | /**
|
---|
| 305 | * Prevents the agent from overshooting. Replaces the planned bid (i.e, {@link Bundle}) by
|
---|
| 306 | * something more appropriate if there was a bid by the opponent that is
|
---|
| 307 | * better than our planned bid.
|
---|
| 308 | */
|
---|
| 309 | private Bundle makeAppropriate(Bundle myPlannedBid) {
|
---|
| 310 | Bundle bestBidByOpponent = opponentHistory.getBestBid();
|
---|
| 311 | // Bid by opponent is better than our planned bid
|
---|
| 312 | double bestUtilityByOpponent = getUtility(bestBidByOpponent);
|
---|
| 313 | double myPlannedUtility = getUtility(myPlannedBid);
|
---|
| 314 | if (bestUtilityByOpponent >= myPlannedUtility) {
|
---|
| 315 | log("Opponent previously made a better offer to me than my planned util "
|
---|
| 316 | + myPlannedUtility + ", namely " + bestUtilityByOpponent
|
---|
| 317 | + ", so I make that bid instead.");
|
---|
| 318 | return bestBidByOpponent;
|
---|
| 319 | }
|
---|
| 320 | return myPlannedBid;
|
---|
| 321 | }
|
---|
| 322 |
|
---|
| 323 | @Override
|
---|
| 324 | public Bundle chooseOpeningBid() {
|
---|
| 325 | try {
|
---|
| 326 | return outcomeSpace.getMaxBidPossible(u);
|
---|
| 327 | } catch (Exception e) {
|
---|
| 328 | e.printStackTrace();
|
---|
| 329 | return outcomeSpace.getRandom(random100); // get a bid in random
|
---|
| 330 | }
|
---|
| 331 | }
|
---|
| 332 |
|
---|
| 333 | @Override
|
---|
| 334 | public Bundle chooseFirstCounterBid() {
|
---|
| 335 | if (TEST_EQUIVALENCE) {
|
---|
| 336 | // try {
|
---|
| 337 | // opponentModel.updateBeliefs( // using OPPONENT MODEL
|
---|
| 338 | // opponentHistory.getLastBidDetails().getBid());
|
---|
| 339 | // } catch (Exception e) {
|
---|
| 340 | // e.printStackTrace();
|
---|
| 341 | // }
|
---|
| 342 | }
|
---|
| 343 | return chooseOpeningBid();
|
---|
| 344 | }
|
---|
| 345 |
|
---|
| 346 | @Override
|
---|
| 347 | public boolean isAcceptable(Bundle plannedBid) {
|
---|
| 348 | double time = getTime();
|
---|
| 349 | Bundle opponentBid = getOpponentLastBid();
|
---|
| 350 | Bundle myNextBid = plannedBid;
|
---|
| 351 |
|
---|
| 352 | if (utility(opponentBid) >= utility(myNextBid))
|
---|
| 353 | return true;
|
---|
| 354 |
|
---|
| 355 | if (time < 0.98)
|
---|
| 356 | return false;
|
---|
| 357 |
|
---|
| 358 | double offeredUtility = opponentHistory.getLastBidEvent()
|
---|
| 359 | .getMyUtility();
|
---|
| 360 | double now = time;
|
---|
| 361 | double timeLeft = 1 - now;
|
---|
| 362 | log("<======= new AC ========= (t = " + round3(now) + ", remaining: "
|
---|
| 363 | + round3(timeLeft) + ")");
|
---|
| 364 |
|
---|
| 365 | // if we will still see a lot of bids (more than enoughBidsToCome), we
|
---|
| 366 | // do not accept
|
---|
| 367 | BidEventHistory recentBids = opponentHistory
|
---|
| 368 | .filterBetweenTime(now - timeLeft, now);
|
---|
| 369 | int recentBidsSize = recentBids.size();
|
---|
| 370 | int enoughBidsToCome = 10;
|
---|
| 371 | if (isDomainBig())
|
---|
| 372 | enoughBidsToCome = 40;
|
---|
| 373 | if (recentBidsSize > enoughBidsToCome) {
|
---|
| 374 | log("I expect to see " + recentBidsSize
|
---|
| 375 | + " more bids. I will only consider accepting when it falls below "
|
---|
| 376 | + enoughBidsToCome);
|
---|
| 377 | return false;
|
---|
| 378 | }
|
---|
| 379 |
|
---|
| 380 | // v2.0
|
---|
| 381 | double window = timeLeft;
|
---|
| 382 | // double window = 5 * timeLeft;
|
---|
| 383 | BidEventHistory recentBetterBids = opponentHistory.filterBetween(
|
---|
| 384 | offeredUtility, 1, now - window, now);
|
---|
| 385 | int n = recentBetterBids.size();
|
---|
| 386 | double p = timeLeft / window;
|
---|
| 387 | if (p > 1)
|
---|
| 388 | p = 1;
|
---|
| 389 |
|
---|
| 390 | double pAllMiss = Math.pow(1 - p, n);
|
---|
| 391 | if (n == 0)
|
---|
| 392 | pAllMiss = 1;
|
---|
| 393 | double pAtLeastOneHit = 1 - pAllMiss;
|
---|
| 394 |
|
---|
| 395 | double avg = recentBetterBids.getAverageUtility();
|
---|
| 396 |
|
---|
| 397 | double expectedUtilOfWaitingForABetterBid = pAtLeastOneHit * avg;
|
---|
| 398 |
|
---|
| 399 | log("In the previous time window of size " + window + " I have seen "
|
---|
| 400 | + n + " better offers (out of a total of " + recentBidsSize
|
---|
| 401 | + ") than " + round2(offeredUtility)
|
---|
| 402 | + ", with avg util " + round2(avg));
|
---|
| 403 | log("p = " + p + ", so pAtLeastOneHit = " + pAtLeastOneHit);
|
---|
| 404 | log("expectedUtilOfWaitingForABetterBid = "
|
---|
| 405 | + round2(expectedUtilOfWaitingForABetterBid));
|
---|
| 406 | log("Acceptable? "
|
---|
| 407 | + (offeredUtility > expectedUtilOfWaitingForABetterBid));
|
---|
| 408 | log("========================>\n");
|
---|
| 409 | if (offeredUtility > expectedUtilOfWaitingForABetterBid)
|
---|
| 410 | return true;
|
---|
| 411 | return false;
|
---|
| 412 | }
|
---|
| 413 |
|
---|
| 414 |
|
---|
| 415 | @Override
|
---|
| 416 | protected Offer sendOffer()
|
---|
| 417 | {
|
---|
| 418 | Offer myAction = null;
|
---|
| 419 |
|
---|
| 420 | // Nothing received yet
|
---|
| 421 | if (lastReceivedOffer == null)
|
---|
| 422 | {
|
---|
| 423 | Bundle myBid = chooseOpeningBid(); //getNextBid();
|
---|
| 424 | remember(new Offer(myBid,OfferType.BID));
|
---|
| 425 | return new Bid(myBid);
|
---|
| 426 | }
|
---|
| 427 |
|
---|
| 428 | // Our last bid got accepted. We are also accepting (and we should notify the coordinator).
|
---|
| 429 | if (lastReceivedOffer.isPreAccept())
|
---|
| 430 | return new FinalAccept(lastReceivedOffer);
|
---|
| 431 |
|
---|
| 432 | Bundle lastReceivedBid = lastReceivedOffer.getBundle();
|
---|
| 433 |
|
---|
| 434 | // Accept
|
---|
| 435 | if (isAcceptable(lastReceivedBid))
|
---|
| 436 | return new PreAccept(lastReceivedOffer);
|
---|
| 437 | // Counter offer based actions
|
---|
| 438 | else
|
---|
| 439 | {
|
---|
| 440 | // we are the second
|
---|
| 441 | if (getMyLastBid() == null)
|
---|
| 442 | {
|
---|
| 443 | Bundle firstCounterBid = chooseFirstCounterBid();
|
---|
| 444 | // Check to see if we want to accept the first offer
|
---|
| 445 | if (isAcceptable(firstCounterBid))
|
---|
| 446 | {
|
---|
| 447 | myAction = makeAcceptAction();
|
---|
| 448 | return myAction;
|
---|
| 449 | }
|
---|
| 450 | else
|
---|
| 451 | {
|
---|
| 452 | myAction = new Offer(firstCounterBid,OfferType.BID);
|
---|
| 453 | remember(myAction);
|
---|
| 454 | return new Bid(firstCounterBid);
|
---|
| 455 | }
|
---|
| 456 | }
|
---|
| 457 |
|
---|
| 458 | // We make a normal counter-offer
|
---|
| 459 | Bundle counterBid = chooseCounterBid();
|
---|
| 460 | // Check to see if we want to accept
|
---|
| 461 | if (isAcceptable(counterBid))
|
---|
| 462 | {
|
---|
| 463 | myAction = makeAcceptAction();
|
---|
| 464 | return myAction;
|
---|
| 465 | }
|
---|
| 466 | else
|
---|
| 467 | {
|
---|
| 468 | myAction = new Offer(counterBid, OfferType.BID);
|
---|
| 469 | remember(myAction);
|
---|
| 470 | return new Bid(counterBid);
|
---|
| 471 | }
|
---|
| 472 |
|
---|
| 473 | // if (myAction == null)
|
---|
| 474 | // { // need this code to check equivalence
|
---|
| 475 | // myAction = new Offer(counterBid, OfferType.BID);
|
---|
| 476 | //
|
---|
| 477 | // remember(myAction);
|
---|
| 478 | // return new Bid(counterBid);
|
---|
| 479 | // }
|
---|
| 480 | }
|
---|
| 481 | //return new Bid(chooseCounterBid());
|
---|
| 482 | }
|
---|
| 483 |
|
---|
| 484 |
|
---|
| 485 | @SuppressWarnings("unused")
|
---|
| 486 | @Override
|
---|
| 487 | public Offer makeAcceptAction() {
|
---|
| 488 | log("We are in the final phase because the AC accepted. We have offered the best bid of our opponent "
|
---|
| 489 | + offeredOpponentBestBid + " time(s).");
|
---|
| 490 |
|
---|
| 491 | // However, if it wants to accept and we have some time left, we try to
|
---|
| 492 | // replace it with the best offer so far
|
---|
| 493 | Bundle opponentBid = getOpponentLastBid();
|
---|
| 494 | double offeredUtility = utility(opponentBid);
|
---|
| 495 | double now = getTime();
|
---|
| 496 | double timeLeft = 1 - now;
|
---|
| 497 | BidEventHistory recentBids = opponentHistory
|
---|
| 498 | .filterBetweenTime(now - timeLeft, now);
|
---|
| 499 | int expectedBids = recentBids.size();
|
---|
| 500 | Bundle bestBid = opponentHistory.getBestBid();
|
---|
| 501 | double bestBidUtility = utility(bestBid);
|
---|
| 502 | log("We expect to see " + expectedBids
|
---|
| 503 | + " more bids. The AC wants to accept the offer of utility "
|
---|
| 504 | + round2(offeredUtility) + " (max offered = "
|
---|
| 505 | + round2(bestBidUtility) + ").");
|
---|
| 506 |
|
---|
| 507 | if (TEST_EQUIVALENCE) {
|
---|
| 508 | // we expect to see more bids, that are strictly better than what we
|
---|
| 509 | // are about to offer
|
---|
| 510 | if (!(expectedBids > 1 && bestBidUtility > offeredUtility)) {
|
---|
| 511 | return new PreAccept(new Offer(opponentBid,OfferType.BID));
|
---|
| 512 | }
|
---|
| 513 | } else {
|
---|
| 514 | // we expect to see more bids, that are strictly better than what we
|
---|
| 515 | // are about to offer
|
---|
| 516 | if (expectedBids > 1 && bestBidUtility > offeredUtility
|
---|
| 517 | && offeredOpponentBestBid <= 3) {
|
---|
| 518 | log("Which is not enough, so we will not accept now. Instead, we offer the opponent's best bid so far, which gives us utility "
|
---|
| 519 | + utility(bestBid));
|
---|
| 520 | offeredOpponentBestBid++;
|
---|
| 521 | return new Bid(bestBid);
|
---|
| 522 | } else {
|
---|
| 523 | log("Therefore, we will accept now.");
|
---|
| 524 | return new PreAccept(new Offer(opponentBid,OfferType.BID));
|
---|
| 525 | }
|
---|
| 526 | }
|
---|
| 527 | return null;
|
---|
| 528 | }
|
---|
| 529 |
|
---|
| 530 | private double utility(Bundle b) {
|
---|
| 531 | try {
|
---|
| 532 | return u.getUtility(b);
|
---|
| 533 | } catch (Exception e) {
|
---|
| 534 | e.printStackTrace();
|
---|
| 535 | return 0;
|
---|
| 536 | }
|
---|
| 537 | }
|
---|
| 538 |
|
---|
| 539 | protected static void log(String s) {
|
---|
| 540 | }
|
---|
| 541 |
|
---|
| 542 | /**
|
---|
| 543 | * Rounds to two decimals
|
---|
| 544 | */
|
---|
| 545 | public static double round2(double x) {
|
---|
| 546 | return Math.round(100 * x) / 100d;
|
---|
| 547 | }
|
---|
| 548 |
|
---|
| 549 | /**
|
---|
| 550 | * Rounds to 3 decimals
|
---|
| 551 | */
|
---|
| 552 | public static double round3(double x) {
|
---|
| 553 | return Math.round(1000 * x) / 1000d;
|
---|
| 554 | }
|
---|
| 555 |
|
---|
| 556 | public static double percentage(double x) {
|
---|
| 557 | return Math.round(1000 * x) / 10d;
|
---|
| 558 | }
|
---|
| 559 |
|
---|
| 560 | /**
|
---|
| 561 | * Get all bids in a utility range.
|
---|
| 562 | */
|
---|
| 563 | private List<Bundle> getBidsOfUtility(double lowerBound, double upperBound) {
|
---|
| 564 | // In big domains, we need to find only this amount of bids around the
|
---|
| 565 | // target. Should be at least 2.
|
---|
| 566 | final int limit = 2;
|
---|
| 567 |
|
---|
| 568 | List<Bundle> bidsInRange = new ArrayList<Bundle>();
|
---|
| 569 | for (Bundle b : space)
|
---|
| 570 | try {
|
---|
| 571 | double util = u.getUtility(b);
|
---|
| 572 | if (util >= lowerBound && util <= upperBound)
|
---|
| 573 | bidsInRange.add(b);
|
---|
| 574 |
|
---|
| 575 | // In big domains, break early
|
---|
| 576 | if (isDomainBig() && bidsInRange.size() >= limit)
|
---|
| 577 |
|
---|
| 578 | return bidsInRange;
|
---|
| 579 | } catch (Exception e) {
|
---|
| 580 | e.printStackTrace();
|
---|
| 581 | }
|
---|
| 582 | return bidsInRange;
|
---|
| 583 | }
|
---|
| 584 |
|
---|
| 585 | /**
|
---|
| 586 | * Get all bids in a utility range.
|
---|
| 587 | */
|
---|
| 588 | private List<Bundle> getBidsOfUtility(double target) {
|
---|
| 589 | if (target > 1)
|
---|
| 590 | target = 1;
|
---|
| 591 |
|
---|
| 592 | double min = target * 0.98;
|
---|
| 593 | double max = target + 0.04;
|
---|
| 594 | do {
|
---|
| 595 | max += 0.01;
|
---|
| 596 | List<Bundle> bids = getBidsOfUtility(min, max);
|
---|
| 597 | int size = bids.size();
|
---|
| 598 |
|
---|
| 599 | log(size + " bids found in [" + round2(min) + ", " + round2(max)
|
---|
| 600 | + "]");
|
---|
| 601 | // We need at least 2 bids. Or if max = 1, then we settle for 1 bid.
|
---|
| 602 | if (size > 1 || (max >= 1 && size > 0)) {
|
---|
| 603 | log(size + " bids of target utility " + round2(target)
|
---|
| 604 | + " found in [" + round2(min) + ", " + round2(max)
|
---|
| 605 | + "]");
|
---|
| 606 | return bids;
|
---|
| 607 | }
|
---|
| 608 | } while (max <= 1);
|
---|
| 609 |
|
---|
| 610 | // Weird if this happens
|
---|
| 611 | ArrayList<Bundle> best = new ArrayList<Bundle>();
|
---|
| 612 | best.add(chooseOpeningBid());
|
---|
| 613 | return best;
|
---|
| 614 | }
|
---|
| 615 |
|
---|
| 616 | private boolean isDomainBig() {
|
---|
| 617 | return DOMAINSIZE > 10000;
|
---|
| 618 | }
|
---|
| 619 |
|
---|
| 620 | private Bundle getBestBidForOpponent(List<Bundle> bids) {
|
---|
| 621 | // We first make a bid history for the opponent, then pick the best one.
|
---|
| 622 | BidEventHistory possibleBidHistory = new BidEventHistory();
|
---|
| 623 | for (Bundle b : bids) {
|
---|
| 624 | double utility;
|
---|
| 625 | try {
|
---|
| 626 | utility =0; //utility = opponentModel.getNormalizedUtility(b);
|
---|
| 627 | BidEvent bidEvent = new BidEvent(b, utility, 0);
|
---|
| 628 | possibleBidHistory.add(bidEvent);
|
---|
| 629 | } catch (Exception e) {
|
---|
| 630 | e.printStackTrace();
|
---|
| 631 | }
|
---|
| 632 | }
|
---|
| 633 |
|
---|
| 634 | // Pick the top 3 to 20 bids, depending on the domain size
|
---|
| 635 | int n = (int) Math.round(bids.size() / 10.0);
|
---|
| 636 | if (n < 3)
|
---|
| 637 | n = 3;
|
---|
| 638 | if (n > 20)
|
---|
| 639 | n = 20;
|
---|
| 640 |
|
---|
| 641 | BidEventHistory bestN = possibleBidHistory.getBestBidHistory(n);
|
---|
| 642 | BidEvent randomBestN = bestN.getRandom(random100);
|
---|
| 643 | log("Random bid chosen out of the top " + n + " of " + bids.size()
|
---|
| 644 | + " bids, with opp util: "
|
---|
| 645 | + round2(randomBestN.getMyUtility()));
|
---|
| 646 | return randomBestN.getBid();
|
---|
| 647 |
|
---|
| 648 | }
|
---|
| 649 |
|
---|
| 650 | // @Override
|
---|
| 651 | // public SupportedNegotiationSetting getSupportedNegotiationSetting() {
|
---|
| 652 | // return SupportedNegotiationSetting.getLinearUtilitySpaceInstance();
|
---|
| 653 | // }
|
---|
| 654 |
|
---|
| 655 | @Override
|
---|
| 656 | public String toDescription()
|
---|
| 657 | {
|
---|
| 658 | return name + " with u = " + u + " using " + this.getClass().getSimpleName() + " strategy ";
|
---|
| 659 | }
|
---|
| 660 |
|
---|
| 661 | }
|
---|
| 662 |
|
---|