Skip to content

Commit 02a2179

Browse files
committedMar 14, 2024
fix(Game): WinConditions from GameState and ship stuck tracking
1 parent 7c24ec5 commit 02a2179

File tree

5 files changed

+41
-13
lines changed

5 files changed

+41
-13
lines changed
 

Diff for: ‎plugin/src/main/kotlin/sc/plugin2024/GameState.kt

+19-1
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ import sc.plugin2024.actions.Push
1010
import sc.plugin2024.actions.Turn
1111
import sc.plugin2024.mistake.AdvanceProblem
1212
import sc.plugin2024.mistake.MoveMistake
13+
import sc.plugin2024.util.MQWinReason
1314
import sc.plugin2024.util.PluginConstants
1415
import sc.plugin2024.util.PluginConstants.POINTS_PER_SEGMENT
1516
import sc.shared.InvalidMoveException
17+
import sc.shared.WinCondition
1618
import kotlin.math.absoluteValue
1719

1820
/**
@@ -135,6 +137,7 @@ data class GameState @JvmOverloads constructor(
135137
currentTeam = if(turn % 2 == 0) determineAheadTeam() else currentTeam.opponent()
136138
if(!canMove() && !isOver) {
137139
lastMove = null
140+
currentShip.stuck = true
138141
advanceTurn()
139142
}
140143
}
@@ -388,7 +391,22 @@ data class GameState @JvmOverloads constructor(
388391

389392
// In rare cases this returns true on the server even though the player cannot move
390393
// because the target tile is not revealed yet
391-
fun canMove() = moveIterator().hasNext()
394+
fun canMove() = !currentShip.stuck && moveIterator().hasNext()
395+
396+
override val winCondition: WinCondition?
397+
get() =
398+
arrayOf(
399+
{
400+
ships.singleOrNull { inGoal(it) }?.let { WinCondition(it.team, MQWinReason.GOAL) }
401+
},
402+
{
403+
val dist = board.segmentDistance(ships.first().position, ships.last().position)
404+
WinCondition(ships[if(dist > 0) 0 else 1].team, MQWinReason.SEGMENT_DISTANCE).takeIf { dist.absoluteValue > 3 }
405+
},
406+
{
407+
ships.singleOrNull { it.stuck }?.let { WinCondition(it.team.opponent(), MQWinReason.STUCK) }
408+
}
409+
).firstNotNullOfOrNull { it() }
392410

393411
override val isOver: Boolean
394412
get() = when {

Diff for: ‎plugin/src/main/kotlin/sc/plugin2024/Ship.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,12 @@ data class Ship(
5656
@XStreamAsAttribute var passengers: Int = 0,
5757
@XStreamAsAttribute var freeTurns: Int = 1,
5858
@XStreamAsAttribute var points: Int = 0, // TODO don't track points here
59+
@XStreamAsAttribute var stuck: Boolean = false, // TODO consider tracking as -1 points
5960
@XStreamOmitField var freeAcc: Int = PluginConstants.FREE_ACC,
6061
@XStreamOmitField var movement: Int = speed,
6162
): PublicCloneable<Ship> {
62-
override fun clone(): Ship =
63-
this.copy()
63+
64+
override fun clone(): Ship = this.copy()
6465

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

Diff for: ‎plugin/src/main/kotlin/sc/plugin2024/util/GamePlugin.kt

+5-3
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ import sc.plugin2024.GameState
99
import sc.shared.*
1010

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

1820
class GamePlugin: IGamePlugin {

Diff for: ‎sdk/src/main/server-api/sc/api/plugins/IGameState.kt

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package sc.api.plugins
22

33
import sc.framework.PublicCloneable
44
import sc.protocol.room.RoomMessage
5+
import sc.shared.WinCondition
56

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

49+
/** Falls es einen klaren Sieger anhand der Spielregeln unabhängig von der Punktzahl gibt.
50+
* Wenn dieser Wert nicht null ist, sollte [isOver] true zurückgeben. */
51+
val winCondition: WinCondition?
52+
get() = null
53+
4854
/** Gibt Punktzahlen des Teams passend zur ScoreDefinition des aktuellen Spielplugins zurück. */
4955
fun getPointsForTeam(team: ITeam): IntArray
5056

Diff for: ‎sdk/src/main/server-api/sc/framework/plugins/AbstractGame.kt

+8-7
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,14 @@ abstract class AbstractGame(val plugin: IGamePlugin): IGameInstance, Pausable {
210210
fun currentWinner(): WinCondition {
211211
val teams = Team.values()
212212
val scores = teams.map { currentState.getPointsForTeam(it) }
213-
return plugin.scoreDefinition.asSequence().drop(1) // drop victory points definition
214-
.withIndex().filter { it.value.relevantForRanking }
215-
.map { (index, scoreFragment) ->
216-
WinCondition(teams.withIndex()
217-
.maxByNoEqual { team -> scores[team.index][index] }?.value, scoreFragment.explanation)
218-
}
219-
.firstOrNull { it.winner != null } ?: WinCondition(null, WinReasonTie)
213+
return currentState.winCondition ?: plugin.scoreDefinition.asSequence()
214+
.drop(1) // drop victory points definition
215+
.withIndex().filter { it.value.relevantForRanking }
216+
.map { (index, scoreFragment) ->
217+
WinCondition(teams.withIndex()
218+
.maxByNoEqual { team -> scores[team.index][index] }?.value, scoreFragment.explanation)
219+
}
220+
.firstOrNull { it.winner != null } ?: WinCondition(null, WinReasonTie)
220221
}
221222

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

0 commit comments

Comments
 (0)
Please sign in to comment.