Skip to content

Commit c0b5697

Browse files
TomekRDxeruf
authored andcommitted
feat(plugin26): added GameRuleLogic.kt
1 parent 05a79d9 commit c0b5697

File tree

1 file changed

+300
-0
lines changed

1 file changed

+300
-0
lines changed
Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
package sc.plugin2026
2+
3+
import sc.api.plugins.Direction
4+
import sc.api.plugins.Team
5+
import sc.plugin2026.util.PiranhaConstants
6+
7+
class GameRuleLogic private constructor() {
8+
init {
9+
throw java.lang.IllegalStateException("Can't be instantiated.")
10+
}
11+
12+
companion object {
13+
/**
14+
* Gibt eine Liste aller möglichen Züge zurück
15+
*
16+
* @return Liste von Move Objekten
17+
*/
18+
fun getPossibleMoves(state: sc.plugin2026.GameState): java.util.ArrayList<Move> {
19+
val possibleMoves = java.util.ArrayList<Move>()
20+
val fields: Collection<Field> = getOwnFields(state.board, state.getCurrentPlayerColor())
21+
for(field in fields) {
22+
for(direction in Direction.values()) {
23+
val x = field.x
24+
val y = field.y
25+
val dist = calculateMoveDistance(state.board, x, y, direction)
26+
try {
27+
if(dist > 0 && isValidToMove(state, x, y, direction, dist)) {
28+
val m = Move(x, y, direction)
29+
//TODO ((Player.getPosition), Destination)
30+
possibleMoves.add(m)
31+
}
32+
} catch(ignore: InvalidMoveException) {
33+
/**TODO PiranhaMoveMistake for InvalidMoveException? because InvalidMoveException
34+
* is missing in skd shared
35+
* /
36+
}
37+
}
38+
}
39+
return possibleMoves
40+
}
41+
42+
@Throws(InvalidMoveException::class)
43+
fun isValidToMove(
44+
state: sc.plugin2019.GameState,
45+
x: Int,
46+
y: Int,
47+
direction: Direction,
48+
distance: Int
49+
): Boolean {
50+
if(x >= Constants.BOARD_SIZE || y >= Constants.BOARD_SIZE || x < 0 || y < 0) throw InvalidMoveException("x or y are not within the field range")
51+
val board = state.board
52+
val curField: Field = board.getField(x, y)
53+
val curFieldPlayer: java.util.Optional<PlayerColor> = curField.piranha
54+
if(!curFieldPlayer.isPresent() || curFieldPlayer.get() !== state.getCurrentPlayerColor()) {
55+
throw InvalidMoveException("Field does not belong to the current player")
56+
}
57+
58+
59+
if(calculateMoveDistance(
60+
board,
61+
x,
62+
y,
63+
direction
64+
) != distance
65+
) throw InvalidMoveException("Move distance was incorrect")
66+
67+
val nextField: Field
68+
try {
69+
nextField = getFieldInDirection(board, x, y, direction, distance)
70+
} catch(e: ArrayIndexOutOfBoundsException) {
71+
throw InvalidMoveException("Move in that direction would not be on the board")
72+
}
73+
74+
val fieldsInDirection = getFieldsInDirection(board, x, y, direction)
75+
76+
val opponentFieldColor = FieldState.from(state.getCurrentPlayerColor().opponent())
77+
78+
for(f in fieldsInDirection) {
79+
if(f.state == opponentFieldColor) {
80+
throw InvalidMoveException("Path to the new position is not clear")
81+
}
82+
}
83+
84+
val nextFieldPlayer: java.util.Optional<PlayerColor> = nextField.piranha
85+
if(nextFieldPlayer.isPresent() && nextFieldPlayer.get() === state.getCurrentPlayerColor()) {
86+
throw InvalidMoveException("Field obstructed with own piranha")
87+
}
88+
if(nextField.isObstructed) {
89+
throw InvalidMoveException("Field is obstructed")
90+
}
91+
return true
92+
}
93+
94+
fun getOwnFields(board: Board, player: PlayerColor?): Set<Field> {
95+
val fields: MutableSet<Field> = java.util.HashSet()
96+
var size = 0
97+
var i = 0
98+
while(i < BOARD_LENGTH && MAX_FISH > size) {
99+
var j = 0
100+
while(j < BOARD_SIZE && MAX_FISH > size) {
101+
val curField: Field = board.getField(i, j)
102+
if(curField.piranha.isPresent() && curField.piranha.get().equals(player)) {
103+
fields.add(curField)
104+
size++
105+
}
106+
j++
107+
}
108+
i++
109+
}
110+
return fields
111+
}
112+
113+
private fun getDirectNeighbour(board: Board, f: Field, parentSet: Set<Field>): Set<Field> {
114+
val returnSet: MutableSet<Field> = java.util.HashSet()
115+
for(i in -1..1) {
116+
for(j in -1..1) {
117+
val x = f.x + i
118+
val y = f.y + j
119+
if(x < 0 || x >= Constants.BOARD_SIZE || y < 0 || y >= Constants.BOARD_SIZE || (i == 0 && j == 0)) continue
120+
121+
val field: Field = board.getField(x, y)
122+
if(parentSet.contains(field)) {
123+
returnSet.add(field)
124+
}
125+
}
126+
}
127+
return returnSet
128+
}
129+
130+
private fun getSwarm(board: Board, found: MutableSet<Field>, swarm: MutableSet<Field>): MutableSet<Field> {
131+
if(swarm.isEmpty() && !found.isEmpty()) {
132+
val field = found.iterator().next()
133+
swarm.add(field)
134+
found.remove(field)
135+
}
136+
137+
var tmpSwarm: MutableSet<Field> = java.util.HashSet(swarm)
138+
// O(swarm.size()) time
139+
for(field in swarm) {
140+
// Constant time for both calls (max of 8 neighbors)
141+
val neighbours = getDirectNeighbour(board, field, found)
142+
tmpSwarm.addAll(neighbours)
143+
}
144+
145+
// O(found.size()*swarm.size()) time
146+
// FIXME: Might be improved O(swarm.size()) should be possible
147+
if(swarm.size != tmpSwarm.size) tmpSwarm = getSwarm(board, found, tmpSwarm)
148+
149+
swarm.addAll(tmpSwarm)
150+
151+
found.removeAll(swarm)
152+
return swarm
153+
}
154+
155+
fun greatestSwarm(board: Board, fieldsToCheck: Set<Field>): Set<Field> {
156+
// Make a copy, so there will be no conflict with direct calls.
157+
val occupiedFields: MutableSet<Field> = java.util.HashSet(fieldsToCheck)
158+
var greatestSwarm: Set<Field> = java.util.HashSet()
159+
var maxSize = -1
160+
161+
// this is a maximum of MAX_FISH iterations, so it is a linear iteration altogether
162+
while(!occupiedFields.isEmpty() && occupiedFields.size > maxSize) {
163+
val empty: MutableSet<Field> = java.util.HashSet()
164+
val swarm: Set<Field> = getSwarm(board, occupiedFields, empty)
165+
if(maxSize < swarm.size) {
166+
maxSize = swarm.size
167+
greatestSwarm = swarm
168+
}
169+
}
170+
return greatestSwarm
171+
}
172+
173+
fun greatestSwarm(board: Board, player: PlayerColor?): Set<Field> {
174+
val occupiedFields = getOwnFields(board, player)
175+
return greatestSwarm(board, occupiedFields)
176+
}
177+
178+
fun greatestSwarmSize(board: Board, player: PlayerColor?): Int {
179+
return greatestSwarm(board, player).size
180+
}
181+
182+
fun greatestSwarmSize(board: Board, set: Set<Field?>): Int {
183+
return greatestSwarm(board, set).size
184+
}
185+
186+
fun isSwarmConnected(board: Board, player: PlayerColor?): Boolean {
187+
val fieldsWithFish = getOwnFields(board, player)
188+
val numGreatestSwarm: Int = greatestSwarmSize(board, fieldsWithFish)
189+
return numGreatestSwarm == fieldsWithFish.size
190+
}
191+
192+
/** Überprüft nicht, ob Feld innerhalb der Feldgrenzen */
193+
fun getFieldInDirection(board: Board, x: Int, y: Int, direction: Direction, distance: Int): Field {
194+
val shift: Point = direction.shift()
195+
return board.getField(x + shift.getX() * distance, y + shift.getY() * distance)
196+
}
197+
198+
fun getFieldsInDirection(board: Board, x: Int, y: Int, d: Direction): List<Field> {
199+
val distance = calculateMoveDistance(board, x, y, d)
200+
val fields: MutableList<Field> = java.util.ArrayList()
201+
val shift: Point = d.shift()
202+
203+
for(i in 0 until distance) {
204+
fields.add(board.getField(x + shift.getX() * i, y + shift.getY() * i))
205+
}
206+
return fields
207+
}
208+
209+
/**
210+
* Calculate the minimum steps to take from given position in given direction
211+
*
212+
* @param x coordinate to calculate from
213+
* @param y coordinate to calculate from
214+
* @param direction of the calcualtion
215+
*
216+
* @return -1 if Invalid move, else the steps to take
217+
*/
218+
fun calculateMoveDistance(board: Board, x: Int, y: Int, direction: Direction): Int {
219+
when(direction) {
220+
LEFT, RIGHT -> return moveDistanceHorizontal(board, x, y)
221+
UP, DOWN -> return moveDistanceVertical(board, x, y)
222+
UP_RIGHT, DOWN_LEFT -> return moveDistanceDiagonalRising(board, x, y)
223+
DOWN_RIGHT, UP_LEFT -> return moveDistanceDiagonalFalling(board, x, y)
224+
}
225+
return -1
226+
}
227+
228+
private fun moveDistanceHorizontal(board: Board, ignore: Int, y: Int): Int {
229+
var count = 0
230+
for(i in 0 until BOARD_SIZE) {
231+
if(board.getField(i, y).getPiranha().isPresent()) {
232+
count++
233+
}
234+
}
235+
return count
236+
}
237+
238+
private fun moveDistanceVertical(board: Board, x: Int, ignore: Int): Int {
239+
var count = 0
240+
for(i in 0 until BOARD_SIZE) {
241+
if(board.getField(x, i).getPiranha().isPresent()) {
242+
count++
243+
}
244+
}
245+
return count
246+
}
247+
248+
private fun moveDistanceDiagonalRising(board: Board, x: Int, y: Int): Int {
249+
var count = 0
250+
var cX = x
251+
var cY = y
252+
// Move down left
253+
while(cX >= 0 && cY >= 0) {
254+
if(board.getField(cX, cY).getPiranha().isPresent()) {
255+
count++
256+
}
257+
cY--
258+
cX--
259+
}
260+
261+
// Move up right
262+
cX = x + 1
263+
cY = y + 1
264+
while(cX < BOARD_SIZE && cY < BOARD_SIZE) {
265+
if(board.getField(cX, cY).getPiranha().isPresent()) {
266+
count++
267+
}
268+
cY++
269+
cX++
270+
}
271+
return count
272+
}
273+
274+
private fun moveDistanceDiagonalFalling(board: Board, x: Int, y: Int): Int {
275+
var count = 0
276+
var cX = x
277+
var cY = y
278+
// Move down left
279+
while(cX < BOARD_SIZE && cY >= 0) {
280+
if(board.getField(cX, cY).getPiranha().isPresent()) {
281+
count++
282+
}
283+
cY--
284+
cX++
285+
}
286+
287+
// Move up right
288+
cX = x - 1
289+
cY = y + 1
290+
while(cX >= 0 && cY < BOARD_SIZE) {
291+
if(board.getField(cX, cY).getPiranha().isPresent()) {
292+
count++
293+
}
294+
cY++
295+
cX--
296+
}
297+
return count
298+
}
299+
}
300+
}

0 commit comments

Comments
 (0)