source: CSE3210/agent64/agent64.py

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

#6 Added CSE3210 parties

File size: 10.8 KB
Line 
1import logging
2import time
3from datetime import datetime, timedelta
4from typing import cast
5
6import numpy as np
7from geniusweb.actions.Accept import Accept
8from geniusweb.actions.Action import Action
9from geniusweb.actions.Offer import Offer
10from geniusweb.bidspace.AllBidsList import AllBidsList
11from geniusweb.inform.ActionDone import ActionDone
12from geniusweb.inform.Finished import Finished
13from geniusweb.inform.Inform import Inform
14from geniusweb.inform.Settings import Settings
15from geniusweb.inform.YourTurn import YourTurn
16from geniusweb.issuevalue.Bid import Bid
17from geniusweb.opponentmodel.FrequencyOpponentModel import FrequencyOpponentModel
18from geniusweb.party.Capabilities import Capabilities
19from geniusweb.party.DefaultParty import DefaultParty
20from geniusweb.profileconnection.ProfileConnectionFactory import (ProfileConnectionFactory)
21from geniusweb.progress.ProgressRounds import ProgressRounds
22from tudelft_utilities_logging.Reporter import Reporter
23
24
25class Agent64(DefaultParty):
26 """
27 Template agent that offers random bids until a bid with sufficient utility is offered.
28 """
29
30 cur_max = 0.8
31 cur_min = 0.0
32
33 def __init__(self, reporter: Reporter = None):
34 super().__init__(reporter)
35 self.getReporter().log(logging.INFO, "party is initialized")
36 self._profile = None
37 self._last_received_bid: Bid = None
38 self._last_received_action: Action = None
39 self._opponent_model: FrequencyOpponentModel = None
40 # Keeping track of best bid up until now for later usage
41 self._best_bid: Bid = None
42 self._lowest_bid: Bid = None
43 self._all_received_offers = []
44 self._past_20_offers = []
45 self._opponent_concedes = False
46 self._can_modify = True
47 self.cmin = 0.95
48 self.cmax = 1.0
49 self.niceness = 0.00125
50 self._concesssion_treshold = 1.2
51 self.accept_offer_now_time = None
52
53 def notifyChange(self, info: Inform):
54 """This is the entry point of all interaction with your agent after is has been initialised.
55
56 Args:
57 info (Inform): Contains either a request for action or information.
58 """
59
60 # a Settings message is the first message that will be send to your
61 # agent containing all the information about the negotiation session.
62 if isinstance(info, Settings):
63 self._settings: Settings = cast(Settings, info)
64 self._me = self._settings.getID()
65
66 # progress towards the deadline has to be tracked manually through the use of the Progress object
67 self._progress: ProgressRounds = self._settings.getProgress()
68 self.accept_offer_now_time = self._progress.getTerminationTime() - timedelta(seconds=2)
69
70 # the profile contains the preferences of the agent over the domain
71 self._profile = ProfileConnectionFactory.create(
72 info.getProfile().getURI(), self.getReporter()
73 )
74 # ActionDone is an action send by an opponent (an offer or an accept)
75 elif isinstance(info, ActionDone):
76 action: Action = cast(ActionDone, info).getAction()
77
78 # if it is an offer, set the last received bid
79 if isinstance(action, Offer):
80 self._last_received_bid = cast(Offer, action).getBid()
81 self._last_received_action = cast(Offer, action)
82 # YourTurn notifies you that it is your turn to act
83 elif isinstance(info, YourTurn):
84 action = self._myTurn()
85 if isinstance(self._progress, ProgressRounds):
86 self._progress = self._progress.advance()
87 self.getConnection().send(action)
88
89 # Finished will be send if the negotiation has ended (through agreement or deadline)
90 elif isinstance(info, Finished):
91 # terminate the agent MUST BE CALLED
92 self.terminate()
93 else:
94 self.getReporter().log(
95 logging.WARNING, "Ignoring unknown info " + str(info)
96 )
97
98 # lets the geniusweb system know what settings this agent can handle
99 # leave it as it is for this competition
100 def getCapabilities(self) -> Capabilities:
101 return Capabilities(
102 set(["SAOP"]),
103 set(["geniusweb.profile.utilityspace.LinearAdditive"]),
104 )
105
106 # terminates the agent and its connections
107 # leave it as it is for this competition
108 def terminate(self):
109 self.getReporter().log(logging.INFO, "party is terminating:")
110 super().terminate()
111 if self._profile is not None:
112 self._profile.close()
113 self._profile = None
114
115
116
117 # give a description of your agent
118 def getDescription(self) -> str:
119 return "Agnet64"
120
121 # execute a turn
122 def _myTurn(self):
123 self._opponentModelling()
124
125 # Store all received bids for acceptance purposes
126 self._all_received_offers.append(self._last_received_bid)
127
128 if self._last_received_bid is not None:
129 # Store past 20 recieved bid utilities
130 if (len(self._past_20_offers) < 20):
131 self._past_20_offers.append(self._profile.getProfile().getUtility(self._last_received_bid))
132 else:
133 self._past_20_offers = self._past_20_offers[1:]
134
135 if (self._profile.getProfile().getUtility(self._last_received_bid) / np.mean(
136 self._past_20_offers) > self._concesssion_treshold):
137 self._opponent_concedes = True
138 if (self._profile.getProfile().getUtility(self._last_received_bid) / np.mean(
139 self._past_20_offers) < self._concesssion_treshold):
140 self._opponent_concedes = False
141 self._can_modify = True
142
143 profile = self._profile.getProfile()
144 if self._best_bid is None or profile.getUtility(self._last_received_bid) > profile.getUtility(self._best_bid):
145 self._best_bid = self._last_received_bid
146 if self._lowest_bid is None or profile.getUtility(self._last_received_bid) < profile.getUtility(
147 self._lowest_bid):
148 self._lowest_bid = self._last_received_bid
149
150 # Find an appropriate bid
151 bid = self._findBid()
152 # check if the last received offer if the opponent is good enough
153 if self._last_received_bid is not None and self._accept(bid):
154 # print("Utility of the accepted bid: " + str(profile.getUtility(self._last_received_bid)))
155 action = Accept(self._me, self._last_received_bid)
156 else:
157 # if not, propose a bid as counter offer
158 action = Offer(self._me, bid)
159
160 # send the action
161 return action
162
163 # method that checks if we would agree with an offer
164 def _accept(self, bid: Bid) -> bool:
165 # First move, no offer yet
166 if bid is None:
167 return False
168
169 current_round = self._progress.get(time.time() * 1000)
170 profile = self._profile.getProfile()
171 current_offer = profile.getUtility(self._last_received_bid)
172 own_bid_util = profile.getUtility(bid)
173
174 # if near deadline
175 if current_round >= 0.98: # or datetime.now() >= self.accept_offer_now_time:
176 window = int((1 - current_round) * 200)
177 bids = []
178 for offer in self._all_received_offers[-window:]:
179 if offer:
180 bids.append(profile.getUtility(offer) * self._opponent_model.getUtility(offer))
181
182 # (This max method checks if the current offer is better than the all the offers in the window)
183 if current_offer >= np.max(bids):
184 # print("Current offer (" + (str(round(current_offer, 2))) + ") is better than the max ("
185 # + str(round(np.max(bids), 2)) + ") of the previous "
186 # + str(window) + " offers, accepting...")
187 return True
188
189 # C_next, checks if the offer we are about to make is equal or worse to the current offer from opponent
190 # If so, a consensus has been achieved
191
192 if current_offer >= own_bid_util:
193 # print("About to offer something worse than what we were offered " + str(round(own_bid_util, 3))
194 # + ", accepting: " + str(round(current_offer, 3)))
195 return True
196 else:
197 return False
198
199 def _findBid(self) -> Bid:
200 if self._opponent_concedes is False:
201 self.cmin -= self.niceness
202 if self._opponent_concedes is True and self._can_modify is True:
203
204 if self.cmin <= 0.85:
205 self.cmin += 0.05
206 self._can_modify = False
207
208 if self._progress.get(time.time() * 1000) > 0.2:
209 bid = self.get_random_bid()
210 else:
211 bid = self.get_true_random_bid()
212 if self._progress.get(time.time() * 1000) >= 0.995:
213 bid = self._best_bid
214
215 return bid
216
217 def get_random_bid(self):
218 available_bids = AllBidsList(self._profile.getProfile().getDomain())
219 random_better_bids = [bid for bid in available_bids if
220 self.cmin <= self._profile.getProfile().getUtility(bid) <= self.cmax]
221 if len(random_better_bids) == 0:
222 random_better_bids = [
223 max(map(lambda bid: (bid, self._profile.getProfile().getUtility(bid)), available_bids),
224 key=lambda tup: tup[1])[0]]
225 return random_better_bids[0]
226 best_bid_for_opponent = [
227 max(map(
228 lambda bid: (bid, self._opponent_model.getUtility(bid) * self._profile.getProfile().getUtility(bid)),
229 random_better_bids),
230 key=lambda tup: tup[1])[0]]
231 return best_bid_for_opponent[0]
232
233 def get_true_random_bid(self):
234 available_bids = AllBidsList(self._profile.getProfile().getDomain())
235 random_better_bids = [bid for bid in available_bids if self._profile.getProfile().getUtility(bid) >= self.cmin]
236 if len(random_better_bids) == 0:
237 random_better_bids = [
238 max(map(lambda bid: (bid, self._profile.getProfile().getUtility(bid)), available_bids),
239 key=lambda tup: tup[1])[0]]
240 return np.random.choice(random_better_bids)
241
242 def _opponentModelling(self):
243 if self._opponent_model is None:
244 self._createFrequencyOpponentModelling()
245 self._opponent_model = self._opponent_model.WithAction(self._last_received_action, self._progress)
246
247 def _createFrequencyOpponentModelling(self):
248 domain = self._profile.getProfile().getDomain()
249 self._opponent_model: FrequencyOpponentModel = FrequencyOpponentModel.create().With(domain, newResBid=None)
Note: See TracBrowser for help on using the repository browser.