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
|
---|