Skip to content

Commit 62e392b

Browse files
committed
feat(plugin): remove colors without moves left
This also includes a refactor to calculate possible moves lazily using sequences in order to efficiently calculate if colors still have moves left
1 parent 550808d commit 62e392b

File tree

3 files changed

+55
-29
lines changed

3 files changed

+55
-29
lines changed

plugin/src/server/sc/plugin2021/Game.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ class Game(UUID: String = GamePlugin.PLUGIN_UUID): RoundBasedGameInstance<Player
160160
logger.debug("Current State: $gameState")
161161
logger.debug("Performing Move $data")
162162
GameRuleLogic.performMove(gameState, data)
163+
GameRuleLogic.validateMovability(gameState)
163164
next(if(gameState.orderedColors.isNotEmpty()) gameState.currentPlayer else null)
164165
logger.debug("Current Board:\n${gameState.board}")
165166
} catch(e: InvalidMoveException) {

plugin/src/shared/sc/plugin2021/util/GameRuleLogic.kt

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ object GameRuleLogic {
99

1010
const val SMALLEST_SCORE_POSSIBLE = -89
1111

12-
// TODO: Add all the needed logic as static (@JvmStatic) functions here
1312
/** Calculates the score for the given list in pieces.
1413
* Assumes the game has ended and the pieces are in order of placement.
1514
*/
@@ -96,6 +95,16 @@ object GameRuleLogic {
9695
}
9796
}
9897

98+
/** Returns true if [move] is valid, false otherwise. */
99+
@JvmStatic
100+
fun isValidSetMove(gameState: GameState, move: SetMove) =
101+
try {
102+
validateSetMove(gameState, move)
103+
true
104+
} catch (e: InvalidMoveException) {
105+
false
106+
}
107+
99108
/** Checks if the given [position] is already obstructed by another piece. */
100109
@JvmStatic
101110
fun isObstructed(board: Board, position: Coordinates): Boolean =
@@ -130,6 +139,10 @@ object GameRuleLogic {
130139
fun isOnCorner(position: Coordinates): Boolean =
131140
Corner.asSet().contains(position)
132141

142+
@JvmStatic
143+
fun isFirstMove(gameState: GameState) =
144+
gameState.deployedPieces.getValue(gameState.currentColor).isEmpty()
145+
133146
/** Returns a random pentomino which is not the `x` one (Used to get a valid starting piece). */
134147
@JvmStatic
135148
fun getRandomPentomino() =
@@ -139,35 +152,13 @@ object GameRuleLogic {
139152

140153
/** Returns a list of all possible SetMoves. */
141154
@JvmStatic
142-
fun getPossibleMoves(gameState: GameState): Set<SetMove> {
143-
// TODO: Use appropriate move calculation here
144-
if (gameState.deployedPieces.getValue(gameState.currentColor).isEmpty()) return getPossibleStartMoves(gameState)
145-
val color = gameState.currentColor
146-
147-
val moves = mutableSetOf<SetMove>()
148-
gameState.undeployedPieceShapes.getValue(color).map {
149-
val area = it.coordinates.area()
150-
for (y in 0 until Constants.BOARD_SIZE - area.dy)
151-
for (x in 0 until Constants.BOARD_SIZE - area.dx)
152-
for (variant in it.variants)
153-
moves += SetMove(Piece(color, it, variant.key, Coordinates(x, y)))
154-
}
155-
return moves
156-
}
155+
fun getPossibleMoves(gameState: GameState) =
156+
streamPossibleMoves(gameState).toSet()
157157

158158
/** Returns a list of possible SetMoves if it's the first round. */
159159
@JvmStatic
160-
fun getPossibleStartMoves(gameState: GameState): Set<SetMove> {
161-
val color = gameState.currentColor
162-
val kind = gameState.startPiece
163-
val moves = mutableSetOf<SetMove>()
164-
for (variant in kind.variants) {
165-
for (corner in Corner.values()) {
166-
moves.add(SetMove(Piece(color, kind, variant.key, corner.align(variant.key.area()))))
167-
}
168-
}
169-
return moves.filterValidMoves(gameState)
170-
}
160+
fun getPossibleStartMoves(gameState: GameState) =
161+
streamPossibleStartMoves(gameState).toSet()
171162

172163
/**
173164
* Returns a list of all moves, impossible or not.
@@ -176,7 +167,7 @@ object GameRuleLogic {
176167
* Set as `::getPossibleMoves`
177168
*/
178169
@JvmStatic
179-
fun getAllMoves(gameState: GameState): Set<SetMove> {
170+
fun getAllMoves(): Set<SetMove> {
180171
val moves = mutableSetOf<SetMove>()
181172
for (color in Color.values()) {
182173
for (shape in PieceShape.values()) {
@@ -193,4 +184,38 @@ object GameRuleLogic {
193184
}
194185
return moves
195186
}
187+
188+
/** Ensures the currently active color of [gameState] can perform a move. */
189+
@JvmStatic
190+
fun validateMovability(gameState: GameState) {
191+
if (streamPossibleMoves(gameState).none { isValidSetMove(gameState, it) })
192+
gameState.removeActiveColor()
193+
}
194+
195+
/** Streams all possible moves in the current turn of [gameState]. */
196+
@JvmStatic
197+
fun streamPossibleMoves(gameState: GameState) =
198+
if (isFirstMove(gameState))
199+
streamPossibleStartMoves(gameState)
200+
else sequence<SetMove> {
201+
val color = gameState.currentColor
202+
gameState.undeployedPieceShapes.getValue(color).map {
203+
val area = it.coordinates.area()
204+
for (y in 0 until Constants.BOARD_SIZE - area.dy)
205+
for (x in 0 until Constants.BOARD_SIZE - area.dx)
206+
for (variant in it.variants)
207+
yield(SetMove(Piece(color, it, variant.key, Coordinates(x, y))))
208+
}
209+
}.filter { isValidSetMove(gameState, it) }
210+
211+
/** Streams all possible moves if it's the first turn of [gameState]. */
212+
@JvmStatic
213+
fun streamPossibleStartMoves(gameState: GameState) = sequence<SetMove> {
214+
val kind = gameState.startPiece
215+
for (variant in kind.variants) {
216+
for (corner in Corner.values()) {
217+
yield(SetMove(Piece(gameState.currentColor, kind, variant.key, corner.align(variant.key.area()))))
218+
}
219+
}
220+
}.filter { isValidSetMove(gameState, it) }
196221
}

plugin/src/test/sc/plugin2021/GameRuleLogicTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class GameRuleLogicTest: StringSpec({
156156

157157
state = GameState()
158158
GameRuleLogic.getPossibleMoves(state) shouldContainExactlyInAnyOrder
159-
GameRuleLogic.getAllMoves(state).filterValidMoves(state)
159+
GameRuleLogic.getAllMoves().filterValidMoves(state)
160160

161161
GameRuleLogic.getPossibleMoves(state) shouldContainExactlyInAnyOrder
162162
GameRuleLogic.getPossibleMoves(state).filterValidMoves(state)

0 commit comments

Comments
 (0)