Skip to content

Instantly share code, notes, and snippets.

@saidaspen
Created November 15, 2021 22:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save saidaspen/9309fea891fa55a799bfb188401b9d66 to your computer and use it in GitHub Desktop.
Save saidaspen/9309fea891fa55a799bfb188401b9d66 to your computer and use it in GitHub Desktop.
VM Playoff Simulator
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