source: CSE3210/agent11/MyOpponentModel.py

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

#6 Added CSE3210 parties

File size: 6.4 KB
Line 
1from decimal import Decimal
2from typing import Optional, Dict
3
4from geniusweb.actions.Action import Action
5from geniusweb.issuevalue.Bid import Bid
6from geniusweb.issuevalue.Domain import Domain
7from geniusweb.issuevalue.Value import Value
8from geniusweb.opponentmodel.OpponentModel import OpponentModel
9from geniusweb.profile.utilityspace.UtilitySpace import UtilitySpace
10from geniusweb.progress.Progress import Progress
11from geniusweb.references.Parameters import Parameters
12
13
14class MyOpponentModel(UtilitySpace, OpponentModel):
15
16 def __init__(self, domain: Domain, frequencies: [str, Dict[Value, int]], total: int, resBid: Optional[Bid]):
17 """
18 Initializes the opponent model with the given parameters.
19 @param domain the domain of the negotiation
20 @param frequencies the frequencies dictionary
21 @param total the total amount of bids that have been recorded in the opponent model
22 """
23 self.domain = domain
24 self.frequencies = frequencies
25 self.total = total
26 self.resBid = resBid
27
28 # amount of unique bids, we will stop learning after this amount
29 self.stop_learning_after = 30
30 self.seen_bids = set()
31
32 def With(self, domain: Domain, resBid: Optional[Bid]) -> "OpponentModel":
33 return MyOpponentModel(domain=domain, frequencies={iss: {} for iss in domain.getIssues()}, total=0,
34 resBid=resBid)
35
36 def WithAction(self, action: Action, progress: Progress) -> "OpponentModel":
37 """
38 Updates the opponent model with the given action.
39 @param action most recent action to update the model with
40 @param progress the progress of the negotiation expressed as a percentage of the amount of rounds
41 that have passed
42 @return The updated opponent model.
43 """
44 bid: Bid = action.getBid()
45 if not bid:
46 raise ValueError('No bid provided!')
47 self.seen_bids.add(bid)
48 if len(self.seen_bids) >= self.stop_learning_after:
49 return self
50 for issue in self.domain.getIssues():
51 value = bid.getValue(issue)
52 if value not in self.frequencies[issue]:
53 self.frequencies[issue][value] = 0
54 self.frequencies[issue][value] += 1
55 self.total += 1
56 return self
57
58 @staticmethod
59 def create():
60 """
61 Creates an empty opponent model without any of the parameters set
62
63 @return an empty instance of MyOpponentModel
64 """
65 return MyOpponentModel(None, {}, 0, None)
66
67 # Override
68 def getUtility(self, bid: Bid) -> Decimal:
69 """
70 Estimates the opponents utility of a certain bid.
71 @param bid the bid to estimate the utility for
72 @return the utility as a decimal value between [0, 1]
73 """
74 if self.total == 0:
75 return Decimal(1)
76
77 issues = self.domain.getIssues()
78 issue_weights = self._get_issue_weights(issues=issues)
79 value_utilities = self._get_value_utilities_of_bid(issues=issues, bid=bid)
80
81 total = Decimal(0)
82 for issue in issue_weights:
83 weight_of_value = value_utilities[issue]
84
85 total += Decimal(issue_weights[issue] * weight_of_value)
86 return total
87
88 def _get_issue_weights(self, issues) -> dict:
89 """
90 Gets the estimated weight for each issue. It does this by first counting the amount
91 of unique values we have been offered for each
92 issue. Then it divides the total amount of bids by the amount of unique values this issue has.
93 This number is then used to calculate the weight by dividing itself by the total amount of unique values.
94
95 The logic behind this is that if the opponent only bids a few unique values for a issue, this issue
96 probably has high importance to it since it is unwilling to compromise on this issue, whereas it is
97 happy to concede on other issues.
98
99 We have verified this method produces the expected outcome.
100
101 @param issues all the issues to consider
102 @return a dictionary with the estimated weights for each issue.
103 """
104 unique_bids_per_issue = dict()
105 iwl = []
106 issue_weights_dict = dict()
107 total_unique_bids = 0
108 for issue in issues:
109 unique_bids = 0
110 for value in self.frequencies[issue]:
111 if self.frequencies[issue][value]:
112 unique_bids += 1
113 unique_bids_per_issue[issue] = unique_bids
114 total_unique_bids += unique_bids
115 for issue in issues:
116 score = total_unique_bids / unique_bids_per_issue[issue]
117 iwl.append(score)
118 issue_weights_dict[issue] = score
119 total = sum(iwl)
120 res = {k: (lambda x: (x / total))(v) for k, v in issue_weights_dict.items()}
121 # debug purposes:
122 # print('unique bids', unique_bids_per_issue)
123 # print('frequencies', self.frequencies)
124 # print('res', res)
125 return res
126
127 def _get_value_utilities_of_bid(self, issues, bid: Bid):
128 """
129 This method calculates the estimated utility for each value proposed in the bid.
130 It does this by dividing the frequency of which the value has been offered by the total number of offers.
131 You then get an utility in the range of [0, 1]
132
133 We have verified this method produces the expected outcome.
134
135 @param issues all the issues to consider
136 @param bid the bid to calculate this for
137 @return a dictionary with the utilities for each value in the bid
138 """
139 utilities = dict()
140 for issue in issues:
141 utilities[issue] = 0.5
142 value = bid.getValue(issue)
143 if issue in self.frequencies and value in self.frequencies[issue]:
144 # print('Frequencies', self.frequencies[issue])
145 # print('Value we are checking for', value)
146 # print('Result', self.frequencies[issue][value] / self.total)
147 utilities[issue] = self.frequencies[issue][value] / self.total
148 return utilities
149
150 def WithParameters(self, parameters: Parameters) -> "OpponentModel":
151 """
152 Does nothing, just returns itself.
153 @return itself
154 """
155 return self
Note: See TracBrowser for help on using the repository browser.