source: anac2021/dicehaggler/src/main/java/geniusweb/exampleparties/learningagent/LearningAgent.java@ 77

Last change on this file since 77 was 44, checked in by wouter, 3 years ago

#3

File size: 11.8 KB
Line 
1package geniusweb.exampleparties.learningagent; // TODO: change name
2
3import java.io.File;
4import java.io.IOException;
5import java.math.BigInteger;
6import java.util.ArrayList;
7import java.util.Arrays;
8import java.util.Collections;
9import java.util.HashSet;
10import java.util.List;
11import java.util.Random;
12import java.util.UUID;
13import java.util.logging.Level;
14
15import com.fasterxml.jackson.databind.ObjectMapper;
16
17import geniusweb.actions.Accept;
18import geniusweb.actions.Action;
19import geniusweb.actions.FileLocation;
20import geniusweb.actions.LearningDone;
21import geniusweb.actions.Offer;
22import geniusweb.actions.PartyId;
23import geniusweb.bidspace.AllBidsList;
24import geniusweb.inform.ActionDone;
25import geniusweb.inform.Agreements;
26import geniusweb.inform.Finished;
27import geniusweb.inform.Inform;
28import geniusweb.inform.Settings;
29import geniusweb.inform.YourTurn;
30import geniusweb.issuevalue.Bid;
31import geniusweb.party.Capabilities;
32import geniusweb.party.DefaultParty;
33import geniusweb.profile.Profile;
34import geniusweb.profile.utilityspace.UtilitySpace;
35import geniusweb.profileconnection.ProfileConnectionFactory;
36import geniusweb.profileconnection.ProfileInterface;
37import geniusweb.progress.Progress;
38import geniusweb.progress.ProgressRounds;
39import geniusweb.references.Parameters;
40import tudelft.utilities.logging.Reporter;
41
42public class LearningAgent extends DefaultParty { // TODO: change name
43
44 private Bid lastReceivedBid = null;
45 private PartyId me;
46 private final Random random = new Random();
47 protected ProfileInterface profileint = null;
48 private Progress progress;
49 private String protocol;
50 private Parameters parameters;
51 private UtilitySpace utilitySpace;
52 private PersistentState persistentState;
53 private NegotiationData negotiationData;
54 private List<File> dataPaths;
55 private File persistentPath;
56 private String opponentName;
57
58 public LearningAgent() { // TODO: change name
59 }
60
61 public LearningAgent(Reporter reporter) { // TODO: change name
62 super(reporter); // for debugging
63 }
64
65 /**
66 * This method mostly contains utility functionallity for the agent to function
67 * properly. The code that is of most interest for the ANL competition is
68 * further below and in the other java files in this directory. It does,
69 * however, not hurt to read through this code to have a better understanding of
70 * what is going on.
71 *
72 * @param info information object for agent
73 */
74 @Override
75 public void notifyChange(Inform info) {
76 try {
77 if (info instanceof Settings) {
78 // info is a Settings object that is passed at the start of a negotiation
79 Settings settings = (Settings) info;
80
81 // ID of my agent
82 this.me = settings.getID();
83
84 // The progress object keeps track of the deadline
85 this.progress = settings.getProgress();
86
87 // Protocol that is initiate for the agent
88 this.protocol = settings.getProtocol().getURI().getPath();
89
90 // Parameters for the agent (can be passed through the GeniusWeb GUI, or a
91 // JSON-file)
92 this.parameters = settings.getParameters();
93
94 // The PersistentState is loaded here (see 'PersistenData,java')
95 if (this.parameters.containsKey("persistentstate"))
96 this.persistentPath = new FileLocation(
97 UUID.fromString((String) this.parameters.get("persistentstate"))).getFile();
98 if (this.persistentPath != null && this.persistentPath.exists()) {
99 ObjectMapper objectMapper = new ObjectMapper();
100 this.persistentState = objectMapper.readValue(this.persistentPath, PersistentState.class);
101 } else {
102 this.persistentState = new PersistentState();
103 }
104
105 // The negotiation data paths are converted here from List<String> to List<File>
106 // for improved usage. For safety reasons, this is more comprehensive than
107 // normally.
108 if (this.parameters.containsKey("negotiationdata")) {
109 List<String> dataPaths_raw = (List<String>) this.parameters.get("negotiationdata");
110 this.dataPaths = new ArrayList<>();
111 for (String path : dataPaths_raw)
112 this.dataPaths.add(new FileLocation(UUID.fromString(path)).getFile());
113 }
114 if ("Learn".equals(protocol)) {
115 // We are in the learning step: We execute the learning and notify when we are
116 // done. REMEMBER that there is a deadline of 60 seconds for this step.
117 learn();
118 getConnection().send(new LearningDone(me));
119 } else {
120 // We are in the negotiation step.
121
122 // Create a new NegotiationData object to store information on this negotiation.
123 // See 'NegotiationData.java'.
124 this.negotiationData = new NegotiationData();
125
126 // Obtain our utility space, i.e. the problem we are negotiating and our
127 // preferences over it.
128 try {
129 this.profileint = ProfileConnectionFactory.create(settings.getProfile().getURI(),
130 getReporter());
131 this.utilitySpace = ((UtilitySpace) profileint.getProfile());
132 } catch (IOException e) {
133 throw new IllegalStateException(e);
134 }
135 }
136 } else if (info instanceof ActionDone) {
137 // The info object is an action that is performed by an agent.
138 Action action = ((ActionDone) info).getAction();
139
140 // Check if this is not our own action
141 if (!this.me.equals(action.getActor())) {
142 // Check if we already know who we are playing against.
143 if (this.opponentName == null) {
144 // The part behind the last _ is always changing, so we must cut it off.
145 String fullOpponentName = action.getActor().getName();
146 int index = fullOpponentName.lastIndexOf("_");
147 this.opponentName = fullOpponentName.substring(0, index);
148
149 // Add name of the opponent to the negotiation data
150 this.negotiationData.setOpponentName(this.opponentName);
151 }
152 // Process the action of the opponent.
153 processAction(action);
154 }
155 } else if (info instanceof YourTurn) {
156 // Advance the round number if a round-based deadline is set.
157 if (progress instanceof ProgressRounds) {
158 progress = ((ProgressRounds) progress).advance();
159 }
160
161 // The info notifies us that it is our turn
162 myTurn();
163 } else if (info instanceof Finished) {
164 // The info is a notification that th negotiation has ended. This Finished
165 // object also contains the final agreement (if any).
166 Agreements agreements = ((Finished) info).getAgreements();
167 processAgreements(agreements);
168
169 // Write the negotiation data that we collected to the path provided.
170 if (this.dataPaths != null && this.negotiationData != null) {
171 try {
172 ObjectMapper objectMapper = new ObjectMapper();
173 objectMapper.writerWithDefaultPrettyPrinter().writeValue(this.dataPaths.get(0),
174 this.negotiationData);
175 } catch (IOException e) {
176 throw new RuntimeException("Failed to write negotiation data to disk", e);
177 }
178 }
179
180 // Log the final outcome and terminate
181 getReporter().log(Level.INFO, "Final outcome:" + info);
182 terminate();
183 }
184 } catch (Exception e) {
185 throw new RuntimeException("Failed to handle info", e);
186 }
187 }
188
189 /** Let GeniusWeb know what protocols that agent is capable of handling */
190 @Override
191 public Capabilities getCapabilities() {
192 return new Capabilities(new HashSet<>(Arrays.asList("SAOP", "Learn")), Collections.singleton(Profile.class));
193 }
194
195 /** Terminate agent */
196 @Override
197 public void terminate() {
198 super.terminate();
199 if (this.profileint != null) {
200 this.profileint.close();
201 this.profileint = null;
202 }
203 }
204
205 /*
206 * *****************************NOTE:************************************
207 * Everything below this comment is most relevant for the ANL competition.
208 * **********************************************************************
209 */
210
211 /** Provide a description of the agent */
212 @Override
213 public String getDescription() {
214 return "This is the example party of ANL 2021. It can handle the Learn protocol and learns simple characteristics of the opponent.";
215 }
216
217 /**
218 * Processes an Action performed by the opponent.
219 *
220 * @param action
221 */
222 private void processAction(Action action) {
223 if (action instanceof Offer) {
224 // If the action was an offer: Obtain the bid and add it's value to our
225 // negotiation data.
226 this.lastReceivedBid = ((Offer) action).getBid();
227 this.negotiationData.addBidUtil(this.utilitySpace.getUtility(this.lastReceivedBid).doubleValue());
228 }
229 }
230
231 /**
232 * This method is called when the negotiation has finished. It can process the
233 * final agreement.
234 *
235 * @param agreements
236 */
237 private void processAgreements(Agreements agreements) {
238 // Check if we reached an agreement (walking away or passing the deadline
239 // results in no agreement)
240 if (!agreements.getMap().isEmpty()) {
241 // Get the bid that is agreed upon and add it's value to our negotiation data
242 Bid agreement = agreements.getMap().values().iterator().next();
243 this.negotiationData.addAgreementUtil(this.utilitySpace.getUtility(agreement).doubleValue());
244 }
245 }
246
247 /**
248 * send our next offer
249 */
250 private void myTurn() throws IOException {
251 Action action;
252 if (isGood(lastReceivedBid)) {
253 // If the last received bid is good: create Accept action
254 action = new Accept(me, lastReceivedBid);
255 } else {
256 // Obtain ist of all bids
257 AllBidsList bidspace = new AllBidsList(this.utilitySpace.getDomain());
258 Bid bid = null;
259
260 // Iterate randomly through list of bids until we find a good bid
261 for (int attempt = 0; attempt < 500 && !isGood(bid); attempt++) {
262 long i = random.nextInt(bidspace.size().intValue());
263 bid = bidspace.get(BigInteger.valueOf(i));
264 }
265
266 // Create offer action
267 action = new Offer(me, bid);
268 }
269
270 // Send action
271 getConnection().send(action);
272 }
273
274 /**
275 * The method checks if a bid is good.
276 *
277 * @param bid the bid to check
278 * @return true iff bid is good for us.
279 */
280 private boolean isGood(Bid bid) {
281 if (bid == null)
282 return false;
283
284 // Check if we already know the opponent
285 if (this.persistentState.knownOpponent(this.opponentName)) {
286 // Obtain the average of the max utility that the opponent has offered us in
287 // previous negotiations.
288 Double avgMaxUtility = this.persistentState.getAvgMaxUtility(this.opponentName);
289
290 // Request 5% more than the average max utility offered by the opponent.
291 return this.utilitySpace.getUtility(bid).doubleValue() > (avgMaxUtility * 1.05);
292 }
293
294 // Check a simple business rule
295 Boolean nearDeadline = progress.get(System.currentTimeMillis()) > 0.95;
296 Boolean acceptable = this.utilitySpace.getUtility(bid).doubleValue() > 0.7;
297 Boolean good = this.utilitySpace.getUtility(bid).doubleValue() > 0.9;
298 return (nearDeadline && acceptable) || good;
299 }
300
301 /**
302 * This method is invoked if the learning phase is started. There is now time to
303 * process previously stored data and use it to update our persistent state.
304 * This persistent state is passed to the agent again in future negotiation
305 * session. REMEMBER that there is a deadline of 60 seconds for this step.
306 */
307 private void learn() {
308 ObjectMapper objectMapper = new ObjectMapper();
309
310 // Iterate through the negotiation data file paths
311 for (File dataPath : this.dataPaths) {
312 NegotiationData negotiationData;
313 try {
314 // Load the negotiation data object of a previous negotiation
315 negotiationData = objectMapper.readValue(dataPath, NegotiationData.class);
316 } catch (IOException e) {
317 throw new RuntimeException("Negotiation data provided to learning step does not exist", e);
318 }
319
320 // Process the negotiation data in our persistent state
321 this.persistentState.update(negotiationData);
322 }
323
324 // Write the persistent state object to file
325 try {
326 objectMapper.writerWithDefaultPrettyPrinter().writeValue(this.persistentPath, this.persistentState);
327 } catch (IOException e) {
328 throw new RuntimeException("Failed to write persistent state to disk", e);
329 }
330 }
331}
Note: See TracBrowser for help on using the repository browser.