1 | package agents.anac.y2017.madagent;
|
---|
2 |
|
---|
3 | import java.util.ArrayList;
|
---|
4 | import java.util.Arrays;
|
---|
5 | import java.util.Comparator;
|
---|
6 | import java.util.HashMap;
|
---|
7 | import java.util.List;
|
---|
8 |
|
---|
9 | import genius.core.Bid;
|
---|
10 | import genius.core.issue.Issue;
|
---|
11 | import genius.core.issue.Value;
|
---|
12 | import genius.core.utility.UtilitySpace;
|
---|
13 |
|
---|
14 | public class OpponentModel {
|
---|
15 |
|
---|
16 | private static final int MAXIMUM_BOULWARE_LEVEL = 5;
|
---|
17 | private static final int BOULWARE_MULTIPLIER = 10;
|
---|
18 |
|
---|
19 | private class Preference {
|
---|
20 | private Issue issue;
|
---|
21 | private Value value;
|
---|
22 | private int count;
|
---|
23 | }
|
---|
24 |
|
---|
25 | private class Weight {
|
---|
26 | private Issue issue;
|
---|
27 | private double value;
|
---|
28 | }
|
---|
29 |
|
---|
30 | private UtilitySpace utilitySpace = null;
|
---|
31 | private Bid lastLastReceivedBid = null;
|
---|
32 | private Bid mostPreferredBid = null;
|
---|
33 | private double threshold = 0;
|
---|
34 | private int boulwareLevel = 0;
|
---|
35 | private int numberOfIssues = 0;
|
---|
36 | private List<Preference> preferences = null;
|
---|
37 |
|
---|
38 | public OpponentModel(UtilitySpace utilitySpace, double threshold) {
|
---|
39 | this.utilitySpace = utilitySpace;
|
---|
40 | this.threshold = threshold;
|
---|
41 |
|
---|
42 | numberOfIssues = utilitySpace.getDomain().getIssues().size();
|
---|
43 | preferences = new ArrayList<Preference>();
|
---|
44 | }
|
---|
45 |
|
---|
46 | public void offer(Bid lastReceivedBid, double numberOfRounds) {
|
---|
47 | decideBoulwareLevel(lastReceivedBid);
|
---|
48 | addPreference(lastReceivedBid);
|
---|
49 | }
|
---|
50 |
|
---|
51 | /*
|
---|
52 | * -------------------------- The Concept of Boulware Level
|
---|
53 | * -------------------------- - Boulware level is used for modifying the
|
---|
54 | * behavior of our agent. - If the boulware level is high, agent will
|
---|
55 | * increase threshold and will become more boulware. - The boulware level is
|
---|
56 | * decided by checking the offers of the opponent. - If the utilities of
|
---|
57 | * offers that is given by the opponent are low, according to our utility
|
---|
58 | * function, the boulware level will be high. - Boulware level is calculated
|
---|
59 | * by checking the previous offers. - Last last received bid is the bid
|
---|
60 | * received just before the last received bid.
|
---|
61 | */
|
---|
62 | public void decideBoulwareLevel(Bid lastReceivedBid) {
|
---|
63 | final double EDGE_OF_CONCEDING = threshold * 0.8;
|
---|
64 |
|
---|
65 | if (lastLastReceivedBid == null)
|
---|
66 | lastLastReceivedBid = lastReceivedBid;
|
---|
67 |
|
---|
68 | double lastLastReceivedUtility = utilitySpace.getUtility(lastLastReceivedBid);
|
---|
69 | double lastReceivedUtility = utilitySpace.getUtility(lastReceivedBid);
|
---|
70 | double finalReceivedUtility = (lastLastReceivedUtility + lastReceivedUtility) / 2;
|
---|
71 |
|
---|
72 | /*
|
---|
73 | * If final received utility (the average of the last 2 utilities) is
|
---|
74 | * greater than the edge of conceding, the boulware level becomes 0
|
---|
75 | * which means that our agent is ready to concede.
|
---|
76 | */
|
---|
77 | if (finalReceivedUtility > EDGE_OF_CONCEDING)
|
---|
78 | boulwareLevel = 0;
|
---|
79 | else
|
---|
80 | boulwareLevel = (int) ((EDGE_OF_CONCEDING - finalReceivedUtility) * BOULWARE_MULTIPLIER);
|
---|
81 |
|
---|
82 | /*
|
---|
83 | * The maximum boulware level is 5, so make it 5 if it's greater than 5.
|
---|
84 | */
|
---|
85 | if (boulwareLevel > MAXIMUM_BOULWARE_LEVEL)
|
---|
86 | boulwareLevel = MAXIMUM_BOULWARE_LEVEL;
|
---|
87 |
|
---|
88 | lastLastReceivedBid = lastReceivedBid;
|
---|
89 | }
|
---|
90 |
|
---|
91 | private void addPreference(Bid lastReceivedBid) {
|
---|
92 | /*
|
---|
93 | * Whenever a new offer is given, this method will be called For each
|
---|
94 | * item, number of occurrences will be stored in preferences list.
|
---|
95 | */
|
---|
96 | for (int issueNr = 1; issueNr <= numberOfIssues; issueNr++) {
|
---|
97 | Preference preference = new Preference();
|
---|
98 |
|
---|
99 | preference.issue = lastReceivedBid.getIssues().get(issueNr - 1);
|
---|
100 | preference.value = lastReceivedBid.getValue(issueNr);
|
---|
101 | preference.count = 1;
|
---|
102 |
|
---|
103 | int index = getIndexIfPreferredBefore(preference.value);
|
---|
104 |
|
---|
105 | if (index == -1)
|
---|
106 | preferences.add(preference);
|
---|
107 | else
|
---|
108 | preferences.get(index).count++;
|
---|
109 | }
|
---|
110 | }
|
---|
111 |
|
---|
112 | private int getIndexIfPreferredBefore(Value value) {
|
---|
113 | for (int i = 0; i < preferences.size(); i++)
|
---|
114 | if (preferences.get(i).value == value)
|
---|
115 | return i;
|
---|
116 |
|
---|
117 | return -1;
|
---|
118 | }
|
---|
119 |
|
---|
120 | public List<Bid> getAcceptableBids() {
|
---|
121 | /*
|
---|
122 | * Return list of bids that will possibly be accepted by the opponent.
|
---|
123 | */
|
---|
124 | computeMostPreferredBid();
|
---|
125 |
|
---|
126 | List<Bid> acceptableBids = new ArrayList<Bid>();
|
---|
127 | Weight[] weights = getWeights();
|
---|
128 |
|
---|
129 | for (double i = 1, previousWeight = 0; i < weights.length; i++) {
|
---|
130 | if (i > 2 && weights[(int) i].value - previousWeight > 0.02)
|
---|
131 | break;
|
---|
132 |
|
---|
133 | Issue currentIssue = weights[(int) i].issue;
|
---|
134 | addBidsWithDifferentValues(acceptableBids, currentIssue);
|
---|
135 |
|
---|
136 | previousWeight = weights[(int) i].value;
|
---|
137 | }
|
---|
138 |
|
---|
139 | return acceptableBids;
|
---|
140 | }
|
---|
141 |
|
---|
142 | public void computeMostPreferredBid() {
|
---|
143 | HashMap<Integer, Value> values = new HashMap<Integer, Value>();
|
---|
144 | sortPreferences();
|
---|
145 |
|
---|
146 | for (int i = 0, j = 0; i < preferences.size() && j < numberOfIssues; i++) {
|
---|
147 | Issue currentIssue = preferences.get(i).issue;
|
---|
148 | Value currentValue = preferences.get(i).value;
|
---|
149 |
|
---|
150 | if (!values.containsKey(currentIssue.getNumber())) {
|
---|
151 | values.put(currentIssue.getNumber(), currentValue);
|
---|
152 | j++;
|
---|
153 | }
|
---|
154 | }
|
---|
155 |
|
---|
156 | mostPreferredBid = new Bid(utilitySpace.getDomain(), values);
|
---|
157 | }
|
---|
158 |
|
---|
159 | private void sortPreferences() {
|
---|
160 | preferences.sort(new Comparator<Preference>() {
|
---|
161 | @Override
|
---|
162 | public int compare(Preference a, Preference b) {
|
---|
163 | return b.count - a.count;
|
---|
164 | }
|
---|
165 | });
|
---|
166 | }
|
---|
167 |
|
---|
168 | private Weight[] getWeights() {
|
---|
169 | /* Estimates the weight of each issue. */
|
---|
170 | Weight[] weights = new Weight[numberOfIssues + 1];
|
---|
171 |
|
---|
172 | calculateWeights(weights);
|
---|
173 | sortWeights(weights);
|
---|
174 |
|
---|
175 | return weights;
|
---|
176 | }
|
---|
177 |
|
---|
178 | private void calculateWeights(Weight[] weights) {
|
---|
179 | double sum = 0;
|
---|
180 |
|
---|
181 | for (int i = 0, j = 0; i < preferences.size() && j < numberOfIssues; i++) {
|
---|
182 | Issue currentIssue = preferences.get(i).issue;
|
---|
183 | int issueId = currentIssue.getNumber();
|
---|
184 |
|
---|
185 | if (weights[issueId] == null) {
|
---|
186 | weights[issueId] = new Weight();
|
---|
187 |
|
---|
188 | weights[issueId].issue = currentIssue;
|
---|
189 | weights[issueId].value = preferences.get(i).count;
|
---|
190 |
|
---|
191 | sum += weights[issueId].value;
|
---|
192 | j++;
|
---|
193 | }
|
---|
194 | }
|
---|
195 |
|
---|
196 | for (int i = 1; i < weights.length; i++) // Normalizing the weights
|
---|
197 | weights[i].value /= sum;
|
---|
198 | }
|
---|
199 |
|
---|
200 | private void sortWeights(Weight[] weights) {
|
---|
201 | weights[0] = new Weight();
|
---|
202 |
|
---|
203 | Arrays.sort(weights, new Comparator<Weight>() {
|
---|
204 | @Override
|
---|
205 | public int compare(Weight a, Weight b) {
|
---|
206 | if (a.value > b.value)
|
---|
207 | return 1;
|
---|
208 | if (a.value < b.value)
|
---|
209 | return -1;
|
---|
210 |
|
---|
211 | return 0;
|
---|
212 | }
|
---|
213 | });
|
---|
214 | }
|
---|
215 |
|
---|
216 | private void addBidsWithDifferentValues(List<Bid> acceptableBids, Issue currentIssue) {
|
---|
217 | /*
|
---|
218 | * Modifies the most proffered bid by opponent to increase utility for
|
---|
219 | * our agent.
|
---|
220 | */
|
---|
221 | int issueId = currentIssue.getNumber();
|
---|
222 |
|
---|
223 | List<Value> values = new ArrayList<Value>();
|
---|
224 | values.add(mostPreferredBid.getValue(issueId));
|
---|
225 |
|
---|
226 | for (int j = 0; j < preferences.size(); j++)
|
---|
227 | if (preferences.get(j).issue == currentIssue)
|
---|
228 | if (!values.contains((preferences.get(j).value)))
|
---|
229 | values.add(preferences.get(j).value);
|
---|
230 |
|
---|
231 | for (int j = 1; j < values.size(); j++) {
|
---|
232 | HashMap<Integer, Value> valueMap = mostPreferredBid.getValues();
|
---|
233 | valueMap.put(issueId, values.get(j));
|
---|
234 |
|
---|
235 | acceptableBids.add(new Bid(utilitySpace.getDomain(), valueMap));
|
---|
236 | }
|
---|
237 | }
|
---|
238 |
|
---|
239 | public double getNewThreshold() {
|
---|
240 | /*
|
---|
241 | * c is the constant value which makes threshold 1 if the boulware level
|
---|
242 | * is maximum (5)
|
---|
243 | */
|
---|
244 | final double c = (threshold * MAXIMUM_BOULWARE_LEVEL) / (1 - threshold);
|
---|
245 | return threshold * (1 + boulwareLevel / c);
|
---|
246 | }
|
---|
247 |
|
---|
248 | public Bid getMostPreferredBid() {
|
---|
249 | return mostPreferredBid;
|
---|
250 | }
|
---|
251 | }
|
---|