Skip to content

Package: TicTacToeBoardImpl

TicTacToeBoardImpl

nameinstructionbranchcomplexitylinemethod
TicTacToeBoardImpl(TicTacToeBoardImpl)
M: 0 C: 60
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 14
100%
M: 0 C: 1
100%
TicTacToeBoardImpl(int)
M: 18 C: 49
73%
M: 1 C: 5
83%
M: 1 C: 3
75%
M: 4 C: 11
73%
M: 0 C: 1
100%
addRowIfUniformlyMarked(Set, TicTacToeRow)
M: 0 C: 17
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
deepCopy()
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
equals(Object)
M: 2 C: 12
86%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 1 C: 3
75%
M: 0 C: 1
100%
getFieldAt(TicTacToePosition)
M: 0 C: 22
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
getFieldAtInternal(TicTacToePosition)
M: 0 C: 12
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
getFields()
M: 0 C: 12
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
getFields(TicTacToePosition, TicTacToeDirection)
M: 0 C: 30
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 8
100%
M: 0 C: 1
100%
getFieldsBeing(TicTacToeFieldState)
M: 0 C: 43
100%
M: 0 C: 6
100%
M: 0 C: 4
100%
M: 0 C: 9
100%
M: 0 C: 1
100%
getRowOfFields(TicTacToeRowType, int)
M: 12 C: 49
80%
M: 1 C: 5
83%
M: 1 C: 4
80%
M: 1 C: 10
91%
M: 0 C: 1
100%
getRowsUniformlyMarked()
M: 0 C: 47
100%
M: 0 C: 6
100%
M: 0 C: 4
100%
M: 0 C: 8
100%
M: 0 C: 1
100%
getSize()
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
hasFieldAt(TicTacToePosition)
M: 0 C: 23
100%
M: 0 C: 8
100%
M: 0 C: 5
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
hashCode()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
lambda$getFieldAtInternal$1(TicTacToePosition, Map)
M: 0 C: 7
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
lambda$getFields$0(List, Map)
M: 0 C: 9
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
toString()
M: 47 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * Copyright © 2021 Fachhochschule für die Wirtschaft (FHDW) Hannover
3: *
4: * This file is part of ipspiel22-tictactoe-core.
5: *
6: * ipspiel22-tictactoe-core is free software: you can redistribute it and/or modify it under
7: * the terms of the GNU General Public License as published by the Free Software
8: * Foundation, either version 3 of the License, or (at your option) any later
9: * version.
10: *
11: * ipspiel22-tictactoe-core is distributed in the hope that it will be useful, but WITHOUT
12: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14: * details.
15: *
16: * You should have received a copy of the GNU General Public License along with
17: * ipspiel22-tictactoe-core. If not, see <http://www.gnu.org/licenses/>.
18: */
19: package de.fhdw.gaming.ipspiel22.tictactoe.core.domain.impl;
20:
21: import java.util.ArrayList;
22: import java.util.HashMap;
23: import java.util.LinkedHashMap;
24: import java.util.LinkedHashSet;
25: import java.util.List;
26: import java.util.Map;
27: import java.util.Objects;
28: import java.util.Optional;
29: import java.util.Set;
30:
31: import de.fhdw.gaming.ipspiel22.tictactoe.core.domain.TicTacToeBoard;
32: import de.fhdw.gaming.ipspiel22.tictactoe.core.domain.TicTacToeDirection;
33: import de.fhdw.gaming.ipspiel22.tictactoe.core.domain.TicTacToeField;
34: import de.fhdw.gaming.ipspiel22.tictactoe.core.domain.TicTacToeFieldState;
35: import de.fhdw.gaming.ipspiel22.tictactoe.core.domain.TicTacToePosition;
36: import de.fhdw.gaming.ipspiel22.tictactoe.core.domain.TicTacToeRow;
37: import de.fhdw.gaming.ipspiel22.tictactoe.core.domain.TicTacToeRowType;
38:
39: /**
40: * Implements {@link TicTacToeBoard}.
41: */
42: final class TicTacToeBoardImpl implements TicTacToeBoard {
43:
44: /**
45: * The minimum number of rows (and columns).
46: */
47: private static final int MINIMUM_BOARD_SIZE = 2;
48:
49: /**
50: * The fields of the board, sorted first by row and then by column.
51: */
52: private final Map<Integer, Map<Integer, TicTacToeFieldImpl>> fields;
53:
54: /**
55: * Creates a Tic Tac Toe board.
56: *
57: * @param size The number of rows (and columns). It must be a positive number greater than or equal to 2.
58: * @throws IllegalArgumentException if the size does not meet the requirements.
59: */
60: TicTacToeBoardImpl(final int size) throws IllegalArgumentException {
61:• if (size < TicTacToeBoardImpl.MINIMUM_BOARD_SIZE) {
62: throw new IllegalArgumentException(
63: String.format(
64: "The board size %d is not a positive number >= %d.",
65: size,
66: TicTacToeBoardImpl.MINIMUM_BOARD_SIZE));
67: }
68:
69: this.fields = new LinkedHashMap<>();
70:• for (int rowIndex = 0; rowIndex < size; ++rowIndex) {
71: final Map<Integer, TicTacToeFieldImpl> row = new HashMap<>();
72: this.fields.put(rowIndex, row);
73:
74:• for (int columnIndex = 0; columnIndex < size; ++columnIndex) {
75: row.put(
76: columnIndex,
77: new TicTacToeFieldImpl(
78: this,
79: TicTacToePosition.of(rowIndex, columnIndex),
80: TicTacToeFieldState.EMPTY));
81: }
82: }
83: }
84:
85: /**
86: * Copies an TicTacToe board.
87: *
88: * @param source The board to copy.
89: */
90: private TicTacToeBoardImpl(final TicTacToeBoardImpl source) {
91: Objects.requireNonNull(source, "source");
92:
93: final int size = source.getSize();
94: this.fields = new LinkedHashMap<>();
95:• for (int rowIndex = 0; rowIndex < size; ++rowIndex) {
96: final Map<Integer, TicTacToeFieldImpl> row = new HashMap<>();
97: this.fields.put(rowIndex, row);
98:
99:• for (int columnIndex = 0; columnIndex < size; ++columnIndex) {
100: row.put(
101: columnIndex,
102: new TicTacToeFieldImpl(
103: this,
104: TicTacToePosition.of(rowIndex, columnIndex),
105: source.getFieldAtInternal(TicTacToePosition.of(rowIndex, columnIndex)).orElseThrow()
106: .getState()));
107: }
108: }
109: }
110:
111: @Override
112: public String toString() {
113: final StringBuilder result = new StringBuilder();
114:• for (int row = 0; row < this.fields.size(); ++row) {
115: final Map<Integer, TicTacToeFieldImpl> rowMap = this.fields.get(row);
116:• for (int column = 0; column < rowMap.size(); ++column) {
117: result.append(rowMap.get(column).getState().toString()).append(' ');
118: }
119: result.append('\n');
120: }
121: return result.toString();
122: }
123:
124: @Override
125: public boolean equals(final Object obj) {
126:• if (obj instanceof TicTacToeBoardImpl) {
127: final TicTacToeBoardImpl other = (TicTacToeBoardImpl) obj;
128: return this.fields.equals(other.fields);
129: }
130: return false;
131: }
132:
133: @Override
134: public int hashCode() {
135: return this.fields.hashCode();
136: }
137:
138: @Override
139: public int getSize() {
140: return this.fields.size();
141: }
142:
143: @Override
144: public boolean hasFieldAt(final TicTacToePosition position) {
145: final int size = this.getSize();
146: final int row = position.getRow();
147: final int column = position.getColumn();
148:• return row >= 0 && row < size && column >= 0 && column < size;
149: }
150:
151: @Override
152: public TicTacToeFieldImpl getFieldAt(final TicTacToePosition position) {
153:• if (!this.hasFieldAt(position)) {
154: throw new IllegalArgumentException(String.format("Position %s out of range.", position));
155: }
156:
157: return this.getFieldAtInternal(position).orElseThrow();
158: }
159:
160: @Override
161: public List<List<? extends TicTacToeField>> getFields() {
162: final List<List<? extends TicTacToeField>> result = new ArrayList<>();
163: this.fields.values()
164: .forEach((final Map<Integer, TicTacToeFieldImpl> row) -> result.add(new ArrayList<>(row.values())));
165: return result;
166: }
167:
168: @Override
169: public Set<TicTacToeRow> getRowsUniformlyMarked() {
170: final Set<TicTacToeRow> result = new LinkedHashSet<>();
171:• for (int rowIndex = 0; rowIndex < this.getSize(); ++rowIndex) {
172: addRowIfUniformlyMarked(result, this.getRowOfFields(TicTacToeRowType.ROW, rowIndex));
173: }
174:• for (int columnIndex = 0; columnIndex < this.getSize(); ++columnIndex) {
175: addRowIfUniformlyMarked(result, this.getRowOfFields(TicTacToeRowType.COLUMN, columnIndex));
176: }
177:• for (int diagonalIndex = 0; diagonalIndex < 2; ++diagonalIndex) {
178: addRowIfUniformlyMarked(result, this.getRowOfFields(TicTacToeRowType.DIAGONAL, diagonalIndex));
179: }
180: return result;
181: }
182:
183: @Override
184: public Map<TicTacToePosition, TicTacToeFieldImpl> getFieldsBeing(final TicTacToeFieldState fieldState) {
185: final Map<TicTacToePosition, TicTacToeFieldImpl> result = new LinkedHashMap<>();
186: final int size = this.getSize();
187:• for (int rowIndex = 0; rowIndex < size; ++rowIndex) {
188:• for (int columnIndex = 0; columnIndex < size; ++columnIndex) {
189: final TicTacToePosition position = TicTacToePosition.of(rowIndex, columnIndex);
190: final TicTacToeFieldImpl field = this.getFieldAtInternal(position).orElseThrow();
191:• if (field.getState().equals(fieldState)) {
192: result.put(position, field);
193: }
194: }
195: }
196:
197: return result;
198: }
199:
200: /**
201: * Adds a row to a set of rows if it is uniformly marked, i.e. contains only crosses or only noughts.
202: *
203: * @param result The result set.
204: * @param row The row to add.
205: */
206: private static void addRowIfUniformlyMarked(final Set<TicTacToeRow> result, final TicTacToeRow row) {
207: final Optional<TicTacToeFieldState> rowState = row.getState();
208:• if (rowState.isPresent() && !rowState.get().equals(TicTacToeFieldState.EMPTY)) {
209: result.add(row);
210: }
211: }
212:
213: @Override
214: public TicTacToeBoardImpl deepCopy() {
215: return new TicTacToeBoardImpl(this);
216: }
217:
218: /**
219: * Returns a row of fields given the row type and the row index.
220: *
221: * @param type The row type.
222: * @param index The row index.
223: */
224: private TicTacToeRow getRowOfFields(final TicTacToeRowType type, final int index) {
225: final List<? extends TicTacToeField> fieldsOfRow;
226:
227:• switch (type) {
228: case ROW:
229: fieldsOfRow = this.getFields(TicTacToePosition.of(index, 0), TicTacToeDirection.EAST);
230: break;
231: case COLUMN:
232: fieldsOfRow = this.getFields(TicTacToePosition.of(0, index), TicTacToeDirection.SOUTH);
233: break;
234: case DIAGONAL:
235:• if (index == 0) {
236: fieldsOfRow = this.getFields(TicTacToePosition.of(0, 0), TicTacToeDirection.SOUTHEAST);
237: } else {
238: fieldsOfRow = this.getFields(TicTacToePosition.of(0, this.getSize() - 1), TicTacToeDirection.SOUTHWEST);
239: }
240: break;
241: default:
242: throw new IllegalArgumentException(String.format("Unknown TicTacToeRowType %s.", type));
243: }
244:
245: return new TicTacToeRowImpl(type, index, fieldsOfRow);
246: }
247:
248: /**
249: * Returns a list of fields starting at a given position and extending towards a given direction till the board's
250: * end.
251: *
252: * @param start The starting position.
253: * @param direction The direction.
254: */
255: private List<? extends TicTacToeField> getFields(final TicTacToePosition start,
256: final TicTacToeDirection direction) {
257: final List<TicTacToeField> result = new ArrayList<>();
258:
259: TicTacToePosition pos = start;
260: Optional<TicTacToeFieldImpl> field = this.getFieldAtInternal(pos);
261:• while (field.isPresent()) {
262: result.add(field.get());
263: pos = direction.step(pos);
264: field = this.getFieldAtInternal(pos);
265: }
266:
267: return result;
268: }
269:
270: /**
271: * Returns a field at a given position. If the position does not exist, {@link Optional#empty()} is returned.
272: *
273: * @param position The position of the field.
274: * @return The field.
275: */
276: private Optional<TicTacToeFieldImpl> getFieldAtInternal(final TicTacToePosition position) {
277: return Optional.ofNullable(this.fields.get(position.getRow()))
278: .map((final Map<Integer, TicTacToeFieldImpl> map) -> map.get(position.getColumn()));
279: }
280: }