Skip to content

Commit 51d93d4

Browse files
authored
test(plugin): add basic terminal UI to test moves on a game (#273)
This is a kind of manual smoke test for the game logic. No parts of the SDK or the server are involved; they'll need separate testing at a later point
1 parent df35dfb commit 51d93d4

File tree

11 files changed

+195
-35
lines changed

11 files changed

+195
-35
lines changed

plugin/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
tasks.withType<Test> {
22
useJUnitPlatform()
33
}
4+
dependencies {
5+
testImplementation(kotlin("script-runtime"))
6+
}

plugin/src/server/sc/plugin2021/Game.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import sc.plugin2021.util.GameRuleLogic
1111
import sc.plugin2021.util.WinReason
1212
import sc.protocol.responses.ProtocolMessage
1313
import sc.shared.*
14+
import kotlin.math.log
1415

1516

1617
@XStreamAlias(value = "game")
@@ -151,9 +152,12 @@ class Game(UUID: String = GamePlugin.PLUGIN_UUID): RoundBasedGameInstance<Player
151152
try {
152153
if (data !is Move)
153154
throw InvalidMoveException("${fromPlayer.displayName} hat keinen validen Zug gesendet.")
155+
156+
logger.debug("Current State: $gameState")
154157
logger.debug("Performing Move $data")
155-
logger.debug("Current Board: ${gameState.board}")
156158
GameRuleLogic.performMove(gameState, data)
159+
gameState.turn++
160+
logger.debug("Current Board:\n${gameState.board}")
157161
next(gameState.currentPlayer)
158162
} catch(e: InvalidMoveException) {
159163
super.catchInvalidMove(e, fromPlayer)

plugin/src/shared/sc/plugin2021/Board.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class Board(
4545

4646
override fun toString(): String {
4747
return gameField.joinToString(separator = "") {
48-
"${it.joinToString(separator = "") { it.letter.toString() }}\n"
48+
"${it.joinToString(separator = " ") { it.letter.toString() }}\n"
4949
}
5050
}
5151
}

plugin/src/shared/sc/plugin2021/Color.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ package sc.plugin2021
22

33
enum class Color(val team: Team) {
44
BLUE (Team.ONE),
5-
6-
YELLOW(Team.TWO) { init {
7-
BLUE.next = YELLOW }},
8-
RED (Team.ONE) { init {
9-
YELLOW.next = RED }},
5+
YELLOW(Team.TWO),
6+
RED (Team.ONE),
107
GREEN (Team.TWO) { init {
11-
RED.next = GREEN
12-
GREEN.next = BLUE }};
8+
BLUE.next = YELLOW
9+
YELLOW.next = RED
10+
RED.next = GREEN
11+
GREEN.next = BLUE
12+
}};
1313

1414
lateinit var next: Color
1515
private set

plugin/src/shared/sc/plugin2021/GameState.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,16 @@ class GameState @JvmOverloads constructor(
8585
return GameRuleLogic.getPointsFromDeployedPieces(pieces)
8686
}
8787

88-
override fun toString(): String = "GameState Zug $turn"
88+
/** Removes the currently active color from the queue.
89+
* The resulting active color will be the previous one; note that this is but a temporary value
90+
* Do not do anything with currentColor before the turn is done for good.
91+
*/
92+
fun removeActiveColor() {
93+
orderedColors.remove(currentColor)
94+
currentColorIndex = (currentColorIndex + orderedColors.size - 1) % orderedColors.size
95+
}
96+
97+
override fun toString(): String = "GameState $round/$turn -> $currentColor"
8998

9099
override fun equals(other: Any?): Boolean {
91100
return !(this === other) &&

plugin/src/shared/sc/plugin2021/util/Constants.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ object Constants {
1313

1414
const val SOFT_TIMEOUT = 2000L
1515
const val HARD_TIMEOUT = 10000L
16+
17+
/** Used to turn off move validation. If turned off, the GameState tests are supposed to and *will* fail. */
18+
const val VALIDATE_MOVE = true
1619
}

plugin/src/shared/sc/plugin2021/util/GameRuleLogic.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@ object GameRuleLogic {
2525
/** Performs the given [move] on the [gameState] if possible. */
2626
@JvmStatic
2727
fun performMove(gameState: GameState, move: Move) {
28-
validateMoveColor(gameState, move)
28+
if (Constants.VALIDATE_MOVE)
29+
validateMoveColor(gameState, move)
2930

3031
when (move) {
3132
is PassMove -> {
32-
gameState.orderedColors.remove(move.color)
33+
gameState.removeActiveColor()
3334
}
3435
is SetMove -> {
35-
validateSetMove(gameState, move)
36+
if (Constants.VALIDATE_MOVE)
37+
validateSetMove(gameState, move)
3638

3739
move.piece.coordinates.forEach {
3840
gameState.board[it] = +move.color
@@ -42,7 +44,7 @@ object GameRuleLogic {
4244

4345
// If it was the last piece for this color, remove him from the turn queue
4446
if (gameState.undeployedPieceShapes.getValue(move.color).isEmpty())
45-
gameState.orderedColors.remove(move.color)
47+
gameState.removeActiveColor()
4648
}
4749
}
4850
}
@@ -127,7 +129,6 @@ object GameRuleLogic {
127129
@JvmStatic
128130
fun getRandomPentomino() =
129131
PieceShape.values()
130-
.slice(9 until Constants.TOTAL_PIECE_SHAPES)
131132
.filter{ it.size == 5 && it != PieceShape.PENTO_X }
132133
.random()
133134
}

plugin/src/test/sc/plugin2021/BoardTest.kt

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,26 @@ class BoardTest : StringSpec({
2626
board[8, 6] = FieldContent.YELLOW
2727

2828
board.toString() shouldBe """
29-
R-------------------
30-
--------------------
31-
--------------------
32-
-G------------------
33-
--------------------
34-
--------------------
35-
--------Y-----------
36-
--------------------
37-
--------------------
38-
-----B--------------
39-
--------------------
40-
--------------------
41-
--------------------
42-
--------------------
43-
--------------------
44-
--------------------
45-
--------------------
46-
--------------------
47-
--------------------
48-
--------------------
29+
R - - - - - - - - - - - - - - - - - - -
30+
- - - - - - - - - - - - - - - - - - - -
31+
- - - - - - - - - - - - - - - - - - - -
32+
- G - - - - - - - - - - - - - - - - - -
33+
- - - - - - - - - - - - - - - - - - - -
34+
- - - - - - - - - - - - - - - - - - - -
35+
- - - - - - - - Y - - - - - - - - - - -
36+
- - - - - - - - - - - - - - - - - - - -
37+
- - - - - - - - - - - - - - - - - - - -
38+
- - - - - B - - - - - - - - - - - - - -
39+
- - - - - - - - - - - - - - - - - - - -
40+
- - - - - - - - - - - - - - - - - - - -
41+
- - - - - - - - - - - - - - - - - - - -
42+
- - - - - - - - - - - - - - - - - - - -
43+
- - - - - - - - - - - - - - - - - - - -
44+
- - - - - - - - - - - - - - - - - - - -
45+
- - - - - - - - - - - - - - - - - - - -
46+
- - - - - - - - - - - - - - - - - - - -
47+
- - - - - - - - - - - - - - - - - - - -
48+
- - - - - - - - - - - - - - - - - - - -
4949
5050
""".trimIndent()
5151
}

plugin/src/test/sc/plugin2021/GameStateTest.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,27 @@ class GameStateTest: StringSpec({
100100
gameState.round shouldBe 4
101101
gameState.currentColor shouldBe Color.BLUE
102102
}
103+
"Passed out colors behave correctly" {
104+
val gameState = GameState(startTurn = 3)
105+
gameState.orderedColors shouldBe listOf(Color.BLUE, Color.YELLOW, Color.RED, Color.GREEN)
106+
gameState.currentColor shouldBe Color.RED
107+
108+
GameRuleLogic.performMove(gameState, PassMove(Color.RED))
109+
gameState.orderedColors shouldBe listOf(Color.BLUE, Color.YELLOW, Color.GREEN)
110+
gameState.currentColor shouldBe Color.YELLOW
111+
gameState.turn++
112+
gameState.currentColor shouldBe Color.GREEN
113+
114+
GameRuleLogic.performMove(gameState, PassMove(Color.GREEN))
115+
gameState.orderedColors shouldBe listOf(Color.BLUE, Color.YELLOW)
116+
gameState.currentColor shouldBe Color.YELLOW
117+
gameState.turn++
118+
gameState.currentColor shouldBe Color.BLUE
119+
120+
GameRuleLogic.performMove(gameState, PassMove(Color.BLUE))
121+
gameState.orderedColors shouldBe listOf(Color.YELLOW)
122+
gameState.currentColor shouldBe Color.YELLOW
123+
gameState.turn++
124+
gameState.currentColor shouldBe Color.YELLOW
125+
}
103126
})
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import org.junit.jupiter.api.assertDoesNotThrow
2+
import sc.plugin2021.Color
3+
import sc.plugin2021.Game
4+
import sc.plugin2021.Move
5+
import sc.plugin2021.SetMove
6+
import sc.plugin2021.helper.MoveParser
7+
import kotlin.concurrent.fixedRateTimer
8+
import kotlin.system.exitProcess
9+
10+
assertDoesNotThrow { MoveParser.selfCheck() }
11+
12+
loop()
13+
14+
fun loop() {
15+
Color.GREEN.team
16+
while (true) {
17+
val game = Game()
18+
var current = game.onPlayerJoined()
19+
game.onPlayerJoined()
20+
game.start()
21+
println("First piece is: ${game.gameState.startPiece}")
22+
while (true) {
23+
println(game.gameState)
24+
println("Enter a move (see helper.MoveParser) or command (`:reset` or `:stop`)")
25+
print("> ")
26+
27+
val input: String = readLine() ?: continue
28+
if (input == ":reset") break
29+
if (input == ":stop") exitProcess(0)
30+
if (input.first() == ':') {
31+
println("Unknown command. Expect a move, `:reset` or `:stop`")
32+
continue
33+
}
34+
35+
var move: Move
36+
try {
37+
move = MoveParser.parse(input)
38+
} catch (e: Exception) {
39+
println(e)
40+
continue
41+
}
42+
println("$input -> $move")
43+
44+
try {
45+
game.onAction(game.gameState.getPlayer(move.color.team), move)
46+
current = game.gameState.getOpponent(current)!!
47+
} catch (e: Exception) {
48+
println(e)
49+
if (move is SetMove)
50+
println("Piece was ${move.piece.coordinates}")
51+
}
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)