Created
November 15, 2021 22:25
-
-
Save saidaspen/9309fea891fa55a799bfb188401b9d66 to your computer and use it in GitHub Desktop.
VM Playoff Simulator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.text.DecimalFormat | |
val seeded = listOf("Portugal", "Ryssland", "Schweiz", "Sverige", "Polen", "Wales") | |
val notSeeded = listOf("Skottland", "Nordmakedonien", "Turkiet", "Finland", "Tjeckien", "Österrike") | |
val fifaPoints = seeded.zip(listOf(1681.73, 1497.45, 1633.8, 1618.69, 1542.2, 1566.77)) | |
.union(notSeeded.zip(listOf(1451.37, 1343.19, 1460.53, 1403.96, 1503.23, 1501.09))).toMap() | |
const val ANSI_YELLOW = "\u001B[33m" | |
const val ANSI_RESET = "\u001B[0m" | |
private const val HOME_FIELD_ADV = 10.0 | |
val df = DecimalFormat("###,###.00") | |
data class Match(val homeTeam: String, val awayTeam: String) { | |
override fun toString() = "$homeTeam - $awayTeam" | |
fun hasTeam(s: String) = homeTeam == s || awayTeam == s | |
} | |
fun main() { | |
val semiOpponents = mutableMapOf<String, Int>() | |
val finalOpponents = mutableMapOf<String, Int>() | |
var sweQualified = 0 | |
val simulations = 10_000_000 | |
for (n in 1..simulations) { | |
val trees = listOf(mutableListOf<String>(), mutableListOf(), mutableListOf()) | |
seeded.shuffled().forEachIndexed { i, s -> trees[i % trees.size].add(s) } | |
notSeeded.shuffled().forEachIndexed { i, s -> trees[i % trees.size].add(s) } | |
val semis = trees.map { Pair(Match(it[0], it[2]), Match(it[1], it[3])) }.toList() | |
val finals = semis.map { Pair(winnerOf(it.first), winnerOf(it.second)).toMatch() } | |
val qualified = finals.map { winnerOf(it) } | |
sweQualified += if (qualified.contains("Sverige")) 1 else 0 | |
val semiOpponent = semis.flatMap { it.toList() }.first { it.homeTeam == "Sverige" }.awayTeam | |
val final = finals.firstOrNull { it.hasTeam("Sverige") } | |
val finalOpponent = if (final != null) if (final.homeTeam == "Sverige") final.awayTeam else final.homeTeam else null | |
semiOpponents[semiOpponent] = semiOpponents.getOrDefault(semiOpponent, 0) + 1 | |
if (finalOpponent != null) { | |
finalOpponents[finalOpponent] = finalOpponents.getOrDefault(finalOpponent, 0) + 1 | |
} | |
} | |
val timesInFinal = finalOpponents.entries.sumOf { it.value } | |
println("SEMI OPPONENTs") | |
semiOpponents.entries.sortedByDescending { it.value }.forEach { println(toPct(it.value, simulations) + it.key) } | |
println("\n Sweden made it to final: " + ANSI_YELLOW + toPct(timesInFinal, simulations) + ANSI_RESET + "of the simulations\n") | |
println("FINAL OPPONENTs") | |
finalOpponents.entries.sortedByDescending { it.value }.forEach { println(toPct(it.value, timesInFinal) + it.key) } | |
println("\n Sweden qualified to the world cup: " + ANSI_YELLOW + toPct(sweQualified, simulations) + ANSI_RESET + "of the simulations\n") | |
} | |
private fun toPct(b: Int, d: Int) = df.format((b * 1.0 / d) * 100) + "% " | |
private fun winnerOf(m: Match): String { | |
val homeChance = (fifaPoints[m.homeTeam]!! / fifaPoints[m.awayTeam]!!) * 50.0 + HOME_FIELD_ADV | |
return if ((1..100).random() <= homeChance) m.homeTeam else m.awayTeam | |
} | |
private fun Pair<String, String>.toMatch() = | |
if (seeded.indexOf(first) < seeded.indexOf(second) || notSeeded.indexOf(first) < notSeeded.indexOf(second)) Match(first, second) else Match(second, first) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment