Skip to content

Commit 2d274af

Browse files
Maxxeruf
authored andcommitted
feat(Board): generate random fish distribution on game board
This commit refactors the generateFields() method in the companion object to produce a better distribution of fish on the game board. Previously, the method randomly generated the fish placement with a maximum of 5 holes per side, and a minimum of 5 1-fish fields per side. The remainingFish variable was used to keep track of the number of fish left to place, and maxholes was used to ensure the maximum number of holes was not exceeded. The new implementation replaces the previous algorithm with a weighted probability distribution. The method now uses a list of Field objects and their corresponding probabilities to determine the number of fish that should be placed on each field. To ensure that the sum of the probabilities is 1, an IllegalArgumentException is thrown if the weighted sum does not equal 1. The method then determines the range of possible fish values that can be placed on the field, based on the number of fields remaining to be filled and the sum of fish already placed. The method then generates a random float between 0 and 1 and selects the corresponding fish value based on the probability distribution. The selected value is then assigned to the current field. Finally, the method shuffles the board to ensure that the placement of fish is random, and returns the resulting game board. Overall, this refactor results in a more evenly distributed placement of fish on the board, and eliminates the likelihood of having less than 8 1-fish fields. It also adds a constant distribution of (BoardSize*BoardSize*2) so 128 Fish on the Board.
1 parent e2a37e1 commit 2d274af

File tree

1 file changed

+79
-3
lines changed
  • plugin/src/main/kotlin/sc/plugin2023

1 file changed

+79
-3
lines changed

plugin/src/main/kotlin/sc/plugin2023/Board.kt

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package sc.plugin2023
33
import com.thoughtworks.xstream.annotations.XStreamAlias
44
import com.thoughtworks.xstream.annotations.XStreamImplicit
55
import sc.api.plugins.*
6+
import kotlin.math.min
7+
import kotlin.math.roundToInt
68
import sc.framework.deepCopy
79
import kotlin.random.Random
810
import sc.plugin2023.util.PenguinConstants as Constants
@@ -11,7 +13,7 @@ import sc.plugin2023.util.PenguinConstants as Constants
1113
@XStreamAlias(value = "board")
1214
class Board(
1315
@XStreamImplicit(itemFieldName = "row")
14-
override val gameField: MutableTwoDBoard<Field> = generateFields()
16+
override val gameField: MutableTwoDBoard<Field> = generateFields(),
1517
): RectangularBoard<Field>(), IBoard {
1618

1719
constructor(board: Board): this(board.gameField.deepCopy())
@@ -69,9 +71,10 @@ class Board(
6971
companion object {
7072
/** Generiert ein neues Spielfeld mit zufällig auf dem Spielbrett verteilten Fischen. */
7173
private fun generateFields(seed: Int = Random.nextInt()): MutableTwoDBoard<Field> {
72-
var remainingFish = Constants.BOARD_SIZE * Constants.BOARD_SIZE
7374
val random = Random(seed)
74-
println("Board Seed: $seed")
75+
println("Board seed: $seed")
76+
77+
var remainingFish = Constants.BOARD_SIZE * Constants.BOARD_SIZE
7578
var maxholes = 5
7679
// Pro Hälfte 32 Felder, mind. 27 Schollen
7780
// Maximal (64-20)/2 = 22 2-Fisch-Schollen,
@@ -95,5 +98,78 @@ class Board(
9598
}
9699
}
97100

101+
private fun generateFieldsRandom(seed: Int = Random.nextInt()): MutableTwoDBoard<Field> {
102+
val random = Random(seed)
103+
println("Board seed: $seed")
104+
105+
val length = Constants.BOARD_SIZE
106+
val width = Constants.BOARD_SIZE
107+
val weightedInts =
108+
listOf(Field(0) to 0.1f, Field(1) to 0.2f, Field(2) to 0.4f, Field(3) to 0.2f, Field(4) to 0.1f)
109+
val totalSum = length * width
110+
val halfWidth = width / 2
111+
val halfEnforcedOnes = Constants.BOARD_SIZE / 2
112+
val fields: TwoDBoard<Field> = Array(length) { Array(width) { Field(0) } }
113+
var countOne = 0
114+
115+
for(i in 0 until length) {
116+
for(j in 0 until halfWidth) {
117+
if(i * halfWidth + j < halfEnforcedOnes) {
118+
fields[i][j] = Field(1)
119+
countOne += 1
120+
continue
121+
}
122+
123+
val currentSum = fields.sumOf { it -> it.sumOf { it.fish } }
124+
val notFilled = totalSum - (i * halfWidth + j)
125+
126+
val weightedSum = weightedInts.sumOf { it.second.toDouble() }
127+
if(weightedSum.roundToInt() != 1) {
128+
throw IllegalArgumentException("The sum of the probabilities must be 1. It is $weightedSum")
129+
}
130+
131+
val lowestPossible = weightedInts.filter { it.first.fish >= (totalSum - currentSum) / notFilled }
132+
.minOf { it.first.fish }
133+
val highestPossible = min(totalSum - currentSum, weightedInts.maxOf { it.first.fish })
134+
135+
val possibleValues =
136+
weightedInts.filter { it.first.fish in lowestPossible..highestPossible }.map { it.first.fish }
137+
val possibleWeights =
138+
weightedInts.filter { it.first.fish in lowestPossible..highestPossible }.map { it.second }
139+
140+
val value = random.nextFloat()
141+
var cumulativeWeight = 0f
142+
var index = 0
143+
while(index < possibleValues.size && cumulativeWeight + possibleWeights[index] < value) {
144+
cumulativeWeight += possibleWeights[index]
145+
index++
146+
}
147+
148+
fields[i][j] = Field(possibleValues[index])
149+
countOne += if(fields[i][j].fish == 1) 1 else 0
150+
}
151+
}
152+
153+
for(i in 0 until length) {
154+
for(j in 0 until halfWidth) {
155+
val x = random.nextInt(length)
156+
val y = random.nextInt(halfWidth)
157+
fields[i][j] = fields[x][y].also { fields[x][y] = fields[i][j] }
158+
}
159+
}
160+
161+
for(i in 0 until length) {
162+
for(j in 0 until halfWidth) {
163+
// TODO ??
164+
// fields[i] += fields[length - i - 1][halfWidth - j - 1]
165+
}
166+
}
167+
168+
return fields.let {
169+
it + it.reversedArray().map { list ->
170+
Array(Constants.BOARD_SIZE) { index -> list[Constants.BOARD_SIZE - index - 1].deepCopy() }
171+
}
172+
}
173+
}
98174
}
99175
}

0 commit comments

Comments
 (0)