Skip to contentMethod: equals(Object)
      1: package de.fhdw.gaming.ipspiel23.dilemma.domain.internals;
2: 
3: import java.util.LinkedHashMap;
4: import java.util.LinkedHashSet;
5: import java.util.Map;
6: import java.util.Objects;
7: import java.util.Set;
8: 
9: import de.fhdw.gaming.core.domain.GameException;
10: import de.fhdw.gaming.core.domain.PlayerState;
11: import de.fhdw.gaming.ipspiel23.dilemma.domain.DilemmaAnswerType;
12: import de.fhdw.gaming.ipspiel23.dilemma.domain.IDilemmaPlayer;
13: import de.fhdw.gaming.ipspiel23.dilemma.domain.IDilemmaState;
14: import de.fhdw.gaming.ipspiel23.dilemma.moves.IDilemmaMove;
15: import de.fhdw.gaming.ipspiel23.dilemma.moves.IDilemmaMoveFactory;
16: import de.fhdw.gaming.ipspiel23.dilemma.moves.internals.DilemmaDefaultMoveFactory;
17: import de.fhdw.gaming.ipspiel23.dilemma.strategy.internals.DilemmaRoundData;
18: import de.fhdw.gaming.ipspiel23.memory.GameMemoryIdentifier;
19: import de.fhdw.gaming.ipspiel23.memory.MemoryState;
20: 
21: import static de.fhdw.gaming.ipspiel23.dilemma.domain.IDilemmaGameBuilder.DEFAULT_OUTCOME_COOPERATE_COOPERATE;
22: import static de.fhdw.gaming.ipspiel23.dilemma.domain.IDilemmaGameBuilder.DEFAULT_OUTCOME_COOPERATE_DEFECT;
23: import static de.fhdw.gaming.ipspiel23.dilemma.domain.IDilemmaGameBuilder.DEFAULT_OUTCOME_DEFECT_COOPERATE;
24: import static de.fhdw.gaming.ipspiel23.dilemma.domain.IDilemmaGameBuilder.DEFAULT_OUTCOME_DEFECT_DEFECT;
25: 
26: /**
27:  * Implements {@link IDilemmaState}.
28:  */
29: public final class DilemmaState extends MemoryState<IDilemmaPlayer, IDilemmaState, DilemmaRoundData> 
30:     implements IDilemmaState {
31: 
32:     /**
33:      * The first player.
34:      */
35:     private final IDilemmaPlayer firstPlayer;
36:     /**
37:      * The second player.
38:      */
39:     private final IDilemmaPlayer secondPlayer;
40: 
41:     /**
42:      * Creates a Dilemma state.
43:      *
44:      * @param firstPlayer  The first player.
45:      * @param secondPlayer The second player.
46:      * @throws GameException if less than two players are found and if both players share the same name.
47:      */
48:     public DilemmaState(final IDilemmaPlayer firstPlayer, final IDilemmaPlayer secondPlayer)
49:             throws GameException {
50: 
51:         this.firstPlayer = Objects.requireNonNull(firstPlayer, "firstPlayer");
52:         this.secondPlayer = Objects.requireNonNull(secondPlayer, "secondPlayer");
53: 
54:         if (this.firstPlayer.getName().equals(this.secondPlayer.getName())) {
55:             throw new IllegalArgumentException(
56:                 String.format("Both players have the same name '%s'.", this.firstPlayer.getName()));
57:         }
58:     }
59: 
60:     /**
61:      * Creates a Dilemma state by copying an existing one.
62:      *
63:      * @param source The state to copy.
64:      */
65:     DilemmaState(final DilemmaState source) {
66:         this.firstPlayer = source.firstPlayer.deepCopy();
67:         this.secondPlayer = source.secondPlayer.deepCopy();
68:     }
69: 
70:     /**
71:      * Returns the first player.
72:      */
73:     @Override
74:     public IDilemmaPlayer getFirstPlayer() {
75:         return this.firstPlayer;
76:     }
77: 
78:     /**
79:      * Returns the second player.
80:      */
81:     @Override
82:     public IDilemmaPlayer getSecondPlayer() {
83:         return this.secondPlayer;
84:     }
85: 
86:     @Override
87:     public String toString() {
88:         return String.format("DilemmaState[firstPlayer=%s, secondPlayer=%s]",
89:             this.firstPlayer,
90:             this.secondPlayer);
91:     }
92: 
93:     @Override
94:     public boolean equals(final Object obj) {
95:•        if (obj instanceof DilemmaState) {
96:             final DilemmaState other = (DilemmaState) obj;
97:•            return this.firstPlayer.equals(other.firstPlayer) && this.secondPlayer.equals(other.secondPlayer);
98:         }
99:         return false;
100:     }
101: 
102:     @Override
103:     public IDilemmaState deepCopy() {
104:         return new DilemmaState(this);
105:     }
106: 
107:     @Override
108:     public int hashCode() {
109:         return Objects.hash(this.firstPlayer, this.secondPlayer);
110:     }
111: 
112:     @Override
113:     public Map<String, IDilemmaPlayer> getPlayers() {
114:         final Map<String, IDilemmaPlayer> result = new LinkedHashMap<>();
115:         result.put(this.firstPlayer.getName(), this.firstPlayer);
116:         result.put(this.secondPlayer.getName(), this.secondPlayer);
117:         return result;
118:     }
119: 
120:     /**
121:      *  Collects every player who didn't chose a move yet.
122:      */
123:     @Override
124:     public Set<IDilemmaPlayer> computeNextPlayers() {
125:         final Set<IDilemmaPlayer> playersWithoutMove = new LinkedHashSet<>();
126:         if (this.firstPlayer.getAnswer().isEmpty()) {
127:             playersWithoutMove.add(this.firstPlayer);
128:         }
129:         if (this.secondPlayer.getAnswer().isEmpty()) {
130:             playersWithoutMove.add(this.secondPlayer);
131:         }
132:         return playersWithoutMove;
133:     }
134: 
135:     @Override
136:     public IDilemmaPlayer getOpponentOf(final IDilemmaPlayer player) {
137:         if (this.firstPlayer.getName().equals(player.getName())) {
138:             return this.secondPlayer;
139:         }
140:         if (this.secondPlayer.getName().equals(player.getName())) {
141:             return this.firstPlayer;
142:         }
143:         throw new IllegalArgumentException("Unknown player: " + player);
144:     }
145: 
146:     /**
147:      *  Changes the state of the game according to the players' answers.
148:      */
149:     @Override
150:     public void nextTurn() {
151:         final Set<IDilemmaPlayer> playersWithoutMove = this.computeNextPlayers();
152:         if (playersWithoutMove.isEmpty()) {
153:             final DilemmaAnswerType answerOfFirstPlayer = this.firstPlayer.getAnswer().orElseThrow();
154:             final DilemmaAnswerType answerOfSecondPlayer = this.secondPlayer.getAnswer().orElseThrow();
155: 
156:             final Double outcomeOfFirstPlayer = this.firstPlayer
157:                 .getPossibleOutcomes()
158:                 .get(answerOfFirstPlayer)
159:                 .get(answerOfSecondPlayer);
160:             this.firstPlayer.setState(outcomeToState(outcomeOfFirstPlayer));
161:             this.firstPlayer.setOutcome(outcomeOfFirstPlayer);
162: 
163:             final Double outcomeOfSecondPlayer = this.secondPlayer
164:                 .getPossibleOutcomes()
165:                 .get(answerOfSecondPlayer)
166:                 .get(answerOfFirstPlayer);
167:             this.secondPlayer.setState(outcomeToState(outcomeOfSecondPlayer));
168:             this.secondPlayer.setOutcome(outcomeOfSecondPlayer);
169: 
170:             // update memory as needed
171:             final IDilemmaMoveFactory moveFactory = new DilemmaDefaultMoveFactory();
172:             final IDilemmaMove player1Move = moveFactory.fromAnswer(answerOfFirstPlayer);
173:             final IDilemmaMove player2Move = moveFactory.fromAnswer(answerOfSecondPlayer);
174:             
175:             final DilemmaRoundData round = DilemmaRoundData
176:                 .fromRaw(firstPlayer, player1Move, secondPlayer, player2Move);
177: 
178:             storeRoundData(GameMemoryIdentifier.of(firstPlayer, firstPlayer.getStrategy(), 
179:                 secondPlayer.getStrategy()), round);
180:             storeRoundData(GameMemoryIdentifier.of(secondPlayer, secondPlayer.getStrategy(),
181:                 firstPlayer.getStrategy()), round);
182:         }
183:     }
184: 
185:     /**
186:      * Computes a player state from an outcome.
187:      *
188:      * @param outcome The player's outcome.
189:      */
190:     private static PlayerState outcomeToState(final Double outcome) {
191:         final double[] predefinedOutcomes = {
192:             (double) DEFAULT_OUTCOME_COOPERATE_COOPERATE,
193:             (double) DEFAULT_OUTCOME_COOPERATE_DEFECT,
194:             (double) DEFAULT_OUTCOME_DEFECT_COOPERATE,
195:             (double) DEFAULT_OUTCOME_DEFECT_DEFECT
196:         };
197:         final PlayerState[] states = {
198:             PlayerState.WON,
199:             PlayerState.LOST,
200:             PlayerState.WON,
201:             PlayerState.LOST,
202:         };
203:         for (int i = 0; i < states.length; i++) {
204:             if (isWithinDelta(outcome, predefinedOutcomes[i], 0.01d)) {
205:                 return states[i];
206:             }
207:         }
208:         throw new IllegalStateException("Unable to map unknown outcome to player state :C");
209:     }
210:     
211:     /**
212:      * determines whether the specified value is within the delta of the comparand.
213:      * @param value the value
214:      * @param comparand the comparand
215:      * @param delta the delta
216:      */
217:     private static boolean isWithinDelta(final double value, final double comparand, final double delta) {
218:         return Math.abs(value - comparand) < delta;
219:     }
220: }