Moved the Glicko class to a separate file

Removed some unused 'custom upset' code
This commit is contained in:
moosecrab 2020-02-24 22:49:05 -08:00
parent c8bdbaef86
commit f35bb019cc
3 changed files with 229 additions and 248 deletions

224
PepperBet/Glicko.vb Executable file
View File

@ -0,0 +1,224 @@
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

View File

@ -1,19 +1,7 @@
Module Pepperbet Module Pepperbet
'This constant defines how RD increases over time, higher=faster
Const c = 0.6
'Minimum RD (to ensure liquidity)
Const RDmin = 30
'Defined as part of the Glicko system
Const q = 0.005756462732485
'Seconds between updates 'Seconds between updates
Const updatedelay = 2 Const updatedelay = 2
'Upset RD maximum increase factor (infinite matches, as a fraction of RD)
Const u = 0.5
'Upset RD increase fluke-factor, more matches creates less chance of a fluke
Const f = 0.5
'Upset RD exponent, steepens modification curve with confidence
Const e = 1.5
Const version As String = "1.9.12" Const version As String = "1.9.12"
Const downloadurl As String = "https://long-cat.net/projects/pepperbet/pepperbet.exe?v=" & version Const downloadurl As String = "https://long-cat.net/projects/pepperbet/pepperbet.exe?v=" & version
Const versionurl As String = "https://long-cat.net/projects/pepperbet/version.txt?v=" & version Const versionurl As String = "https://long-cat.net/projects/pepperbet/version.txt?v=" & version
@ -24,6 +12,10 @@
Dim watching As Boolean = True Dim watching As Boolean = True
Dim autoupdate As Boolean = False Dim autoupdate As Boolean = False
Dim lastupdatecheck As New DateTime(0) Dim lastupdatecheck As New DateTime(0)
Dim accuracies(10) As Double
'These are the time constants for the exponentially weighted falloff
Dim accuracytimeconstants() As Double = {10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000}
Enum ModeTypes Enum ModeTypes
Matchmaking Matchmaking
Tournament Tournament
@ -888,242 +880,6 @@
End Function End Function
Private Class Glicko
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
Public Shared Sub UpdateTiers(a As Glicko, b As Glicko)
If Now.DayOfWeek = DayOfWeek.Thursday And Now.Hour >= 18 And Now.Hour < 21 Then
'shaker classic
Exit Sub
End If
If a.Tier = Tiers.Unknown And b.Tier = Tiers.Unknown Then
Exit Sub
ElseIf a.Tier = Tiers.Unknown Then
a.Tier = b.Tier
ElseIf b.Tier = Tiers.Unknown Then
b.Tier = a.Tier
ElseIf b.Tier = a.Tier Then
Exit Sub
Else
a.Tier = Tiers.Unknown
b.Tier = Tiers.Unknown
End If
Save()
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
Private Sub Save() Private Sub Save()
Dim thr As New Threading.Thread(AddressOf SaveDelegate) Dim thr As New Threading.Thread(AddressOf SaveDelegate)
thr.Start() thr.Start()

View File

@ -97,6 +97,7 @@
<Import Include="System.Xml.Linq" /> <Import Include="System.Xml.Linq" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Glicko.vb" />
<Compile Include="PepperBet.vb" /> <Compile Include="PepperBet.vb" />
<Compile Include="My Project\AssemblyInfo.vb" /> <Compile Include="My Project\AssemblyInfo.vb" />
<Compile Include="My Project\Application.Designer.vb"> <Compile Include="My Project\Application.Designer.vb">