Skip to contentMethod: addPlayer(ByRef, ByRef, IC4GameBuilder)
      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: }