source: CSE3210/agent19/agent19.py

Last change on this file was 74, checked in by wouter, 21 months ago

#6 Added CSE3210 parties

File size: 20.0 KB
Line 
1import logging
2import time
3from random import randint
4from typing import cast
5import random
6import functools
7
8from geniusweb.actions.Accept import Accept
9from geniusweb.actions.Action import Action
10from geniusweb.actions.ActionWithBid import ActionWithBid
11from geniusweb.actions.Offer import Offer
12from geniusweb.actions.PartyId import PartyId
13from geniusweb.bidspace.AllBidsList import AllBidsList
14from geniusweb.inform.ActionDone import ActionDone
15from geniusweb.inform.Finished import Finished
16from geniusweb.inform.Inform import Inform
17from geniusweb.inform.Settings import Settings
18from geniusweb.inform.YourTurn import YourTurn
19from geniusweb.issuevalue.Bid import Bid
20from geniusweb.issuevalue.Domain import Domain
21from geniusweb.issuevalue.Value import Value
22from geniusweb.issuevalue.ValueSet import ValueSet
23from geniusweb.opponentmodel.FrequencyOpponentModel import FrequencyOpponentModel
24from geniusweb.party.Capabilities import Capabilities
25from geniusweb.party.DefaultParty import DefaultParty
26from geniusweb.profile.utilityspace.UtilitySpace import UtilitySpace
27from geniusweb.profileconnection.ProfileConnectionFactory import (
28 ProfileConnectionFactory,
29)
30from geniusweb.progress.ProgressRounds import ProgressRounds
31from tudelft_utilities_logging.Reporter import Reporter
32import numpy as np
33
34
35class Acceptinator:
36 def __init__(self, bid_window, acceptance_threshold, trajectory_threshold):
37 self.my_bids_utility = []
38 self.my_bid_window_average = []
39 self.other_bids_utility = []
40 self.other_bid_window_average = []
41 self.min_bid = 1
42 self.max_bid = 0
43 self.bid_window = bid_window
44 self.acceptance_threshold = acceptance_threshold
45 self.bid_trajectory = ""
46 self.trajectory_threshold = trajectory_threshold
47 self.currentBid = None
48
49 def process_bid_utility(self, my_utility, other_agent_utility):
50 self.my_bids_utility.append(float(my_utility))
51 self.other_bids_utility.append(float(other_agent_utility))
52 self.min_bid = min(self.min_bid, other_agent_utility)
53 self.max_bid = max(self.max_bid, other_agent_utility)
54
55 def get_current_window_average(self):
56 """get the current window average of two agents based on the data stored by the process_bid utility"""
57 if len(self.my_bids_utility) - self.bid_window > 1:
58 my_window_utility_bid_mean = np.mean(self.my_bids_utility[len(self.my_bids_utility) - self.bid_window:])
59 other_window_utility_bid_mean = np.mean(
60 self.other_bids_utility[len(self.my_bids_utility) - self.bid_window:])
61 self.my_bid_window_average.append(my_window_utility_bid_mean)
62 self.other_bid_window_average.append(other_window_utility_bid_mean)
63
64 return (my_window_utility_bid_mean, other_window_utility_bid_mean)
65 return None
66
67 def get_current_window_average_trend(self):
68 """get the current window average trend of two agents based on the data stored by the process_bid utility"""
69 if len(self.my_bids_utility) - self.bid_window > 1:
70 curr_window_bid = np.mean(self.my_bids_utility[len(self.my_bids_utility) - self.bid_window:])
71 curr_window_utility = np.mean(self.other_bids_utility[len(self.my_bids_utility) - self.bid_window:])
72 self.my_bid_window_average.append(curr_window_bid)
73 self.other_bid_window_average.append(curr_window_utility)
74 # try to establish what is the trajectory of the other agent
75 if len(self.my_bid_window_average) >= 1:
76 if self.my_bid_window_average[len(self.my_bid_window_average) - 2] < self.my_bid_window_average[
77 len(self.my_bid_window_average) - 1]:
78 if self.my_bid_window_average[
79 len(self.my_bid_window_average) - 1] + self.trajectory_threshold >= \
80 self.my_bid_window_average[len(self.my_bid_window_average) - 1]:
81 pass
82 else:
83 self.bid_trajectory = "DOWNTREND"
84 else:
85 # check if the bid utility given by the other agent represents an uptrend
86 if self.my_bid_window_average[
87 len(self.my_bid_window_average) - 1] - self.trajectory_threshold >= \
88 self.my_bid_window_average[len(self.my_bid_window_average) - 1]:
89 pass
90 else:
91 self.bid_trajectory = "UPTREND"
92
93 return (curr_window_bid, curr_window_utility)
94 return None
95
96 def is_bid_good_enough_utility(self, utility, progress):
97 if progress < 0.6:
98 return False
99
100 return utility <= self.other_bid_window_average[len(self.other_bid_window_average) - 1]
101
102 def is_bid_good_enough(self, my_utility, other_utility, progress):
103 """check if received bid is good enough with respect to our agent's
104 average utility over a time window and time (progress)"""
105 if progress > 0.35 and other_utility >= my_utility:
106 return True
107 elif progress < 0.25:
108 return False
109 elif len(self.my_bid_window_average) >= 1 and progress < 0.7 and other_utility >= self.my_bid_window_average[
110 len(self.my_bid_window_average) - 1]:
111 return True
112
113 def is_bid_good_enough_trend(self, progress):
114 """check if the bid is good enough with respect to time, the other agent utility, and
115 our average utility at the end of each time window"""
116 if self.bid_trajectory == "DOWNTREND" and progress < 0.6:
117 return False
118
119 return self.get_current_window_average()[1] / self.get_current_window_average()[0] > self.acceptance_threshold
120
121
122class Agent19(DefaultParty):
123 """
124 Group19_NegotiationAssignment_Agent first sorts the bids according to their utility
125 and offer random bids with a high utily for him in the begining and do frequency oponent modeling
126 after 20% of the rounds he only offers bids that have a good utility for him and its oponent
127 """
128
129 def __init__(self, reporter: Reporter = None):
130 super().__init__(reporter)
131 self.getReporter().log(logging.INFO, "party is initialized")
132 self._profile = None
133 self._last_received_bid: Bid = None
134 self._sortedList = None
135 self._opponent = None
136 self._opponentAction = None
137 self.counter = 0
138
139 self.acceptinator = Acceptinator(20, 1.7, 0.05)
140
141 self.bestBid = None
142 self.bestBidsFirst = None
143 self.bestBidsSecond = None
144 self.bestBidsThird = None
145 self.bestBidsFourth = None
146 self.bestBidsFifth = None
147
148 def notifyChange(self, info: Inform):
149 """This is the entry point of all interaction with your agent after is has been initialised.
150
151 Args:
152 info (Inform): Contains either a request for action or information.
153 """
154
155 # a Settings message is the first message that will be send to your
156 # agent containing all the information about the negotiation session.
157 if isinstance(info, Settings):
158 self._settings: Settings = cast(Settings, info)
159 self._me = self._settings.getID()
160
161 # progress towards the deadline has to be tracked manually through the use of the Progress object
162 self._progress: ProgressRounds = self._settings.getProgress()
163
164 # the profile contains the preferences of the agent over the domain
165 self._profile = ProfileConnectionFactory.create(
166 info.getProfile().getURI(), self.getReporter()
167 )
168 opponent = FrequencyOpponentModel.create()
169 opponent = opponent.With(self._profile.getProfile().getDomain(), None)
170 self._opponent = opponent
171 # ActionDone is an action send by an opponent (an offer or an accept)
172 elif isinstance(info, ActionDone):
173 action: Action = cast(ActionDone, info).getAction()
174
175 # if it is an offer, set the last received bid
176 if isinstance(action, Offer):
177 self._opponentAction = action
178 self._last_received_bid = cast(Offer, action).getBid()
179 # YourTurn notifies you that it is your turn to act
180 elif isinstance(info, YourTurn):
181 action = self._myTurn()
182 if isinstance(self._progress, ProgressRounds):
183 self._progress = self._progress.advance()
184 self.getConnection().send(action)
185
186 # Finished will be send if the negotiation has ended (through agreement or deadline)
187 elif isinstance(info, Finished):
188 # terminate the agent MUST BE CALLED
189 self.terminate()
190 else:
191 self.getReporter().log(
192 logging.WARNING, "Ignoring unknown info " + str(info)
193 )
194
195 # lets the geniusweb system know what settings this agent can handle
196 # leave it as it is for this competition
197 def getCapabilities(self) -> Capabilities:
198 return Capabilities(
199 set(["SAOP"]),
200 set(["geniusweb.profile.utilityspace.LinearAdditive"]),
201 )
202
203 # terminates the agent and its connections
204 # leave it as it is for this competition
205 def terminate(self):
206 self.getReporter().log(logging.INFO, "party is terminating:")
207 super().terminate()
208 if self._profile is not None:
209 self._profile.close()
210 self._profile = None
211
212
213
214 # give a description of your agent
215 def getDescription(self) -> str:
216 return "Agent19"
217
218 # execute a turn
219 def _myTurn(self):
220 # check if the last received offer if the opponent is good enough
221 self._opponent = self._opponent.WithAction(self._opponentAction, self._progress)
222 if self._isGood(self._last_received_bid):
223 # if so, accept the offer
224 action = Accept(self._me, self._last_received_bid)
225
226 else:
227 # if not, find a bid to propose as counter offer
228 bid = self._findBid()
229
230 b = self._opponent.getUtility(self._last_received_bid)
231 self.acceptinator.currentBid = bid
232
233 # process the bid in the acceptinator
234 self.acceptinator.process_bid_utility(self._profile.getProfile().getUtility(bid), b)
235
236
237
238
239 action = Offer(self._me, bid)
240
241 # send the action
242 return action
243
244 # method that checks if we would agree with an offer
245 # method that checks if we would agree with an offer
246
247 def _isGood(self, bid: Bid) -> bool:
248 if bid is None:
249 return False
250
251 profile = self._profile.getProfile()
252
253 progress = self._progress.get(1)
254
255 if self.bestBid is None or profile.getUtility(bid) > profile.getUtility(self.bestBid):
256 self.bestBid = bid
257
258 ##Update acceptinator
259 if self.acceptinator.currentBid is not None and self.acceptinator.is_bid_good_enough(profile.getUtility(self.acceptinator.currentBid),
260 profile.getUtility(bid), progress):
261 return True
262
263 # very basic approach that accepts if the offer is valued above 0.6 and
264 # 80% of the rounds towards the deadline have passed
265 return profile.getUtility(bid) > 0.6 + 0.4 * (1 - progress)
266
267 def _findBid(self) -> Bid:
268 # compose a list of all possible bids
269
270 domain = self._profile.getProfile().getDomain()
271 profile = self._profile.getProfile()
272 progress = self._progress.get(1)
273
274 if (self._sortedList == None):
275 """Sort the list. Highest utility first"""
276 all_bids = AllBidsList(domain)
277 self._sortedList = sorted(all_bids, key=functools.cmp_to_key(
278 lambda bid1, bid2: profile.getUtility(bid2) - profile.getUtility(bid1)))
279
280 if (progress <= 0.2):
281 bestBids = [x for x in self._sortedList if (profile.getUtility(x) >= float(
282 profile.getUtility(self._sortedList[0])) * 0.95 and profile.getUtility(x) <= profile.getUtility(
283 self._sortedList[0]))]
284 bid = random.choice(bestBids)
285
286 elif (progress > 0.2 and progress <= 0.3):
287
288 bestBids = [x for x in self._sortedList if (profile.getUtility(x) >= float(
289 profile.getUtility(self._sortedList[0])) * 0.9 and profile.getUtility(x) <= profile.getUtility(
290 self._sortedList[0]))]
291 bid = random.choice(bestBids)
292
293 elif (progress > 0.3 and progress <= 0.4):
294 bestBids = [x for x in self._sortedList if (profile.getUtility(x) >= float(
295 profile.getUtility(self._sortedList[0])) * 0.85 and profile.getUtility(x) <= profile.getUtility(
296 self._sortedList[0]))]
297 bid = random.choice(bestBids)
298
299 elif (progress > 0.4 and progress <= 0.65):
300 if (self.bestBidsFirst == None):
301 bestBids = [x for x in self._sortedList if (profile.getUtility(x) >= float(
302 profile.getUtility(self._sortedList[0])) * 0.8 and profile.getUtility(x) < 0.95)]
303 self.bestBidsFirst = [x for x in bestBids if
304 (self._opponent.getUtility(x) >= 0.35 and self._opponent.getUtility(x) < 0.95)]
305 self.bestBidsFirst = sorted(self.bestBidsFirst, key=functools.cmp_to_key(
306 lambda bid1, bid2: self._opponent.getUtility(bid2) - self._opponent.getUtility(bid1)))
307 util = 0.35
308 while (len(self.bestBidsFirst) < 2):
309 self.bestBidsFirst = [x for x in bestBids if (
310 self._opponent.getUtility(x) >= util and self._opponent.getUtility(x) < 0.95)]
311 self.bestBidsFirst = sorted(self.bestBidsFirst, key=functools.cmp_to_key(
312 lambda bid1, bid2: self._opponent.getUtility(bid2) - self._opponent.getUtility(bid1)))
313 util = util - 0.01
314 length = len(self.bestBidsFirst)
315 counter = random.randint(0, length - 1)
316 bid = self.bestBidsFirst[counter]
317
318 elif (progress > 0.65 and progress <= 0.8):
319 """Sort the list. Highest utility first and also consider opponents utility"""
320 if (self.bestBidsSecond == None):
321 bestBids = [x for x in self._sortedList if (profile.getUtility(x) >= float(
322 profile.getUtility(self._sortedList[0])) * 0.75 and profile.getUtility(x) < 0.95)]
323 self.bestBidsSecond = [x for x in bestBids if
324 (self._opponent.getUtility(x) >= 0.4 and self._opponent.getUtility(x) < 0.95)]
325 self.bestBidsSecond = sorted(self.bestBidsSecond, key=functools.cmp_to_key(
326 lambda bid1, bid2: self._opponent.getUtility(bid2) - self._opponent.getUtility(bid1)))
327 util = 0.45
328 while (len(self.bestBidsSecond) < 2):
329 """ Check if there are bids in bidspace according to our util and opponents. If there is not lower the util of the opponent """
330 self.bestBidsSecond = [x for x in bestBids if (
331 self._opponent.getUtility(x) >= util and self._opponent.getUtility(x) < 0.95)]
332 self.bestBidsSecond = sorted(self.bestBidsSecond, key=functools.cmp_to_key(
333 lambda bid1, bid2: self._opponent.getUtility(bid2) - self._opponent.getUtility(bid1)))
334 util = util - 0.025
335
336 length = len(self.bestBidsSecond)
337 counter = random.randint(0, length - 1)
338 bid = self.bestBidsSecond[counter]
339 elif (progress > 0.8 and progress <= 0.95):
340 if (self.bestBidsThird == None):
341 bestBids = [x for x in self._sortedList if
342 (profile.getUtility(x) >= 0.65 and profile.getUtility(x) < 0.95)]
343 self.bestBidsThird = [x for x in bestBids if
344 (self._opponent.getUtility(x) >= 0.4 and self._opponent.getUtility(x) < 0.95)]
345 self.bestBidsThird = sorted(self.bestBidsThird, key=functools.cmp_to_key(
346 lambda bid1, bid2: self._opponent.getUtility(bid2) - self._opponent.getUtility(bid1)))
347 util = 0.5
348 while (len(self.bestBidsThird) < 2):
349 """ Check if there are bids in bidspace according to our util and opponents. If there is not lower the util of the opponent """
350 self.bestBidsThird = [x for x in bestBids if (
351 self._opponent.getUtility(x) >= util and self._opponent.getUtility(x) < 0.95)]
352 self.bestBidsThird = sorted(self.bestBidsThird, key=functools.cmp_to_key(
353 lambda bid1, bid2: self._opponent.getUtility(bid2) - self._opponent.getUtility(bid1)))
354 util = util - 0.025
355
356 length = len(self.bestBidsThird)
357 counter = random.randint(0, length - 1)
358 bid = self.bestBidsThird[counter]
359 elif (progress > 0.95 and progress <= 0.99):
360 if (self.bestBidsFourth == None):
361 bestBids = [x for x in self._sortedList if
362 (profile.getUtility(x) >= 0.60 and profile.getUtility(x) < 0.95)]
363 self.bestBidsFourth = [x for x in bestBids if
364 (self._opponent.getUtility(x) >= 0.5 and self._opponent.getUtility(x) < 0.95)]
365 self.bestBidsFourth = sorted(self.bestBidsFourth, key=functools.cmp_to_key(
366 lambda bid1, bid2: self._opponent.getUtility(bid2) - self._opponent.getUtility(bid1)))
367 util = 0.5
368 while (len(self.bestBidsFourth) < 2):
369 """ Check if there are bids in bidspace according to our util and opponents. If there is not lower the util of the opponent """
370 self.bestBidsFourth = [x for x in bestBids if (
371 self._opponent.getUtility(x) >= util and self._opponent.getUtility(x) < 0.95)]
372 self.bestBidsFourth = sorted(self.bestBidsFourth, key=functools.cmp_to_key(
373 lambda bid1, bid2: self._opponent.getUtility(bid2) - self._opponent.getUtility(bid1)))
374 util = util - 0.025
375
376 length = len(self.bestBidsFourth)
377 counter = random.randint(0, length - 1)
378 bid = self.bestBidsFourth[counter]
379
380 else:
381 if (self.bestBidsFifth == None):
382 bestBids = [x for x in self._sortedList if
383 (profile.getUtility(x) >= 0.55 and profile.getUtility(x) < 0.95)]
384 self.bestBidsFifth = [x for x in bestBids if
385 (self._opponent.getUtility(x) >= 0.5 and self._opponent.getUtility(x) < 0.95)]
386 self.bestBidsFifth = sorted(self.bestBidsFifth, key=functools.cmp_to_key(
387 lambda bid1, bid2: self._opponent.getUtility(bid2) - self._opponent.getUtility(bid1)))
388 util = 0.55
389 while (len(self.bestBidsFifth) < 2):
390 """ Check if there are bids in bidspace according to our util and opponents. If there is not lower the util of the opponent """
391 self.bestBidsFifth = [x for x in bestBids if (
392 self._opponent.getUtility(x) >= util and self._opponent.getUtility(x) < 0.95)]
393 self.bestBidsFifth = sorted(self.bestBidsFifth, key=functools.cmp_to_key(
394 lambda bid1, bid2: self._opponent.getUtility(bid2) - self._opponent.getUtility(bid1)))
395 util = util - 0.025
396 length = len(self.bestBidsFifth)
397 counter = random.randint(0, length - 1)
398 bid = self.bestBidsFifth[counter]
399 return bid
Note: See TracBrowser for help on using the repository browser.