All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.eu.codlab.lorcana.math.Math.kt Maven / Gradle / Ivy

There is a newer version: 0.15.0
Show newest version
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