Skip to content

Method: hashCode()

1: /*
2: * Copyright © 2020-2023 Fachhochschule für die Wirtschaft (FHDW) Hannover
3: *
4: * This file is part of othello-core.
5: *
6: * Othello-core is free software: you can redistribute it and/or modify
7: * it under the terms of the GNU General Public License as published by
8: * the Free Software Foundation, either version 3 of the License, or
9: * (at your option) any later version.
10: *
11: * Othello-core is distributed in the hope that it will be useful,
12: * but WITHOUT ANY WARRANTY; without even the implied warranty of
13: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14: * GNU General Public License for more details.
15: *
16: * You should have received a copy of the GNU General Public License
17: * along with othello-core. If not, see <http://www.gnu.org/licenses/>.
18: */
19: package de.fhdw.gaming.othello.core.domain.impl;
20:
21: import java.util.ArrayList;
22: import java.util.Collections;
23: import java.util.LinkedHashMap;
24: import java.util.List;
25: import java.util.Map;
26: import java.util.Objects;
27:
28: import de.fhdw.gaming.othello.core.domain.OthelloBoard;
29: import de.fhdw.gaming.othello.core.domain.OthelloField;
30: import de.fhdw.gaming.othello.core.domain.OthelloFieldState;
31: import de.fhdw.gaming.othello.core.domain.OthelloPosition;
32:
33: /**
34: * Implements {@link OthelloBoard}.
35: */
36: final class OthelloBoardImpl implements OthelloBoard {
37:
38: /**
39: * The minimum number of rows (and columns).
40: */
41: private static final int MINIMUM_BOARD_SIZE = 4;
42:
43: /**
44: * The fields of the board.
45: */
46: private final List<List<OthelloFieldImpl>> fields;
47: /**
48: * The fields sorted by state.
49: */
50: private final Map<OthelloFieldState, Map<OthelloPosition, OthelloFieldImpl>> fieldsByState;
51:
52: /**
53: * Creates an Othello board.
54: *
55: * @param size The number of rows (and columns). It must be an even positive number greater than or equal to 4.
56: * @throws IllegalArgumentException if the size does not meet the requirements.
57: */
58: OthelloBoardImpl(final int size) throws IllegalArgumentException {
59: if (size < OthelloBoardImpl.MINIMUM_BOARD_SIZE || size % 2 != 0) {
60: throw new IllegalArgumentException(
61: String.format("The board size %d is not an even positive number.", size));
62: }
63:
64: this.fieldsByState = new LinkedHashMap<>();
65: final Map<OthelloPosition, OthelloFieldImpl> emptyFields = new LinkedHashMap<>();
66: this.fieldsByState.put(OthelloFieldState.EMPTY, emptyFields);
67: this.fieldsByState.put(OthelloFieldState.BLACK, new LinkedHashMap<>());
68: this.fieldsByState.put(OthelloFieldState.WHITE, new LinkedHashMap<>());
69:
70: this.fields = new ArrayList<>(size);
71: for (int rowIndex = 0; rowIndex < size; ++rowIndex) {
72: final List<OthelloFieldImpl> row = new ArrayList<>(size);
73: for (int columnIndex = 0; columnIndex < size; ++columnIndex) {
74: final OthelloPosition position = OthelloPosition.of(rowIndex, columnIndex);
75: final OthelloFieldImpl field = new OthelloFieldImpl(this, position, OthelloFieldState.EMPTY);
76: row.add(field);
77: emptyFields.put(position, field);
78: }
79: this.fields.add(row);
80: }
81:
82: this.setup();
83: }
84:
85: /**
86: * Copies an Othello board.
87: *
88: * @param source The board to copy.
89: */
90: private OthelloBoardImpl(final OthelloBoardImpl source) {
91: Objects.requireNonNull(source, "source");
92:
93: this.fieldsByState = new LinkedHashMap<>();
94: this.fieldsByState.put(OthelloFieldState.EMPTY, new LinkedHashMap<>());
95: this.fieldsByState.put(OthelloFieldState.BLACK, new LinkedHashMap<>());
96: this.fieldsByState.put(OthelloFieldState.WHITE, new LinkedHashMap<>());
97:
98: final int size = source.getSize();
99: this.fields = new ArrayList<>(size);
100: for (int rowIndex = 0; rowIndex < size; ++rowIndex) {
101: final List<OthelloFieldImpl> originRow = source.fields.get(rowIndex);
102: final List<OthelloFieldImpl> row = new ArrayList<>(size);
103: for (int columnIndex = 0; columnIndex < size; ++columnIndex) {
104: final OthelloFieldImpl originField = originRow.get(columnIndex);
105: final OthelloFieldImpl field = new OthelloFieldImpl(
106: this,
107: OthelloPosition.of(rowIndex, columnIndex),
108: originField.getState());
109: row.add(field);
110: this.fieldsByState.get(field.getState()).put(field.getPosition(), field);
111: }
112: this.fields.add(row);
113: }
114: }
115:
116: /**
117: * Sets up the board by placing the first four tokens into the centre of the board.
118: */
119: private void setup() {
120: final int start = this.getSize() / 2 - 1;
121: this.fields.get(start).get(start).setState(OthelloFieldState.WHITE);
122: this.fields.get(start).get(start + 1).setState(OthelloFieldState.BLACK);
123: this.fields.get(start + 1).get(start).setState(OthelloFieldState.BLACK);
124: this.fields.get(start + 1).get(start + 1).setState(OthelloFieldState.WHITE);
125: }
126:
127: @Override
128: public String toString() {
129: return String.format("OthelloBoard[size=%d, fields=%s]", this.fields.size(), this.fields);
130: }
131:
132: @Override
133: public boolean equals(final Object obj) {
134: if (obj instanceof OthelloBoardImpl) {
135: final OthelloBoardImpl other = (OthelloBoardImpl) obj;
136: return this.fields.equals(other.fields);
137: }
138: return false;
139: }
140:
141: @Override
142: public int hashCode() {
143: return this.fields.hashCode();
144: }
145:
146: @Override
147: public int getSize() {
148: return this.fields.size();
149: }
150:
151: @Override
152: public boolean hasFieldAt(final OthelloPosition position) {
153: final int size = this.getSize();
154: final int row = position.getRow();
155: final int column = position.getColumn();
156: return row >= 0 && row < size && column >= 0 && column < size;
157: }
158:
159: @Override
160: public OthelloFieldImpl getFieldAt(final OthelloPosition position) {
161: if (!this.hasFieldAt(position)) {
162: throw new IllegalArgumentException(String.format("Position %s out of range.", position));
163: }
164:
165: return this.fields.get(position.getRow()).get(position.getColumn());
166: }
167:
168: @Override
169: public List<List<? extends OthelloField>> getFields() {
170: final List<List<? extends OthelloField>> result = new ArrayList<>();
171: this.fields.forEach((final List<OthelloFieldImpl> row) -> result.add(new ArrayList<>(row)));
172: return result;
173: }
174:
175: @Override
176: public Map<OthelloPosition, OthelloFieldImpl> getFieldsBeing(final OthelloFieldState fieldState) {
177: return Collections.unmodifiableMap(this.fieldsByState.get(fieldState));
178: }
179:
180: @Override
181: public OthelloBoardImpl deepCopy() {
182: return new OthelloBoardImpl(this);
183: }
184:
185: /**
186: * This operation is called by a {@link OthelloFieldImpl} when a field changes its state.
187: *
188: * @param field The field that changed its state.
189: * @param oldState The old state of the field.
190: */
191: void fieldChangedState(final OthelloFieldImpl field, final OthelloFieldState oldState) {
192: this.fieldsByState.get(oldState).remove(field.getPosition());
193: this.fieldsByState.get(field.getState()).put(field.getPosition(), field);
194: }
195: }