[75] | 1 | import logging
|
---|
| 2 | import math
|
---|
| 3 | import os.path
|
---|
| 4 | import random
|
---|
| 5 | import pickle
|
---|
| 6 | from time import time
|
---|
| 7 | from typing import cast
|
---|
| 8 | from collections import defaultdict
|
---|
| 9 | from typing import List
|
---|
| 10 | from geniusweb.profileconnection.ProfileInterface import ProfileInterface
|
---|
| 11 | from geniusweb.actions.Accept import Accept
|
---|
| 12 | from geniusweb.actions.Action import Action
|
---|
| 13 | from geniusweb.actions.Offer import Offer
|
---|
| 14 | from geniusweb.actions.PartyId import PartyId
|
---|
| 15 | from geniusweb.inform.ActionDone import ActionDone
|
---|
| 16 | from geniusweb.inform.Finished import Finished
|
---|
| 17 | from geniusweb.inform.Inform import Inform
|
---|
| 18 | from geniusweb.inform.Settings import Settings
|
---|
| 19 | from geniusweb.inform.YourTurn import YourTurn
|
---|
| 20 | from geniusweb.issuevalue.Bid import Bid
|
---|
| 21 | from geniusweb.bidspace.AllBidsList import AllBidsList
|
---|
| 22 | from geniusweb.party.Capabilities import Capabilities
|
---|
| 23 | from geniusweb.party.DefaultParty import DefaultParty
|
---|
| 24 | from geniusweb.utils import val
|
---|
| 25 | from geniusweb.issuevalue.Value import Value
|
---|
| 26 | from geniusweb.issuevalue import DiscreteValue
|
---|
| 27 | from geniusweb.issuevalue import NumberValue
|
---|
| 28 | from geniusweb.inform.Agreements import Agreements
|
---|
| 29 | from geniusweb.references.Parameters import Parameters
|
---|
| 30 | from geniusweb.profileconnection.ProfileConnectionFactory import (
|
---|
| 31 | ProfileConnectionFactory,
|
---|
| 32 | )
|
---|
| 33 | from geniusweb.progress.ProgressRounds import ProgressRounds
|
---|
| 34 |
|
---|
| 35 | from .utils.utils import get_ms_current_time
|
---|
| 36 | from .utils.pair import Pair
|
---|
| 37 | from .utils.persistent_data import PersistentData
|
---|
| 38 | from .utils.negotiation_data import NegotiationData
|
---|
| 39 |
|
---|
| 40 |
|
---|
| 41 | class SuperAgent(DefaultParty):
|
---|
| 42 | """
|
---|
| 43 | A Super party that places empty bids because it can't download the profile,
|
---|
| 44 | and accepts the first incoming offer.
|
---|
| 45 | """
|
---|
| 46 |
|
---|
| 47 | def __init__(self):
|
---|
| 48 | super().__init__()
|
---|
| 49 | self.getReporter().log(logging.INFO, "party is initialized")
|
---|
| 50 | self._last_received_bid: Bid = None
|
---|
| 51 | self._me = None
|
---|
| 52 | self._profile_interface: ProfileInterface = None
|
---|
| 53 | self._progress = None
|
---|
| 54 | self._protocol = None
|
---|
| 55 | self._parameters: Parameters = None
|
---|
| 56 | self._utility_space = None
|
---|
| 57 | self._domain = None
|
---|
| 58 | self._settings: Settings = None
|
---|
| 59 |
|
---|
| 60 | self._best_offer_bid: Bid = None
|
---|
| 61 | self._profile = None
|
---|
| 62 | self._persistent_path: str = None
|
---|
| 63 | self._persistent_data: PersistentData = None
|
---|
| 64 | # NeogtiationData
|
---|
| 65 | self._negotiation_data: NegotiationData = None
|
---|
| 66 | self._data_paths_raw: List[str] = []
|
---|
| 67 | # self._data_paths: List[str] = []
|
---|
| 68 | self._negotiation_data_paths: List[str] = []
|
---|
| 69 | self._opponent_name = None
|
---|
| 70 | self._freq_map = defaultdict()
|
---|
| 71 | self._avg_utility = 0.95
|
---|
| 72 | self._std_utility = 0.15
|
---|
| 73 | self._util_threshold = 0.95
|
---|
| 74 | self._min_utility = 0.6
|
---|
| 75 | self.default_alpha = 10.7
|
---|
| 76 | self.alpha = self.default_alpha
|
---|
| 77 | self.t_split = 40
|
---|
| 78 | self.op_counter = [0] * self.t_split
|
---|
| 79 | self.op_sum = [0.0] * self.t_split
|
---|
| 80 | self.op_threshold = [0.0] * self.t_split
|
---|
| 81 | self.t_phase = 0.2
|
---|
| 82 | self.t_social_welfare = 0.997
|
---|
| 83 |
|
---|
| 84 | self._max_bid_space_iteration = 50000
|
---|
| 85 | self._optimal_bid: Bid = None
|
---|
| 86 | self._all_bid_list: AllBidsList = None
|
---|
| 87 | self._sorted_bid_list: List = None
|
---|
| 88 | self._len_sorted_bid_list: int = 0
|
---|
| 89 | self._storage_dir: str = None
|
---|
| 90 |
|
---|
| 91 | def create_empty_negotiation_data(self, opponent_name):
|
---|
| 92 | self._negotiation_data = NegotiationData(opponent_name=opponent_name)
|
---|
| 93 |
|
---|
| 94 | def initialize_negotiation_data(self, opponent_name):
|
---|
| 95 | self._negotiation_data_paths = []
|
---|
| 96 | data_path_raw = os.path.join(self._storage_dir, f"negotiation_data_{opponent_name}.log")
|
---|
| 97 | self._negotiation_data_paths.append(data_path_raw)
|
---|
| 98 | if self._negotiation_data is not None and os.path.exists(data_path_raw):
|
---|
| 99 | # print("non-empty NegotiationData")
|
---|
| 100 | with open(data_path_raw, "rb") as negotiation_data_file:
|
---|
| 101 | self._negotiation_data: NegotiationData = pickle.load(negotiation_data_file)
|
---|
| 102 | else:
|
---|
| 103 | # print("empty NegotiationData")
|
---|
| 104 | self.create_empty_negotiation_data(opponent_name=opponent_name)
|
---|
| 105 |
|
---|
| 106 | def initialize_persistent_data(self, opponent_name):
|
---|
| 107 | self._persistent_path = os.path.join(self._storage_dir, f"persistent_data_{opponent_name}.log")
|
---|
| 108 | if self._persistent_path is not None and os.path.exists(self._persistent_path):
|
---|
| 109 | # json load
|
---|
| 110 | # print("non-empty PersistentData")
|
---|
| 111 | with open(self._persistent_path, "rb") as persistent_file:
|
---|
| 112 | self._persistent_data: PersistentData = pickle.load(persistent_file)
|
---|
| 113 | self._avg_utility = self._persistent_data.get_avg_utility()
|
---|
| 114 | self._std_utility = self._persistent_data.get_std_utility()
|
---|
| 115 | else:
|
---|
| 116 | self._persistent_data: PersistentData = PersistentData()
|
---|
| 117 |
|
---|
| 118 | def first_better_then(self, utility):
|
---|
| 119 | idx = None
|
---|
| 120 | try:
|
---|
| 121 | idx = next(len(self._sorted_bid_list) - 1 - x for x, val in enumerate(self._sorted_bid_list[-1::-1]) if
|
---|
| 122 | self.calc_utility(val) > utility)
|
---|
| 123 | except StopIteration:
|
---|
| 124 | pass
|
---|
| 125 | finally:
|
---|
| 126 | return idx
|
---|
| 127 |
|
---|
| 128 | def last_bids(self, good_bid: int):
|
---|
| 129 | # this session's max utility got
|
---|
| 130 | if self._progress.get(get_ms_current_time()) <= 0.97 and self.is_good(self._best_offer_bid):
|
---|
| 131 | return self._best_offer_bid
|
---|
| 132 | # all session's max utility got
|
---|
| 133 | avg_max_util = self._persistent_data.get_avg_max_utility(self._opponent_name)
|
---|
| 134 | if not avg_max_util:
|
---|
| 135 | return self._best_offer_bid
|
---|
| 136 | if self._progress.get(get_ms_current_time()) <= 0.99:
|
---|
| 137 | idx = self.first_better_then(avg_max_util)
|
---|
| 138 | if idx != None:
|
---|
| 139 | self.getReporter().log(logging.INFO, "avg_max_util: {0}, bid_utility: {1}".format(avg_max_util,
|
---|
| 140 | self._utility_space.getUtility(
|
---|
| 141 | self._sorted_bid_list[
|
---|
| 142 | idx])))
|
---|
| 143 | if self.is_good(self._sorted_bid_list[idx]):
|
---|
| 144 | return self._sorted_bid_list[idx]
|
---|
| 145 |
|
---|
| 146 | # last try we give him the best possible suggestion we have
|
---|
| 147 | if good_bid == 0:
|
---|
| 148 | bid = self._optimal_bid
|
---|
| 149 | else:
|
---|
| 150 | bid = max(self._sorted_bid_list[0:good_bid], key=self.calc_op_value)
|
---|
| 151 |
|
---|
| 152 | self.getReporter().log(logging.INFO, "chosen bid utility: {}".format(self._utility_space.getUtility(bid)))
|
---|
| 153 | return bid
|
---|
| 154 |
|
---|
| 155 | @classmethod
|
---|
| 156 | def parse_opponent_name(cls, full_opponent_name):
|
---|
| 157 | agent_index = full_opponent_name.rindex("_")
|
---|
| 158 | if agent_index != -1:
|
---|
| 159 | return full_opponent_name[:agent_index]
|
---|
| 160 | return None
|
---|
| 161 |
|
---|
| 162 | def initialize_storage(self, opponent_name):
|
---|
| 163 | if self._storage_dir is not None:
|
---|
| 164 | self.initialize_persistent_data(opponent_name=opponent_name)
|
---|
| 165 | self.initialize_negotiation_data(opponent_name=opponent_name)
|
---|
| 166 | else:
|
---|
| 167 | self.create_empty_negotiation_data(opponent_name=opponent_name)
|
---|
| 168 | self._persistent_data: PersistentData = PersistentData()
|
---|
| 169 |
|
---|
| 170 | # Override
|
---|
| 171 | def notifyChange(self, info: Inform):
|
---|
| 172 | # self.getReporter().log(logging.INFO, "received info:" + str(info))
|
---|
| 173 | if isinstance(info, Settings):
|
---|
| 174 | # self.getReporter().log(logging.WARNING, "SETTINGS")
|
---|
| 175 | settings: Settings = cast(Settings, info)
|
---|
| 176 | self._settings = settings
|
---|
| 177 | self._me: PartyId = settings.getID()
|
---|
| 178 | self._progress = settings.getProgress()
|
---|
| 179 | self._protocol = str(settings.getProtocol().getURI())
|
---|
| 180 | self._parameters = settings.getParameters()
|
---|
| 181 | if "storage_dir" in self._parameters.getParameters():
|
---|
| 182 | self.getReporter().log(logging.INFO, "storage_dir is on parameters")
|
---|
| 183 | self._storage_dir = self._parameters.get("storage_dir")
|
---|
| 184 |
|
---|
| 185 | try:
|
---|
| 186 | self._profile_interface: ProfileInterface = ProfileConnectionFactory.create(
|
---|
| 187 | settings.getProfile().getURI(), self.getReporter()
|
---|
| 188 | )
|
---|
| 189 | self._profile = self._profile_interface.getProfile()
|
---|
| 190 | self._domain = self._profile.getDomain()
|
---|
| 191 |
|
---|
| 192 | if self._freq_map is None:
|
---|
| 193 | self._freq_map = defaultdict()
|
---|
| 194 | else:
|
---|
| 195 | self._freq_map.clear()
|
---|
| 196 |
|
---|
| 197 | issues = self._domain.getIssues()
|
---|
| 198 | for issue in issues:
|
---|
| 199 | p = Pair()
|
---|
| 200 | vs = self._domain.getValues(issue)
|
---|
| 201 | if isinstance(vs.get(0), DiscreteValue.DiscreteValue):
|
---|
| 202 | p.value_type = 0
|
---|
| 203 | elif isinstance(vs.get(0), NumberValue.NumberValue):
|
---|
| 204 | p.value_type = 1
|
---|
| 205 | for v in vs:
|
---|
| 206 | vstr = self.value_to_str(v, p)
|
---|
| 207 | p.vlist[vstr] = 0
|
---|
| 208 | self._freq_map[issue] = p
|
---|
| 209 |
|
---|
| 210 | self._utility_space = self._profile_interface.getProfile()
|
---|
| 211 | self._all_bid_list: AllBidsList = AllBidsList(domain=self._domain)
|
---|
| 212 | self._sorted_bid_list = sorted(AllBidsList(domain=self._domain),
|
---|
| 213 | key=self._utility_space.getUtility, reverse=True)
|
---|
| 214 | self._len_sorted_bid_list = len(self._sorted_bid_list)
|
---|
| 215 | # after sort of bid list the optimal bid is in the first element
|
---|
| 216 | self._optimal_bid = self._sorted_bid_list[0]
|
---|
| 217 |
|
---|
| 218 | except Exception as e:
|
---|
| 219 | print("error in settings:{}", e)
|
---|
| 220 | self.getReporter().log(logging.WARNING, "Error in {}".format(str(e)))
|
---|
| 221 |
|
---|
| 222 | elif isinstance(info, ActionDone):
|
---|
| 223 | # self.getReporter().log(logging.WARNING, "ActionDone")
|
---|
| 224 | # TODO: initalizie with negotiaiondata
|
---|
| 225 | action: Action = cast(ActionDone, info).getAction()
|
---|
| 226 | if self._me is not None and self._me != action.getActor():
|
---|
| 227 | opponent_name = self.parse_opponent_name(full_opponent_name=action.getActor().getName())
|
---|
| 228 | if self._opponent_name is None and opponent_name is not None:
|
---|
| 229 | self.initialize_storage(opponent_name)
|
---|
| 230 | # which means index found
|
---|
| 231 | self._opponent_name = opponent_name
|
---|
| 232 | self._negotiation_data.set_opponent_name(self._opponent_name)
|
---|
| 233 |
|
---|
| 234 | self.op_threshold = self._persistent_data.get_smooth_threshold_over_time(self._opponent_name
|
---|
| 235 | )
|
---|
| 236 | if self.op_threshold is not None:
|
---|
| 237 | for i in range(1, self.t_split):
|
---|
| 238 | self.op_threshold[i] = self.op_threshold[i] if self.op_threshold[i] > 0 else \
|
---|
| 239 | self.op_threshold[i - 1]
|
---|
| 240 | self.alpha = self._persistent_data.get_opponent_alpha(self._opponent_name)
|
---|
| 241 | self.alpha = self.alpha if self.alpha > 0.0 else self.default_alpha
|
---|
| 242 | self.process_action(action)
|
---|
| 243 |
|
---|
| 244 | elif isinstance(info, YourTurn):
|
---|
| 245 | # This is a super party
|
---|
| 246 | # self.getReporter().log(logging.WARNING, "YourTurn")
|
---|
| 247 | if isinstance(self._progress, ProgressRounds):
|
---|
| 248 | self._progress = self._progress.advance()
|
---|
| 249 | # self.initialize_storage(self._opponent_name)
|
---|
| 250 | action = self._my_turn()
|
---|
| 251 | val(self.getConnection()).send(action)
|
---|
| 252 |
|
---|
| 253 | elif isinstance(info, Finished):
|
---|
| 254 | # TODO:: handle NEGOTIATIONDATA
|
---|
| 255 | finished_info = cast(Finished, info)
|
---|
| 256 | agreements: Agreements = finished_info.getAgreements()
|
---|
| 257 | self.process_agreements(agreements)
|
---|
| 258 | self.learn()
|
---|
| 259 | if self._negotiation_data_paths is not None and len(
|
---|
| 260 | self._negotiation_data_paths) > 0 and self._negotiation_data is not None:
|
---|
| 261 | for negotiation_path in self._negotiation_data_paths:
|
---|
| 262 | try:
|
---|
| 263 | with open(negotiation_path, "wb") as negotiation_file:
|
---|
| 264 | pickle.dump(self._negotiation_data, negotiation_file)
|
---|
| 265 | except Exception as e:
|
---|
| 266 | self.getReporter().log(logging.WARNING, "Error in {}".format(str(e)))
|
---|
| 267 | self.terminate()
|
---|
| 268 | else:
|
---|
| 269 | self.getReporter().log(
|
---|
| 270 | logging.WARNING, "Ignoring unknown info " + str(info)
|
---|
| 271 | )
|
---|
| 272 |
|
---|
| 273 | # Override
|
---|
| 274 | def getCapabilities(self) -> Capabilities:
|
---|
| 275 | return Capabilities(
|
---|
| 276 | set(["SAOP", "Learn"]),
|
---|
| 277 | set(["geniusweb.profile.utilityspace.LinearAdditive"]),
|
---|
| 278 | )
|
---|
| 279 |
|
---|
| 280 | # Override
|
---|
| 281 | def getDescription(self) -> str:
|
---|
| 282 | return "This is a party of ANL 2022. It can handle the Learn protocol and learns simple characteristics of the opponent."
|
---|
| 283 |
|
---|
| 284 | # Override
|
---|
| 285 | def terminate(self):
|
---|
| 286 | self.getReporter().log(logging.INFO, "party is terminating:")
|
---|
| 287 | super().terminate()
|
---|
| 288 | if self._profile_interface is not None:
|
---|
| 289 | self._profile_interface.close()
|
---|
| 290 |
|
---|
| 291 | def value_to_str(self, v: Value, p: Pair) -> str:
|
---|
| 292 | v_str = ""
|
---|
| 293 | if p.value_type == 0:
|
---|
| 294 | v_str = str(cast(DiscreteValue, v).getValue())
|
---|
| 295 | elif p.value_type == 1:
|
---|
| 296 | v_str = str(cast(NumberValue, v).getValue())
|
---|
| 297 |
|
---|
| 298 | if v_str == "":
|
---|
| 299 | self.getReporter().log(logging.WARNING, "Warning: Value wasn't found")
|
---|
| 300 | return v_str
|
---|
| 301 |
|
---|
| 302 | def process_action(self, action: Action):
|
---|
| 303 | if isinstance(action, Offer):
|
---|
| 304 | self._last_received_bid = cast(Offer, action).getBid()
|
---|
| 305 | self.update_freq_map(self._last_received_bid)
|
---|
| 306 | util_value = float(self._utility_space.getUtility(self._last_received_bid))
|
---|
| 307 | self._negotiation_data.add_bid_util(util_value)
|
---|
| 308 |
|
---|
| 309 | def update_freq_map(self, bid: Bid):
|
---|
| 310 | if bid is not None:
|
---|
| 311 | issues = bid.getIssues()
|
---|
| 312 | for issue in issues:
|
---|
| 313 | p: Pair = self._freq_map[issue]
|
---|
| 314 | v: Value = bid.getValue(issue)
|
---|
| 315 | vs: str = self.value_to_str(v, p)
|
---|
| 316 | p.vlist[vs] = p.vlist[vs] + 1
|
---|
| 317 |
|
---|
| 318 | def calc_op_value(self, bid: Bid):
|
---|
| 319 | value: float = 0
|
---|
| 320 | issues: set[str] = bid.getIssues()
|
---|
| 321 | val_util: list[float] = [0] * len(issues)
|
---|
| 322 | is_weight: list[float] = [0] * len(issues)
|
---|
| 323 | k: int = 0
|
---|
| 324 | for issue in issues:
|
---|
| 325 | p: Pair = self._freq_map[issue]
|
---|
| 326 | v: Value = bid.getValue(issue)
|
---|
| 327 | vs: str = self.value_to_str(v=v, p=p)
|
---|
| 328 | sum_of_values = 0
|
---|
| 329 | max_value = 1
|
---|
| 330 | for vString in p.vlist.keys():
|
---|
| 331 | sum_of_values = sum_of_values + p.vlist.get(vString)
|
---|
| 332 | max_value = max(max_value, p.vlist.get(vString))
|
---|
| 333 | val_util[k] = float(p.vlist.get(vs)) / max_value
|
---|
| 334 | mean = sum_of_values / len(p.vlist)
|
---|
| 335 | for v_string in p.vlist.keys():
|
---|
| 336 | is_weight[k] = is_weight[k] + math.pow(p.vlist.get(v_string) - mean, 2)
|
---|
| 337 | is_weight[k] = 1 / math.sqrt((is_weight[k] + 0.1) / len(p.vlist))
|
---|
| 338 | k = k + 1
|
---|
| 339 | sum_of_weight = 0
|
---|
| 340 | for k in range(len(issues)):
|
---|
| 341 | value = value + val_util[k] * is_weight[k]
|
---|
| 342 | sum_of_weight = sum_of_weight + is_weight[k]
|
---|
| 343 | return value / sum_of_weight
|
---|
| 344 |
|
---|
| 345 | def is_op_good(self, bid: Bid):
|
---|
| 346 | if bid is None:
|
---|
| 347 | return False
|
---|
| 348 | value = self.calc_op_value(bid=bid)
|
---|
| 349 | index = int(
|
---|
| 350 | ((self.t_split - 1) / (1 - self.t_phase) * (self._progress.get(get_ms_current_time()) - self.t_phase)))
|
---|
| 351 | op_threshold = max(1 - 2 * self.op_threshold[index], 0.2) if self.op_threshold is not None else 0.6
|
---|
| 352 | return value > op_threshold
|
---|
| 353 | # index = (int)((t_split - 1) / (1 - t_phase) * (progress.get(System.currentTimeMillis()) - t_phase));
|
---|
| 354 |
|
---|
| 355 | def is_last_turn(self):
|
---|
| 356 | return self._progress.get(time() * 1000) > 0.997
|
---|
| 357 |
|
---|
| 358 | def is_near_negotiation_end(self):
|
---|
| 359 | return self._progress.get(time() * 1000) > self.t_phase
|
---|
| 360 |
|
---|
| 361 | def is_social_welfare_time(self):
|
---|
| 362 | return self._progress.get(time() * 1000) > self.t_social_welfare
|
---|
| 363 |
|
---|
| 364 | def calc_utility(self, bid):
|
---|
| 365 | # get utility from utility space
|
---|
| 366 | return self._utility_space.getUtility(bid)
|
---|
| 367 |
|
---|
| 368 | def calc_social_welfare(self, bid: Bid):
|
---|
| 369 | return 0.8 * float(self.calc_utility(bid)) + math.fabs(1 - 0.8) * float(self.calc_op_value(bid))
|
---|
| 370 |
|
---|
| 371 | def cmp_social_welfare(self, first_bid, second_bid):
|
---|
| 372 | return self.calc_social_welfare(first_bid) >= self.calc_social_welfare(second_bid)
|
---|
| 373 |
|
---|
| 374 | def is_good(self, bid):
|
---|
| 375 | if bid is None:
|
---|
| 376 | return False
|
---|
| 377 | max_value = 0.95 if self._optimal_bid is None else 0.95 * float(self.calc_utility(self._optimal_bid))
|
---|
| 378 | avg_max_utility = self._persistent_data.get_avg_max_utility(self._opponent_name) \
|
---|
| 379 | if self._persistent_data._known_opponent(self._opponent_name) \
|
---|
| 380 | else self._avg_utility
|
---|
| 381 | self._util_threshold = max_value - (
|
---|
| 382 | max_value - 0.55 * self._avg_utility - 0.4 * avg_max_utility + 0.5 * pow(self._std_utility, 2)) * \
|
---|
| 383 | (math.exp(self.alpha * self._progress.get(get_ms_current_time())) - 1) / (math.exp(
|
---|
| 384 | self.alpha) - 1)
|
---|
| 385 | if self._util_threshold < self._min_utility:
|
---|
| 386 | self._util_threshold = self._min_utility
|
---|
| 387 | return float(self.calc_utility(bid)) >= self._util_threshold
|
---|
| 388 |
|
---|
| 389 | def first_is_good_idx(self):
|
---|
| 390 | for i in range(len(self._sorted_bid_list)):
|
---|
| 391 | if not self.is_good(self._sorted_bid_list[i]):
|
---|
| 392 | return i
|
---|
| 393 | return len(self._sorted_bid_list) - 1
|
---|
| 394 |
|
---|
| 395 | def on_negotiation_near_end(self):
|
---|
| 396 | slice_idx = self.first_is_good_idx()
|
---|
| 397 | end_slice = int(min(slice_idx + 0.005 * self._len_sorted_bid_list - 1, self._len_sorted_bid_list - 1))
|
---|
| 398 | idx = random.randint(0, slice_idx)
|
---|
| 399 |
|
---|
| 400 | if self._progress.get(get_ms_current_time()) >= 0.95:
|
---|
| 401 | bid = self.last_bids(idx)
|
---|
| 402 | if self.calc_utility(bid) <= 0.5:
|
---|
| 403 | bid = self._optimal_bid
|
---|
| 404 | return bid
|
---|
| 405 | # if self._progress.get(get_ms_current_time()) > 0.992 and self.is_good(self._best_offer_bid):
|
---|
| 406 | # return self._best_offer_bid
|
---|
| 407 | return self._sorted_bid_list[idx]
|
---|
| 408 |
|
---|
| 409 | def on_negotiation_continues(self):
|
---|
| 410 | bid: Bid = None
|
---|
| 411 |
|
---|
| 412 | slice_idx = self.first_is_good_idx()
|
---|
| 413 | end_slice = int(min(slice_idx + 0.005 * self._len_sorted_bid_list - 1, self._len_sorted_bid_list - 1))
|
---|
| 414 | for i in range(slice_idx, 0, -1):
|
---|
| 415 | tmp_bid = self._sorted_bid_list[i]
|
---|
| 416 | if tmp_bid == self._optimal_bid or self.is_op_good(tmp_bid):
|
---|
| 417 | bid = tmp_bid
|
---|
| 418 | break
|
---|
| 419 | if self._progress.get(get_ms_current_time()) > 0.992 and self.is_good(self._best_offer_bid):
|
---|
| 420 | bid = self._best_offer_bid
|
---|
| 421 | if bid is None or not self.is_good(bid):
|
---|
| 422 | idx = random.randint(0, slice_idx)
|
---|
| 423 | bid = self._sorted_bid_list[idx]
|
---|
| 424 | return bid
|
---|
| 425 |
|
---|
| 426 | def cmp_utility(self, first_bid, second_bid):
|
---|
| 427 | # return 1 if first_bid with higher utility, 0 else
|
---|
| 428 | return self._utility_space.getUtility(first_bid) > self._utility_space.getUtility(second_bid)
|
---|
| 429 |
|
---|
| 430 | def _find_bid(self):
|
---|
| 431 | bid: Bid = None
|
---|
| 432 | if self._best_offer_bid is None:
|
---|
| 433 | self._best_offer_bid = self._last_received_bid
|
---|
| 434 | elif self.cmp_utility(self._last_received_bid, self._best_offer_bid):
|
---|
| 435 | self._best_offer_bid = self._last_received_bid
|
---|
| 436 | # if self.is_social_welfare_time():
|
---|
| 437 | # self.on_negotiation_social_welfare()
|
---|
| 438 | if self.is_near_negotiation_end():
|
---|
| 439 | bid = self.on_negotiation_near_end()
|
---|
| 440 | else:
|
---|
| 441 | bid = self.on_negotiation_continues()
|
---|
| 442 |
|
---|
| 443 | action: Offer = Offer(self._me, bid)
|
---|
| 444 | return action
|
---|
| 445 |
|
---|
| 446 | def _my_turn(self):
|
---|
| 447 | # save average of the last avgSplit offers (only when frequency table is stabilized)
|
---|
| 448 | if self._opponent_name is None:
|
---|
| 449 | return Offer(self._me, self._optimal_bid)
|
---|
| 450 |
|
---|
| 451 | if self.is_near_negotiation_end():
|
---|
| 452 | index = int(
|
---|
| 453 | (self.t_split - 1) / (1 - self.t_phase) * (self._progress.get(get_ms_current_time()) - self.t_phase))
|
---|
| 454 | self.op_sum[index] += self.calc_op_value(self._last_received_bid)
|
---|
| 455 | self.op_counter[index] += 1
|
---|
| 456 | if self.is_good(self._last_received_bid) or (self.is_last_turn() and self.calc_utility(self._last_received_bid)>=0.5):
|
---|
| 457 | # if the last bid is good - accept it.
|
---|
| 458 | action = Accept(self._me, self._last_received_bid)
|
---|
| 459 | else:
|
---|
| 460 | action = self._find_bid()
|
---|
| 461 | return action
|
---|
| 462 |
|
---|
| 463 | def learn(self):
|
---|
| 464 | self.getReporter().log(logging.INFO, "party is learning")
|
---|
| 465 | # probably have to shift to self._negotiation_data_paths
|
---|
| 466 | for path in self._negotiation_data_paths:
|
---|
| 467 | try:
|
---|
| 468 | with open(path, "rb") as f:
|
---|
| 469 | nego_data = pickle.load(f)
|
---|
| 470 | self._persistent_data.update(nego_data)
|
---|
| 471 | except Exception as e:
|
---|
| 472 | print("error in learn function - persistent data update, error:{}", str(e))
|
---|
| 473 |
|
---|
| 474 | try:
|
---|
| 475 | with open(self._persistent_path, "wb") as pers_file:
|
---|
| 476 | pickle.dump(self._persistent_data, pers_file)
|
---|
| 477 | except Exception as e:
|
---|
| 478 | print("error in persistent path dump:{}", str(e))
|
---|
| 479 |
|
---|
| 480 | def process_agreements(self, agreements: Agreements):
|
---|
| 481 | # Check if we reached an agreement (walking away or passing the deadline
|
---|
| 482 | # results in no agreement)
|
---|
| 483 | self.getReporter().log(logging.INFO, "Length of agreements: {} :{}".format(len(agreements.getMap().items()),
|
---|
| 484 | agreements.getMap()))
|
---|
| 485 | if len(agreements.getMap().items()) > 0:
|
---|
| 486 | # Get the bid that is agreed upon and add it's value to our negotiation data
|
---|
| 487 | agreement: Bid = agreements.getMap().values().__iter__().__next__()
|
---|
| 488 | self._negotiation_data.add_agreement_util(float(self.calc_utility(agreement)))
|
---|
| 489 | self._negotiation_data.set_opponent_util(self.calc_op_value(agreement))
|
---|
| 490 | self.getReporter().log(logging.INFO, "Agreement in time: {} percent".format(self._progress.get(get_ms_current_time())))
|
---|
| 491 | self.getReporter().log(logging.INFO, "MY OWN THRESHOLD: {}".format(self._util_threshold))
|
---|
| 492 | self.getReporter().log(logging.INFO, "MY OWN UTIL:{}".format(self.calc_utility(agreement)))
|
---|
| 493 | self.getReporter().log(logging.INFO, "EXP OPPONENT UTIL:{}".format(self.calc_op_value(agreement)))
|
---|
| 494 | else:
|
---|
| 495 | if self._best_offer_bid is not None:
|
---|
| 496 | self._negotiation_data.add_agreement_util(float(self.calc_utility(self._best_offer_bid)))
|
---|
| 497 | self.getReporter().log(logging.INFO,
|
---|
| 498 | "!!!!!!!!!!!!!! NO AGREEMENT !!!!!!!!!!!!!!! /// MY THRESHOLD: {}".format(
|
---|
| 499 | self._util_threshold))
|
---|
| 500 |
|
---|
| 501 | self.getReporter().log(logging.INFO, "TIME OF AGREEMENT: {}".format(self._progress.get(get_ms_current_time())))
|
---|
| 502 | # update the opponent offers map, regardless of achieving agreement or not
|
---|
| 503 | try:
|
---|
| 504 | self._negotiation_data.update_opponent_offers(self.op_sum, self.op_counter)
|
---|
| 505 | except Exception as e:
|
---|
| 506 | self.getReporter().log(logging.INFO, "Error in process_agreements,{}".format(str(e)))
|
---|