Skip to content

Method: getStrategies()

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.LinkedHashSet;
23: import java.util.List;
24: import java.util.Map;
25: import java.util.Optional;
26: import java.util.Set;
27: import java.util.regex.Pattern;
28:
29: import de.fhdw.gaming.core.domain.GameBuilder;
30: import de.fhdw.gaming.core.domain.GameBuilderFactory;
31: import de.fhdw.gaming.core.domain.GameException;
32: import de.fhdw.gaming.core.domain.Strategy;
33: import de.fhdw.gaming.core.ui.InputProvider;
34: import de.fhdw.gaming.core.ui.InputProviderException;
35: import de.fhdw.gaming.core.ui.type.validator.MaxValueValidator;
36: import de.fhdw.gaming.core.ui.type.validator.MinValueValidator;
37: import de.fhdw.gaming.core.ui.type.validator.PatternValidator;
38: import de.fhdw.gaming.core.ui.type.validator.Validator;
39: import de.fhdw.gaming.othello.core.domain.OthelloGameBuilder;
40: import de.fhdw.gaming.othello.core.domain.OthelloGameBuilderFactory;
41: import de.fhdw.gaming.othello.core.domain.OthelloPlayer;
42: import de.fhdw.gaming.othello.core.domain.OthelloPlayerBuilder;
43: import de.fhdw.gaming.othello.core.domain.OthelloStrategy;
44: import de.fhdw.gaming.othello.core.domain.factory.OthelloDefaultStrategyFactoryProvider;
45: import de.fhdw.gaming.othello.core.domain.factory.OthelloStrategyFactory;
46: import de.fhdw.gaming.othello.core.domain.factory.OthelloStrategyFactoryProvider;
47: import de.fhdw.gaming.othello.core.moves.factory.OthelloMoveFactory;
48: import de.fhdw.gaming.othello.core.moves.impl.OthelloDefaultMoveFactory;
49:
50: /**
51: * Implements {@link GameBuilderFactory} by creating an Othello game builder.
52: */
53: public final class OthelloGameBuilderFactoryImpl implements OthelloGameBuilderFactory {
54:
55: /**
56: * The number of players.
57: */
58: private static final int NUMBER_OF_PLAYERS = 2;
59: /**
60: * Minimum number of rows (and columns) of the board.
61: */
62: private static final int MIN_BOARD_SIZE = 4;
63: /**
64: * Maximum number of rows (and columns) of the board.
65: */
66: private static final int MAX_BOARD_SIZE = 16;
67: /**
68: * Smallest allowed maximum computation time per move in seconds.
69: */
70: private static final int MIN_MAX_COMPUTATION_TIME_PER_MOVE = 1;
71: /**
72: * Largest allowed maximum computation time per move in seconds.
73: */
74: private static final int MAX_MAX_COMPUTATION_TIME_PER_MOVE = 3600;
75:
76: /**
77: * All available Othello strategies.
78: */
79: private final Set<OthelloStrategy> strategies;
80:
81: /**
82: * Creates an Othello game factory. Othello strategies are loaded by using the {@link java.util.ServiceLoader}.
83: * <p>
84: * This constructor is meant to be used by the {@link java.util.ServiceLoader}.
85: */
86: public OthelloGameBuilderFactoryImpl() {
87: this(new OthelloDefaultStrategyFactoryProvider());
88: }
89:
90: /**
91: * Creates an Othello game factory.
92: *
93: * @param strategyFactoryProvider The {@link OthelloStrategyFactoryProvider} for loading Othello strategies.
94: */
95: OthelloGameBuilderFactoryImpl(final OthelloStrategyFactoryProvider strategyFactoryProvider) {
96: final OthelloMoveFactory moveFactory = new OthelloDefaultMoveFactory();
97:
98: final List<OthelloStrategyFactory> factories = strategyFactoryProvider.getStrategyFactories();
99: this.strategies = new LinkedHashSet<>();
100: for (final OthelloStrategyFactory factory : factories) {
101: this.strategies.add(factory.create(moveFactory));
102: }
103: }
104:
105: @Override
106: public String getName() {
107: return "Othello";
108: }
109:
110: @Override
111: public int getMinimumNumberOfPlayers() {
112: return OthelloGameBuilderFactoryImpl.NUMBER_OF_PLAYERS;
113: }
114:
115: @Override
116: public int getMaximumNumberOfPlayers() {
117: return OthelloGameBuilderFactoryImpl.NUMBER_OF_PLAYERS;
118: }
119:
120: @Override
121: public List<? extends Strategy<?, ?, ?>> getStrategies() {
122: return new ArrayList<>(this.strategies);
123: }
124:
125: @Override
126: public OthelloGameBuilder createGameBuilder(final InputProvider inputProvider) throws GameException {
127: try {
128: final OthelloGameBuilder gameBuilder = new OthelloGameBuilderImpl();
129:
130: @SuppressWarnings("unchecked")
131: final Map<String,
132: Object> gameData = inputProvider.needInteger(
133: OthelloGameBuilderFactory.PARAM_BOARD_SIZE,
134: "Number of rows (and columns)",
135: Optional.of(OthelloGameBuilder.DEFAULT_BOARD_SIZE),
136: new MinValueValidator<>(OthelloGameBuilderFactoryImpl.MIN_BOARD_SIZE),
137: new MaxValueValidator<>(OthelloGameBuilderFactoryImpl.MAX_BOARD_SIZE),
138: new Validator<>() {
139:
140: @Override
141: public Integer validate(final Integer value) throws InputProviderException {
142: if (value % 2 != 0) {
143: throw new InputProviderException(this.getInfo());
144: } else {
145: return value;
146: }
147: }
148:
149: @Override
150: public String getInfo() {
151: return "The value must be an even number.";
152: }
153: })
154: .needInteger(
155: GameBuilderFactory.PARAM_MAX_COMPUTATION_TIME_PER_MOVE,
156: "Maximum computation time per move in seconds",
157: Optional.of(GameBuilder.DEFAULT_MAX_COMPUTATION_TIME_PER_MOVE),
158: new MinValueValidator<>(
159: OthelloGameBuilderFactoryImpl.MIN_MAX_COMPUTATION_TIME_PER_MOVE),
160: new MaxValueValidator<>(
161: OthelloGameBuilderFactoryImpl.MAX_MAX_COMPUTATION_TIME_PER_MOVE))
162: .requestData("Board properties");
163:
164: gameBuilder.changeBoardSize((Integer) gameData.get(OthelloGameBuilderFactory.PARAM_BOARD_SIZE));
165: gameBuilder.changeMaximumComputationTimePerMove(
166: (Integer) gameData.get(GameBuilderFactory.PARAM_MAX_COMPUTATION_TIME_PER_MOVE));
167:
168: final InputProvider firstPlayerInputProvider = inputProvider.getNext(gameData);
169: final Map<String, Object> firstPlayerData = this
170: .requestPlayerData(firstPlayerInputProvider, "Player 1", Optional.empty());
171: final OthelloPlayer firstPlayerBuilder = this
172: .createPlayer(gameBuilder.createPlayerBuilder(), firstPlayerData);
173: final OthelloStrategy firstPlayerStrategy = this.getStrategy(firstPlayerData);
174: gameBuilder.addPlayer(firstPlayerBuilder, firstPlayerStrategy);
175:
176: final InputProvider secondPlayerInputProvider = firstPlayerInputProvider.getNext(firstPlayerData);
177: final Map<String, Object> secondPlayerData = this.requestPlayerData(
178: secondPlayerInputProvider,
179: "Player 2",
180: Optional.of(
181: !(Boolean) firstPlayerData.get(OthelloGameBuilderFactory.PARAM_PLAYER_USING_BLACK_TOKENS)));
182: final OthelloPlayer secondPlayerBuilder = this
183: .createPlayer(gameBuilder.createPlayerBuilder(), secondPlayerData);
184: final OthelloStrategy secondPlayerStrategy = this.getStrategy(secondPlayerData);
185: gameBuilder.addPlayer(secondPlayerBuilder, secondPlayerStrategy);
186:
187: return gameBuilder;
188: } catch (final InputProviderException e) {
189: throw new GameException(String.format("Creating Othello game was aborted: %s", e.getMessage()), e);
190: }
191: }
192:
193: /**
194: * Initialises an Othello player builder.
195: *
196: * @param inputProvider The input provider.
197: * @param title The title for the UI.
198: * @param usingBlackTokens If set, determines the mandatory colour of the player. Otherwise, the colour is initially
199: * set to black, and the user can change it at will.
200: * @return {@code playerBuilder}.
201: * @throws InputProviderException if the operation has been aborted prematurely (e.g. if the user cancelled a
202: * dialog).
203: */
204: @SuppressWarnings("unchecked")
205: private Map<String, Object> requestPlayerData(final InputProvider inputProvider, final String title,
206: final Optional<Boolean> usingBlackTokens) throws InputProviderException {
207:
208: inputProvider
209: .needString(
210: GameBuilderFactory.PARAM_PLAYER_NAME,
211: "Name",
212: Optional.empty(),
213: new PatternValidator(Pattern.compile("\\S+(\\s+\\S+)*")))
214: .needBoolean(
215: OthelloGameBuilderFactory.PARAM_PLAYER_USING_BLACK_TOKENS,
216: "Black colour",
217: Optional.of(Boolean.TRUE))
218: .needObject(GameBuilderFactory.PARAM_PLAYER_STRATEGY, "Strategy", Optional.empty(), this.strategies);
219:
220: if (usingBlackTokens.isPresent()) {
221: inputProvider
222: .fixedBoolean(OthelloGameBuilderFactory.PARAM_PLAYER_USING_BLACK_TOKENS, usingBlackTokens.get());
223: }
224:
225: return inputProvider.requestData(title);
226: }
227:
228: /**
229: * Creates an Othello player.
230: *
231: * @param playerBuilder The player builder.
232: * @param playerData The requested player data.
233: * @return The created {@link OthelloPlayer}.
234: */
235: private OthelloPlayer createPlayer(final OthelloPlayerBuilder playerBuilder, final Map<String, Object> playerData)
236: throws GameException {
237:
238: return playerBuilder.changeName((String) playerData.get(GameBuilderFactory.PARAM_PLAYER_NAME))
239: .changeUsingBlackTokens(
240: (Boolean) playerData.get(OthelloGameBuilderFactory.PARAM_PLAYER_USING_BLACK_TOKENS))
241: .build();
242: }
243:
244: /**
245: * Returns an Othello strategy.
246: *
247: * @param playerData The requested player data.
248: * @return The Othello strategy.
249: */
250: private OthelloStrategy getStrategy(final Map<String, Object> playerData) {
251: return (OthelloStrategy) playerData.get(GameBuilderFactory.PARAM_PLAYER_STRATEGY);
252: }
253: }