source: CSE3210/agent43/agent43.py

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

#6 Added CSE3210 parties

File size: 13.0 KB
Line 
1import logging
2import time
3from random import randint
4from typing import cast
5
6from geniusweb.actions.Accept import Accept
7from geniusweb.actions.Action import Action
8from geniusweb.actions.Offer import Offer
9from geniusweb.actions.PartyId import PartyId
10from geniusweb.inform.ActionDone import ActionDone
11from geniusweb.inform.Finished import Finished
12from geniusweb.inform.Inform import Inform
13from geniusweb.bidspace.Interval import Interval
14from geniusweb.inform.Settings import Settings
15from geniusweb.inform.YourTurn import YourTurn
16from geniusweb.issuevalue.Bid import Bid
17from geniusweb.issuevalue.Value import Value
18from geniusweb.party.Capabilities import Capabilities
19from geniusweb.party.DefaultParty import DefaultParty
20from geniusweb.profileconnection.ProfileConnectionFactory import (
21 ProfileConnectionFactory,
22)
23from geniusweb.profile.utilityspace.LinearAdditive import LinearAdditive
24from decimal import Decimal
25from decimal import Context
26from typing import Dict, Optional
27from geniusweb.progress.ProgressRounds import ProgressRounds
28from geniusweb.bidspace.BidsWithUtility import BidsWithUtility
29from .extended_util_space_group_43 import ExtendedUtilSpace
30from .frequency_opponent_model_group_43 import FrequencyOpponentModel
31from tudelft_utilities_logging.Reporter import Reporter
32
33
34
35class Agent43(DefaultParty):
36 """ Group 43's agent """
37
38 def __init__(self, reporter: Reporter = None):
39 super().__init__(reporter)
40 self.getReporter().log(logging.INFO, "party is initialized")
41 self._profile = None
42 self._me: PartyId = None
43 self._last_received_bid: Bid = None
44 self._last_received_utility = 0
45 self._highest_received_bid: Bid = None
46 self._highest_received_utility = 0
47 self._estimate_nash = 0
48 self._bids_with_util : BidsWithUtility = None
49 # self._progress: Progress = None
50 self._util_space : LinearAdditive = None
51 self._extended_space: ExtendedUtilSpace = None
52 self._frequency_opponent_model : FrequencyOpponentModel = None
53 self._tracker = []
54 # self._our_utilities = None
55 self._number_of_potential_bids = 0
56 self._conceding_parameter = 0.1
57
58 def notifyChange(self, info: Inform):
59 """ This is the entry point of all interaction with your agent after is has been initialised.
60 Args:
61 info (Inform): Contains either a request for action or information.
62 """
63 # a Settings message is the first message that will be send to your
64 # agent containing all the information about the negotiation session.
65 if isinstance(info, Settings):
66 self._initialize(info)
67
68 # ActionDone is an action send by an opponent (an offer or an accept)
69 elif isinstance(info, ActionDone):
70 self._actionDone(info)
71
72 # YourTurn notifies you that it is your turn to act
73 elif isinstance(info, YourTurn):
74 action = self._myTurn()
75 if isinstance(self._progress, ProgressRounds):
76 self._progress = self._progress.advance()
77 self.getConnection().send(action)
78
79 # Finished will be send if the negotiation has ended (through agreement or deadline)
80 elif isinstance(info, Finished):
81 # terminate the agent MUST BE CALLED
82 # print("OPPONENT MODEL ", self._frequency_opponent_model.toString())
83 self.terminate()
84 else:
85 self.getReporter().log(
86 logging.WARNING, "Ignoring unknown info " + str(info)
87 )
88
89 # lets the geniusweb system know what settings this agent can handle
90 # leave it as it is for this competition
91 def getCapabilities(self) -> Capabilities:
92 return Capabilities(
93 set(["SAOP"]),
94 set(["geniusweb.profile.utilityspace.LinearAdditive"]),
95 )
96
97 # Terminates the agent and its connections
98 # leave it as it is for this competition
99 def terminate(self):
100 self.getReporter().log(logging.INFO, "party is terminating:")
101 super().terminate()
102 if self._profile is not None:
103 self._profile.close()
104 self._profile = None
105
106
107
108 def _initialize(self, info: Settings):
109 self._settings: Settings = cast(Settings, info)
110 self._me = self._settings.getID()
111
112 # Progress towards the deadline has to be tracked manually through the use of the Progress object
113 self._progress: ProgressRounds = self._settings.getProgress()
114
115 # The profile contains the preferences of the agent over the domain
116 self._profile = ProfileConnectionFactory.create(
117 info.getProfile().getURI(), self.getReporter()
118 )
119
120 # Create bids and opponent model
121 self._bids_with_util = BidsWithUtility.create(cast(LinearAdditive, self._profile.getProfile()))
122 opponent_model : Dict[str, Dict[Value, float]] = {}
123
124 # Init issues and set values to 0.5 for all issues
125 for issue in self._profile.getProfile().getDomain().getIssues():
126 for_issue : Dict[Value, float] = {}
127 for value in self._profile.getProfile().getDomain().getValues(issue):
128 for_issue[value] = 0.5
129 opponent_model[issue] = for_issue
130
131 # Init Frequency opponent model
132 self._frequency_opponent_model = FrequencyOpponentModel(self._profile.getProfile().getDomain(), opponent_model, 0, None)
133 # self._frequency_opponent_model = FrequencyOpponentModel.create().With(self._profile.getProfile().getDomain(), None)
134
135 ### PREVIOUS IMPLEMENTATION ###
136 # Get bids and sort on utility
137 # all_bids = self._bids_with_util.getBids(Interval(Decimal(0), Decimal(1)))
138 # self._number_of_potential_bids = all_bids.size()
139 # bids_list = []
140 # for i in range(self._number_of_potential_bids - 1):
141 # bids_list.append(all_bids.get(i))
142 # bids_list.sort(key=self._profile.getProfile().getUtility)
143 # self._our_utilities = bids_list
144
145 def _actionDone(self, actionDone):
146 action: Action = cast(ActionDone, actionDone).getAction()
147 # If it is an offer, set the last received bid
148 if isinstance(action, Offer):
149 current_bid = cast(Offer, action).getBid()
150 self._last_received_bid = current_bid
151
152 # Update opponent's method
153 if not action.getActor() == self._me:
154 self._updateOpponentModel(action)
155
156 # Update highest uility received so far by an opponent's offer
157 if self._profile.getProfile().getUtility(cast(Offer, action).getBid()) > self._highest_received_utility:
158 self._highest_received_utility = self._profile.getProfile().getUtility(cast(Offer, action).getBid())
159
160 self._last_received_bid = cast(Offer, action).getBid()
161 pass
162
163
164 # Give a description of your agent
165 def getDescription(self) -> str:
166 return "Agent43"
167
168 # Execute a turn
169 def _myTurn(self):
170 self._updateUtilSpace()
171 progress_so_far = self._progress.get(time.time() * 1000)
172
173 # Compute reservation value if it exists
174 res_bid = self._profile.getProfile().getReservationBid()
175 res_value_satisfied = True
176 if res_bid is not None:
177 reservation_value = self._profile.getProfile().getUtility(self._profile.getProfile().getReservationBid())
178 res_value_satisfied = (self._profile.getProfile().getUtility(self._last_received_bid) > reservation_value)
179
180 # Check if the last received offer of the opponent is good enough, and set window
181 if progress_so_far > 0.5:
182 r = 1 - progress_so_far
183 percentage = (progress_so_far - r) / progress_so_far
184 print (percentage)
185 window = [1 * percentage, 1]
186
187 # If almost nearing end accept everything above reservation value
188 # This reservation value still needs to be added
189 if progress_so_far > 0.99 and res_value_satisfied:
190 action = Accept (self._me, self._last_received_bid)
191
192 # After 50% of progress, accept if better offer then in previous window.
193 elif progress_so_far > 0.5 and self._acceptance(window):
194 action = Accept(self._me, self._last_received_bid)
195
196 # First part, accept depending on utility
197 elif progress_so_far > 0 and self._last_received_bid and self._acceptance_time():
198 action = Accept(self._me, self._last_received_bid)
199
200 # If not, find a bid to propose as counter offer
201 else:
202 bid = self._findBid()
203 action = Offer(self._me, bid)
204
205 # If we dont receive a bid
206 if (self._last_received_bid != None):
207 self._tracker.append(self._profile.getProfile().getUtility(self._last_received_bid))
208
209 # Send the action
210 return action
211
212 # Accept offer if it satisfies both conditions
213 def _acceptance_time(self):
214 # percentage = 1 / self._progress.getTotalRounds() * (1 - self._progress.get(time.time() * 1000))
215 percentage = pow(self._progress.get(time.time() * 1000), 1 / self._conceding_parameter)
216 # bid_threshold = self._our_utilities[round((1 - percentage) * (len(self._our_utilities) - 1))]
217 # threshold = self._profile.getProfile().getUtility(bid_threshold)
218 util_received_last = self._profile.getProfile().getUtility(self._last_received_bid)
219 return util_received_last >= percentage and (util_received_last >= self._highest_received_utility)
220
221 # Construct the time window and compute acceptance condition
222 def _acceptance(self, window) -> bool:
223 length = len(self._tracker)
224 lower = round(length * window[0])
225 higher = round(length * window[1])
226 utilitiesWindow = self._tracker[lower:higher]
227 maxUtil = max(utilitiesWindow)
228 util_recieved_last = self._profile.getProfile().getUtility(self._last_received_bid)
229 self._progress.get(time.time() * 1000)
230
231 # Acceptance condition next
232 AC_next = (util_recieved_last > self._profile.getProfile().getUtility(self._findBid()))
233
234 if util_recieved_last >= maxUtil and self._acceptance_time() and AC_next:
235 return True
236 else:
237 return False
238
239 # Update Utility space.
240 def _updateUtilSpace(self) -> LinearAdditive: # throws IOException
241 newutilspace = self._profile.getProfile()
242 if not newutilspace == self._util_space:
243 self._util_space = cast(LinearAdditive, newutilspace)
244 self._extended_space = ExtendedUtilSpace(self._util_space)
245 return self._util_space
246
247 # Find utility of a given offer
248 def findUtility(self, bid):
249 return self._profile.getProfile().getUtility(bid)
250
251 # Update opponent model and derive some social welfare results
252 def _updateOpponentModel(self, offer: Action):
253 self._frequency_opponent_model = FrequencyOpponentModel.WithAction(self._frequency_opponent_model, offer, self._progress)
254 self._last_received_utility = self.findUtility(self._last_received_bid)
255 if self._progress.get(time.time() * 1000) > 0:
256 area = Decimal(Context.multiply(Context(),
257 self._frequency_opponent_model.getUtility(
258 self._last_received_bid),
259 self._last_received_utility))
260
261 if area > self._estimate_nash:
262 self._estimate_nash = area
263 self._my_nash_utility = self._last_received_utility
264
265 # Find a bid to offer
266 def _findBid(self) -> Bid:
267 total_range = self._bids_with_util.getRange()
268 range_min = total_range.getMin()
269 range_max = total_range.getMax()
270
271 # get the bid that are within time-based percentile of the set range
272 percentage = pow(self._progress.get(time.time() * 1000), 1 / self._conceding_parameter)
273 percentile = Context.subtract(Context(), range_max, Context.multiply(Context(), Context.subtract(Context(), range_max, range_min), Decimal.from_float(0.1 + percentage)))
274 range_of_bids = self._bids_with_util.getBids(Interval(percentile, range_max))
275
276 # Using opponent model, filter out those bids that will not be highly valued by opponent.
277 socialy_acceptably_bids = []
278 for b in range_of_bids:
279 if self._progress.get(time.time() * 1000) < 0.5:
280 return range_of_bids.get(randint(0, range_of_bids.size() - 1))
281 else:
282 if self._frequency_opponent_model.getUtility(b) >= 0.5:
283 socialy_acceptably_bids.append(b)
284
285 if len(socialy_acceptably_bids) < 1:
286 return range_of_bids.get(randint(0, range_of_bids.size() - 1))
287
288 # Once bids that do not yield a high social welfare are filtered out, select one randomly
289 bid_chosen = socialy_acceptably_bids[randint(0, len(socialy_acceptably_bids) - 1)]
290 return bid_chosen
291
292
293
Note: See TracBrowser for help on using the repository browser.