commonMain.eu.codlab.lorcana.math.Math.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lorcana-math Show documentation
Show all versions of lorcana-math Show documentation
Lorcana set descriptions and data
package eu.codlab.lorcana.math
fun factorial(x: Long) = factorial(1, x)
fun factorial(startAt: Long, x: Long): List {
if (startAt <= 0 || x <= 0) {
return listOf(1)
}
return (startAt..x).map { it }
}
fun choose(k: Long, n: Long): Long {
val kPos = if (k < 0) 0 else k
val nPos = if (n < 0) 0 else n
// we normally do n!/(k! * (n - k)!)
// meaning that it's 1*...*n / ( (1*...*k) * (1*...*(n-k))!
// we can then already simplify n! with -> (k+1)*...*n / (n-k)!
// instead of having the following ->
//val numerator = factorial(nPos)
//val denominator = listOf(
// factorial(nPos - kPos),
// factorial(kPos)
//).flatten()
// we can have
val numerator = factorial(kPos + 1, nPos)
val denominator = factorial(nPos - kPos)
val (reducedNumerator, reducedDenominator) = reduceNumeratorDenominator(numerator, denominator)
// compute both terms
val numeratorValue = reducedNumerator.reduce { acc, l -> acc * l }
val denominatorValue = reducedDenominator.reduce { acc, l -> acc * l }
return numeratorValue / denominatorValue
}
fun calculate(
deckSize: Long,
handSize: Long,
miscAmount: Long,
objects: List
): Double {
if (miscAmount == 0.toLong() && deckSize == handSize) {
return 100.0
}
val recursive = recursiveCombination(handSize, miscAmount, listOf(), 0, objects)
return (recursive * 1.0 / choose(handSize, deckSize)) * 100
}
private fun recursiveCombination(
handSize: Long,
miscAmount: Long,
currentHand: List>,
currentHandSize: Long,
objects: List
): Long {
// if we are in an invalid state
if (currentHandSize > handSize) {
return 0
}
if (currentHandSize == handSize) {
val invalid = objects.firstOrNull { it.min != 0.toLong() }
if (null != invalid) return 0
}
// we don't have any objects anymore, we can check the resulting result
// by combining all choose(k,n) & remaining hand size if any
if (objects.isEmpty()) {
// calculate the probability to have at least "k" cards in amount of said cards
return (currentHand.map { choose(it.first, it.second) }
.reduceOrNull { acc, l -> acc * l } ?: 0).let { result ->
// and now calculate how much cards remaining we can have to complete the hand
if (currentHandSize < handSize) {
result * choose(handSize - currentHandSize, miscAmount)
} else {
result
}
}
}
// we extract can calculate the combinations
return objects.last().let { last ->
(last.min..last.max).sumOf { i ->
recursiveCombination(
handSize,
miscAmount,
currentHand + (i to last.amount),
currentHandSize + i,
objects.dropLast(1)
)
}
}
}
private fun reduceNumeratorDenominator(
numerator: List,
denominator: List
): Pair, List> {
val reducedNumerator = numerator.toMutableList()
val reducedDenominator = denominator.toMutableList()
var i = 0
while (i < reducedDenominator.size) {
val value = reducedDenominator[i]
val j = reducedNumerator.indexOf(value)
if (j >= 0) {
reducedDenominator.removeAt(i)
reducedNumerator.removeAt(j)
} else {
i++
}
}
return reducedNumerator to reducedDenominator
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy