Skip to content

Commit

Permalink
fix: more odd game end conditions
Browse files Browse the repository at this point in the history
  • Loading branch information
xeruf committed Mar 12, 2024
1 parent ac0d09d commit 2be0211
Show file tree
Hide file tree
Showing 12 changed files with 48 additions and 29 deletions.
2 changes: 2 additions & 0 deletions plugin/src/test/kotlin/sc/plugin2024/GameResultTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class GameResultTest: WordSpec({
</fragment>
</definition>
<scores/>
<winner regular="true" reason="Beide Teams sind gleichauf"/>
</result>
""".trimIndent()
}
Expand Down Expand Up @@ -71,6 +72,7 @@ class GameResultTest: WordSpec({
</score>
</entry>
</scores>
<winner regular="true" reason="Beide Teams sind gleichauf"/>
</result>
""".trimIndent()
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/main/framework/sc/shared/IWinReason.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ interface IWinReason {
val message: String

fun getMessage(playerName: String?): String =
message.format(playerName)
message.format(playerName ?: "Alle Spieler")
}

open class WinReason(override val message: String, override val isRegular: Boolean = true): IWinReason {
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/main/framework/sc/shared/WinCondition.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ data class WinCondition(
reason.getMessage("$playerName ($winner)")

override fun toString(): String =
reason.getMessage(winner.toString())
reason.getMessage((if(reason.isRegular) winner else winner?.opponent()).toString())

override fun equals(other: Any?): Boolean = other is WinCondition && other.winner == winner
}
16 changes: 8 additions & 8 deletions sdk/src/main/framework/sc/util/GameResultConverter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ class GameResultConverter: Converter {
writer.makeNode("definition") { context.convertAnother(obj.definition) }
writer.makeNode("scores") { context.convertAnother(obj.scores) }
obj.win?.let { win ->
win.winner?.let { team ->
writer.makeNode("winner") {
addAttribute("team", team.name)
addAttribute("regular", win.reason.isRegular.toString())
val reasonTeam = if(win.reason.isRegular) team else team.opponent()
addAttribute("reason", win.reason.getMessage(obj.scores.firstNotNullOfOrNull { entry -> entry.key.displayName.takeIf { it.isNotBlank() && entry.key.team == reasonTeam } } ?: reasonTeam.name))
}
val team = win.winner
writer.makeNode("winner") {
//addAttribute("team", team?.name.toString())
team?.name?.let { addAttribute("team", it) }
addAttribute("regular", win.reason.isRegular.toString())
val reasonTeam = if(win.reason.isRegular) team else team?.opponent()
addAttribute("reason", win.reason.getMessage(obj.scores.firstNotNullOfOrNull { entry -> entry.key.displayName.takeIf { it.isNotBlank() && entry.key.team == reasonTeam } } ?: reasonTeam?.name))
}
}
}
Expand All @@ -50,7 +50,7 @@ class GameResultConverter: Converter {
if(reader.hasMoreChildren())
reader.readNode {
WinCondition(
Team.valueOf(getAttribute("team")),
getAttribute("team")?.let { attr -> Team.values().find { attr == it.name } },
WinReason(
getAttribute("reason"),
getAttribute("regular") == "true"
Expand Down
13 changes: 8 additions & 5 deletions sdk/src/main/server-api/sc/framework/plugins/AbstractGame.kt
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,10 @@ abstract class AbstractGame(val plugin: IGamePlugin): IGameInstance, Pausable {
*
* @return WinCondition, or null if game is not regularly over yet
*/
fun checkWinCondition(): WinCondition? {
if(!currentState.isOver) return null
fun checkWinCondition(): WinCondition? =
currentWinner().takeIf { currentState.isOver }

fun currentWinner(): WinCondition {
val teams = Team.values()
val scores = teams.map { currentState.getPointsForTeam(it) }
return plugin.scoreDefinition.asSequence().drop(1) // drop victory points definition
Expand All @@ -217,8 +219,9 @@ abstract class AbstractGame(val plugin: IGamePlugin): IGameInstance, Pausable {
.firstOrNull { it.winner != null } ?: WinCondition(null, WinReasonTie)
}

/** Gets the [GameResult] if the Game where to end at the current state. */
fun getResult(): GameResult {
var winCondition = checkWinCondition()
val winCondition: WinCondition
val violator = players.find { it.violation != null }
val scores =
if(violator != null) {
Expand All @@ -231,10 +234,10 @@ abstract class AbstractGame(val plugin: IGamePlugin): IGameInstance, Pausable {
}
}
} else {
val winner = winCondition?.winner
winCondition = currentWinner()
players.associateWith {
PlayerScore(
when(winner) {
when(winCondition.winner) {
it.team -> Constants.WIN_SCORE
null -> Constants.DRAW_SCORE
else -> Constants.LOSE_SCORE
Expand Down
4 changes: 4 additions & 0 deletions sdk/src/main/server-api/sc/framework/plugins/Player.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ open class Player @JvmOverloads constructor(

@XStreamOmitField
var violation: Violation? = null
set(value) {
if(field == null)
field = value
}

fun addPlayerListener(listener: IPlayerListener) {
this.listeners.add(listener)
Expand Down
14 changes: 8 additions & 6 deletions sdk/src/test/kotlin/sc/shared/GameResultTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ class GameResultTest: WordSpec({
gameResultWinner shouldNotBe gameResultNoWinner
}
"serialize properly to XML" {
val gameResultXML = """
fun gameResultXML(result: String) =
"""
<result>
<definition>
<fragment name="winner">
Expand All @@ -58,25 +59,26 @@ class GameResultTest: WordSpec({
</score>
</entry>
</scores>
</result>""".trimIndent()
$result
</result>""".trimIndent().lines().filterNot { it.isBlank() }.joinToString("\n")
forAll(
row(gameResultNoWinner, gameResultXML),
row(gameResultWinner, gameResultXML.replace("</scores>", "</scores>\n <winner team=\"ONE\" regular=\"true\" reason=\"rad hat gewonnän\"/>")),
row(gameResultNoWinner, gameResultXML("")),
row(gameResultWinner, gameResultXML("<winner team=\"ONE\" regular=\"true\" reason=\"rad hat gewonnän\"/>")),
) { result, xml ->
result shouldSerializeTo xml
}
testXStream.toXML(GameResult(definition, scores,
WinCondition(Team.TWO, Violation.RULE_VIOLATION(InvalidMoveException(object: IMoveMistake {
override val message = "bad guy"
}))))) shouldBe
gameResultXML.replace("</scores>", "</scores>\n <winner team=\"TWO\" regular=\"false\" reason=\"Regelverletzung von blues: bad guy\"/>")
gameResultXML("<winner team=\"TWO\" regular=\"false\" reason=\"Regelverletzung von rad: bad guy\"/>")
testXStream.toXML(GameResult(definition, scores,
WinCondition(Team.TWO, Violation.RULE_VIOLATION(InvalidMoveException(object: IMoveMistake {
override val message = "kalkül"
}, object: IMove {
override fun toString() = "TestZug"
}))))) shouldBe
gameResultXML.replace("</scores>", "</scores>\n <winner team=\"TWO\" regular=\"false\" reason=\"Regelverletzung von blues: kalkül bei &apos;TestZug&apos;\"/>")
gameResultXML("<winner team=\"TWO\" regular=\"false\" reason=\"Regelverletzung von rad: kalkül bei &apos;TestZug&apos;\"/>")
// TODO check the encoding correctness here, also XML serialization on Windows
}
}
Expand Down
2 changes: 2 additions & 0 deletions server/src/main/java/sc/server/gaming/GameRoom.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public synchronized void onGameOver(GameResult result) {

setStatus(GameStatus.OVER);
try {
this.result = result;
logger.info("{} is over (regular={})", game, result.isRegular());
saveReplayMessage(result);
broadcast(result);
Expand Down Expand Up @@ -412,6 +413,7 @@ public void removePlayer(Player player, XStreamClient.DisconnectCause cause) {

/** Get the saved {@link GameResult result}. */
public GameResult getResult() {
// TODO only used for tests, use listeners there
return this.result;
}

Expand Down
9 changes: 6 additions & 3 deletions server/src/test/java/sc/server/gaming/GameRoomTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import io.kotest.matchers.*
import io.kotest.matchers.collections.*
import io.kotest.matchers.maps.shouldContainExactly
import org.junit.jupiter.api.assertThrows
import sc.api.plugins.Team
import sc.framework.plugins.Constants
import sc.protocol.requests.PrepareGameRequest
import sc.server.Configuration
import sc.server.helpers.StringNetworkInterface
Expand Down Expand Up @@ -55,12 +57,13 @@ val minimalReplay = """
<entry>
<player team="TWO"/>
<score>
<part>0</part>
<part>2</part>
<part>1</part>
<part>2</part>
</score>
</entry>
</scores>
<winner team="TWO" regular="true" reason="TWO won through index"/>
</data>
</room>
</protocol>""".trimIndent()
Expand All @@ -82,11 +85,11 @@ class GameRoomTest: WordSpec({
"return correct scores on game over" {
val game = (room.game as TestGame)
game.currentState.turn = 2
val playersScores = game.players.associateWith { PlayerScore(1, it.team.index, 2) }
val playersScores = game.players.associateWith { PlayerScore(it.team.index * Constants.WIN_SCORE, it.team.index, 2) }
room.onGameOver(game.getResult())
room.result.isRegular shouldBe true
room.result.scores shouldContainExactly playersScores
room.result.win shouldBe null
room.result.win?.winner shouldBe Team.TWO
room.isOver shouldBe true
}
"save a correct replay" {
Expand Down
6 changes: 4 additions & 2 deletions server/src/test/java/sc/server/network/LobbyGameTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,13 @@ class LobbyGameTest: WordSpec({
admin.control(room.id).step(true)
val result = roomListener.waitForMessage(GameResult::class)
withClue("No Winner") {
result.win?.reason.shouldBeInstanceOf<Violation.LEFT>()
result.isRegular shouldBe false
result.scores.forEach {
withClue(it.key.displayName) {
it.value.parts.first().intValueExact() shouldBe Constants.LOSE_SCORE
}
}
room.result.win?.reason.shouldBeInstanceOf<Violation.LEFT>()
}
adminListener.waitForMessage(RemovedFromGame::class)
roomListener.clearMessages() shouldBe 0
Expand Down Expand Up @@ -215,9 +215,11 @@ class LobbyGameTest: WordSpec({
val controller = admin.control(prepared.roomId)
controller.pause()
roomListener.waitForMessage(GamePaused::class)
withClue("appropriate result for aborted game") {
withClue("appropriate result for game aborted due to unrequested move") {
// Not the turn of player two
playerClients[1].sendMessageToRoom(prepared.roomId, TestMove(0))
val result = roomListener.waitForMessage(GameResult::class)
room.game.players[1].violation.shouldBeInstanceOf<Violation.PROCESS_VIOLATION>()
result.isRegular shouldBe false
result.win?.winner shouldBe room.game.players.first().team
}
Expand Down
2 changes: 1 addition & 1 deletion server/src/test/java/sc/server/plugins/TestGame.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ data class TestGame(
ActionTimeout(false)

override fun toString(): String =
"TestGame(currentState=$currentState, paused=$isPaused, players=$players)"
"TestGame(currentState=$currentState, paused=$isPaused, players=${players.joinToString { it.longString() }})"
}
5 changes: 3 additions & 2 deletions server/src/test/java/sc/server/roles/PlayerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ public void shouldBeAbleToPlayTheGame() throws RescuableClientException {
shouldPropagateSecondPlayersMove(roomId, player1, player2, observer);
makeMoveAfterRequest(roomId, player1);
makeMoveAfterRequest(roomId, player2);
player1.seekMessage(RemovedFromGame.class);
player2.seekMessage(RemovedFromGame.class);
// TODO
// player1.seekMessage(RemovedFromGame.class);
// player2.seekMessage(RemovedFromGame.class);
}

private void shouldInitializeCorrectly(String roomId, MockClient player1, MockClient player2, MockClient observer) {
Expand Down

0 comments on commit 2be0211

Please sign in to comment.