Skip to content

Method: getMinimumNumberOfPlayers()

1: package de.fhdw.gaming.ipspiel23.c4.domain.impl;
2:
3: import java.util.ArrayList;
4: import java.util.HashSet;
5: import java.util.List;
6: import java.util.Map;
7: import java.util.Optional;
8: import java.util.Set;
9: import java.util.regex.Pattern;
10:
11: import de.fhdw.gaming.core.domain.GameException;
12: import de.fhdw.gaming.core.domain.Strategy;
13: import de.fhdw.gaming.core.ui.InputProvider;
14: import de.fhdw.gaming.core.ui.InputProviderException;
15: import de.fhdw.gaming.core.ui.type.validator.MaxValueValidator;
16: import de.fhdw.gaming.core.ui.type.validator.MinValueValidator;
17: import de.fhdw.gaming.core.ui.type.validator.PatternValidator;
18: import de.fhdw.gaming.ipspiel23.c4.domain.IC4GameBuilder;
19: import de.fhdw.gaming.ipspiel23.c4.domain.IC4GameBuilderFactory;
20: import de.fhdw.gaming.ipspiel23.c4.domain.IC4Player;
21: import de.fhdw.gaming.ipspiel23.c4.domain.IC4PlayerBuilder;
22: import de.fhdw.gaming.ipspiel23.c4.domain.impl.validation.C4BoardLimits;
23: import de.fhdw.gaming.ipspiel23.c4.domain.impl.validation.C4BoardValidator;
24: import de.fhdw.gaming.ipspiel23.c4.domain.impl.validation.C4BoardValidatorInfo;
25: import de.fhdw.gaming.ipspiel23.c4.moves.factory.IC4MoveFactory;
26: import de.fhdw.gaming.ipspiel23.c4.moves.impl.C4DefaultMoveFactory;
27: import de.fhdw.gaming.ipspiel23.c4.strategies.C4DefaultStrategyFactoryProvider;
28: import de.fhdw.gaming.ipspiel23.c4.strategies.IC4Strategy;
29: import de.fhdw.gaming.ipspiel23.c4.strategies.IC4StrategyFactory;
30: import de.fhdw.gaming.ipspiel23.c4.strategies.IC4StrategyFactoryProvider;
31: import de.fhdw.gaming.ipspiel23.c4.utils.ByRef;
32: import de.fhdw.gaming.ipspiel23.c4.utils.MakeRef;
33:
34: import static de.fhdw.gaming.ipspiel23.c4.domain.impl.validation.C4BoardLimits.MAX_NUMBER_OF_PLAYERS;
35: import static de.fhdw.gaming.ipspiel23.c4.domain.impl.validation.C4BoardLimits.MIN_NUMBER_OF_PLAYERS;
36:
37: /**
38: * The default implementation of {@link IC4GameBuilderFactory}.
39: */
40: public final class C4GameBuilderFactory implements IC4GameBuilderFactory {
41:
42: /**
43: * Smallest allowed maximum computation time per move in seconds.
44: */
45: private static final int MIN_MAX_COMPUTATION_TIME_PER_MOVE = 1;
46: /**
47: * Largest allowed maximum computation time per move in seconds.
48: */
49: private static final int MAX_MAX_COMPUTATION_TIME_PER_MOVE = 3600;
50:
51: /**
52: * The set of strategies that are available for the game.
53: */
54: private final Set<IC4Strategy> strategies;
55:
56: /**
57: * Creates a new instance of {@link C4GameBuilderFactory} with the default strategy factory provider.
58: */
59: public C4GameBuilderFactory() {
60: this(new C4DefaultStrategyFactoryProvider());
61: }
62:
63: /**
64: * Creates a new instance of {@link C4GameBuilderFactory} with the given strategy factory provider.
65: *
66: * @param strategyFactoryProvider The strategy factory provider to use.
67: */
68: public C4GameBuilderFactory(final IC4StrategyFactoryProvider strategyFactoryProvider) {
69: final IC4MoveFactory moveFactory = new C4DefaultMoveFactory();
70: final List<IC4StrategyFactory> factories = strategyFactoryProvider.getStrategyFactories();
71: this.strategies = new HashSet<>();
72: for (final IC4StrategyFactory factory : factories) {
73: final IC4Strategy strategy = factory.create(moveFactory);
74: strategies.add(strategy);
75: }
76: }
77:
78: @Override
79: public String getName() {
80: // int.MAX / 2 (because 2 is the minimum solution size)
81: // at least in theory every player should be allowed to make
82: // solution size moves :)
83: return "Connect 2 <= N <= 1,073,741,823";
84: }
85:
86: @Override
87: public int getMinimumNumberOfPlayers() {
88: return MIN_NUMBER_OF_PLAYERS;
89: }
90:
91: @Override
92: public int getMaximumNumberOfPlayers() {
93: return MAX_NUMBER_OF_PLAYERS;
94: }
95:
96: @Override
97: public List<? extends Strategy<?, ?, ?>> getStrategies() {
98: return new ArrayList<>(this.strategies);
99: }
100:
101: @SuppressWarnings("unchecked")
102: @Override
103: public IC4GameBuilder createGameBuilder(final InputProvider inputProvider) throws GameException {
104: try {
105: final IC4GameBuilder gameBuilder = new C4GameBuilder();
106:
107: // basic game properties
108: final Map<String, Object> dataSet = inputProvider
109: .needInteger(IC4GameBuilderFactory.PARAM_PLAYER_COUNT,
110: "Number of players",
111: Optional.of(IC4GameBuilder.DEFAULT_PLAYER_COUNT),
112: new MinValueValidator<>(MIN_NUMBER_OF_PLAYERS),
113: new MaxValueValidator<>(MAX_NUMBER_OF_PLAYERS))
114: .needInteger(IC4GameBuilderFactory.PARAM_MAX_COMPUTATION_TIME_PER_MOVE,
115: "Maximum computation time per move in seconds",
116: Optional.of(IC4GameBuilder.DEFAULT_MAX_COMPUTATION_TIME_PER_MOVE),
117: new MinValueValidator<>(MIN_MAX_COMPUTATION_TIME_PER_MOVE),
118: new MaxValueValidator<>(MAX_MAX_COMPUTATION_TIME_PER_MOVE))
119: .requestData("Game properties");
120:
121: final int maxComputationTime = (int) dataSet.get(IC4GameBuilderFactory.PARAM_MAX_COMPUTATION_TIME_PER_MOVE);
122: gameBuilder.changeMaximumComputationTimePerMove(maxComputationTime);
123:
124: final int playerCount = (int) dataSet.get(IC4GameBuilderFactory.PARAM_PLAYER_COUNT);
125:
126: // players
127: // we need to keep track of the input provider and data set as they have
128: // to be passed to the next player input providers
129: // therefore "pass by reference" or well... just wrap them in a reference type
130: // to emulate that behavior :D
131: // In C you'd just pass the addresses of inputProvider and dataSet
132: final ByRef<InputProvider> inputProviderRef = MakeRef.of(inputProvider);
133: final ByRef<Map<String, Object>> dataSetRef = MakeRef.of(dataSet);
134: for (int i = 0; i < playerCount; i++) {
135: addPlayer(inputProviderRef, dataSetRef, gameBuilder);
136: }
137:
138: // board properties
139: // we have cross dependencies relevant for validation between the board properties.
140: // therefore we share a validation state between the responsible input validators
141: final C4BoardLimits sharedValidationState = new C4BoardLimits(playerCount,
142: IC4GameBuilder.DEFAULT_BOARD_ROWS,
143: IC4GameBuilder.DEFAULT_BOARD_COLUMNS,
144: IC4GameBuilder.DEFAULT_REQUIRED_SOLUTION_SIZE);
145:
146: final C4BoardValidator rowCountValidator = new C4BoardValidator(sharedValidationState,
147: context -> context.getState().setRowCount(context.getValue()), C4BoardValidatorInfo.ROW_COUNT);
148: final C4BoardValidator columnCountValidator = new C4BoardValidator(sharedValidationState,
149: context -> context.getState().setColumnCount(context.getValue()), C4BoardValidatorInfo.COLUMN_COUNT);
150: final C4BoardValidator solutionSizeValidator = new C4BoardValidator(sharedValidationState,
151: context -> context.getState().setSolutionSize(context.getValue()), C4BoardValidatorInfo.SOLUTION_SIZE);
152:
153: final InputProvider boardInputProvider = inputProviderRef.getValue().getNext(dataSetRef.getValue());
154: final Map<String, Object> boardDataSet = boardInputProvider
155: .needInteger(IC4GameBuilderFactory.PARAM_BOARD_ROWS,
156: "Number of rows",
157: Optional.of(IC4GameBuilder.DEFAULT_BOARD_ROWS),
158: rowCountValidator)
159: .needInteger(IC4GameBuilderFactory.PARAM_BOARD_COLUMNS,
160: "Number of columns",
161: Optional.of(IC4GameBuilder.DEFAULT_BOARD_COLUMNS),
162: columnCountValidator)
163: .needInteger(IC4GameBuilderFactory.PARAM_REQUIRED_SOLUTION_SIZE,
164: "Number of consecutive pieces required to win",
165: Optional.of(IC4GameBuilder.DEFAULT_REQUIRED_SOLUTION_SIZE),
166: solutionSizeValidator)
167: .requestData("Board properties");
168:
169: // assert the provided state is valid
170: sharedValidationState.assertIsValid();
171:
172: // apply changes
173: gameBuilder
174: .changeBoardRows((int) boardDataSet.get(IC4GameBuilderFactory.PARAM_BOARD_ROWS))
175: .changeBoardColumns((int) boardDataSet.get(IC4GameBuilderFactory.PARAM_BOARD_COLUMNS))
176: .changeRequiredSolutionSize((int) boardDataSet.get(IC4GameBuilderFactory.PARAM_REQUIRED_SOLUTION_SIZE));
177:
178: // done! :)
179: return gameBuilder;
180:
181: } catch (final InputProviderException e) {
182: throw new GameException(String.format("Creating the Connect N game was aborted: %s", e.getMessage()), e);
183: }
184: }
185:
186: /**
187: * Adds a player to the game.
188: *
189: * @param inputProvider A reference to the input provider to use (InputProvider pointer)
190: * @param lastDataSet A reference to the last data set (Map<String, Object> pointer)
191: * @param gameBuilder The game builder to use.
192: * @throws InputProviderException If the input provider fails.
193: * @throws GameException If the game builder fails.
194: */
195: @SuppressWarnings("unchecked")
196: private void addPlayer(final ByRef<InputProvider> inputProvider,
197: final ByRef<Map<String, Object>> lastDataSet,
198: final IC4GameBuilder gameBuilder) throws InputProviderException, GameException {
199:
200: final IC4PlayerBuilder playerBuilder = gameBuilder.createPlayerBuilder();
201: final int playerToken = playerBuilder.getToken();
202: final String title = String.format("Player %d", playerToken);
203:
204: final InputProvider playerInputProvider = inputProvider.getValue().getNext(lastDataSet.getValue());
205:
206: final Map<String, Object> dataSet = playerInputProvider
207: .needString(IC4GameBuilderFactory.PARAM_PLAYER_NAME,
208: "Name",
209: Optional.empty(),
210: new PatternValidator(Pattern.compile("\\S+(\\s+\\S+)*")))
211: .needObject(IC4GameBuilderFactory.PARAM_PLAYER_STRATEGY,
212: "Strategy",
213: Optional.empty(),
214: this.strategies)
215: .requestData(title);
216:
217: final String name = (String) dataSet.get(IC4GameBuilderFactory.PARAM_PLAYER_NAME);
218: final IC4Strategy strategy = (IC4Strategy) dataSet.get(IC4GameBuilderFactory.PARAM_PLAYER_STRATEGY);
219:
220: final IC4Player player = playerBuilder
221: .changeName(name)
222: .build();
223:
224: gameBuilder.addPlayer(player, strategy);
225:
226: // update ref parameters
227: inputProvider.setValue(playerInputProvider);
228: lastDataSet.setValue(dataSet);
229: }
230: }