Skip to content

Commit

Permalink
fix(Game): WinConditions from GameState and ship stuck tracking
Browse files Browse the repository at this point in the history
  • Loading branch information
xeruf committed Mar 14, 2024
1 parent 7c24ec5 commit 02a2179
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 13 deletions.
20 changes: 19 additions & 1 deletion plugin/src/main/kotlin/sc/plugin2024/GameState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import sc.plugin2024.actions.Push
import sc.plugin2024.actions.Turn
import sc.plugin2024.mistake.AdvanceProblem
import sc.plugin2024.mistake.MoveMistake
import sc.plugin2024.util.MQWinReason
import sc.plugin2024.util.PluginConstants
import sc.plugin2024.util.PluginConstants.POINTS_PER_SEGMENT
import sc.shared.InvalidMoveException
import sc.shared.WinCondition
import kotlin.math.absoluteValue

/**
Expand Down Expand Up @@ -135,6 +137,7 @@ data class GameState @JvmOverloads constructor(
currentTeam = if(turn % 2 == 0) determineAheadTeam() else currentTeam.opponent()
if(!canMove() && !isOver) {
lastMove = null
currentShip.stuck = true
advanceTurn()
}
}
Expand Down Expand Up @@ -388,7 +391,22 @@ data class GameState @JvmOverloads constructor(

// In rare cases this returns true on the server even though the player cannot move
// because the target tile is not revealed yet
fun canMove() = moveIterator().hasNext()
fun canMove() = !currentShip.stuck && moveIterator().hasNext()

override val winCondition: WinCondition?
get() =
arrayOf(
{
ships.singleOrNull { inGoal(it) }?.let { WinCondition(it.team, MQWinReason.GOAL) }
},
{
val dist = board.segmentDistance(ships.first().position, ships.last().position)
WinCondition(ships[if(dist > 0) 0 else 1].team, MQWinReason.SEGMENT_DISTANCE).takeIf { dist.absoluteValue > 3 }
},
{
ships.singleOrNull { it.stuck }?.let { WinCondition(it.team.opponent(), MQWinReason.STUCK) }
}
).firstNotNullOfOrNull { it() }

override val isOver: Boolean
get() = when {
Expand Down
5 changes: 3 additions & 2 deletions plugin/src/main/kotlin/sc/plugin2024/Ship.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,12 @@ data class Ship(
@XStreamAsAttribute var passengers: Int = 0,
@XStreamAsAttribute var freeTurns: Int = 1,
@XStreamAsAttribute var points: Int = 0, // TODO don't track points here
@XStreamAsAttribute var stuck: Boolean = false, // TODO consider tracking as -1 points
@XStreamOmitField var freeAcc: Int = PluginConstants.FREE_ACC,
@XStreamOmitField var movement: Int = speed,
): PublicCloneable<Ship> {
override fun clone(): Ship =
this.copy()

override fun clone(): Ship = this.copy()

fun canTurn() = freeTurns > 0 || coal > 0

Expand Down
8 changes: 5 additions & 3 deletions plugin/src/main/kotlin/sc/plugin2024/util/GamePlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import sc.plugin2024.GameState
import sc.shared.*

@XStreamAlias(value = "winreason")
enum class MQWinReason(override val message: String): IWinReason {
enum class MQWinReason(override val message: String, override val isRegular: Boolean = true): IWinReason {
DIFFERING_SCORES("%s hat mehr Punkte."),
DIFFERING_PASSENGERS("%S hat mehr Passagiere befördert.");
override val isRegular = true
DIFFERING_PASSENGERS("%S hat mehr Passagiere befördert."),
SEGMENT_DISTANCE("%s liegt 3 Segmente vorne."),
GOAL("%s hat das Ziel zuerst erreicht."),
STUCK("%s kann sich nicht mehr bewegen.", false);
}

class GamePlugin: IGamePlugin {
Expand Down
6 changes: 6 additions & 0 deletions sdk/src/main/server-api/sc/api/plugins/IGameState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sc.api.plugins

import sc.framework.PublicCloneable
import sc.protocol.room.RoomMessage
import sc.shared.WinCondition

/**
* Ein `GameState` beinhaltet alle Informationen,
Expand Down Expand Up @@ -45,6 +46,11 @@ interface IGameState: RoomMessage, PublicCloneable<IGameState> {
/** Ob das Spiel zu Ende ist. */
val isOver: Boolean

/** Falls es einen klaren Sieger anhand der Spielregeln unabhängig von der Punktzahl gibt.
* Wenn dieser Wert nicht null ist, sollte [isOver] true zurückgeben. */
val winCondition: WinCondition?
get() = null

/** Gibt Punktzahlen des Teams passend zur ScoreDefinition des aktuellen Spielplugins zurück. */
fun getPointsForTeam(team: ITeam): IntArray

Expand Down
15 changes: 8 additions & 7 deletions sdk/src/main/server-api/sc/framework/plugins/AbstractGame.kt
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,14 @@ abstract class AbstractGame(val plugin: IGamePlugin): IGameInstance, Pausable {
fun currentWinner(): WinCondition {
val teams = Team.values()
val scores = teams.map { currentState.getPointsForTeam(it) }
return plugin.scoreDefinition.asSequence().drop(1) // drop victory points definition
.withIndex().filter { it.value.relevantForRanking }
.map { (index, scoreFragment) ->
WinCondition(teams.withIndex()
.maxByNoEqual { team -> scores[team.index][index] }?.value, scoreFragment.explanation)
}
.firstOrNull { it.winner != null } ?: WinCondition(null, WinReasonTie)
return currentState.winCondition ?: plugin.scoreDefinition.asSequence()
.drop(1) // drop victory points definition
.withIndex().filter { it.value.relevantForRanking }
.map { (index, scoreFragment) ->
WinCondition(teams.withIndex()
.maxByNoEqual { team -> scores[team.index][index] }?.value, scoreFragment.explanation)
}
.firstOrNull { it.winner != null } ?: WinCondition(null, WinReasonTie)
}

/** Gets the [GameResult] if the Game where to end at the current state. */
Expand Down

0 comments on commit 02a2179

Please sign in to comment.