source: CSE3210/agent32/agent32.py

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

#6 Added CSE3210 parties

File size: 9.0 KB
Line 
1import logging
2import time
3from random import randint
4from typing import cast
5import numpy as np
6
7from geniusweb.actions.Accept import Accept
8from geniusweb.actions.Action import Action
9from geniusweb.actions.Offer import Offer
10from geniusweb.actions.PartyId import PartyId
11from geniusweb.bidspace.AllBidsList import AllBidsList
12from geniusweb.inform.ActionDone import ActionDone
13from geniusweb.inform.Finished import Finished
14from geniusweb.inform.Inform import Inform
15from geniusweb.inform.Settings import Settings
16from geniusweb.inform.YourTurn import YourTurn
17from geniusweb.issuevalue.Bid import Bid
18from geniusweb.issuevalue.Domain import Domain
19from geniusweb.issuevalue.Value import Value
20from geniusweb.issuevalue.ValueSet import ValueSet
21from geniusweb.party.Capabilities import Capabilities
22from geniusweb.party.DefaultParty import DefaultParty
23from geniusweb.profile.utilityspace.UtilitySpace import UtilitySpace
24from geniusweb.profileconnection.ProfileConnectionFactory import (
25 ProfileConnectionFactory,
26)
27from geniusweb.progress.ProgressRounds import ProgressRounds
28from tudelft_utilities_logging.Reporter import Reporter
29
30
31class Agent32(DefaultParty):
32 """
33 RAT4TA: Random Tit 4 Tat agent by group 32
34 """
35 def __init__(self, reporter: Reporter = None):
36 super().__init__(reporter)
37 self.getReporter().log(logging.INFO, "party is initialized")
38 self._profile = None
39 self._last_received_bid: Bid = None
40 self.previousReceivedBids = []
41 self.previousReceivedUtils = []
42 self.hasGoodEnemy = True
43
44
45 def notifyChange(self, info: Inform):
46 """This is the entry point of all interaction with your agent after is has been initialised.
47
48 Args:
49 info (Inform): Contains either a request for action or information.
50 """
51
52 # a Settings message is the first message that will be send to your
53 # agent containing all the information about the negotiation session.
54 if isinstance(info, Settings):
55 self._settings: Settings = cast(Settings, info)
56 self._me = self._settings.getID()
57
58 # progress towards the deadline has to be tracked manually through the use of the Progress object
59 self._progress = self._settings.getProgress()
60
61 # the profile contains the preferences of the agent over the domain
62 self._profile = ProfileConnectionFactory.create(
63 info.getProfile().getURI(), self.getReporter()
64 )
65 # ActionDone is an action send by an opponent (an offer or an accept)
66 elif isinstance(info, ActionDone):
67 action: Action = cast(ActionDone, info).getAction()
68
69 # if it is an offer, set the last received bid
70 if isinstance(action, Offer):
71 self._last_received_bid = cast(Offer, action).getBid()
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 self.terminate()
83 else:
84 self.getReporter().log(
85 logging.WARNING, "Ignoring unknown info " + str(info)
86 )
87
88 # lets the geniusweb system know what settings this agent can handle
89 # leave it as it is for this competition
90 def getCapabilities(self) -> Capabilities:
91 return Capabilities(
92 set(["SAOP"]),
93 set(["geniusweb.profile.utilityspace.LinearAdditive"]),
94 )
95
96 # terminates the agent and its connections
97 # leave it as it is for this competition
98 def terminate(self):
99 self.getReporter().log(logging.INFO, "party is terminating:")
100 super().terminate()
101 if self._profile is not None:
102 self._profile.close()
103 self._profile = None
104
105
106
107 # give a description of your agent
108 def getDescription(self) -> str:
109 return "RAT4TA: RAndom Tit-4-Tat Agent by group 32"
110
111 # Detects wether an enemy is conceiding or hard lining.
112 # This is done by analyzing the latest bits of the opponent.
113 # It's not water tight but it functions good enough.
114 def enemyConceiding(self):
115 if len(self.previousReceivedUtils) < 10:
116 return False
117 value = np.std(self.previousReceivedUtils)
118 last10Values = self.previousReceivedUtils[-10:]
119 last5Better = (last10Values[0] + last10Values[1] + last10Values[2] + last10Values[3] + last10Values[4]) < (last10Values[-5] + last10Values[-1] + last10Values[-2] + last10Values[-3] + last10Values[-4])
120 # print(value, np.std(last10Values))
121 if value > 0.1: return True
122 if np.std(last10Values) > 0.1 and last5Better: return True
123 return False
124
125 # execute a turn
126 def _myTurn(self):
127 profile = self._profile.getProfile()
128 # check if the last received offer if the opponent is good enough
129 if self._isGood(self._last_received_bid):
130 # if so, accept the offer
131 action = Accept(self._me, self._last_received_bid)
132 else:
133 # if not, find a bid to propose as counter offer
134 if self._last_received_bid is not None:
135 self.previousReceivedBids.append([profile.getUtility(self._last_received_bid), self._last_received_bid])
136 self.previousReceivedUtils.append(profile.getUtility(self._last_received_bid))
137 bid = self._findBid()
138 action = Offer(self._me, bid)
139 # send the action
140 return action
141
142 # method that checks if we would agree with an offer
143 def _isGood(self, bid: Bid) -> bool:
144 if bid is None:
145 return False
146 profile = self._profile.getProfile()
147 progress = self._progress.get(time.time() * 1000)
148
149 #To still get some points from a hardlining enemy accept their last bid (since there is no gurantee they will accept our last bid)
150 if progress >= 0.995: return True
151 # Checks if the enemy is also conceiding, otherwise only send bids of 0.95 utility
152 if not self.hasGoodEnemy: return profile.getUtility(bid) > 0.95
153 # Send a bid as good as possible at the start
154 if progress == 0: return profile.getUtility(bid) > 0.98
155 # Creates an linear conceiding line ending at 0.65 user utility at the end.
156 return profile.getUtility(bid) > max (0.99 - 0.35 * progress, 0.65)
157
158 # Used to sort the list of bids
159 def takeUtility(elem, elem2):
160 return elem2[0]
161
162 def _findBid(self) -> Bid:
163 # compose a list of all possible bids
164 domain = self._profile.getProfile().getDomain()
165 all_bids = AllBidsList(domain)
166 profile = self._profile.getProfile()
167 progress = self._progress.get(time.time() * 1000)
168 self.validBidOptions = []
169 self.allBidOptions = []
170
171
172 self.previousReceivedBids.sort(key= self.takeUtility, reverse=True)
173
174 # After 45% of the bids happend it will check if the enemy is conceiding.
175 if progress > 0.45:
176 self.hasGoodEnemy = True if self.enemyConceiding() else False
177 # take 1000 attempts at finding a random bid that is acceptable to us
178 for _ in range(1000):
179 bid = all_bids.get(randint(0, all_bids.size() - 1))
180 # Save all bid options generated in the format [[utility_1,bid_1], [utility_2,bid_2] , ... [utility_n,bid_n]]
181 # This format is used to later sort on these values
182 self.allBidOptions.append([profile.getUtility(bid), bid])
183 if self._isGood(bid):
184 # Save all valid options in the before mentioned format. note that the bids are not sorted!
185 self.validBidOptions.append([profile.getUtility(bid), bid])
186 try:
187 # Sort all bid options so that some checks on the best util can be performed
188 self.allBidOptions.sort(key= self.takeUtility, reverse=True)
189 except:
190 print("\n")
191
192 nextBid = None
193 # Sends the best bid it received back to the other agent if it is the last bid
194 if(progress >= 0.99 and len(self.previousReceivedBids) > 0):
195 nextBid = self.previousReceivedBids[0]
196 # checks if a previous received bit is better than the current selected option. If so send back that bid
197 elif(len(self.previousReceivedBids) > 0 and len(self.validBidOptions) > 0 and self.previousReceivedBids[0][0] > self.validBidOptions[0][0]):
198 nextBid = self.previousReceivedBids[0]
199 else:
200 # Send back a random valid bid if there is one, otherwise send the best bid for our selves.
201 # (the first bid in the validBidOptions list is already random since it isnt sorted)
202 nextBid = self.validBidOptions[0] if len(self.validBidOptions) > 0 else self.allBidOptions[0]
203 # return the bid
204 return nextBid[1]
Note: See TracBrowser for help on using the repository browser.