source: CSE3210/agent7/agent7.py

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

#6 Added CSE3210 parties

File size: 12.9 KB
Line 
1import decimal
2import logging
3import time
4import operator
5from random import randint
6from typing import cast, Set
7from bisect import bisect_left
8
9from geniusweb.actions.Accept import Accept
10from geniusweb.actions.Action import Action
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.party.Capabilities import Capabilities
24from geniusweb.party.DefaultParty import DefaultParty
25from geniusweb.profile.FullOrdering import FullOrdering
26from geniusweb.profile.PartialOrdering import PartialOrdering
27from geniusweb.profile.utilityspace.DiscreteValueSetUtilities import DiscreteValueSetUtilities
28from geniusweb.profile.utilityspace.LinearAdditive import LinearAdditive
29from geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace import LinearAdditiveUtilitySpace
30from geniusweb.profile.utilityspace.NumberValueSetUtilities import NumberValueSetUtilities
31from geniusweb.profile.utilityspace.UtilitySpace import UtilitySpace
32from geniusweb.profile.utilityspace.ValueSetUtilities import ValueSetUtilities
33from geniusweb.profileconnection.ProfileConnectionFactory import (
34 ProfileConnectionFactory,
35)
36from geniusweb.progress.ProgressRounds import ProgressRounds
37from geniusweb.references.ProfileRef import ProfileRef
38from tudelft_utilities_logging.Reporter import Reporter
39
40
41class Agent7(DefaultParty):
42 """
43 Template agent that offers random bids until a bid with sufficient utility is offered.
44 """
45
46 listOfUtil = [];
47 utilBidMap = dict();
48
49 def __init__(self, reporter: Reporter = None):
50 super().__init__(reporter)
51 self.getReporter().log(logging.INFO, "party is initialized")
52 self._profile: ProfileRef = None
53 self._last_received_bid: Bid = None
54 self._last_offer: Bid = None
55
56 self._all_offers: list = []
57 self._progress_offset = 0
58
59 self._is_trading = False
60 self._trade_offers = []
61 self._trade_offer_index = 0
62
63 def notifyChange(self, info: Inform):
64 """This is the entry point of all interaction with your agent after is has been initialised.
65
66 Args:
67 info (Inform): Contains either a request for action or information.
68 """
69
70 # a Settings message is the first message that will be send to your
71 # agent containing all the information about the negotiation session.
72 if isinstance(info, Settings):
73 self._settings: Settings = cast(Settings, info)
74 self._me = self._settings.getID()
75
76 # progress towards the deadline has to be tracked manually through the use of the Progress object
77 self._progress = self._settings.getProgress()
78
79 # the profile contains the preferences of the agent over the domain
80 self._profile = ProfileConnectionFactory.create(
81 info.getProfile().getURI(), self.getReporter()
82 )
83 ####Very important line to set up the list of possible values####
84 ####Takes a lot of time####
85 self._createLists()
86
87 # ActionDone is an action send by an opponent (an offer or an accept)
88 elif isinstance(info, ActionDone):
89 action: Action = cast(ActionDone, info).getAction()
90
91 # if it is an offer, set the last received bid
92 if isinstance(action, Offer):
93 self._last_received_bid = cast(Offer, action).getBid()
94 # YourTurn notifies you that it is your turn to act
95 elif isinstance(info, YourTurn):
96 action = self._myTurn()
97 if isinstance(self._progress, ProgressRounds):
98 self._progress = self._progress.advance()
99 self.getConnection().send(action)
100
101 # Finished will be send if the negotiation has ended (through agreement or deadline)
102 elif isinstance(info, Finished):
103 # terminate the agent MUST BE CALLED
104 self.terminate()
105 else:
106 self.getReporter().log(
107 logging.WARNING, "Ignoring unknown info " + str(info)
108 )
109
110 # lets the geniusweb system know what settings this agent can handle
111 # leave it as it is for this competition
112 def getCapabilities(self) -> Capabilities:
113 return Capabilities(
114 set(["SAOP"]),
115 set(["geniusweb.profile.utilityspace.LinearAdditive"]),
116 )
117
118 # terminates the agent and its connections
119 # leave it as it is for this competition
120 def terminate(self):
121 self.getReporter().log(logging.INFO, "party is terminating:")
122 super().terminate()
123 if self._profile is not None:
124 self._profile.close()
125 self._profile = None
126
127
128
129 # give a description of your agent
130 def getDescription(self) -> str:
131 return "Agent7"
132
133 # execute a turn
134 def _myTurn(self):
135
136 # check if the last received offer if the opponent is good enough
137 if self._isGood(self._last_received_bid):
138 # if so, accept the offer
139 action = Accept(self._me, self._last_received_bid)
140 else:
141 # if not, find a bid to propose as counter offer
142 bid = self._findBid()
143 action = Offer(self._me, bid)
144
145 # send the action
146 return action
147
148 # method that checks if we would agree with an offer
149 def _isGood(self, bid: Bid) -> bool:
150 if bid is None:
151 return False
152 profile = self._profile.getProfile()
153
154 progress = self._progress.get(time.time() * 1000)
155
156 accpet_value = [0.9, 0.8, 0.7, 0.9, 0.5]
157 next_step = [0, 0.2, 0.4, 0.9, 0.95, 1]
158 optimal_util =0
159 for i, x in enumerate(next_step):
160 if progress <= x and x != 0:
161 optimal_util = accpet_value[i-1]
162 break
163
164
165
166
167 # # very basic approach that accepts if the offer is valued above 0.6 and
168 # # 80% of the rounds towards the deadline have passed
169 # return profile.getUtility(bid) > 0.6 and progress > 0.8
170
171 return profile.getUtility(bid) >= optimal_util #profile.getUtility(lowest_acceptable)
172
173 def _findBid(self) -> Bid:
174 # Decide whether we are trading off or continuing with our strategy
175
176 # If no last offer and not trading, go on with the strategy
177 bid = None
178 self._progress_offset = 0
179
180 while bid is None or bid in self._all_offers and self._progress_offset < 0.05:
181 if self._last_offer == None or self._is_trading == False:
182 bid = self._findStategyBid()
183 else:
184 bid = self._findTradeOff()
185
186 self._progress_offset += 0.001
187 #print(bid)
188
189
190 self._all_offers.append(bid)
191 if bid == None:
192 print("Returning bid: " + str(bid))
193
194 return bid
195
196 # Duyemo's strategy
197 def _findStategyBid(self):
198 self._issue_index = 0
199 self._value_index = 0
200
201 upper_lower = [[1, 0.9], [1, 0.8], [1, 0.7], [1, 0.6], [0.9, 0.5], [0.8, 0.5], [1, 0.8], [1, 0.5]]
202 next_step = [0, 0.05, 0.15, 0.3, 0.5, 0.7, 0.85, 0.95, 1]
203 #upper_lower = [[1,0.8],[1,0.6],[1,0.7]]
204 #next_step = [0, 0.5,1]
205
206 progress = self._progress.get(time.time() * 1000)
207
208 for i, x in enumerate(next_step):
209 if progress <= x and x != 0:
210 step_size = x - next_step[i - 1]
211 step_progress = progress - next_step[i - 1]
212 upper_lower_factor = step_progress / step_size
213 optimal_util = (1 - upper_lower_factor) * upper_lower[i - 1][0] + (upper_lower_factor) * \
214 upper_lower[i - 1][1] + self._progress_offset
215
216 # print("####################")
217 # print(step_size)
218 # print(step_progress)
219 # print(upper_lower_factor)
220 # print(optimal_util)
221 # print("####################")
222 closest = self._take_closest(self.listOfUtil, decimal.Decimal(optimal_util))
223 bid = self.utilBidMap[closest]
224
225 self._last_offer = bid
226 self._is_trading = True
227 self._trade_offers = []
228
229 return bid
230
231 def _createIssueMap(self):
232 lau: LinearAdditive = cast(LinearAdditive, self._profile.getProfile())
233
234 issues = self._profile.getProfile().getDomain().getIssues();
235
236 for issue in issues:
237 self._issue_by_weight[issue] = lau.getWeight(issue)
238
239 self._issue_by_weight = sorted(self._issue_by_weight.items(), key=operator.itemgetter(1))
240
241 print(self._issue_by_weight)
242
243 def _findTradeOff(self):
244 if len(self._trade_offers) == 0:
245 self._trade_offer_index = 0
246 # Find all trade offers possible with same utility
247 current_bid = self._last_offer.getIssueValues()
248
249 lau: LinearAdditive = cast(LinearAdditive, self._profile.getProfile())
250
251 issues = self._profile.getProfile().getDomain().getIssues();
252 utils = lau.getUtilities()
253
254 for issue in issues:
255 weight = lau.getWeight(issue)
256 values = self._profile.getProfile().getDomain().getIssuesValues()[issue]
257 valueSet: ValueSetUtilities = utils.get(issue)
258
259 for value in values:
260 newUtil = valueSet.getUtility(value)
261 if current_bid.get(issue) != value and \
262 abs(newUtil - valueSet.getUtility(current_bid.get(issue))) * weight < 0.05:
263 # New bid has almost same util
264 newBid = self._last_offer.getIssueValues()
265
266 newBid[issue] = value
267
268 self._trade_offers.append(Bid(newBid))
269 if self._trade_offer_index < len(self._trade_offers):
270 bid = self._trade_offers[self._trade_offer_index];
271
272 self._trade_offer_index += 1
273 return bid
274
275 self._is_trading = False
276 return self._findStategyBid()
277 """
278 # Get all weight to calculate util change
279
280
281
282 # Find a bid with same util, but different offers.
283
284 current_bid = self._last_offer.getIssueValues()
285
286 issues: Set[str] = self._last_offer.getIssues()
287 values: Set[str] = self._profile.getProfile().getDomain().getIssuesValues()
288
289 # Check if cast is valid, should always be
290 # if isinstance(self._profile.getProfile(), LinearAdditiveUtilitySpace):
291 # print("Tewst")
292
293 lau: LinearAdditive = cast(LinearAdditive, self._profile.getProfile())
294
295 current_issue = self._issue_by_weight[self._issue_index]
296
297 utils = lau.getUtilities()
298 # Get value set of the lowest weight issue
299 valueSet: ValueSetUtilities = utils.get(current_issue[0])
300
301 values = self._profile.getProfile().getDomain().getIssuesValues()[current_issue[0]]
302 sorted_values = {}
303
304 for value in values:
305 sorted_values[value] = valueSet.getUtility(value)
306
307 sorted_values = sorted(sorted_values.items(), key=operator.itemgetter(1), reverse=True)
308
309 current_bid[current_issue[0]] = list(sorted_values)[self._value_index][0];
310
311 self._value_index += 1
312
313 if self._value_index >= len(sorted_values):
314 self._value_index = 0
315 self._issue_index += 1
316
317 print(current_bid)
318
319 return Bid(current_bid)"""
320
321 def _createLists(self):
322 profile = self._profile.getProfile()
323 domain = self._profile.getProfile().getDomain()
324 all_bids = AllBidsList(domain)
325
326 for bid in all_bids:
327 self.utilBidMap[profile.getUtility(bid)] = bid
328 self.listOfUtil = sorted(self.utilBidMap)
329
330 clostest = self._take_closest(self.listOfUtil, decimal.Decimal(0.7))
331
332 def _take_closest(self, myList, myNumber) -> decimal.Decimal:
333 """
334 Assumes myList is sorted. Returns closest value to myNumber.
335
336 If two numbers are equally close, return the smallest number.
337 """
338 pos = bisect_left(myList, myNumber)
339 if pos == 0:
340 return myList[0]
341 if pos == len(myList):
342 return myList[-1]
343 before = myList[pos - 1]
344 after = myList[pos]
345 if after - myNumber < myNumber - before:
346 return after
347 else:
348 return before
Note: See TracBrowser for help on using the repository browser.