1 | package agents.anac.y2017.madagent;
|
---|
2 |
|
---|
3 | import java.util.List;
|
---|
4 |
|
---|
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 "
|
---|
66 | + getUtilitySpace().getDiscountFactor());
|
---|
67 | System.out.println("Reservation Value is "
|
---|
68 | + getUtilitySpace().getReservationValueUndiscounted());
|
---|
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 | }
|
---|