225 lines
7.3 KiB
VB.net
Executable File
225 lines
7.3 KiB
VB.net
Executable File
Public Class Glicko
|
|
|
|
'This constant defines how RD increases over time, higher=faster
|
|
Private Const c = 0.6
|
|
'Minimum RD (to ensure liquidity)
|
|
Private Const RDmin = 30
|
|
'Defined as part of the Glicko system
|
|
Private Const q = 0.005756462732485
|
|
|
|
Enum Tiers
|
|
Unknown
|
|
Potato
|
|
B
|
|
A
|
|
S
|
|
X
|
|
End Enum
|
|
|
|
Property K As Double
|
|
Private _RD As Double
|
|
Property RD As Double
|
|
Set(value As Double)
|
|
If value < RDmin Then
|
|
_RD = RDmin
|
|
Else
|
|
_RD = value
|
|
End If
|
|
End Set
|
|
Get
|
|
Return _RD
|
|
End Get
|
|
End Property
|
|
Property Name As String
|
|
Property LastMatch As Long
|
|
Property Tier As Tiers = Tiers.Unknown
|
|
ReadOnly Property TierString As String
|
|
Get
|
|
Select Case Me.Tier
|
|
Case Tiers.A
|
|
Return " A "
|
|
Case Tiers.B
|
|
Return " B "
|
|
Case Tiers.Potato
|
|
Return " P "
|
|
Case Tiers.S
|
|
Return " S"
|
|
Case Tiers.X
|
|
Return " X "
|
|
Case Tiers.Unknown
|
|
Return "U "
|
|
End Select
|
|
Return ""
|
|
End Get
|
|
End Property
|
|
ReadOnly Property TierLetter As String
|
|
Get
|
|
Select Case Me.Tier
|
|
Case Tiers.A
|
|
Return "A"
|
|
Case Tiers.B
|
|
Return "B"
|
|
Case Tiers.Potato
|
|
Return "P"
|
|
Case Tiers.S
|
|
Return "S"
|
|
Case Tiers.X
|
|
Return "X"
|
|
Case Tiers.Unknown
|
|
Return "U"
|
|
End Select
|
|
Return ""
|
|
End Get
|
|
End Property
|
|
|
|
''' <summary>
|
|
''' Updates this Glicko rating after a match. MAKE SURE TO COPY THE OPPONENT BEFORE UPDATING
|
|
''' </summary>
|
|
''' <param name="opp">Opponent that was faced</param>
|
|
''' <param name="win">Did this Glicko win? Draw=0.5</param>
|
|
''' <param name="t">Match number of this match</param>
|
|
''' <remarks></remarks>
|
|
Private Sub Update(opp() As Glicko, win() As Double, t As Long)
|
|
Dim E As Double = 0
|
|
Dim g As Double = 0
|
|
Dim dsum As Double = 0
|
|
Dim rsum As Double = 0
|
|
|
|
For i = 0 To opp.Count - 1
|
|
g = 1 / Math.Sqrt(1 + (3 * Math.Pow(q, 2) * Math.Pow(opp(i).RD, 2)) / Math.Pow(Math.PI, 2))
|
|
E = 1 / (1 + Math.Pow(10, (g * (Me.K - opp(i).K)) / -400))
|
|
dsum += Math.Pow(g, 2) * E * (1 - E)
|
|
rsum += g * (win(i) - E)
|
|
Next
|
|
|
|
Dim dsq As Double = Math.Pow(Math.Pow(q, 2) * dsum, -1)
|
|
|
|
Me.K = Me.K + (q / ((1 / Math.Pow(Me.RD, 2)) + (1 / dsq))) * rsum
|
|
Me.RD = Math.Max(Math.Sqrt(Math.Pow((1 / Math.Pow(Me.RD, 2)) + (1 / dsq), -1)), 30)
|
|
Me.LastMatch = t
|
|
End Sub
|
|
|
|
Public Shared Sub UpdateBatch(a As Glicko, b As Glicko, Acount As Integer, Bcount As Integer, t As Long, Optional Dcount As Integer = 0)
|
|
Dim total As Integer = Acount + Bcount + Dcount
|
|
Dim Aresults(total - 1) As Double
|
|
Dim aopps(total - 1) As Glicko
|
|
Dim Bresults(total - 1) As Double
|
|
Dim bopps(total - 1) As Glicko
|
|
Dim upset As Boolean = False
|
|
Dim confidence As Double = Conf(a, b)
|
|
|
|
Dim tempb As New Glicko("", -1)
|
|
tempb.K = b.K
|
|
tempb.RD = b.RD
|
|
|
|
For i = 0 To total - 1
|
|
aopps(i) = tempb
|
|
bopps(i) = a
|
|
Next
|
|
For i = 0 To Acount - 1
|
|
Aresults(i) = 1
|
|
Next
|
|
|
|
For i = Acount To Acount + Bcount - 1
|
|
Bresults(i) = 1
|
|
Next
|
|
|
|
For i = Acount + Bcount To total - 1
|
|
Aresults(i) = 0.5
|
|
Bresults(i) = 0.5
|
|
Next
|
|
|
|
If (Aresults.Average > 0.5 And b.K > a.K) Or (Bresults.Average > 0.5 And a.K > b.K) Then
|
|
upset = True
|
|
End If
|
|
|
|
b.Update(bopps, Bresults, t)
|
|
a.Update(aopps, Aresults, t)
|
|
|
|
'My custom RD-increasing function in case of upsets
|
|
'Note this increases after Glicko's decrease, so they oppose each other
|
|
'If upset = True Then
|
|
' 'U-factor is upset factor, the maximum possible increase in RD for a character if we trusted the results 100% accurately
|
|
' Dim aUfactor As Double = u * a.RD * (confidence ^ e)
|
|
' Dim bUfactor As Double = u * b.RD * (confidence ^ e)
|
|
' 'F-factor is the fluke factor, more rounds means it's less of a fluke and we should increase more
|
|
' Dim Ffactor As Double = 1 - (1 / (Aresults.Count + f))
|
|
' 'C-factor is the closeness factor, close matches are weighted less (5-0 more increase than 5-4)
|
|
' Dim Cfactor As Double = (Math.Max(Aresults.Average, Bresults.Average) - 0.5) * 2
|
|
' a.RD = Math.Min(350, a.RD + (aUfactor * Ffactor * Cfactor))
|
|
' b.RD = Math.Min(350, b.RD + (bUfactor * Ffactor * Cfactor))
|
|
'End If
|
|
|
|
|
|
End Sub
|
|
|
|
|
|
|
|
''' <summary>
|
|
''' Time-decays RD in preperation for a match
|
|
''' </summary>
|
|
''' <param name="t">Current match number</param>
|
|
''' <remarks></remarks>
|
|
Public Sub Prep(t As Long)
|
|
Me.RD = Math.Min(350, Math.Sqrt(Math.Pow(Me.RD, 2) + Math.Pow(c, 2) * (t - Me.LastMatch)))
|
|
End Sub
|
|
|
|
Public Sub New(name As String, t As Long)
|
|
Me.K = 1500
|
|
Me.RD = 350
|
|
Me.Name = name
|
|
Me.LastMatch = t
|
|
End Sub
|
|
|
|
Public Function GenerateXMLItem() As XElement
|
|
Dim ret As New XElement("glicko")
|
|
ret.SetAttributeValue("name", Me.Name)
|
|
ret.SetAttributeValue("k", Me.K)
|
|
ret.SetAttributeValue("rd", Me.RD)
|
|
ret.SetAttributeValue("lastmatch", Me.LastMatch)
|
|
ret.SetAttributeValue("tier", CInt(Me.Tier))
|
|
Return ret
|
|
End Function
|
|
|
|
Private Shared Function erf(x As Double) As Double
|
|
'Erf(x) approximation, max error 3e-7
|
|
Dim a1 As Double = 0.0705230784
|
|
Dim a2 As Double = 0.0422820123
|
|
Dim a3 As Double = 0.0092705272
|
|
Dim a4 As Double = 0.0001520143
|
|
Dim a5 As Double = 0.0002765672
|
|
Dim a6 As Double = 0.0000430638
|
|
|
|
a1 = a1 * x
|
|
a2 = a2 * (x ^ 2)
|
|
a3 = a3 * (x ^ 3)
|
|
a4 = a4 * (x ^ 4)
|
|
a5 = a5 * (x ^ 5)
|
|
a6 = a6 * (x ^ 6)
|
|
Return 1 - (1 / ((1 + a1 + a2 + a3 + a4 + a5 + a6) ^ 16))
|
|
End Function
|
|
|
|
Private Shared Function CDF(x As Double, mu As Double, sigma As Double) As Double
|
|
Return 0.5 * (1 + erf((x - mu) / (sigma * Math.Sqrt(2))))
|
|
End Function
|
|
|
|
Private Shared Function StdNorm(x As Double) As Double
|
|
Return Math.Exp(-0.5 * (x ^ 2)) / (Math.Sqrt(2 * Math.PI))
|
|
End Function
|
|
|
|
Private Shared Function Norm(x As Double, mu As Double, sigma As Double) As Double
|
|
Return (1 / sigma) * StdNorm((x - mu) / sigma)
|
|
End Function
|
|
|
|
Public Shared Function Conf(a As Glicko, b As Glicko) As Double
|
|
Dim sum As Double
|
|
For i = 0 To 3000 Step 1
|
|
sum += Math.Max(Norm(i, a.K, a.RD) - Norm(i, b.K, b.RD), 0)
|
|
Next
|
|
Return sum
|
|
End Function
|
|
|
|
End Class
|
|
|
|
|