Skip to content

Method: getPlayers()

1: package de.fhdw.gaming.ipspiel23.c4.domain.impl;
2:
3: import java.util.Arrays;
4: import java.util.Collections;
5: import java.util.HashMap;
6: import java.util.Map;
7: import java.util.Objects;
8: import java.util.Optional;
9: import java.util.Set;
10:
11: import de.fhdw.gaming.core.domain.PlayerState;
12: import de.fhdw.gaming.ipspiel23.c4.domain.IC4Board;
13: import de.fhdw.gaming.ipspiel23.c4.domain.IC4Player;
14: import de.fhdw.gaming.ipspiel23.c4.domain.IC4Solution;
15: import de.fhdw.gaming.ipspiel23.c4.domain.IC4State;
16:
17: /**
18: * The default implementation of the {@link IC4State} interface.
19: */
20: public class C4State implements IC4State {
21:
22: /**
23: * The board of the Connect Four game.
24: */
25: private final IC4Board board;
26:
27: /**
28: * The players of the Connect Four game, ordered by their respective tokens such that
29: * {@code index == players[index].getToken() - 1 }
30: * holds true for all players.
31: */
32: private final IC4Player[] players;
33:
34: /**
35: * The index of the current player in the {@link #players} array.
36: */
37: private int currentPlayerIndex;
38:
39: /**
40: * The {@link Map} of players, mapping the player names to the respective player instances.
41: */
42: private Map<String, IC4Player> playerMap;
43:
44: /**
45: * Constructs a new {@link C4State} instance.
46: * @param board The board of the Connect Four game.
47: * @param players The players of the Connect Four game, ordered by their respective tokens such that
48: * {@code index == players[index].getToken() - 1 }
49: * holds true for all players.
50: * @throws NullPointerException If {@code board} or {@code players} is {@code null}.
51: * @throws IllegalArgumentException If the {@code players} array is not ordered by player tokens ascending,
52: * starting at token 1 and incrementing by 1 for each following player.
53: */
54: public C4State(final IC4Player[] players, final IC4Board board) {
55: this.board = Objects.requireNonNull(board, "board");
56: this.players = Objects.requireNonNull(players, "players");
57: for (int i = 0; i < players.length; i++) {
58: if (players[i].getToken() != i + 1) {
59: throw new IllegalArgumentException(String.format("The 'players' array must be ordered by player "
60: + "tokens ascending, starting at token 1 and incrementing by 1 for each following player!. "
61: + "Player %s at index %s violates this rule!",
62: players[i], i));
63: }
64: }
65: }
66:
67: /**
68: * Constructs a new {@link C4State} instance by copying the given {@code source} state.
69: * @param source The source state to copy.
70: */
71: private C4State(final C4State source) {
72: this.board = source.board.deepCopy();
73: this.players = new IC4Player[source.players.length];
74: this.currentPlayerIndex = source.currentPlayerIndex;
75: for (int i = 0; i < this.players.length; i++) {
76: this.players[i] = source.players[i].deepCopy();
77: }
78: }
79:
80: @Override
81: public Map<String, IC4Player> getPlayers() {
82:• if (this.playerMap == null) {
83: final HashMap<String, IC4Player> localPlayerMap = new HashMap<>(players.length);
84:• for (final IC4Player player : players) {
85: localPlayerMap.put(player.getName(), player);
86: }
87: this.playerMap = localPlayerMap;
88: }
89: return this.playerMap;
90: }
91:
92: @Override
93: public Set<IC4Player> computeNextPlayers() {
94: final IC4Player currentPlayer = this.getCurrentPlayer();
95: if (currentPlayer.getState().equals(PlayerState.PLAYING)) {
96: return Collections.singleton(currentPlayer);
97: }
98: return Collections.emptySet();
99: }
100:
101: @Override
102: public void nextTurn() {
103: this.currentPlayerIndex = (this.currentPlayerIndex + 1) % this.players.length;
104: System.out.println(String.format("next player: %s", this.players[currentPlayerIndex]));
105: }
106:
107: @Override
108: public IC4State deepCopy() {
109: // we implemented a mutable state pattern
110: // *never* did any of the sparse documentation of the gaming framework state
111: // anywhere that the state should be immutable
112: // that would have been nice to know that ahead of time :P
113: // Anyway, it should work with immutable states as well now.
114: return new C4State(this);
115: }
116:
117: @Override
118: public IC4Board getBoard() {
119: return this.board;
120: }
121:
122: @Override
123: public IC4Player getCurrentPlayer() {
124: return this.players[currentPlayerIndex];
125: }
126:
127: @Override
128: public void onMoveCompleted() {
129: // quick pass scanning for *any* solution
130: final Optional<IC4Solution> solution = this.board.tryFindFirstSolution();
131: if (solution.isPresent()) {
132: // second slower pass scanning for *all* solutions.
133: // we allow 2^31 - 1 different players on a board of up to 2^31 - 1 fields with
134: // a valid solution length between 2 and 2^31-1 ¯\_(ツ)_/¯
135: // if we allow for stuff like this, we should probably also handle changed rules
136: // where multiple winners could be possible :P
137: final Set<IC4Solution> solutions = this.board.findAllSolutions();
138: this.gameOver(Optional.of(solutions));
139: } else {
140: final boolean isFull = this.board.isFull();
141: if (isFull) {
142: this.gameOver(Optional.empty());
143: }
144: }
145: }
146:
147: /**
148: * Sets the state of all players to {@link PlayerState#LOST} except for the players that own at least
149: * one of the given {@code solutions}.
150: * If no solutions are given, all players are set to {@link PlayerState#DRAW}.
151: * @param optionalSolutions The solutions, if any.
152: */
153: private void gameOver(final Optional<Set<IC4Solution>> optionalSolutions) {
154: if (optionalSolutions.isPresent()) {
155: final Set<IC4Solution> solutions = optionalSolutions.get();
156: for (final IC4Player player : this.players) {
157: // we can't just do
158: // sln.getOwner().setState(WON)
159: // here because players are apparently deep copied with every move... :/
160: // so yeah... Now we have this nightmare here:
161: if (solutions.stream().anyMatch(sln -> sln.getOwner().equals(player))) {
162: player.setState(PlayerState.WON);
163: } else {
164: player.setState(PlayerState.LOST);
165: }
166: }
167: } else {
168: for (final IC4Player player : players) {
169: player.setState(PlayerState.DRAW);
170: }
171: }
172: }
173:
174: @Override
175: public String toString() {
176: final StringBuilder builder = new StringBuilder(64);
177: builder.append("C4State [board=").append(board.toString())
178: .append(", players=").append(Arrays.toString(players))
179: .append(", currentPlayer=").append(players[currentPlayerIndex].toString())
180: .append(']');
181: return builder.toString();
182: }
183: }