Skip to content

Commit 3c1c8b5

Browse files
committed
refactor(plugin): clean up GameRuleLogic and Helpers
1 parent a11b4fc commit 3c1c8b5

File tree

7 files changed

+134
-154
lines changed

7 files changed

+134
-154
lines changed

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ data class Coordinates(
2929

3030
/** Gibt ein Set der vier Ecken dieser Koordinaten zurück. */
3131
val corners: Set<Coordinates>
32-
get() = Vector.diagonals.map { this + it }.toSet()
32+
get() = Vector.diagonals.mapTo(HashSet()) { this + it }
3333

3434
/** Gibt ein Set der vier benachbarten Felder dieser Koordinaten zurück. */
3535
val neighbors: Set<Coordinates>
36-
get() = Vector.cardinals.map { this + it }.toSet()
36+
get() = Vector.cardinals.mapTo(HashSet()) { this + it }
3737

3838
companion object {
3939
/** Der Ursprung des Koordinatensystems (0, 0). */
@@ -46,9 +46,7 @@ data class Coordinates(
4646
* @property dx die Differenz in x-Richtung
4747
* @property dy die Differenz in y-Richtung
4848
*/
49-
data class Vector(
50-
@XStreamAsAttribute val dx: Int,
51-
@XStreamAsAttribute val dy: Int) {
49+
data class Vector(val dx: Int, val dy: Int) {
5250
/** Die Fläche des Rechtecks, dessen Diagonale der Vector ist. */
5351
val area: Int = dx * dy
5452

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ data class Piece(
5454
val coordinates: Set<Coordinates>
5555
get() {
5656
if (!::_coordinates.isInitialized)
57-
_coordinates = shape.map { position + +it }.toSet()
57+
_coordinates = shape.mapTo(HashSet()) { position + +it }
5858
return _coordinates
5959
}
6060

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ enum class PieceShape(coordinates: Set<Coordinates>) {
4040
val dimension: Vector = coordinates.area
4141

4242
/** Die Form als Sammlung aus Vektoren. */
43-
val asVectors: Set<Vector> by lazy {coordinates.map {it - Coordinates.origin}.toSet()}
43+
val asVectors: Set<Vector> by lazy {
44+
coordinates.mapTo(HashSet()) {it - Coordinates.origin}
45+
}
4446

4547
/** Die Größe Der Form, als Anzahl an Feldern, die es belegt. */
4648
@XStreamOmitField
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package sc.plugin2021.util
2+
3+
import sc.plugin2021.*
4+
import java.lang.IndexOutOfBoundsException
5+
import kotlin.math.min
6+
7+
// A collection of methods to deal with Collections of Coordinates
8+
9+
/** Drehe die Koordinaten um die gegebene Anzahl an Rotationen. */
10+
fun Set<Coordinates>.rotate(rotation: Rotation) =
11+
when (rotation) {
12+
Rotation.NONE -> this
13+
Rotation.RIGHT -> turnRight().align()
14+
Rotation.MIRROR -> mirror().align()
15+
Rotation.LEFT -> turnLeft().align()
16+
}
17+
18+
/** Spiegel die Koordinaten entlang der y-Achse. */
19+
fun Set<Coordinates>.flip(shouldFlip: Boolean = true) =
20+
when (shouldFlip) {
21+
false -> this
22+
true -> map { Coordinates(-it.x, it.y) }.align()
23+
}
24+
25+
/** Drehe die Koordinaten um 180 Grad. */
26+
fun Collection<Coordinates>.mirror() =
27+
mapTo(HashSet()) { Coordinates(-it.x, -it.y) }
28+
29+
/** Drehe die Koordinaten 90 Grad im Uhrzeigersinn. */
30+
fun Collection<Coordinates>.turnRight() =
31+
mapTo(HashSet()) { Coordinates(-it.y, it.x) }
32+
33+
/** Drehe die Koordinaten 90 Grad gegen den Uhrzeigersinn. */
34+
fun Collection<Coordinates>.turnLeft() =
35+
mapTo(HashSet()) { Coordinates(it.y, -it.x) }
36+
37+
/**
38+
* Bewege die Koordinaten in die linke obere Ecke (Punkt(0, 0)).
39+
* (Dabei werden die Puknte effektiv an den beiden Achsen angelegt).
40+
*/
41+
fun Collection<Coordinates>.align(): Set<Coordinates> {
42+
var minX = Constants.BOARD_SIZE
43+
var minY = Constants.BOARD_SIZE
44+
forEach {
45+
minX = min(it.x, minX)
46+
minY = min(it.y, minY)
47+
}
48+
return mapTo(HashSet()) { Coordinates(it.x - minX, it.y - minY) }
49+
}
50+
51+
/**
52+
* Berechne die Ausmaße des kleinstmöglichen Rechtecks, welches alle Koordinaten umfasst.
53+
* @return ein Vector von der linken oberen Ecke zur rechten unteren Ecke
54+
*/
55+
val Collection<Coordinates>.area: Vector
56+
get() {
57+
var dx = 0
58+
var dy = 0
59+
forEach {
60+
dx = kotlin.math.max(it.x, dx)
61+
dy = kotlin.math.max(it.y, dy)
62+
}
63+
return Vector(dx, dy)
64+
}
65+
66+
/**
67+
* Gebe die Form der Koordinaten zur Konsole aus.
68+
* @param dimension die Ausmaße der entstehenden Graphik
69+
*/
70+
fun Collection<Coordinates>.print(dimension: Vector = area) =
71+
printShapes(this, dimension = dimension)
72+
73+
/** Gebe die gegebenen Formen zur Konsole aus, alle in gegebenen Ausmaßen. */
74+
fun printShapes(vararg shapes: Collection<Coordinates>, dimension: Vector = Vector(4, 5)) {
75+
if (shapes.any { it.area < dimension })
76+
throw IndexOutOfBoundsException("The largest shape has to fit in the given dimension")
77+
78+
val width = shapes.size * (dimension.dx + 1)
79+
val array = Array(dimension.dy * width) { FieldContent.EMPTY.letter }
80+
for (n in array.indices) {
81+
if ((n + 1) % (dimension.dx + 1) == 0) array[n] = ' '
82+
}
83+
for (n in shapes.indices) {
84+
shapes[n].align().forEach {
85+
array[it.x + n * (dimension.dx + 1) + width * it.y] = '#'
86+
}
87+
}
88+
for (x in array.indices) {
89+
print("${array[x]} ")
90+
if ((x + 1) % width == 0) println()
91+
}
92+
println()
93+
}

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

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ object GameRuleLogic {
3434
if (monoLast) 5 else 0
3535
}
3636
// One point per block per piece placed
37-
return SUM_MAX_SQUARES - undeployed.map{ it.coordinates.size }.sum()
37+
return SUM_MAX_SQUARES - undeployed.sumBy { it.coordinates.size }
3838
}
3939

4040
/**
@@ -89,8 +89,6 @@ object GameRuleLogic {
8989
/** Perform the given [SetMove]. */
9090
@JvmStatic
9191
private fun performSetMove(gameState: GameState, move: SetMove) {
92-
validateSetMove(gameState, move)
93-
9492
if (Constants.VALIDATE_MOVE)
9593
validateSetMove(gameState, move)
9694

@@ -236,7 +234,7 @@ object GameRuleLogic {
236234

237235
/** Gib eine Sammlung an möglichen [SetMove]s zurück. */
238236
@JvmStatic
239-
fun getPossibleMoves(gameState: GameState) =
237+
fun getPossibleMoves(gameState: GameState): Collection<SetMove> =
240238
streamPossibleMoves(gameState).toSet()
241239

242240
/** Gib eine Sequenz an möglichen [SetMove]s zurück. */
@@ -276,26 +274,25 @@ object GameRuleLogic {
276274
fun streamPossibleMovesForShape(
277275
gameState: GameState,
278276
shape: PieceShape,
279-
validFields: Set<Coordinates> = getValidFields(gameState.board, gameState.currentColor)
280-
): Sequence<SetMove> {
281-
return if (isFirstMove(gameState)) {
282-
if (shape == gameState.startPiece)
283-
streamPossibleStartMoves(gameState)
284-
else
285-
sequenceOf()
286-
} else sequence {
287-
for (field in validFields) {
288-
for (variant in shape.variants) {
289-
val area = variant.key.area
290-
for (x in field.x - area.dx..field.x) {
291-
for (y in field.y - area.dy..field.y) {
292-
yield(SetMove(Piece(gameState.currentColor, shape, variant.value.first, variant.value.second, Coordinates(x, y))))
277+
validFields: Set<Coordinates> = getValidFields(gameState.board, gameState.currentColor),
278+
): Sequence<SetMove> =
279+
if (isFirstMove(gameState)) {
280+
if (shape == gameState.startPiece)
281+
streamPossibleStartMoves(gameState)
282+
else
283+
sequenceOf()
284+
} else sequence {
285+
for (field in validFields) {
286+
for (variant in shape.variants) {
287+
val area = variant.key.area
288+
for (x in field.x - area.dx..field.x) {
289+
for (y in field.y - area.dy..field.y) {
290+
yield(SetMove(Piece(gameState.currentColor, shape, variant.value.first, variant.value.second, Coordinates(x, y))))
291+
}
293292
}
294293
}
295294
}
296-
}
297-
}.filter { isValidSetMove(gameState, it) }
298-
}
295+
}.filter { isValidSetMove(gameState, it) }
299296

300297
/** @return alle [Coordinates], auf die die aktuelle [Color] Steine platzieren könnte. */
301298
@JvmStatic

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

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

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

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder
77
import io.kotest.matchers.shouldBe
88
import sc.plugin2021.util.Constants
99
import sc.plugin2021.util.GameRuleLogic
10-
import sc.plugin2021.util.filterValidMoves
1110
import sc.shared.InvalidMoveException
1211

1312
class GameRuleLogicTest: WordSpec({
@@ -103,7 +102,7 @@ class GameRuleLogicTest: WordSpec({
103102
val piece = PieceShape.PENTO_W
104103
var state = GameState(startPiece = piece)
105104
"return all possible moves that can be placed in a free corner" {
106-
var SHOULD = setOf(
105+
var SHOULD: Collection<Move> = listOf(
107106
Piece(Color.BLUE, piece, Rotation.NONE, false, Coordinates(0, 0)),
108107
Piece(Color.BLUE, piece, Rotation.MIRROR, false, Coordinates(0, 0)),
109108
Piece(Color.BLUE, piece, Rotation.RIGHT, false, Coordinates(17, 0)),
@@ -112,47 +111,47 @@ class GameRuleLogicTest: WordSpec({
112111
Piece(Color.BLUE, piece, Rotation.MIRROR, false, Coordinates(17, 17)),
113112
Piece(Color.BLUE, piece, Rotation.RIGHT, false, Coordinates(0, 17)),
114113
Piece(Color.BLUE, piece, Rotation.LEFT, false, Coordinates(0, 17))
115-
).map { SetMove(it) }.toSet()
114+
).map { SetMove(it) }
116115
var IS = GameRuleLogic.getPossibleMoves(state)
117-
116+
118117
IS shouldContainExactlyInAnyOrder SHOULD
119118
GameRuleLogic.performMove(state, SHOULD.first())
120-
121-
SHOULD = setOf(
119+
120+
SHOULD = listOf(
122121
Piece(Color.YELLOW, piece, Rotation.RIGHT, false, Coordinates(17, 0)),
123122
Piece(Color.YELLOW, piece, Rotation.LEFT, false, Coordinates(17, 0)),
124123
Piece(Color.YELLOW, piece, Rotation.NONE, false, Coordinates(17, 17)),
125124
Piece(Color.YELLOW, piece, Rotation.MIRROR, false, Coordinates(17, 17)),
126125
Piece(Color.YELLOW, piece, Rotation.RIGHT, false, Coordinates(0, 17)),
127126
Piece(Color.YELLOW, piece, Rotation.LEFT, false, Coordinates(0, 17))
128-
).map { SetMove(it) }.toSet()
127+
).map { SetMove(it) }
129128
IS = GameRuleLogic.getPossibleMoves(state)
130-
129+
131130
IS shouldContainExactlyInAnyOrder SHOULD
132131
GameRuleLogic.performMove(state, SHOULD.first())
133-
132+
134133
SHOULD = setOf(
135134
Piece(Color.RED, piece, Rotation.NONE, false, Coordinates(17, 17)),
136135
Piece(Color.RED, piece, Rotation.MIRROR, false, Coordinates(17, 17)),
137136
Piece(Color.RED, piece, Rotation.RIGHT, false, Coordinates(0, 17)),
138137
Piece(Color.RED, piece, Rotation.LEFT, false, Coordinates(0, 17))
139-
).map { SetMove(it) }.toSet()
138+
).map { SetMove(it) }
140139
IS = GameRuleLogic.getPossibleMoves(state)
141-
140+
142141
IS shouldContainExactlyInAnyOrder SHOULD
143142
GameRuleLogic.performMove(state, SHOULD.first())
144-
145-
SHOULD = setOf(
143+
144+
SHOULD = listOf(
146145
Piece(Color.GREEN, piece, Rotation.RIGHT, false, Coordinates(0, 17)),
147146
Piece(Color.GREEN, piece, Rotation.LEFT, false, Coordinates(0, 17))
148-
).map { SetMove(it) }.toSet()
147+
).map { SetMove(it) }
149148
IS = GameRuleLogic.getPossibleMoves(state)
150-
149+
151150
IS shouldContainExactlyInAnyOrder SHOULD
152-
151+
153152
state = GameState()
154153
GameRuleLogic.getPossibleMoves(state) shouldContainExactlyInAnyOrder
155-
GameRuleLogic.getPossibleMoves(state).filterValidMoves(state)
154+
GameRuleLogic.getPossibleMoves(state).filter { GameRuleLogic.isValidSetMove(state, it) }
156155
}
157156
}
158157
})

0 commit comments

Comments
 (0)