Skip to content

Instantly share code, notes, and snippets.

@Noxville
Created February 18, 2015 19:45
Show Gist options
  • Save Noxville/3e0f18c8b793050dcaa2 to your computer and use it in GitHub Desktop.
Save Noxville/3e0f18c8b793050dcaa2 to your computer and use it in GitHub Desktop.
Elo Ratings for Heroes
package twopee
import grails.transaction.Transactional
import org.hibernate.Session
import javax.annotation.PostConstruct
import java.text.DateFormat
import java.text.DecimalFormat
import java.text.SimpleDateFormat
@Transactional
class HeroEloService {
def patchService
def eloService
def heroService
class EloDiff {
double diff = 0.0
int count = 0
void addChange(double change) {
diff += change
count ++
}
double avg() {
return count ? diff / count : null
}
}
Map ensurePatchMap(String patch, Map inMap) {
def mp = inMap[patch]
if (!mp) {
inMap[patch] = new HashMap<String, EloDiff>()
}
return inMap[patch]
}
EloDiff ensureEloDiff(String hero, Map inMap) {
def ed = inMap[hero]
if (!ed) {
inMap[hero] = new EloDiff()
}
return inMap[hero]
}
def stripTimeFromDate(Date date) {
DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy")
return formatter.parse(formatter.format(date))
}
def patchEloHero(List<String> teams) {
// maps[patch] -> map[hero] = EloSummary
Map<String, Map<String, EloDiff>> patchEloMap = new HashMap<String, HashMap<String, EloDiff>>()
Map<String, Map<Date, Double>> teamElo = new HashMap<String, HashMap<Date, Double>>()
DecimalFormat df = new DecimalFormat("0.00");
// should be 6.81+
long firstMatch = 490145180
def matches = teams ?
Mtch.findAllByDireTeamInListOrRadiantTeamInList(teams, teams).findAll { it.matchId > firstMatch} :
Mtch.findAllByMatchIdGreaterThan(firstMatch)
EloTeamDataPoint.withSession { Session session ->
session.createQuery("select eloDate, rating, team from EloTeamDataPoint where " +
"eloDate >= DATE(:start) and DATE(:end) >= eloDate ")
.setParameter("start", matches.min { it.date }.date)
.setParameter("end", matches.max { it.date }.date)
.list().each { it ->
def eloDate = it[0]
def rating = it[1]
def team = it[2]
def tl = teamElo[team] ?: new HashMap<String, Double>()
tl[eloDate] = rating
teamElo[team] = tl
}
}
int num = matches.size()
matches.eachWithIndex { Mtch m, int i ->
log.debug("Processing ${m.matchId} [${i}/${num} ~ (${df.format(100.0 * i/num)})")
try {
def rad = teamElo[m.radiantTeam][stripTimeFromDate(m.date)]
def dire = teamElo[m.direTeam][stripTimeFromDate(m.date)]
def winner = m.winner.trim()
if (rad && dire) {
def radiantShift = eloService.calculateElo(new EloService.Elo(rating: rad), new EloService.Elo(rating: dire), winner == "Radiant") - rad
// print "Rad: ${rad}, Dire: ${dire} for ${m.date.format("yyyy-MM-dd")} with r-shift ${radiantShift}"
def patch = patchService.getPatch(m.matchId)
def map = ensurePatchMap(patch, patchEloMap)
if (!teams || (m.radiantTeam in teams)) {
m.radiantHeroes.each {
ensureEloDiff(it.toString(), map).addChange(radiantShift)
}
}
if (!teams || (m.direTeam in teams)) {
m.direHeroes.each {
ensureEloDiff(it.toString(), map).addChange(-1.0 * radiantShift)
}
}
}
else {
log.debug("Missing Elos for [${m.matchId}]")
}
}
catch (NullPointerException e) {
log.debug("Error processing [${m.matchId}] ==> [$e]")
}
catch (Exception e) {
throw e
}
}
return patchEloMap
}
def getData(List<String> teams) {
def patchEloHero = patchEloHero(teams)
Map<String, String> heroIcons = new HashMap<>()
heroService.stringToId.each { String img, Integer id ->
heroIcons[id.toString()] = img
}
[patchEloHero:patchEloHero, heroLookupMap: heroService.lookupMap, heroIcons: heroIcons, teams: teams]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment