Skip to content

Method: deepCopy()

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