Skip to content

Commit 98670d6

Browse files
authored
fix(plugin): ensure that PassMoves throw and throws remove players (#277)
* `PassMove`s now throw a respective `InvalidMoveException` * Thrown `InvalidMoveException`s were caught and used to set violation flags: -> This is now included into the winner list and `WinCondition` calculation * `WinCondition` calculation now also includes `getPossibleMoves` to check if the current color still has moves
1 parent 845f90b commit 98670d6

File tree

6 files changed

+66
-64
lines changed

6 files changed

+66
-64
lines changed

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

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,22 @@ class Game(UUID: String = GamePlugin.PLUGIN_UUID): RoundBasedGameInstance<Player
4141
}
4242

4343
override fun getWinners(): MutableList<Player> {
44-
val winCondition = checkWinCondition()
45-
if (winCondition != null)
46-
return winCondition.winner.let{players}
44+
if (players.first().violated) {
45+
if (players.last().violated)
46+
return mutableListOf()
47+
return players.subList(1, 2)
48+
}
49+
if (players.last().violated)
50+
return players.subList(0, 1)
51+
52+
val first = gameState.getPointsForPlayer(players.first().color)
53+
val second = gameState.getPointsForPlayer(players.last().color)
4754

48-
// If no win condition is met, the player with highest score wins.
49-
return listOfNotNull(players.maxBy {
50-
gameState.getPointsForPlayer(it.color)
51-
}).toMutableList()
55+
if (first > second)
56+
return players.subList(0, 1)
57+
if (first < second)
58+
return players.subList(1, 2)
59+
return players
5260
}
5361

5462
override fun getPlayerScores(): MutableList<PlayerScore> =
@@ -64,8 +72,9 @@ class Game(UUID: String = GamePlugin.PLUGIN_UUID): RoundBasedGameInstance<Player
6472
* the player with the highest cumulative score of its colors
6573
*/
6674
override fun checkWinCondition(): WinCondition? {
67-
if (gameState.orderedColors.isNotEmpty())
68-
return null
75+
if (gameState.orderedColors.any {
76+
GameRuleLogic.getPossibleMoves(gameState).isNotEmpty()
77+
}) return null
6978

7079
val scoreMap: Map<Team, Int> = Team.values().map {
7180
it to gameState.getPointsForPlayer(it)
@@ -114,28 +123,28 @@ class Game(UUID: String = GamePlugin.PLUGIN_UUID): RoundBasedGameInstance<Player
114123
opponent.hasSoftTimeout() ||
115124
opponent.hasHardTimeout())
116125
score = Constants.WIN_SCORE
117-
118-
when {
119-
player.hasSoftTimeout() -> {
120-
cause = ScoreCause.SOFT_TIMEOUT
121-
reason = "Der Spieler hat innerhalb von ${getTimeoutFor(player).softTimeout / 1000} Sekunden nach Aufforderung keinen Zug gesendet"
122-
}
123-
player.hasHardTimeout() -> {
124-
cause = ScoreCause.SOFT_TIMEOUT
125-
reason = "Der Spieler hat innerhalb von ${getTimeoutFor(player).hardTimeout / 1000} Sekunden nach Aufforderung keinen Zug gesendet"
126+
else
127+
when {
128+
player.hasSoftTimeout() -> {
129+
cause = ScoreCause.SOFT_TIMEOUT
130+
reason = "Der Spieler hat innerhalb von ${getTimeoutFor(player).softTimeout / 1000} Sekunden nach Aufforderung keinen Zug gesendet"
131+
}
132+
player.hasHardTimeout() -> {
133+
cause = ScoreCause.SOFT_TIMEOUT
134+
reason = "Der Spieler hat innerhalb von ${getTimeoutFor(player).hardTimeout / 1000} Sekunden nach Aufforderung keinen Zug gesendet"
135+
}
136+
player.hasViolated() -> {
137+
cause = ScoreCause.RULE_VIOLATION
138+
reason = player.violationReason!!
139+
}
140+
player.hasLeft() -> {
141+
cause = ScoreCause.LEFT
142+
reason = "Der Spieler hat das Spiel verlassen"
143+
}
144+
else -> {
145+
score = Constants.DRAW_SCORE
146+
}
126147
}
127-
player.hasViolated() -> {
128-
cause = ScoreCause.RULE_VIOLATION
129-
reason = player.violationReason!!
130-
}
131-
player.hasLeft() -> {
132-
cause = ScoreCause.LEFT
133-
reason = "Der Spieler hat das Spiel verlassen"
134-
}
135-
else -> {
136-
score = Constants.DRAW_SCORE
137-
}
138-
}
139148
return PlayerScore(cause, reason, score, points)
140149
}
141150

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ object GameRuleLogic {
3333

3434
when (move) {
3535
is PassMove -> {
36-
gameState.removeActiveColor()
36+
throw InvalidMoveException("Color ${move.color} intentionally passed out", move)
3737
}
3838
is SetMove -> {
3939
if (Constants.VALIDATE_MOVE)

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

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,12 @@ class GameRuleLogicTest: StringSpec({
9797
val perfectPieces = allPieces.reversed()
9898
GameRuleLogic.getPointsFromDeployedPieces(perfectPieces) shouldBe 20
9999
}
100-
"PassMoves let you pass out" {
101-
val gameState = GameState()
100+
"After the color check, PassMoves throw" {
101+
val state = GameState()
102102
assertThrows<InvalidMoveException> {
103-
GameRuleLogic.performMove(gameState, PassMove(Color.RED))
103+
GameRuleLogic.performMove(state, PassMove(Color.BLUE))
104104
}
105-
assertDoesNotThrow {
106-
GameRuleLogic.performMove(gameState, PassMove(Color.BLUE))
107-
}
108-
gameState.orderedColors.size shouldBe 3
105+
state.orderedColors.size shouldBe 4
109106
}
110107
"All possible start moves get calculated" {
111108
val piece = PieceShape.PENTO_W

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

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -100,21 +100,4 @@ class GameStateTest: StringSpec({
100100
gameState.round shouldBe 4
101101
gameState.currentColor shouldBe Color.BLUE
102102
}
103-
"Passed out colors behave correctly" {
104-
val gameState = GameState(startTurn = 2)
105-
gameState.orderedColors shouldBe listOf(Color.BLUE, Color.YELLOW, Color.RED, Color.GREEN)
106-
gameState.currentColor shouldBe Color.RED
107-
108-
GameRuleLogic.performMove(gameState, PassMove(Color.RED))
109-
gameState.orderedColors shouldBe listOf(Color.BLUE, Color.YELLOW, Color.GREEN)
110-
gameState.currentColor shouldBe Color.GREEN
111-
112-
GameRuleLogic.performMove(gameState, PassMove(Color.GREEN))
113-
gameState.orderedColors shouldBe listOf(Color.BLUE, Color.YELLOW)
114-
gameState.currentColor shouldBe Color.BLUE
115-
116-
GameRuleLogic.performMove(gameState, PassMove(Color.BLUE))
117-
gameState.orderedColors shouldBe listOf(Color.YELLOW)
118-
gameState.currentColor shouldBe Color.YELLOW
119-
}
120103
})
Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,39 @@
11
package sc.plugin2021
22

3+
import io.kotlintest.matchers.collections.shouldContainExactly
34
import io.kotlintest.shouldBe
45
import io.kotlintest.specs.StringSpec
6+
import org.junit.jupiter.api.assertThrows
57
import sc.plugin2020.util.Constants
8+
import sc.shared.InvalidMoveException
69
import sc.shared.PlayerScore
710
import sc.shared.ScoreCause
811

912
class GameTest: StringSpec({
10-
"Game starting and stopping works" {
13+
"Game starting works" {
1114
Color.BLUE.team
1215
val game = Game()
1316
val state = game.gameState
14-
game.onPlayerJoined().color shouldBe Team.ONE
15-
game.onPlayerJoined().color shouldBe Team.TWO
17+
val first = game.onPlayerJoined()
18+
val second = game.onPlayerJoined()
19+
20+
first.color shouldBe Team.ONE
21+
second.color shouldBe Team.TWO
1622

1723
game.start()
18-
game.onAction(state.currentPlayer, PassMove(state.currentColor))
19-
game.onAction(state.currentPlayer, PassMove(state.currentColor))
20-
game.onAction(state.currentPlayer, PassMove(state.currentColor))
21-
game.onAction(state.currentPlayer, PassMove(state.currentColor))
2224

23-
game.playerScores shouldBe List(2) { PlayerScore(ScoreCause.REGULAR, "", Constants.DRAW_SCORE, -178) }
25+
val e: InvalidMoveException = assertThrows {
26+
state.currentPlayer shouldBe first
27+
state.currentColor shouldBe Color.BLUE
28+
game.onAction(state.currentPlayer, PassMove(state.currentColor))
29+
}
30+
31+
println(game.winners)
32+
game.winners shouldBe listOf(second)
33+
34+
game.playerScores shouldContainExactly listOf(
35+
PlayerScore(ScoreCause.RULE_VIOLATION, e.message, Constants.LOSE_SCORE, -178),
36+
PlayerScore(ScoreCause.REGULAR, "", Constants.WIN_SCORE, -178)
37+
)
2438
}
2539
})

plugin/src/test/sc/plugin2021/ManualGameTest.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ fun loop() {
4545
game.onAction(game.gameState.getPlayer(move.color.team), move)
4646
current = game.gameState.getOpponent(current)!!
4747
} catch (e: Exception) {
48-
println(e)
4948
if (move is SetMove) {
5049
println("Piece was:")
5150
printShapes(move.piece.shape)

0 commit comments

Comments
 (0)