Created
July 4, 2014 21:12
-
-
Save Noxville/a001569ab3499e3a3d02 to your computer and use it in GitHub Desktop.
Monte Carlo Simulation
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
package twopee | |
import grails.transaction.Transactional | |
@Transactional | |
class HeadToHeadService { | |
def eloService | |
List<Double> headToHead(Team team1, Team team2) { | |
double t1_num = 0.0, t2_num = 0.0, t1_den =0.0, t2_den = 0.0 | |
// Results from multivariate linear regression | |
// 6 month history = log(n) / e | |
// 1 month history = log(n) / 2 | |
// monthly elo = 0.50 weighting | |
// current elo = 0.20 weighting | |
// 6 month history | |
Date sixMonths = new Date(System.currentTimeMillis() - (6L * 30 * 24 * 60 * 60 * 1000)) | |
def matches = Mtch.findAllByRadiantTeamInListAndDireTeamInListAndDateGreaterThan( | |
[team1.teamName, team2.teamName], [team1.teamName, team2.teamName], sixMonths).sort { it.date.time } | |
if (matches.size()) { | |
double team1Winrate = ( | |
matches.findAll { (it.winner == "Radiant") && (it.radiantTeam.toLowerCase() == team1.teamName.toLowerCase()) }.size() + | |
matches.findAll { (it.winner == "Dire") && (it.direTeam.toLowerCase() == team1.teamName.toLowerCase()) }.size() ) / | |
matches.size() | |
double team2Winrate = ( | |
matches.findAll { (it.winner == "Radiant") && (it.radiantTeam.toLowerCase() == team2.teamName.toLowerCase()) }.size() + | |
matches.findAll { (it.winner == "Dire") && (it.direTeam.toLowerCase() == team2.teamName.toLowerCase()) }.size() ) / | |
matches.size() | |
double coefficient = (Math.log(matches.size())/ Math.exp(1)) | |
t1_num += (coefficient * team1Winrate) | |
t2_num += (coefficient * team2Winrate) | |
t1_den += coefficient | |
t2_den += coefficient | |
} | |
// last month | |
Date lastMonth = new Date(System.currentTimeMillis() - (1L * 30 * 24 * 60 * 60 * 1000)) | |
matches = Mtch.findAllByRadiantTeamInListAndDireTeamInListAndDateGreaterThan( | |
[team1.teamName, team2.teamName], [team1.teamName, team2.teamName], lastMonth).sort { it.date.time } | |
if (matches.size()) { | |
double team1Winrate = ( | |
matches.findAll { (it.winner == "Radiant") && (it.radiantTeam.toLowerCase() == team1.teamName.toLowerCase()) }.size() + | |
matches.findAll { (it.winner == "Dire") && (it.direTeam.toLowerCase() == team1.teamName.toLowerCase()) }.size() ) / | |
matches.size() | |
double team2Winrate = ( | |
matches.findAll { (it.winner == "Radiant") && (it.radiantTeam.toLowerCase() == team2.teamName.toLowerCase()) }.size() + | |
matches.findAll { (it.winner == "Dire") && (it.direTeam.toLowerCase() == team2.teamName.toLowerCase()) }.size() ) / | |
matches.size() | |
double coefficient = (Math.log(matches.size())/ 2.0) | |
t1_num += (coefficient * team1Winrate) | |
t2_num += (coefficient * team2Winrate) | |
t1_den += coefficient | |
t2_den += coefficient | |
} | |
// monthly elo | |
def averageLastMonth = eloService.averagesLastNPoints(new Date(), 30, [team1, team2]) | |
if (averageLastMonth) { | |
def t1elo = averageLastMonth[team1.teamName] | |
def t2elo = averageLastMonth[team2.teamName] | |
def team1Winrate = 1.0 / ( 1.0 + Math.pow(10.0, ((t2elo-t1elo)/400.0))) | |
def team2Winrate = 1.0 - team1Winrate | |
def coefficient = 0.5 | |
t1_num += (coefficient * team1Winrate) | |
t2_num += (coefficient * team2Winrate) | |
t1_den += coefficient | |
t2_den += coefficient | |
} | |
// monthly elo | |
if (true) { // just done to encapsulate calculations | |
def t1elo = team1.rating | |
def t2elo = team2.rating | |
def team1Winrate = 1.0 / ( 1.0 + Math.pow(10.0, ((t2elo-t1elo)/400.0))) | |
def team2Winrate = 1.0 - team1Winrate | |
def coefficient = 0.2 | |
t1_num += (coefficient * team1Winrate) | |
t2_num += (coefficient * team2Winrate) | |
t1_den += coefficient | |
t2_den += coefficient | |
} | |
// team 1 winrate, team 2 winrate, confidence value | |
return [(t1_num/t1_den), (t2_num/t2_den), t1_den] | |
} | |
} |
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
package twopee | |
class InternationalController { | |
def teamService | |
def eloService | |
def headToHeadService | |
class StatsEntity { | |
long teamId | |
String teamName | |
String prettyName | |
int wins | |
int loses | |
double rating | |
double averageWeekRating | |
double averageMonthRating | |
int groupWins = 0 | |
int groupLoses = 0 | |
} | |
class WLGroupEntity { | |
int wins = 0 | |
int loses = 0 | |
} | |
static Team boX(HashMap lookupMap, Team team1, Team team2, int gamesToWin, int advantage) { | |
int t1W = advantage, t2W = 0 | |
while ((t1W != gamesToWin) && (t2W != gamesToWin)) { | |
if (Math.random() < lookupMap[team1][team2]) { | |
t1W++ | |
} | |
else { | |
t2W++ | |
} | |
} | |
return (t1W == gamesToWin) ? team1 : team2 | |
} | |
static List<Team> boXOrder(HashMap lookupMap, Team team1, Team team2, int gamesToWin, int advantage) { | |
def winner = boX(lookupMap, team1, team2, gamesToWin, advantage) | |
def loser = (team1 == winner) ? team2 : team1 | |
return [winner, loser] | |
} | |
def ti4() { | |
List<String> teamStr = ["Alliance", "Titan", "EG", "Fnatic.EU", "Nb", "VG", "NaVi", "DK", "iG", "C9", | |
"Empire", "Navi.US", "Arrow", "LGD.cn", "mouz", "Liquid"] | |
Map<Team, Map<Team, Double>> lookupMap = new HashMap<>() | |
List<Team> teams = [] | |
HashMap<Team, Integer> groupWinners = new HashMap<>() | |
HashMap<Team, Integer> winnerCount = new HashMap<>() | |
HashMap<Team, Integer> runnerUpCount = new HashMap<>() | |
HashMap<Team, Integer> top4 = new HashMap<>() | |
HashMap<Team, Integer> top6 = new HashMap<>() | |
HashMap<Team, Integer> top10 = new HashMap<>() | |
HashMap<String, Integer> permutationKey = new HashMap<>() | |
for (String ts1: teamStr) { | |
Team t = Team.findByTeamName(ts1) | |
teams.add(t) | |
groupWinners[t] = 0, winnerCount[t] = 0, runnerUpCount[t] = 0 | |
top4[t] = 0, top6[t] = 0, top10[t] = 0 | |
} | |
for (Team t1: teams) { | |
lookupMap[t1] = new HashMap<Team, Double>() | |
for (Team t2: teams) { | |
if (t1 != t2) { | |
lookupMap[t1].put(t2, headToHeadService.headToHead(t1, t2)[0]) | |
} | |
} | |
} | |
long N = 1000000L | |
for (int i = 0; i < N; ++i) { | |
Map<Team, Integer> groupWins = new HashMap<>() | |
teams.each { groupWins[it] = 0 } | |
// phase 2 - bo1 rr | |
for (Team t1: teams) { | |
for (Team t2: teams) { | |
if (t1.id != t2.id) { | |
groupWins[boX(lookupMap, t1, t2, 1, 0)]++ | |
} | |
} | |
} | |
List<Team> topTen = groupWins.keySet().toList().sort { -groupWins[it] }.take(10) | |
groupWinners[topTen[0]] = groupWinners[topTen[0]] + 1 | |
groupWinners[topTen[1]] = groupWinners[topTen[1]] + 1 | |
topTen.each { | |
top10[it]++ | |
} | |
// phase 3 | |
def matchA = boXOrder(lookupMap, topTen[6], topTen[9], 3, 0) | |
def matchB = boXOrder(lookupMap, topTen[5], matchA[0], 3, 0) | |
def matchC = boXOrder(lookupMap, topTen[2], matchB[0], 3, 0) | |
def matchD = boXOrder(lookupMap, topTen[7], topTen[8], 3, 0) | |
def matchE = boXOrder(lookupMap, topTen[4], matchD[0], 3, 0) | |
def matchF = boXOrder(lookupMap, topTen[3], matchE[0], 3, 0) | |
// wb | |
def wb1 = boXOrder(lookupMap, topTen[0], matchF[0], 3, 0) | |
def wb2 = boXOrder(lookupMap, topTen[1], matchC[0], 3, 0) | |
def wbf = boXOrder(lookupMap, wb1[0], wb2[0], 3, 0) | |
// lb | |
def lb1 = boXOrder(lookupMap, matchE[1], matchC[1], 3, 0) | |
def lb2 = boXOrder(lookupMap, matchF[1], matchB[1], 3, 0) | |
def lb3 = boXOrder(lookupMap, lb1[0], wb2[1] , 3, 0) | |
def lb4 = boXOrder(lookupMap, lb2[0], wb1[1] , 3, 0) | |
def lb5 = boXOrder(lookupMap, lb3[0], lb4[0] , 3, 0) | |
def lbf = boXOrder(lookupMap, lb5[0], wbf[1] , 3, 0) | |
// gf | |
def gf = boXOrder(lookupMap, wbf[0], lbf[0] , 5, 0) | |
winnerCount[gf[0]]++ | |
runnerUpCount[gf[1]]++ | |
[gf[0], gf[1], lbf[1], lb5[1]].each { | |
top4[it]++ | |
} | |
[gf[0], gf[1], lbf[1], lb5[1], lb4[1], lb3[1]].each { | |
top6[it]++ | |
} | |
def m = [gf[0], gf[1], lbf[1], lb5[1]] | |
[lb4[1], lb3[1]].sort { it.teamName }.each { | |
m.add(it) | |
} | |
def key = [gf[0], gf[1], lbf[1], lb5[1]].collect { it.teamName }.join("|") | |
permutationKey[key] = 1 + (permutationKey[key] ?: 0) | |
} | |
print "\n --------------------\n" | |
print "Group Winners\n" | |
groupWinners.keySet().toList().sort { -groupWinners[it] }.each { t -> | |
print t.teamName + " :: " + (((double) groupWinners[t]) / N) + "\n" | |
} | |
print "\n --------------------\n" | |
print "First place\n" | |
winnerCount.keySet().toList().sort { -winnerCount[it] }.each { t -> | |
print t.teamName + " :: " + (((double) winnerCount[t]) / N) + "\n" | |
} | |
print "\n --------------------\n" | |
print "Runner up\n" | |
runnerUpCount.keySet().toList().sort { -runnerUpCount[it] }.each { t -> | |
print t.teamName + " :: " + (((double) runnerUpCount[t]) / N) + "\n" | |
} | |
print "\n --------------------\n" | |
print "Top 4\n" | |
top4.keySet().toList().sort { -top4[it] }.each { t -> | |
print t.teamName + " :: " + (((double) top4[t]) / N) + "\n" | |
} | |
print "\n --------------------\n" | |
print "Top 10\n" | |
top10.keySet().toList().sort { -top10[it] }.each { t -> | |
print t.teamName + " :: " + (((double) top10[t]) / N) + "\n" | |
} | |
print "\n --------------------\n" | |
print "Most likely permutations\n" | |
permutationKey.keySet().toList().sort { -permutationKey[it] }.each { t -> | |
double value = (((double) permutationKey[t]) / N) | |
if (value > 0.001) { | |
print t + " :: " + value + "\n" | |
} | |
} | |
[] | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment