Skip to content

Commit 2efde1d

Browse files
committed
feat(plugin26): move validation and generation
1 parent 47d9908 commit 2efde1d

File tree

9 files changed

+200
-294
lines changed

9 files changed

+200
-294
lines changed

plugin2026/src/main/kotlin/sc/plugin2026/Board.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package sc.plugin2026
22

33
import com.thoughtworks.xstream.annotations.XStreamAlias
4+
import sc.api.plugins.Coordinates
45
import sc.api.plugins.MutableTwoDBoard
56
import sc.api.plugins.RectangularBoard
67
import sc.api.plugins.Team
@@ -31,6 +32,9 @@ class Board(gameField: MutableTwoDBoard<FieldS> = randomFields()): RectangularBo
3132
override fun clone(): Board =
3233
Board(Array(gameField.size) { column -> this.gameField[column].clone() })
3334

35+
fun getTeam(pos: Coordinates): Team? =
36+
this[pos].state.team
37+
3438
companion object {
3539
/** Erstellt eine zufälliges Spielbrett. */
3640
private fun randomFields(): MutableTwoDBoard<FieldS> {

plugin2026/src/main/kotlin/sc/plugin2026/Field.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,5 @@ class Field @JvmOverloads constructor(
5959
get() = state == FieldState.OBSTRUCTED
6060

6161
override val isEmpty: Boolean
62-
get() = false
62+
get() = state == FieldState.EMPTY
6363
}

plugin2026/src/main/kotlin/sc/plugin2026/FieldState.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ enum class FieldState {
2424
else -> ' '
2525
}
2626

27+
val team: Team?
28+
get() = when(this) {
29+
ONE -> Team.ONE
30+
TWO -> Team.TWO
31+
OBSTRUCTED -> null
32+
EMPTY -> null
33+
}
34+
2735
companion object {
2836
@JvmStatic
2937
fun from(team: Team): FieldState {

plugin2026/src/main/kotlin/sc/plugin2026/GameState.kt

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,21 @@ package sc.plugin2019
22

33
import com.thoughtworks.xstream.annotations.XStreamAlias
44
import com.thoughtworks.xstream.annotations.XStreamAsAttribute
5+
import sc.api.plugins.Direction
6+
import sc.api.plugins.ITeam
7+
import sc.api.plugins.Stat
58
import sc.api.plugins.Team
69
import sc.api.plugins.TwoPlayerGameState
710
import sc.plugin2026.Board
11+
import sc.plugin2026.FieldState
812
import sc.plugin2026.Move
13+
import sc.plugin2026.PiranhaMoveMistake
14+
import sc.plugin2026.util.GameRuleLogic
15+
import sc.plugin2026.util.PiranhaConstants
16+
import sc.shared.IMoveMistake
17+
import sc.shared.InvalidMoveException
18+
import sc.shared.MoveMistake
19+
import sc.shared.WinCondition
920

1021
/**
1122
* The GameState class represents the current state of the game.
@@ -28,8 +39,91 @@ data class GameState @JvmOverloads constructor(
2839
): TwoPlayerGameState<Move>(Team.ONE) {
2940

3041
override fun getPointsForTeam(team: ITeam): IntArray =
31-
GameRuleLogic.greatestSwarmSize(board, playerColor) // TODO important
42+
GameRuleLogic.greatestSwarmSize(board, team) // TODO important
43+
44+
// TODO test if one player is surrounded he loses
45+
override val isOver: Boolean
46+
get() = players.any { it.inGoal } && turn.mod(2) == 0 || turn / 2 >= PiranhaConstants.ROUND_LIMIT
47+
48+
override val winCondition: WinCondition?
49+
get() = TODO("Not yet implemented")
50+
51+
override fun performMoveDirectly(move: Move) {
52+
if (board.getTeam(move.from) != currentTeam) {
53+
throw InvalidMoveException(PiranhaMoveMistake.WRONG_START, move)
54+
}
55+
checkMove(move)?.let { throw InvalidMoveException(it, move) }
56+
val distance = movementDistance(move)
57+
board[move.from].state = FieldState.EMPTY
58+
board[move.from + move.direction.vector * distance].state = FieldState.from(currentTeam)
59+
}
60+
61+
fun movementDistance(move: Move): Int {
62+
var count = 1
63+
var pos = move.from
64+
while(true) {
65+
pos += move.direction
66+
val field = board.getOrNull(pos) ?: break
67+
if(field.state.team != null) {
68+
count++
69+
}
70+
}
71+
pos = move.from
72+
while(true) {
73+
pos += move.direction.opposite
74+
val field = board.getOrNull(pos) ?: break
75+
if(field.state.team != null) {
76+
count++
77+
}
78+
}
79+
return count
80+
}
81+
82+
fun checkMove(move: Move): IMoveMistake? {
83+
val distance = movementDistance(move)
84+
var pos = move.from
85+
var moved = 1
86+
while(moved < distance) {
87+
pos += move.direction
88+
val field = board.getOrNull(pos) ?: return MoveMistake.DESTINATION_OUT_OF_BOUNDS
89+
if(field.state.team == otherTeam) {
90+
return PiranhaMoveMistake.JUMP_OVER_OPPONENT
91+
}
92+
moved++
93+
}
94+
pos += move.direction
95+
val state = board.getOrNull(pos)?.state
96+
return when(state) {
97+
null -> MoveMistake.DESTINATION_OUT_OF_BOUNDS
98+
FieldState.OBSTRUCTED -> MoveMistake.DESTINATION_BLOCKED
99+
else -> {
100+
if(state.team == currentTeam) {
101+
MoveMistake.DESTINATION_BLOCKED_BY_SELF
102+
} else {
103+
null
104+
}
105+
}
106+
}
107+
}
108+
109+
override fun moveIterator(): Iterator<Move> {
110+
val piranhas = board.filterValues { field -> field.state.team == currentTeam }
111+
val moves = ArrayList<Move>(piranhas.size * 2)
112+
for(piranha in piranhas) {
113+
for(direction in Direction.values()) {
114+
val move = Move(piranha.key, direction)
115+
if(checkMove(move) == null) {
116+
moves.add(move)
117+
}
118+
}
119+
}
120+
return moves.iterator()
121+
}
122+
123+
override fun clone(): TwoPlayerGameState<Move> =
124+
copy(board = board.clone())
125+
126+
override fun teamStats(team: ITeam): List<Stat> = listOf() // TODO
32127

33-
// TODO implement missing methods - check previous years
34128

35129
}

plugin2026/src/main/kotlin/sc/plugin2026/PiranhaMoveMistake.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package sc.plugin2026
22

33
import sc.shared.IMoveMistake
44

5+
/** Spielspezifische Zug-Fehler. Siehe auch [sc.shared.MoveMistake]. */
56
enum class PiranhaMoveMistake(override val message: String) : IMoveMistake {
67
// TODO relevant bei performMove(Directly)
8+
WRONG_START("Das Startfeld ist kein Piranha des eigenen Teams"),
9+
JUMP_OVER_OPPONENT("Gegnerische Piranhas können nicht übersprungen werden"),
710
}

0 commit comments

Comments
 (0)