@@ -44,7 +44,7 @@ object GameRuleLogic {
4444 @JvmStatic
4545 fun performMove (gameState : GameState , move : Move ) {
4646 if (Constants .VALIDATE_MOVE )
47- validateMoveColor(gameState, move)
47+ validateMoveColor(gameState, move, true )
4848
4949 when (move) {
5050 is SkipMove -> performSkipMove(gameState)
@@ -53,31 +53,51 @@ object GameRuleLogic {
5353 gameState.lastMove = move
5454 }
5555
56- /* * Check if the given [move] has the right [Color] . */
56+ /* * Prüfe, ob die Farbe des gegebenen [Move]s der aktiven Farbe des [GameState]s entspricht . */
5757 @JvmStatic
58- private fun validateMoveColor (gameState : GameState , move : Move ) {
58+ fun validateMoveColor (gameState : GameState , move : Move , throws : Boolean = false): MoveMistake ? {
5959 if (move.color != gameState.currentColor)
60- throw InvalidMoveException (" Expected move from ${gameState.currentColor} " , move)
60+ if (throws) {
61+ throw InvalidMoveException (" Expected move from ${gameState.currentColor} " , move)
62+ } else {
63+ return MoveMistake .WRONG_COLOR
64+ }
65+ return null
6166 }
6267
63- /* * Check if the given [move] is able to be performed for the given [gameState]. */
68+ /* *
69+ * Prüfe, ob der gegebene [SetMove] gesetzt werden könnte.
70+ * @param throws wenn true, wirft die Methode bei invaliden Zügen einen Fehler.
71+ *
72+ * @return gibt einen [MoveMistake] zurück, wenn der Zug nicht valide wahr, ansonsten null
73+ */
6474 @JvmStatic
65- private fun validateSetMove (gameState : GameState , move : SetMove ) {
75+ fun validateSetMove (gameState : GameState , move : SetMove , throws : Boolean = false): MoveMistake ? {
6676 // Check whether the color's move is currently active
67- validateMoveColor(gameState, move)
77+ return validateMoveColor(gameState, move, throws) ? :
6878 // Check whether the shape is valid
69- validateShape(gameState, move.piece.kind, move.color)
79+ validateShape(gameState, move.piece.kind, move.color, throws) ? :
7080 // Check whether the piece can be placed
71- validateSetMove(gameState.board, move)
81+ validateSetMove(gameState.board, move, throws) ? :
7282
7383 if (isFirstMove(gameState)) {
7484 // Check if it is placed correctly in a corner
7585 if (move.piece.coordinates.none { isOnCorner(it)})
76- throw InvalidMoveException (" The Piece isn't located in a corner" , move)
86+ if (throws) {
87+ throw InvalidMoveException (" The Piece isn't located in a corner" , move)
88+ } else {
89+ MoveMistake .NOT_IN_CORNER
90+ }
91+ else null
7792 } else {
7893 // Check if the piece is connected to at least one tile of same color by corner
79- if (move.piece.coordinates.none { cornersOnColor(gameState.board, it, move.color) })
80- throw InvalidMoveException (" ${move.piece} shares no corner with another piece of same color" , move)
94+ if (move.piece.coordinates.none { cornersOnColor(gameState.board, Field (it, move.color)) })
95+ if (throws) {
96+ throw InvalidMoveException (" ${move.piece} shares no corner with another piece of same color" , move)
97+ } else {
98+ MoveMistake .NO_SHARED_CORNER
99+ }
100+ else null
81101 }
82102 }
83103
@@ -87,7 +107,7 @@ object GameRuleLogic {
87107 validateSetMove(gameState, move)
88108
89109 if (Constants .VALIDATE_MOVE )
90- validateSetMove(gameState, move)
110+ validateSetMove(gameState, move, true )
91111
92112 performSetMove(gameState.board, move)
93113 gameState.undeployedPieceShapes(move.color).remove(move.piece.kind)
@@ -100,50 +120,75 @@ object GameRuleLogic {
100120 gameState.tryAdvance()
101121 }
102122
103- /* * Validate the [PieceShape] of a [SetMove] depending on the current [GameState]. */
123+ /* *
124+ * Prüfe, ob der gegebene Spielstein auf dem Spielfeld platziert werden könnte.
125+ * Fehler treten auf, wenn
126+ * - im ersten Zug nicht der vorgegebene Stein
127+ * - in nachfolgenden Zügen bereits gesetzte Steine
128+ * gesetzt werden würde(n).
129+ */
104130 @JvmStatic
105- private fun validateShape (gameState : GameState , shape : PieceShape , color : Color = gameState.currentColor) {
131+ fun validateShape (gameState : GameState , shape : PieceShape , color : Color = gameState.currentColor, throws : Boolean = false): MoveMistake ? {
106132 if (isFirstMove(gameState)) {
107133 if (shape != gameState.startPiece)
108- throw InvalidMoveException (" $shape is not the requested first shape, ${gameState.startPiece} " )
134+ if (throws) {
135+ throw InvalidMoveException (" $shape is not the requested first shape, ${gameState.startPiece} " )
136+ } else {
137+ return MoveMistake .WRONG_SHAPE
138+ }
109139 } else {
110140 if (! gameState.undeployedPieceShapes(color).contains(shape))
111- throw InvalidMoveException (" Piece $shape has already been placed before" )
141+ if (throws) {
142+ throw InvalidMoveException (" Piece $shape has already been placed before" )
143+ } else {
144+ return MoveMistake .DUPLICATE_SHAPE
145+ }
112146 }
147+ return null
113148 }
114149
115150 /* *
116- * Prüft , ob der gegebene [Move] zulässig ist.
151+ * Prüfe , ob der gegebene [Move] zulässig ist.
117152 * @param gameState der aktuelle Spielstand
118153 * @param move der zu überprüfende Zug
119154 *
120155 * @return ob der Zug zulässig ist
121156 */
122157 @JvmStatic
123158 fun isValidSetMove (gameState : GameState , move : SetMove ) =
124- try {
125- validateSetMove(gameState, move)
126- true
127- } catch (e: InvalidMoveException ) {
128- false
129- }
159+ validateSetMove(gameState, move) == null
130160
131- /* * Validate a [SetMove] on a [board] . */
161+ /* * Prüfe, ob der gegebene [SetMove] auf dem [Board] platziert werden kann . */
132162 @JvmStatic
133- private fun validateSetMove (board : Board , move : SetMove ) {
163+ fun validateSetMove (board : Board , move : SetMove , throws : Boolean = false): MoveMistake ? {
164+ // throw IndexOutOfBounds if the initial position only is out of bounds
165+ board[move.piece.position]
134166 move.piece.coordinates.forEach {
135167 try {
136168 board[it]
137169 } catch (e: ArrayIndexOutOfBoundsException ) {
138- throw InvalidMoveException (" Field $it is out of bounds" , move)
170+ if (throws) {
171+ throw InvalidMoveException (" Field $it is out of bounds" , move)
172+ } else {
173+ return MoveMistake .OUT_OF_BOUNDS
174+ }
139175 }
140176 // Checks if a part of the piece is obstructed
141177 if (board.isObstructed(it))
142- throw InvalidMoveException (" Field $it already belongs to ${board[it].content} " , move)
178+ if (throws) {
179+ throw InvalidMoveException (" Field $it already belongs to ${board[it].content} " , move)
180+ } else {
181+ return MoveMistake .OBSTRUCTED
182+ }
143183 // Checks if a part of the piece would border on another piece of same color
144- if (bordersOnColor(board, it, move.color))
145- throw InvalidMoveException (" Field $it already borders on ${move.color} " , move)
184+ if (bordersOnColor(board, Field (it, move.color)))
185+ if (throws) {
186+ throw InvalidMoveException (" Field $it already borders on ${move.color} " , move)
187+ } else {
188+ return MoveMistake .TOUCHES_SAME_COLOR
189+ }
146190 }
191+ return null
147192 }
148193
149194 /* * Place a Piece on the given [board] according to [move]. */
@@ -154,42 +199,52 @@ object GameRuleLogic {
154199 }
155200 }
156201
202+ @JvmStatic
203+ fun validateSkipMove (gameState : GameState , throws : Boolean = false): MoveMistake ? {
204+ if (isFirstMove(gameState))
205+ if (throws) {
206+ throw InvalidMoveException (" Can't Skip on first round" , SkipMove (gameState.currentColor))
207+ } else {
208+ return MoveMistake .SKIP_FIRST_TURN
209+ }
210+ return null
211+ }
212+
157213 /* * Skip a turn. */
158214 @JvmStatic
159215 private fun performSkipMove (gameState : GameState ) {
160216 if (! gameState.tryAdvance())
161217 logger.error(" Couldn't proceed to next turn!" )
162- if (isFirstMove(gameState))
163- throw InvalidMoveException (" Can't Skip on first round" , SkipMove (gameState.currentColor))
218+ validateSkipMove(gameState, true )
164219 }
165220
166- /* * Check if the given [position] already borders on another piece of same [color] . */
221+ /* * Prüfe, ob das gegebene [Field] bereits an eins mit gleicher Farbe angrenzt . */
167222 @JvmStatic
168- private fun bordersOnColor (board : Board , position : Coordinates , color : Color ): Boolean = listOf (
223+ fun bordersOnColor (board : Board , field : Field ): Boolean = listOf (
169224 Vector (1 , 0 ),
170225 Vector (0 , 1 ),
171226 Vector (- 1 , 0 ),
172227 Vector (0 , - 1 )).any {
173228 try {
174- board[position + it].content == + color
229+ board[field.coordinates + it].content == field.content && ! field.isEmpty
175230 } catch (e: ArrayIndexOutOfBoundsException ) { false }
176231 }
177232
178- /* * Return true if the given [Coordinates] touch a corner of a field of same color . */
233+ /* * Prüfe, ob das gegebene Feld an die Ecke eines Feldes gleicher Farbe angrenzt . */
179234 @JvmStatic
180- private fun cornersOnColor (board : Board , position : Coordinates , color : Color ): Boolean = listOf (
235+ fun cornersOnColor (board : Board , field : Field ): Boolean = listOf (
181236 Vector (1 , 1 ),
182237 Vector (1 , - 1 ),
183238 Vector (- 1 , - 1 ),
184239 Vector (- 1 , 1 )).any {
185240 try {
186- board[position + it].content == + color
241+ board[field.coordinates + it].content == field.content && ! field.isEmpty
187242 } catch (e: ArrayIndexOutOfBoundsException ) { false }
188243 }
189244
190- /* * Return true if the given [Coordinates] are a corner . */
245+ /* * Prüfe, ob die gegebene Position eine Ecke des Spielfelds ist . */
191246 @JvmStatic
192- private fun isOnCorner (position : Coordinates ): Boolean =
247+ fun isOnCorner (position : Coordinates ): Boolean =
193248 Corner .values().any { it.position == position }
194249
195250 /* * Gib zurück, ob sich der [GameState] noch in der ersten Runde befindet. */
@@ -209,41 +264,6 @@ object GameRuleLogic {
209264 fun getPossibleMoves (gameState : GameState ) =
210265 streamPossibleMoves(gameState).toSet()
211266
212- /* * Return a list of all possible SetMoves, regardless of whether it's the first round. */
213- @JvmStatic
214- private fun getAllPossibleMoves (gameState : GameState ) =
215- streamAllPossibleMoves(gameState).toSet()
216-
217- /* * Return a list of possible SetMoves if it's the first round. */
218- @JvmStatic
219- private fun getPossibleStartMoves (gameState : GameState ) =
220- streamPossibleStartMoves(gameState).toSet()
221-
222- /* *
223- * Return a list of all moves, impossible or not.
224- * There's no real usage, except maybe for cases where no Move validation happens
225- * if `Constants.VALIDATE_MOVE` is false, then this function should return the same
226- * Set as `::getPossibleMoves`
227- */
228- @JvmStatic
229- private fun getAllMoves (): Set <SetMove > {
230- val moves = mutableSetOf<SetMove >()
231- for (color in Color .values()) {
232- for (shape in PieceShape .values()) {
233- for (rotation in Rotation .values()) {
234- for (flip in listOf (false , true )) {
235- for (y in 0 until Constants .BOARD_SIZE ) {
236- for (x in 0 until Constants .BOARD_SIZE ) {
237- moves.add(SetMove (Piece (color, shape, rotation, flip, Coordinates (x, y))))
238- }
239- }
240- }
241- }
242- }
243- }
244- return moves
245- }
246-
247267 /* * Entferne alle Farben, die keine Steine mehr auf dem Feld platzieren können. */
248268 @JvmStatic
249269 fun removeInvalidColors (gameState : GameState ) {
@@ -286,4 +306,4 @@ object GameRuleLogic {
286306 }
287307 }
288308 }.filter { isValidSetMove(gameState, it) }
289- }
309+ }
0 commit comments