Skip to content

Commit f034ed6

Browse files
committed
feat(plugin26): working Piranhas implementation
1 parent 255d8a2 commit f034ed6

File tree

5 files changed

+66
-102
lines changed

5 files changed

+66
-102
lines changed

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

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@ package sc.plugin2026
22

33
import com.thoughtworks.xstream.annotations.XStreamAlias
44
import sc.api.plugins.Coordinates
5+
import sc.api.plugins.IBoard
56
import sc.api.plugins.ITeam
67
import sc.api.plugins.MutableTwoDBoard
78
import sc.api.plugins.RectangularBoard
89
import sc.api.plugins.Team
10+
import sc.api.plugins.deepCopy
911
import sc.plugin2026.util.*
10-
import kotlin.math.floor
11-
12-
typealias FieldS = FieldState
12+
import kotlin.random.Random
1313

1414
/** Spielbrett für Piranhas mit [PiranhaConstants.BOARD_LENGTH]² Feldern. */
1515
@XStreamAlias(value = "board")
16-
class Board(gameField: MutableTwoDBoard<FieldS> = randomFields()): RectangularBoard<FieldS>(gameField) {
16+
class Board(gameField: MutableTwoDBoard<FieldState> = randomFields()):
17+
RectangularBoard<FieldState>(gameField), IBoard {
1718

1819
// TODO later
1920
//override fun toString() =
@@ -31,8 +32,8 @@ class Board(gameField: MutableTwoDBoard<FieldS> = randomFields()): RectangularBo
3132
//}
3233

3334
override fun clone(): Board =
34-
Board(Array(gameField.size) { column -> this.gameField[column].clone() })
35-
35+
Board(gameField.deepCopy())
36+
3637
fun getTeam(pos: Coordinates): Team? =
3738
this[pos].team
3839

@@ -41,41 +42,45 @@ class Board(gameField: MutableTwoDBoard<FieldS> = randomFields()): RectangularBo
4142

4243
companion object {
4344
/** Erstellt ein zufälliges Spielbrett. */
44-
private fun randomFields(): MutableTwoDBoard<FieldS> {
45-
val fields = generateFields { x, y -> FieldS(x, y) }
45+
private fun randomFields(random: Random = Random.Default): MutableTwoDBoard<FieldState> {
46+
val fields = Array(PiranhaConstants.BOARD_LENGTH) {
47+
Array(PiranhaConstants.BOARD_LENGTH) { FieldState.EMPTY }
48+
}
4649

4750
// Place Piranhas
4851
for(index in 1 until PiranhaConstants.BOARD_LENGTH - 1) {
49-
fields[0][index].setPiranha(Team.ONE)
50-
fields[PiranhaConstants.BOARD_LENGTH - 1][index].setPiranha(Team.ONE)
51-
fields[index][0].setPiranha(Team.TWO)
52-
fields[index][PiranhaConstants.BOARD_LENGTH - 1].setPiranha(Team.TWO)
52+
val size1 = random.nextInt(2) + 1
53+
fields[0][index] = FieldState.from(Team.ONE, size1)
54+
fields[index][0] = FieldState.from(Team.TWO, size1)
55+
56+
val size2 = random.nextInt(2) + 1
57+
fields[PiranhaConstants.BOARD_LENGTH - 1][index] = FieldState.from(Team.ONE, size2)
58+
fields[index][PiranhaConstants.BOARD_LENGTH - 1] = FieldState.from(Team.TWO, size2)
5359
}
5460

5561
// Place Obstacles
5662
// only consider fields in the middle of the board
57-
var blockableFields: List<Field> = fields.slice(PiranhaConstants.OBSTACLES_START..PiranhaConstants.OBSTACLES_END).flatMap { it.slice(PiranhaConstants.OBSTACLES_START..PiranhaConstants.OBSTACLES_END) }
63+
val blockableWidth = PiranhaConstants.OBSTACLES_END - PiranhaConstants.OBSTACLES_START + 1
64+
val blockableSize = blockableWidth * blockableWidth
5865
// set fields with randomly selected coordinates to blocked
59-
// coordinates may not lay on same horizontal, vertical or diagonal lines with other selected coordinates
60-
for(i in 0 until PiranhaConstants.NUM_OBSTACLES) {
61-
val indexOfFieldToBlock = floor(Math.random() * blockableFields.size).toInt()
62-
val selectedField = blockableFields[indexOfFieldToBlock]
63-
selectedField.state = FieldState.OBSTRUCTED
64-
blockableFields = blockableFields.filter { field ->
65-
!(field.x == selectedField.x || field.y == selectedField.y ||
66-
field.x - field.y == selectedField.x - selectedField.y ||
67-
field.x + field.y == selectedField.x + selectedField.y)
66+
val obstacles = ArrayList<Coordinates>(PiranhaConstants.NUM_OBSTACLES)
67+
while(obstacles.size < PiranhaConstants.NUM_OBSTACLES) {
68+
val index = random.nextInt(blockableSize + 1)
69+
val pos = Coordinates(
70+
PiranhaConstants.OBSTACLES_START + index.rem(blockableWidth),
71+
PiranhaConstants.OBSTACLES_START + index.div(blockableWidth)
72+
)
73+
// coordinates may not lay on same horizontal, vertical or diagonal lines with other selected coordinates
74+
if(obstacles.none {
75+
it.x == pos.x || it.y == pos.y ||
76+
it.x - it.y == pos.x - pos.y ||
77+
it.x + it.y == pos.x + pos.y
78+
}) {
79+
obstacles.add(pos)
80+
fields[pos.x][pos.y] = FieldState.OBSTRUCTED
6881
}
6982
}
7083
return fields
7184
}
72-
73-
private fun generateFields(generator: (Int, Int) -> FieldS): Array<Array<FieldS>> {
74-
return Array(PiranhaConstants.BOARD_LENGTH) { x ->
75-
Array(PiranhaConstants.BOARD_LENGTH) { y ->
76-
generator(x, y)
77-
}
78-
}
79-
}
8085
}
8186
}

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

Lines changed: 0 additions & 63 deletions
This file was deleted.

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ enum class FieldState(val size: Int): IField<FieldState> {
1313
OBSTRUCTED(0),
1414
EMPTY(0);
1515

16+
override fun copy(): FieldState = this
17+
1618
override val isEmpty: Boolean
1719
get() = this == EMPTY
1820

@@ -37,4 +39,23 @@ enum class FieldState(val size: Int): IField<FieldState> {
3739
else -> team?.letter.toString() + size.toString()
3840
}
3941

42+
companion object {
43+
fun from(team: Team, size: Int): FieldState =
44+
when(team) {
45+
Team.ONE -> when(size) {
46+
1 -> ONE_S
47+
2 -> ONE_M
48+
3 -> ONE_L
49+
else -> throw IllegalArgumentException("Invalid size: $size")
50+
}
51+
Team.TWO -> when(size) {
52+
1 -> TWO_S
53+
2 -> TWO_M
54+
3 -> TWO_L
55+
else -> throw IllegalArgumentException("Invalid size: $size")
56+
}
57+
}
58+
59+
}
60+
4061
}

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,13 @@ data class GameState @JvmOverloads constructor(
6262
}
6363
GameRuleLogic.checkMove(board, move)?.let { throw InvalidMoveException(it, move) }
6464
val distance = GameRuleLogic.movementDistance(board, move)
65-
board[move.from].state = FieldState.EMPTY
66-
board[move.from + move.direction.vector * distance].state = FieldState.from(currentTeam)
65+
board[move.from + move.direction.vector * distance] = board[move.from]
66+
board[move.from] = FieldState.EMPTY
67+
lastMove = move
6768
}
6869

6970
override fun getSensibleMoves(): List<Move> {
70-
val piranhas = board.filterValues { field -> field.state.team == currentTeam }
71+
val piranhas = board.filterValues { field -> field.team == currentTeam }
7172
val moves = ArrayList<Move>(piranhas.size * 2)
7273
for(piranha in piranhas) {
7374
moves.addAll(GameRuleLogic.possibleMovesFor(board, piranha.key))
@@ -78,7 +79,7 @@ data class GameState @JvmOverloads constructor(
7879
override fun moveIterator(): Iterator<Move> =
7980
getSensibleMoves().iterator()
8081

81-
override fun clone(): TwoPlayerGameState<Move> =
82+
override fun clone(): GameState =
8283
copy(board = board.clone())
8384

8485
override fun teamStats(team: ITeam): List<Stat> =

plugin2026/src/main/kotlin/sc/plugin2026/util/GameRuleLogic.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ object GameRuleLogic {
1919
while(true) {
2020
pos += move.direction
2121
val field = board.getOrNull(pos) ?: break
22-
if(field.state.team != null) {
22+
if(field.team != null) {
2323
count++
2424
}
2525
}
2626
pos = move.from
2727
while(true) {
2828
pos += move.direction.opposite
2929
val field = board.getOrNull(pos) ?: break
30-
if(field.state.team != null) {
30+
if(field.team != null) {
3131
count++
3232
}
3333
}
@@ -41,20 +41,20 @@ object GameRuleLogic {
4141
val distance = movementDistance(board, move)
4242
var pos = move.from
4343

44-
val team = board[move.from].state.team ?: return MoveMistake.START_EMPTY
44+
val team = board[move.from].team ?: return MoveMistake.START_EMPTY
4545
val opponent = team.opponent()
4646

4747
var moved = 1
4848
while(moved < distance) {
4949
pos += move.direction
5050
val field = board.getOrNull(pos) ?: return MoveMistake.DESTINATION_OUT_OF_BOUNDS
51-
if(field.state.team == opponent) {
51+
if(field.team == opponent) {
5252
return PiranhaMoveMistake.JUMP_OVER_OPPONENT
5353
}
5454
moved++
5555
}
5656
pos += move.direction
57-
val state = board.getOrNull(pos)?.state
57+
val state = board.getOrNull(pos)
5858
return when(state) {
5959
null -> MoveMistake.DESTINATION_OUT_OF_BOUNDS
6060
FieldState.OBSTRUCTED -> MoveMistake.DESTINATION_BLOCKED

0 commit comments

Comments
 (0)