source: CSE3210/agent24/agent24.py

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

#6 Added CSE3210 parties

File size: 8.3 KB
Line 
1import decimal
2import logging
3import time
4from random import randint
5from typing import cast
6import numpy as np
7
8from geniusweb.actions.Accept import Accept
9from geniusweb.actions.Action import Action
10from geniusweb.actions.Offer import Offer
11from geniusweb.actions.PartyId import PartyId
12from geniusweb.bidspace.AllBidsList import AllBidsList
13from geniusweb.inform.ActionDone import ActionDone
14from geniusweb.inform.Finished import Finished
15from geniusweb.inform.Inform import Inform
16from geniusweb.inform.Settings import Settings
17from geniusweb.inform.YourTurn import YourTurn
18from geniusweb.issuevalue.Bid import Bid
19from geniusweb.issuevalue.Domain import Domain
20from geniusweb.issuevalue.Value import Value
21from geniusweb.issuevalue.ValueSet import ValueSet
22from geniusweb.opponentmodel import FrequencyOpponentModel
23from geniusweb.party.Capabilities import Capabilities
24from geniusweb.party.DefaultParty import DefaultParty
25from geniusweb.profile.utilityspace.UtilitySpace import UtilitySpace
26from geniusweb.profileconnection.ProfileConnectionFactory import (
27 ProfileConnectionFactory,
28)
29from geniusweb.progress.ProgressRounds import ProgressRounds
30from tudelft_utilities_logging.Reporter import Reporter
31
32
33
34class Agent24(DefaultParty):
35 """
36 Tit-for-tat agent that offers bids according to the opponents bids.
37 """
38
39 def __init__(self, reporter: Reporter = None):
40 super().__init__(reporter)
41 self.getReporter().log(logging.INFO, "party is initialized")
42 self._profile = None
43 self._last_received_bid: Bid = None
44 self._opponent_model = FrequencyOpponentModel.FrequencyOpponentModel.create()
45 self._issue_weights = []
46 self._value_changed = []
47 self._bids_matrix = []
48 self._frequency_matrix = []
49 self._previous_bid_enemy = 1
50 self._previous_bid_self = 1
51
52 def notifyChange(self, info: Inform):
53
54 # a Settings message is the first message that will be send to your
55 # agent containing all the information about the negotiation session.
56 if isinstance(info, Settings):
57 self._settings: Settings = cast(Settings, info)
58 self._me = self._settings.getID()
59
60 # progress towards the deadline has to be tracked manually through the use of the Progress object
61 self._progress: ProgressRounds = self._settings.getProgress()
62
63 # the profile contains the preferences of the agent over the domain
64 self._profile = ProfileConnectionFactory.create(
65 info.getProfile().getURI(), self.getReporter()
66 )
67 # ActionDone is an action send by an opponent (an offer or an accept)
68 elif isinstance(info, ActionDone):
69 action: Action = cast(ActionDone, info).getAction()
70
71 # if it is an offer, set the last received bid
72 if isinstance(action, Offer):
73 self._last_received_bid = cast(Offer, action).getBid()
74 # YourTurn notifies you that it is your turn to act
75 elif isinstance(info, YourTurn):
76 action = self._myTurn()
77 if isinstance(self._progress, ProgressRounds):
78 self._progress = self._progress.advance()
79 self.getConnection().send(action)
80
81 # Finished will be send if the negotiation has ended (through agreement or deadline)
82 elif isinstance(info, Finished):
83 # terminate the agent MUST BE CALLED
84 self.terminate()
85 else:
86 self.getReporter().log(
87 logging.WARNING, "Ignoring unknown info " + str(info)
88 )
89
90 # lets the geniusweb system know what settings this agent can handle
91 # leave it as it is for this competition
92 def getCapabilities(self) -> Capabilities:
93 return Capabilities(
94 set(["SAOP"]),
95 set(["geniusweb.profile.utilityspace.LinearAdditive"]),
96 )
97
98 # terminates the agent and its connections
99 # leave it as it is for this competition
100 def terminate(self):
101 self.getReporter().log(logging.INFO, "party is terminating:")
102 super().terminate()
103 if self._profile is not None:
104 self._profile.close()
105 self._profile = None
106
107 # give a description of your agent
108 def getDescription(self) -> str:
109 return "The ultimate Bastard tit-for-tat agent"
110
111 # execute a turn
112 def _myTurn(self):
113
114 if self._last_received_bid is None:
115 bid = self._findBid(1)
116 action = Offer(self._me, bid)
117 else:
118 len_weights = len(self._last_received_bid.getIssues())
119 dict_values = list(self._last_received_bid.getIssueValues().values())
120 self._bids_matrix.append(dict_values)
121
122 if len(self._issue_weights) == 0:
123 for i in range(len_weights):
124 self._issue_weights.append(1 / len_weights)
125 self._value_changed.append(1)
126 self._frequency_matrix.append({dict_values[i]: 1})
127
128 else:
129 for i in range(len_weights):
130 last_bid = list(self._last_received_bid.getIssueValues().values())[i]
131 if self._bids_matrix[-1][i] != last_bid:
132 self._value_changed[i] *= 2
133 self._issue_weights[i] = (sum(self._value_changed) - self._value_changed[i]) / \
134 (sum(self._value_changed) * (len_weights - 1))
135 if last_bid not in self._frequency_matrix[i]:
136 self._frequency_matrix[i][last_bid] = 1
137 else:
138 self._frequency_matrix[i][last_bid] += 1
139
140 utility = 0
141 for i in range(len_weights):
142 utility += self._issue_weights[i] * (self._frequency_matrix[i][list(self._last_received_bid.getIssueValues().values())[i]] / sum(self._frequency_matrix[i].values()))
143
144 bid = self._findBid(utility)
145 if self._isOpponentBidGood(self._last_received_bid):
146 action = Accept(self._me, self._last_received_bid)
147
148 else:
149 # if not, find a bid to propose as counter offer
150 action = Offer(self._me, bid)
151
152 # send the action
153 self._previous_bid_enemy = utility
154 return action
155
156 # determine if bid should be accepted
157 def _isOpponentBidGood(self, bid: Bid) -> bool:
158 if bid is None:
159 return False
160 profile = self._profile.getProfile()
161 progress = self._progress.get(time.time() * 1000)
162
163 # set reservation value
164 if self._profile.getProfile().getReservationBid() is None:
165 reservation = 0.0
166 else:
167 reservation = profile.getUtility(self._profile.getProfile().getReservationBid())
168
169 # ACconst
170 if profile.getUtility(bid) >= 0.99:
171 return True
172
173 # boulware/conceder
174 beta = 0.000000001 # beta: small = boulware, large = conceder, 0.5 = linear
175 k = 0.9
176 a = k + (1.0 - k) * pow(progress, (1.0 / beta))
177 min1 = 0.8
178 max1 = 1.0
179 utility = min1 + (1.0 - a) * (max1 - min1)
180 if profile.getUtility(bid) >= utility:
181 return True
182
183 return progress >= 0.99 and profile.getUtility(bid) > reservation
184
185 def _findBid(self, utility) -> Bid:
186 # compose a list of all possible bids
187 changed_utility = self._previous_bid_enemy - utility
188
189 domain = self._profile.getProfile().getDomain()
190 profile = self._profile.getProfile()
191 all_bids = AllBidsList(domain)
192
193 found = False
194 for _ in range(5000):
195 bid = all_bids.get(randint(0, all_bids.size() - 1))
196 if -0.2 < decimal.Decimal(self._previous_bid_self) - profile.getUtility(bid) - (decimal.Decimal(changed_utility) * decimal.Decimal(0.3)) < 0.05 and self._previous_bid_self - profile.getUtility(bid) < 0.1:
197 found = True
198 break
199 if not found:
200 for _ in range(5000):
201 bid = all_bids.get(randint(0, all_bids.size() - 1))
202 if self._previous_bid_self - profile.getUtility(bid) < 0.1:
203 break
204 self._previous_bid_self = profile.getUtility(bid)
205 return bid
Note: See TracBrowser for help on using the repository browser.