[74] | 1 | import math
|
---|
| 2 | import time
|
---|
| 3 | from typing import List, Set, Dict
|
---|
| 4 |
|
---|
| 5 | from geniusweb.issuevalue.Bid import Bid
|
---|
| 6 | from geniusweb.issuevalue.Domain import Domain
|
---|
| 7 | from geniusweb.issuevalue.Value import Value
|
---|
| 8 | from geniusweb.profile.Profile import Profile
|
---|
| 9 |
|
---|
| 10 |
|
---|
| 11 | class AgentUtility:
|
---|
| 12 |
|
---|
| 13 | def __init__(self, profile: Profile = None, progress=None):
|
---|
| 14 | self._profile = profile
|
---|
| 15 | self._progress = progress
|
---|
| 16 | self._self_bid_history = []
|
---|
| 17 | self._opponent_weight_heuristic = {}
|
---|
| 18 | self._issue_count_dict: Dict[str, Dict[Value, int]] = None
|
---|
| 19 |
|
---|
| 20 | def set_profile(self, profile: Profile):
|
---|
| 21 | self._profile = profile
|
---|
| 22 | self._intialize_weights()
|
---|
| 23 | self._initialize_opponent_issue_count()
|
---|
| 24 |
|
---|
| 25 | def set_progress(self, progress):
|
---|
| 26 | self._progress = progress
|
---|
| 27 |
|
---|
| 28 | def get_weight_heuristic(self):
|
---|
| 29 | return self._opponent_weight_heuristic
|
---|
| 30 |
|
---|
| 31 | def _intialize_weights(self):
|
---|
| 32 | """"
|
---|
| 33 | Initializes the _opponent_weight_heuristic with values that are equally divided over the issues
|
---|
| 34 | If there are 2 issues then the initial weight heuristic becomes {(issue1 : 0.5), (issue2: 0.5)}
|
---|
| 35 | """""
|
---|
| 36 | weightDict = self._profile.getProfile().getWeights()
|
---|
| 37 | weightList = []
|
---|
| 38 | for key, value in weightDict.items():
|
---|
| 39 | weightList.append((key, value))
|
---|
| 40 | # sort the weights in decending order
|
---|
| 41 | self.weights = sorted(weightList, key=lambda tup: tup[1], reverse=True)
|
---|
| 42 |
|
---|
| 43 | # instantiate opponent_weights heuristic
|
---|
| 44 | heuristic_weights_dict = weightDict
|
---|
| 45 | initialWeightDistribution: float = 1 / len(heuristic_weights_dict.items())
|
---|
| 46 | for key, value in heuristic_weights_dict.items():
|
---|
| 47 | heuristic_weights_dict[key] = initialWeightDistribution
|
---|
| 48 | self._opponent_weight_heuristic = heuristic_weights_dict
|
---|
| 49 |
|
---|
| 50 | def _initialize_opponent_issue_count(self):
|
---|
| 51 | """""
|
---|
| 52 | Initializes an empty _issue_count_dict
|
---|
| 53 | """""
|
---|
| 54 | domain: Domain = self._profile.getProfile().getDomain()
|
---|
| 55 | issues: Set[str] = domain.getIssues()
|
---|
| 56 |
|
---|
| 57 | self._issue_count_dict: Dict[str, Dict[Value, int]] = {}
|
---|
| 58 | for issue in issues:
|
---|
| 59 | self._issue_count_dict[issue]: Dict[Value, int] = {}
|
---|
| 60 | for value in domain.getValues(issue):
|
---|
| 61 | self._issue_count_dict[issue][value] = 0
|
---|
| 62 |
|
---|
| 63 | def get_bid_history(self):
|
---|
| 64 | """""
|
---|
| 65 | returns the bid history
|
---|
| 66 | """""
|
---|
| 67 | return self._self_bid_history
|
---|
| 68 |
|
---|
| 69 | def speed_factor(self):
|
---|
| 70 | """""
|
---|
| 71 | returns progress multiplied by a constant
|
---|
| 72 | """""
|
---|
| 73 | return math.ceil(self._progress.get(time.time() * 1000) * 50)
|
---|
| 74 |
|
---|
| 75 | def update_opponent_issue_count(self, bid):
|
---|
| 76 | """""
|
---|
| 77 | Updates the empty _issue_count_dict, by incrementing the counter of that value in each issue of the bid
|
---|
| 78 | """""
|
---|
| 79 | for issue, value in bid.getIssueValues().items():
|
---|
| 80 | self._issue_count_dict[issue][value] += 1
|
---|
| 81 |
|
---|
| 82 | def get_opponent_issue_count(self):
|
---|
| 83 | """""
|
---|
| 84 | Get the normalized _issue_count_dict, it does this by dividing the occurence count of the value by the max occurence count of a value in that issue.
|
---|
| 85 | This is done for each issue.
|
---|
| 86 | """""
|
---|
| 87 |
|
---|
| 88 | normalized_opponent_issue_count: Dict[str, Dict[Value, float]] = {}
|
---|
| 89 |
|
---|
| 90 | for issue in self._issue_count_dict:
|
---|
| 91 | normalized_opponent_issue_count[issue] = {}
|
---|
| 92 | total_issues: int = max(self._issue_count_dict[issue].values())
|
---|
| 93 | division_factor = total_issues if total_issues != 0 else 1
|
---|
| 94 | for value in self._issue_count_dict[issue]:
|
---|
| 95 | normalized_opponent_issue_count[issue][value] = self._issue_count_dict[issue][value] / division_factor
|
---|
| 96 |
|
---|
| 97 | return normalized_opponent_issue_count
|
---|
| 98 |
|
---|
| 99 | def update_opponent_weight_heuristic(self, bid: Bid):
|
---|
| 100 | """"
|
---|
| 101 | This method updates the opponent weight heuristic accordingly so that the total weight is always 1
|
---|
| 102 | if issues values are changed we decrease the weight, if they stay the same we increase the weight'
|
---|
| 103 | "q" is the size of tee changes we make to the weights
|
---|
| 104 | """
|
---|
| 105 | if len(self._self_bid_history) > 2:
|
---|
| 106 | prev_bid = self._self_bid_history[len(self._self_bid_history) - 3][0]
|
---|
| 107 | else:
|
---|
| 108 | return
|
---|
| 109 |
|
---|
| 110 | CONSTANT_q = 0.1
|
---|
| 111 | # If issues get changed a lot then they are probably less important to the opponents
|
---|
| 112 | bidIssueValues = bid.getIssueValues()
|
---|
| 113 | prevBidIssueValues = prev_bid.getIssueValues()
|
---|
| 114 | differentIssueKeys: [str] = []
|
---|
| 115 | sameIssueKeys: [str] = []
|
---|
| 116 |
|
---|
| 117 | for key, value in bidIssueValues.items():
|
---|
| 118 | # to make sure that the weight stays between 0 and 1 there are extra conditions to see
|
---|
| 119 | # if the weight of that particular issue can be further incremented or decremented so we don't go above 1 or below 0
|
---|
| 120 | if value != prevBidIssueValues[key] and self._opponent_weight_heuristic[key] > 0 + CONSTANT_q:
|
---|
| 121 | differentIssueKeys.append(key)
|
---|
| 122 | elif self._opponent_weight_heuristic[key] < 1 - CONSTANT_q:
|
---|
| 123 | sameIssueKeys.append(key)
|
---|
| 124 |
|
---|
| 125 | # if all the issue values change or if none of the values change don't update the heuristic,
|
---|
| 126 | if len(sameIssueKeys) != 0 and len(differentIssueKeys) != 0:
|
---|
| 127 | for key, value in self._opponent_weight_heuristic.items():
|
---|
| 128 | if key in differentIssueKeys:
|
---|
| 129 | self._opponent_weight_heuristic[key] -= (CONSTANT_q / len(
|
---|
| 130 | differentIssueKeys)) # self._opponent_weight_heuristic[key] * n #(n / len(differentIssueKeys))
|
---|
| 131 | else:
|
---|
| 132 | self._opponent_weight_heuristic[key] += (CONSTANT_q / len(
|
---|
| 133 | sameIssueKeys)) # self._opponent_weight_heuristic[key] * n # (n / len(sameIssueKeys))
|
---|
| 134 |
|
---|
| 135 | def append_to_bid_history(self, bid: Bid, own_bid):
|
---|
| 136 | """""
|
---|
| 137 | Adds a bid to the bid history
|
---|
| 138 | """""
|
---|
| 139 | self._self_bid_history.append((bid, self._profile.getProfile().getUtility(bid), own_bid))
|
---|
| 140 |
|
---|
| 141 | def get_last_own_bid_utility(self):
|
---|
| 142 | """""
|
---|
| 143 | returns the utility of the last bid made by the agent itself
|
---|
| 144 | """""
|
---|
| 145 | if not self._self_bid_history[-1][2]:
|
---|
| 146 | if len(self._self_bid_history) <= 1:
|
---|
| 147 | return 1
|
---|
| 148 | return self._self_bid_history[-2][1]
|
---|
| 149 | else:
|
---|
| 150 | return self._self_bid_history[-1][1]
|
---|
| 151 |
|
---|
| 152 | def get_last_opponent_bid_utility(self):
|
---|
| 153 | """""
|
---|
| 154 | returns the utility of the last bid made by the agent itself
|
---|
| 155 | """""
|
---|
| 156 | if not self._self_bid_history[-1][2]:
|
---|
| 157 | return self._self_bid_history[-1][1]
|
---|
| 158 | else:
|
---|
| 159 | return self._self_bid_history[-2][1]
|
---|
| 160 |
|
---|
| 161 | def rate_bid(self, bid: Bid, opponent_issue_percentage: Dict[str, Dict[Value, float]], opponent_issue_weights):
|
---|
| 162 | """""
|
---|
| 163 | Given a bid and the normalized _issue_count_dict this method estimates the utility of a Bid
|
---|
| 164 | """""
|
---|
| 165 | utility = 0
|
---|
| 166 | for issue, value in bid.getIssueValues().items():
|
---|
| 167 | utility += opponent_issue_percentage[issue][value] * opponent_issue_weights[issue]
|
---|
| 168 | return utility
|
---|