[1] | 1 | package agents.anac.y2017.madagent;
|
---|
| 2 |
|
---|
[46] | 3 | import java.util.List;
|
---|
| 4 |
|
---|
[1] | 5 | import java.util.ArrayList;
|
---|
| 6 | import java.util.Comparator;
|
---|
| 7 | import java.util.HashMap;
|
---|
| 8 | import java.util.Random;
|
---|
| 9 |
|
---|
| 10 | import genius.core.AgentID;
|
---|
| 11 | import genius.core.Bid;
|
---|
| 12 | import genius.core.actions.Accept;
|
---|
| 13 | import genius.core.actions.Action;
|
---|
| 14 | import genius.core.actions.Offer;
|
---|
| 15 | import genius.core.boaframework.SortedOutcomeSpace;
|
---|
| 16 | import genius.core.parties.AbstractNegotiationParty;
|
---|
| 17 | import genius.core.parties.NegotiationInfo;
|
---|
| 18 | import genius.core.persistent.PersistentDataType;
|
---|
| 19 |
|
---|
| 20 | public class MadAgent extends AbstractNegotiationParty {
|
---|
| 21 |
|
---|
| 22 | private static final Random RANDOM = new Random();
|
---|
| 23 | private static final int MAXIMUM_NUMBER_OF_TRIALS = 2000;
|
---|
| 24 |
|
---|
| 25 | /*
|
---|
| 26 | * -------------------------------- RISK FUNCTION
|
---|
| 27 | * -------------------------------- f <- Round number to fake (Agent will
|
---|
| 28 | * fake in every f rounds) c <- Risk constant p <- Risk parameter Formula ->
|
---|
| 29 | * f = c / 2 ^ p We choose 5 as our parameter because we want our agent to
|
---|
| 30 | * be both aggressive and defensive
|
---|
| 31 | */
|
---|
| 32 | private static final double RISK_CONSTANT = 100000;
|
---|
| 33 | private static final double RISK_PARAMETER = 5; // Risk Parameter: 0, 1, 2,
|
---|
| 34 | // ..., 8, 9, 10
|
---|
| 35 | private static final int ROUND_NUMBER_TO_FAKE = (int) (RISK_CONSTANT
|
---|
| 36 | / Math.pow(2, RISK_PARAMETER));
|
---|
| 37 |
|
---|
| 38 | private SortedOutcomeSpace sortedOutcomeSpace = null;
|
---|
| 39 | private Bid lastReceivedBid = null;
|
---|
| 40 | private Bid bestReceivedBid = null;
|
---|
| 41 | private Bid secondBestBid = null;
|
---|
| 42 | private String negotiationType = null;
|
---|
| 43 | private double negotiationLimit = 0;
|
---|
| 44 | private double numberOfRoundsPassed = 0;
|
---|
| 45 | private double timeToGetAlmostMad = 0;
|
---|
| 46 | private double timeToGetMad = 0;
|
---|
| 47 | private double threshold = 0.8;
|
---|
| 48 | private double currentThreshold = 0.8;
|
---|
| 49 | /* Variables for Opponent Modeling */
|
---|
| 50 | private int opponentTurn = 0; // Value to keep track opponents
|
---|
| 51 | private int myTurn = 0; // Random value to choose one of the opponents and
|
---|
| 52 | // use its preferences while generating bid
|
---|
| 53 | private int shiftBids[] = null; // Index for bidsPreferredByOpponents for
|
---|
| 54 | // each opponent
|
---|
| 55 | private OpponentModel[] opponentModels = null; // Opponent Model for each
|
---|
| 56 | // opponent.
|
---|
| 57 | private List<List<Bid>> bidsPreferredByOpponents = null; // bidsPrefferredByOpponents
|
---|
| 58 | // for each
|
---|
| 59 | // opponent
|
---|
| 60 |
|
---|
| 61 | @Override
|
---|
| 62 | public void init(NegotiationInfo info) {
|
---|
| 63 | super.init(info);
|
---|
| 64 |
|
---|
| 65 | System.out.println("Discount Factor is "
|
---|
[46] | 66 | + getUtilitySpace().getDiscountFactor());
|
---|
[1] | 67 | System.out.println("Reservation Value is "
|
---|
[46] | 68 | + getUtilitySpace().getReservationValueUndiscounted());
|
---|
[1] | 69 |
|
---|
| 70 | sortedOutcomeSpace = new SortedOutcomeSpace(utilitySpace);
|
---|
| 71 | threshold *= 1.125;
|
---|
| 72 |
|
---|
| 73 | shiftBids = new int[3];
|
---|
| 74 | opponentModels = new OpponentModel[3];
|
---|
| 75 | bidsPreferredByOpponents = new ArrayList<List<Bid>>();
|
---|
| 76 |
|
---|
| 77 | // Filling the array with empty values to avoid 'Null Pointer Exception'
|
---|
| 78 | for (int i = 0; i < 3; i++) {
|
---|
| 79 | opponentModels[i] = new OpponentModel(utilitySpace, threshold);
|
---|
| 80 | bidsPreferredByOpponents.add(null);
|
---|
| 81 | }
|
---|
| 82 |
|
---|
| 83 | try {
|
---|
| 84 | bestReceivedBid = utilitySpace.getMinUtilityBid();
|
---|
| 85 | } catch (Exception e) {
|
---|
| 86 | System.err.println("An exception thrown at init..");
|
---|
| 87 | }
|
---|
| 88 |
|
---|
| 89 | negotiationType = info.getDeadline().getType().toString();
|
---|
| 90 | negotiationLimit = info.getDeadline().getValue();
|
---|
| 91 |
|
---|
| 92 | /* This values will be used for adapting threshold */
|
---|
| 93 | timeToGetMad = negotiationLimit * 0.8; // Agent gets mad in the last 20%
|
---|
| 94 | // of the negotiation
|
---|
| 95 | timeToGetAlmostMad = negotiationLimit * 0.5; // Agent gets almost mad in
|
---|
| 96 | // the last 50% of the
|
---|
| 97 | // negotiation
|
---|
| 98 |
|
---|
| 99 | if (getData().getPersistentDataType() != PersistentDataType.STANDARD)
|
---|
| 100 | throw new IllegalStateException("need standard persistent data");
|
---|
| 101 |
|
---|
| 102 | /* Agent calculates the second best bid */
|
---|
| 103 | try {
|
---|
| 104 | calculateSecondBestBid();
|
---|
| 105 | } catch (Exception e) {
|
---|
| 106 | System.err.println(
|
---|
| 107 | "An exception thrown while calculating the second best bid..");
|
---|
| 108 | }
|
---|
| 109 | }
|
---|
| 110 |
|
---|
| 111 | private void calculateSecondBestBid() throws Exception {
|
---|
| 112 | for (double u = utilitySpace
|
---|
| 113 | .getUtility(utilitySpace.getMaxUtilityBid()); true; u -= 0.01) {
|
---|
| 114 | secondBestBid = sortedOutcomeSpace.getBidNearUtility(u).getBid();
|
---|
| 115 |
|
---|
| 116 | if (utilitySpace.getUtility(secondBestBid) != utilitySpace
|
---|
| 117 | .getUtility(utilitySpace.getMaxUtilityBid()))
|
---|
| 118 | break;
|
---|
| 119 | }
|
---|
| 120 | }
|
---|
| 121 |
|
---|
| 122 | @Override
|
---|
| 123 | public void receiveMessage(AgentID sender, Action action) { // ...
|
---|
| 124 | // Opponent's
|
---|
| 125 | // turn ...
|
---|
| 126 | super.receiveMessage(sender, action);
|
---|
| 127 |
|
---|
| 128 | /*
|
---|
| 129 | * If the action is an Offer, get the last received bid and use it to
|
---|
| 130 | * form Opponent Model
|
---|
| 131 | */
|
---|
| 132 | if (action instanceof Offer) {
|
---|
| 133 | lastReceivedBid = ((Offer) action).getBid();
|
---|
| 134 |
|
---|
| 135 | opponentModels[2].offer(lastReceivedBid, numberOfRoundsPassed);
|
---|
| 136 | opponentModels[opponentTurn++ % 2].offer(lastReceivedBid,
|
---|
| 137 | numberOfRoundsPassed);
|
---|
| 138 | }
|
---|
| 139 | }
|
---|
| 140 |
|
---|
| 141 | @Override
|
---|
| 142 | public Action chooseAction(List<Class<? extends Action>> validActions) { // ...
|
---|
| 143 | // Your
|
---|
| 144 | // agent's
|
---|
| 145 | // turn
|
---|
| 146 | // ...
|
---|
| 147 | numberOfRoundsPassed++;
|
---|
| 148 |
|
---|
| 149 | if (lastReceivedBid == null) { // You are the starter party, offer the
|
---|
| 150 | // best possible bid
|
---|
| 151 | return new Offer(getPartyId(), getBestBidPossible());
|
---|
| 152 | } else { // You are not the starter party
|
---|
| 153 | /* Determine the best received bid */
|
---|
| 154 | if (utilitySpace.getUtility(lastReceivedBid) > utilitySpace
|
---|
| 155 | .getUtility(bestReceivedBid))
|
---|
| 156 | bestReceivedBid = lastReceivedBid;
|
---|
| 157 |
|
---|
| 158 | /*
|
---|
| 159 | * If utility of the last received bid is higher than the threshold,
|
---|
| 160 | * accept the offer.
|
---|
| 161 | */
|
---|
| 162 | /* Else, offer a new bid. */
|
---|
| 163 | if (utilitySpace.getUtility(lastReceivedBid) > currentThreshold)
|
---|
| 164 | return new Accept(getPartyId(), lastReceivedBid);
|
---|
| 165 | else
|
---|
| 166 | return new Offer(getPartyId(), getBestBidPossible());
|
---|
| 167 | }
|
---|
| 168 | }
|
---|
| 169 |
|
---|
| 170 | private Bid getBestBidPossible() {
|
---|
| 171 | try {
|
---|
| 172 | double currentStatus = getCurrentStatus();
|
---|
| 173 |
|
---|
| 174 | if (currentStatus <= negotiationLimit * 0.05) { // First 5% of the
|
---|
| 175 | // negotiation
|
---|
| 176 | return secondBestBid;
|
---|
| 177 | } else if ((int) numberOfRoundsPassed % ROUND_NUMBER_TO_FAKE <= 10
|
---|
| 178 | && currentStatus <= negotiationLimit * 0.9) {
|
---|
| 179 | return getFakeBid();
|
---|
| 180 | } else {
|
---|
| 181 | myTurn = RANDOM.nextInt(3); // Generate random value among {0,
|
---|
| 182 | // 1, 2}
|
---|
| 183 | calculateCurrentThreshold(currentStatus);
|
---|
| 184 |
|
---|
| 185 | if (currentStatus > negotiationLimit * 0.99 && utilitySpace
|
---|
| 186 | .getUtility(bestReceivedBid) >= currentThreshold)
|
---|
| 187 | return bestReceivedBid;
|
---|
| 188 | else
|
---|
| 189 | return getNiceBid(currentStatus);
|
---|
| 190 | }
|
---|
| 191 | } catch (Exception e) {
|
---|
| 192 | System.err.println("An exception thrown while generating bid..");
|
---|
| 193 | }
|
---|
| 194 |
|
---|
| 195 | return generateRandomBid(); // This line will never be executed!!
|
---|
| 196 | }
|
---|
| 197 |
|
---|
| 198 | /* Current status is the time/number of rounds passed */
|
---|
| 199 | private double getCurrentStatus() {
|
---|
| 200 | /* If the negotiation is time limited, use time as current status */
|
---|
| 201 | if (negotiationType.equals("TIME"))
|
---|
| 202 | return timeline.getTime() * timeline.getTotalTime();
|
---|
| 203 |
|
---|
| 204 | /*
|
---|
| 205 | * If the negotiation is round limited, use number of rounds as current
|
---|
| 206 | * status
|
---|
| 207 | */
|
---|
| 208 | return numberOfRoundsPassed;
|
---|
| 209 | }
|
---|
| 210 |
|
---|
| 211 | /*
|
---|
| 212 | * At first 90% of negotiation, agent generates a random bid to fake his
|
---|
| 213 | * opponent with certain frequency
|
---|
| 214 | */
|
---|
| 215 | private Bid getFakeBid() {
|
---|
| 216 | for (int trial = 1; trial <= MAXIMUM_NUMBER_OF_TRIALS; trial++) {
|
---|
| 217 | Bid bid = generateRandomBid();
|
---|
| 218 |
|
---|
| 219 | /*
|
---|
| 220 | * The utility of the bid should be greater than 80% of the
|
---|
| 221 | * threshold
|
---|
| 222 | */
|
---|
| 223 | if (utilitySpace.getUtility(bid) >= threshold * 0.8)
|
---|
| 224 | return bid;
|
---|
| 225 | }
|
---|
| 226 |
|
---|
| 227 | return generateRandomBid();
|
---|
| 228 | }
|
---|
| 229 |
|
---|
| 230 | private void calculateCurrentThreshold(double currentStatus) {
|
---|
| 231 | /* Threshold value is updated according to the agent's boulware level */
|
---|
| 232 | threshold = opponentModels[myTurn].getNewThreshold();
|
---|
| 233 |
|
---|
| 234 | if (numberOfRoundsPassed % 10 == 0) {
|
---|
| 235 | currentThreshold = threshold;
|
---|
| 236 | } else if (currentStatus > timeToGetAlmostMad) {
|
---|
| 237 | currentThreshold = threshold * 0.95;
|
---|
| 238 |
|
---|
| 239 | if (currentStatus > timeToGetMad)
|
---|
| 240 | currentThreshold = threshold * 0.9;
|
---|
| 241 | }
|
---|
| 242 | }
|
---|
| 243 |
|
---|
| 244 | /* Get a nice bid using Opponent Model */
|
---|
| 245 | private Bid getNiceBid(double currentStatus) throws Exception {
|
---|
| 246 | Bid bid = generateRandomBid();
|
---|
| 247 |
|
---|
| 248 | /*
|
---|
| 249 | * Shift Bids is for shifting the index if the bid at the current index
|
---|
| 250 | * is not accepted by opponent
|
---|
| 251 | */
|
---|
| 252 | if (currentStatus > timeToGetAlmostMad) {
|
---|
| 253 | getBidsPreferredByOpponent();
|
---|
| 254 | bid = (bidsPreferredByOpponents.get(myTurn) != null)
|
---|
| 255 | ? bidsPreferredByOpponents.get(myTurn)
|
---|
| 256 | .get(shiftBids[myTurn]++ % bidsPreferredByOpponents
|
---|
| 257 | .get(myTurn).size())
|
---|
| 258 | : utilitySpace.getMaxUtilityBid();
|
---|
| 259 |
|
---|
| 260 | if (utilitySpace.getUtility(bid) >= currentThreshold)
|
---|
| 261 | return bid;
|
---|
| 262 | else
|
---|
| 263 | shiftBids[myTurn] = 0;
|
---|
| 264 | }
|
---|
| 265 |
|
---|
| 266 | for (int trial = 1; trial <= MAXIMUM_NUMBER_OF_TRIALS; trial++) {
|
---|
| 267 | bid = generateRandomBid();
|
---|
| 268 |
|
---|
| 269 | if (utilitySpace.getUtility(bid) >= currentThreshold)
|
---|
| 270 | return bid;
|
---|
| 271 | }
|
---|
| 272 |
|
---|
| 273 | /*
|
---|
| 274 | * If it cannot generate a random bid higher than current threshold in
|
---|
| 275 | * the maximum number of trials, it returns max utility bid
|
---|
| 276 | */
|
---|
| 277 | return utilitySpace.getMaxUtilityBid();
|
---|
| 278 | }
|
---|
| 279 |
|
---|
| 280 | private void getBidsPreferredByOpponent() throws Exception {
|
---|
| 281 | opponentModels[myTurn].computeMostPreferredBid();
|
---|
| 282 |
|
---|
| 283 | bidsPreferredByOpponents.set(myTurn, null);
|
---|
| 284 | bidsPreferredByOpponents.set(myTurn,
|
---|
| 285 | opponentModels[myTurn].getAcceptableBids());
|
---|
| 286 |
|
---|
| 287 | sortBids(bidsPreferredByOpponents.get(myTurn));
|
---|
| 288 |
|
---|
| 289 | /* If there is no element in the list, just add one */
|
---|
| 290 | if (bidsPreferredByOpponents.get(myTurn).size() == 0)
|
---|
| 291 | bidsPreferredByOpponents.get(myTurn)
|
---|
| 292 | .add(utilitySpace.getMaxUtilityBid());
|
---|
| 293 | }
|
---|
| 294 |
|
---|
| 295 | private void sortBids(List<Bid> bids) {
|
---|
| 296 | bids.sort(new Comparator<Bid>() {
|
---|
| 297 | @Override
|
---|
| 298 | public int compare(Bid a, Bid b) {
|
---|
| 299 | if (utilitySpace.getUtility(a) < utilitySpace.getUtility(b))
|
---|
| 300 | return 1;
|
---|
| 301 | if (utilitySpace.getUtility(a) > utilitySpace.getUtility(b))
|
---|
| 302 | return -1;
|
---|
| 303 |
|
---|
| 304 | return 0;
|
---|
| 305 | }
|
---|
| 306 | });
|
---|
| 307 | }
|
---|
| 308 |
|
---|
| 309 | @Override
|
---|
| 310 | public String getDescription() {
|
---|
| 311 | return "ANAC2017";
|
---|
| 312 | }
|
---|
| 313 |
|
---|
| 314 | @Override
|
---|
| 315 | public HashMap<String, String> negotiationEnded(Bid acceptedBid) {
|
---|
| 316 | System.out.println("Negotiation has ended..");
|
---|
| 317 | return null;
|
---|
| 318 | }
|
---|
| 319 | }
|
---|