source: CSE3210/agent26/agent26.py

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

#6 Added CSE3210 parties

File size: 11.2 KB
Line 
1import random
2import time
3from ast import Dict
4from decimal import Decimal
5import logging
6import profile
7from random import randint, shuffle
8import string
9from typing import cast
10
11import geniusweb
12from geniusweb.actions.Accept import Accept
13from geniusweb.actions.Action import Action
14from geniusweb.actions.Offer import Offer
15from geniusweb.bidspace.BidsWithUtility import BidsWithUtility
16from geniusweb.bidspace.Interval import Interval
17from geniusweb.inform.ActionDone import ActionDone
18from geniusweb.inform.Finished import Finished
19from geniusweb.inform.Inform import Inform
20from geniusweb.inform.Settings import Settings
21from geniusweb.inform.YourTurn import YourTurn
22from geniusweb.issuevalue.Bid import Bid
23from geniusweb.issuevalue.Value import Value
24from geniusweb.party.Capabilities import Capabilities
25from geniusweb.party.DefaultParty import DefaultParty
26
27
28from geniusweb.profileconnection.ProfileConnectionFactory import (
29 ProfileConnectionFactory,
30)
31import datetime
32from geniusweb.progress.ProgressRounds import ProgressRounds
33from tudelft_utilities_logging.Reporter import Reporter
34
35
36class Agent26(DefaultParty):
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._last_received_bid: Bid = None
43 self.offers_received: Dict[(str, Value), Decimal] = {}
44 self._beta = 0.05
45 self._accept = 1
46 self._most_similar: Bid = None
47 self._reservation = 0.3
48 self._range = 0.05
49 self._full_time = 0
50 self._round_times: list[Decimal] = []
51 self._last_time = None
52 self._avg_time = None
53
54 def notifyChange(self, info: Inform):
55 """This is the entry point of all interaction with your agent after is has been initialised.
56
57 Args:
58 info (Inform): Contains either a request for action or information.
59 """
60
61 # a Settings message is the first message that will be send to your
62 # agent containing all the information about the negotiation session.
63 if isinstance(info, Settings):
64 self._settings: Settings = cast(Settings, info)
65 self._me = self._settings.getID()
66
67 # progress towards the deadline has to be tracked manually through the use of the Progress object
68 self._progress = self._settings.getProgress()
69 self._full_time = self._progress.getTerminationTime().timestamp() - datetime.datetime.now().timestamp()
70
71 # the profile contains the preferences of the agent over the domain
72 self._profile = ProfileConnectionFactory.create(
73 info.getProfile().getURI(), self.getReporter()
74 )
75 if self._profile.getProfile().getReservationBid() is not None:
76 self._reservation = self._profile.getProfile().getReservationBid()
77
78 # ActionDone is an action send by an opponent (an offer or an accept)
79 elif isinstance(info, ActionDone):
80 action: Action = cast(ActionDone, info).getAction()
81
82 # if it is an offer, set the last received bid
83 if isinstance(action, Offer):
84 self._last_received_bid = cast(Offer, action).getBid()
85
86 # YourTurn notifies you that it is your turn to act
87 elif isinstance(info, YourTurn):
88 action = self._myTurn()
89 if isinstance(self._progress, ProgressRounds):
90 self._progress = self._progress.advance()
91 self.getConnection().send(action)
92
93 # Finished will be send if the negotiation has ended (through agreement or deadline)
94 elif isinstance(info, Finished):
95 # terminate the agent MUST BE CALLED
96 self.terminate()
97 else:
98 self.getReporter().log(
99 logging.WARNING, "Ignoring unknown info " + str(info)
100 )
101
102 # lets the geniusweb system know what settings this agent can handle
103 # leave it as it is for this competition
104 def getCapabilities(self) -> Capabilities:
105 return Capabilities(
106 set(["SAOP"]),
107 set(["geniusweb.profile.utilityspace.LinearAdditive"]),
108 )
109
110 # terminates the agent and its connections
111 # leave it as it is for this competition
112 def terminate(self):
113 self.getReporter().log(logging.INFO, "party is terminating:")
114 super().terminate()
115 if self._profile is not None:
116 self._profile.close()
117 self._profile = None
118
119
120
121 # give a description of your agent
122 def getDescription(self) -> str:
123 return "Giver agent for Collaborative AI course"
124
125 # execute a turn
126 def _myTurn(self):
127
128 # Calculate minimum utility we accept using time dependent formula
129 self._accept = self.get_time_dependent_utility(self._progress.get(time.time() * 1000), 1, self._beta, 1, self._reservation)
130
131 # Creates and updates the Issue Value pair frequency dictionary for opponent modeling
132 if self._last_received_bid is not None:
133 for issue in self._last_received_bid.getIssues():
134 if (issue, self._last_received_bid.getValue(issue)) in self.offers_received:
135 self.offers_received[(issue, self._last_received_bid.getValue(issue))] += 1
136 else:
137 self.offers_received[(issue, self._last_received_bid.getValue(issue))] = 1
138
139 # For calculating average time per round
140 if self._last_time is not None:
141 self._round_times.append(datetime.datetime.now().timestamp() - self._last_time.timestamp())
142 self._avg_time = sum(self._round_times)/len(self._round_times)
143 self._last_time = datetime.datetime.now()
144
145 # Check if the last received offer if the opponent is good enough
146 if self._isGood(self._last_received_bid):
147 # if so, accept the offer
148 action = Accept(self._me, self._last_received_bid)
149 else:
150 # if not, find a bid to propose as counter offer
151 bid = self._findBid()
152 action = Offer(self._me, bid)
153
154 # send the action
155 return action
156
157 # This method checks if we would agree with an offer
158 def _isGood(self, bid: Bid) -> bool:
159 # If there is no bid the agent rejects
160 if bid is None:
161 return False
162 profile = self._profile.getProfile()
163 # Checks the average round time
164 if self._avg_time is not None:
165 # If we are coming close to time deadline the agent drastically go low by changing the beta
166 # in the time dependent formula to 2
167 if self._progress.getTerminationTime().timestamp() - 10 * self._avg_time <= \
168 datetime.datetime.now().timestamp():
169 self._beta = 2
170 # Recalculates the minimum utility the agent accepts
171 self._accept = \
172 self.get_time_dependent_utility(self._progress.get(time.time() * 1000), 1, self._beta, 1, self._reservation)
173 return profile.getUtility(bid) > self._accept
174
175 def _findBid(self) -> Bid:
176 progress = self._progress.get(1)
177 bids_with_utility = BidsWithUtility.create(self._profile.getProfile(), 5)
178 # Calculate the maximum utility (self._accept is the minimum utility we accept calculated by
179 # time dependent formula)
180 max_bid = self._accept + self._range
181 # Select the bids between min and max utility
182 all_bids = bids_with_utility.getBids(Interval(Decimal(self._accept), Decimal(max_bid)))
183 # If there is less than 10 bids in this range we decrease the minimum utility
184 while all_bids.size() < 10:
185 self._accept -= self._range
186 all_bids = bids_with_utility.getBids(Interval(Decimal(self._accept), Decimal(max_bid)))
187 if self._accept <= 0:
188 break
189
190 if all_bids.size() == 0:
191 return self._most_similar
192
193 # Set the best bid to a random bid or global most similar
194 if self._most_similar is None:
195 best_bid = all_bids.get(randint(0, all_bids.size() - 1))
196 else:
197 best_bid = self._most_similar
198 # We create a random integer for using as probability
199 probability = random.randint(0, 100)
200
201 # Return random 10 percent chance
202 if probability >= 90:
203 return all_bids.get(randint(0, all_bids.size() - 1))
204
205 # This loop calculates the new points for our global most similar bid
206 most_similar_sum = 0
207 if self._most_similar is not None:
208 for k, v in self._most_similar.getIssueValues().items():
209 if (k, v) in self.offers_received:
210 # Give points to the bid depending on how many times an issue with a specific
211 # value is offered by the opponent
212 most_similar_sum += self.offers_received[(k, v)]
213
214 # If the progress is very low opponent modeling is not very accurate
215 # This is why we have another strategy for low progress
216 # This is the strategy used after low progress strategy
217 if progress > 0.05:
218
219 max_points = 0
220 # Loop over all the bids in the range
221 for bid in all_bids:
222
223 points = 0
224
225 # Loop over issues in a bid
226 for k, v in bid.getIssueValues().items():
227 if (k, v) in self.offers_received:
228 # Give points to the bid depending on how many times an issue with a specific
229 # value is offered by the opponent
230 points += self.offers_received[(k, v)]
231
232 # Check if any of the bids is more similar than our old most similar bid
233 if points > most_similar_sum:
234 self._most_similar = bid
235 most_similar_sum = points
236 # return most similar bid 45 percent chance
237 best_bid = self._most_similar
238
239 # Return the best bid in the range 45 percent chance
240 if probability >= 45:
241 if points > max_points:
242 best_bid = bid
243 max_points = points
244
245 # If progress is too low, we use random strategy
246 else:
247 points = 0
248 new_bid = all_bids.get(randint(0, all_bids.size() - 1))
249 # Calculates the points of new bid
250 for k, v in new_bid.getIssueValues().items():
251 if (k, v) in self.offers_received:
252 # Give points to the bid depending on how many times an issue with a specific
253 # value is offered by the opponent
254 points += self.offers_received[(k, v)]
255
256 # If it has more points than most similar bid, changes this bid to most similar
257 if most_similar_sum <= points:
258 self._most_similar = new_bid
259
260 return best_bid
261
262 @staticmethod
263 def alpha_time(t, t_max, beta, initial_value=0):
264 return initial_value + (1 - initial_value) * ((min(t, t_max) / t_max) ** (1 / beta))
265
266 def get_time_dependent_utility(self, t, t_max, beta, max_utility, min_utility, initial_value=0):
267 return min_utility + (1 - self.alpha_time(t, t_max, beta, initial_value)) * (max_utility - min_utility)
Note: See TracBrowser for help on using the repository browser.