Implement EloSystem in EloHelper

Calculate new EloData for both sides on serverside
Report scores on both sides
New Data type for sharing EloData
This commit is contained in:
Marcel Schwarz 2020-06-18 20:10:08 +02:00
parent 875524d170
commit f852aae859
5 changed files with 87 additions and 71 deletions

View File

@ -44,6 +44,7 @@
9EC86B9F245C88A300796EF3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC86B9E245C88A300796EF3 /* Modal.swift */; }; 9EC86B9F245C88A300796EF3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC86B9E245C88A300796EF3 /* Modal.swift */; };
9EEDE02D246FCD770096C735 /* SpinningLogoEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EEDE02C246FCD770096C735 /* SpinningLogoEntity.swift */; }; 9EEDE02D246FCD770096C735 /* SpinningLogoEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EEDE02C246FCD770096C735 /* SpinningLogoEntity.swift */; };
AB21D7D5246C748A00B09CBA /* MapFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB21D7D4246C748A00B09CBA /* MapFactory.swift */; }; AB21D7D5246C748A00B09CBA /* MapFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB21D7D4246C748A00B09CBA /* MapFactory.swift */; };
AB671B252494ECF0003FBE8D /* EloHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB671B242494ECF0003FBE8D /* EloHelper.swift */; };
ABA03DA0244BD54F00A66916 /* Base.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA03D9F244BD54F00A66916 /* Base.swift */; }; ABA03DA0244BD54F00A66916 /* Base.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA03D9F244BD54F00A66916 /* Base.swift */; };
ABC0C3732481509300387B8F /* MapUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC0C3722481509300387B8F /* MapUtils.swift */; }; ABC0C3732481509300387B8F /* MapUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC0C3722481509300387B8F /* MapUtils.swift */; };
C04783EE2468583F004961FB /* intro-music.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C04783ED2468583F004961FB /* intro-music.mp3 */; }; C04783EE2468583F004961FB /* intro-music.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C04783ED2468583F004961FB /* intro-music.mp3 */; };
@ -109,6 +110,7 @@
9ECD3699245C91F7008DEEBD /* GoldWars.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = GoldWars.entitlements; sourceTree = "<group>"; }; 9ECD3699245C91F7008DEEBD /* GoldWars.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = GoldWars.entitlements; sourceTree = "<group>"; };
9EEDE02C246FCD770096C735 /* SpinningLogoEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpinningLogoEntity.swift; sourceTree = "<group>"; }; 9EEDE02C246FCD770096C735 /* SpinningLogoEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpinningLogoEntity.swift; sourceTree = "<group>"; };
AB21D7D4246C748A00B09CBA /* MapFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapFactory.swift; sourceTree = "<group>"; }; AB21D7D4246C748A00B09CBA /* MapFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapFactory.swift; sourceTree = "<group>"; };
AB671B242494ECF0003FBE8D /* EloHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EloHelper.swift; sourceTree = "<group>"; };
ABA03D9F244BD54F00A66916 /* Base.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Base.swift; sourceTree = "<group>"; }; ABA03D9F244BD54F00A66916 /* Base.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Base.swift; sourceTree = "<group>"; };
ABC0C3722481509300387B8F /* MapUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapUtils.swift; sourceTree = "<group>"; }; ABC0C3722481509300387B8F /* MapUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapUtils.swift; sourceTree = "<group>"; };
C04783ED2468583F004961FB /* intro-music.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = "intro-music.mp3"; sourceTree = "<group>"; }; C04783ED2468583F004961FB /* intro-music.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = "intro-music.mp3"; sourceTree = "<group>"; };
@ -182,6 +184,7 @@
9E0E459624796262009817A6 /* GameCenterManager.swift */, 9E0E459624796262009817A6 /* GameCenterManager.swift */,
C04783EF24685995004961FB /* SettingsScene.swift */, C04783EF24685995004961FB /* SettingsScene.swift */,
3EAD889424801B6A0048A10A /* RoundTimer.swift */, 3EAD889424801B6A0048A10A /* RoundTimer.swift */,
AB671B242494ECF0003FBE8D /* EloHelper.swift */,
); );
path = GoldWars; path = GoldWars;
sourceTree = "<group>"; sourceTree = "<group>";
@ -407,6 +410,7 @@
9EC2FBA72476B1EC00ABF11F /* PlayerInfoComponent.swift in Sources */, 9EC2FBA72476B1EC00ABF11F /* PlayerInfoComponent.swift in Sources */,
9EEDE02D246FCD770096C735 /* SpinningLogoEntity.swift in Sources */, 9EEDE02D246FCD770096C735 /* SpinningLogoEntity.swift in Sources */,
9E174C86245DD91500209FF0 /* ButtonComponent.swift in Sources */, 9E174C86245DD91500209FF0 /* ButtonComponent.swift in Sources */,
AB671B252494ECF0003FBE8D /* EloHelper.swift in Sources */,
11036113244B3E30008610AF /* MenuScene.swift in Sources */, 11036113244B3E30008610AF /* MenuScene.swift in Sources */,
C099579C246C5E5C0016AA22 /* DataService.swift in Sources */, C099579C246C5E5C0016AA22 /* DataService.swift in Sources */,
AB21D7D5246C748A00B09CBA /* MapFactory.swift in Sources */, AB21D7D5246C748A00B09CBA /* MapFactory.swift in Sources */,

View File

@ -0,0 +1,60 @@
//
// EloHelper.swift
// GoldWars
//
// Created by Marcel Schwarz on 13.06.20.
// Copyright © 2020 SP2. All rights reserved.
//
import GameKit
import os
struct EloDataForPeer : Codable {
let scoreToReport: Int64
}
class EloHelper {
static private let LOG = OSLog.init(subsystem: "EloHelper", category: "EloHelper")
static let IDENTIFIER = "de.hft.stuttgart.ip2.goldwars.matchmaking"
static func updateEloScore(winner: GKPlayer, hatDenNikoGemacht looser: GKPlayer) {
let leaderboard = GKLeaderboard.init(players: [winner, looser])
leaderboard.identifier = EloHelper.IDENTIFIER
leaderboard.loadScores{scores, error in
// Get Scores
let R_looser = scores?.filter { $0.player == looser }.first ?? GKScore(leaderboardIdentifier: EloHelper.IDENTIFIER, player: looser)
let R_winner = scores?.filter { $0.player == winner }.first ?? GKScore(leaderboardIdentifier: EloHelper.IDENTIFIER, player: winner)
// Calc ELO
let Q_looser = pow(10.0, Double(R_looser.value) / 400.0)
let Q_winner = pow(10.0, Double(R_winner.value) / 400.0)
let E_looser = Q_looser / (Q_looser + Q_winner)
let E_winner = Q_winner / (Q_looser + Q_winner)
let R_winner_new = Int64(Double(R_winner.value) + 10.0 * (1.0 - E_winner))
let R_looser_new = Int64(Double(R_looser.value) + 10.0 * (0.0 - E_looser))
// Update Elo on leaderboard
let winner_new = GKScore(leaderboardIdentifier: EloHelper.IDENTIFIER, player: winner)
winner_new.value = R_winner_new
let looser_new = GKScore(leaderboardIdentifier: EloHelper.IDENTIFIER, player: looser)
looser_new.value = R_looser_new
let scoreForPeer = winner == GameCenterManager.sharedInstance.localPlayer ? looser_new : winner_new
let scoreForHost = winner == GameCenterManager.sharedInstance.localPlayer ? winner_new : looser_new
MultiplayerNetwork.sharedInstance.sendEloData(scoreToReport: scoreForPeer)
GKScore.report([scoreForHost], withCompletionHandler: { error in
os_log("New Scores reported to EloSystem", log: LOG, type: .info)
})
}
}
}

View File

@ -120,77 +120,6 @@ final class GameCenterManager: NSObject, GKMatchmakerViewControllerDelegate, GKG
} }
} }
} }
func submitLeaderboardScore(identifier: String, score: Int64) {
let reportedScore = GKScore(leaderboardIdentifier: identifier)
reportedScore.value = score
GKScore.report([reportedScore]) { (error) in
guard error == nil else {
print(error?.localizedDescription ?? "")
return
}
}
}
func calculateLeaderboardScore(identifier: String, scoreOffset: Int64) {
GKLeaderboard.loadLeaderboards { (leaderboards: [GKLeaderboard]?, err: Error?) in
leaderboards?.forEach({ (leaderboard: GKLeaderboard) in
if leaderboard.identifier == identifier {
leaderboard.loadScores { (scores: [GKScore]?, err: Error?) in
scores?.forEach({ (score: GKScore) in
if score.player == self.localPlayer {
score.value += scoreOffset
GKScore.report([score]) { (error) in
guard error == nil else {
print(error?.localizedDescription ?? "")
return
}
}
}
})
}
}
})
}
}
//TODO: not finished -> DELETE if not usefull
// Possible way to implement matchmaking based on 2 players
// can be changed into method with fixed leaderboard and offset, boolean hasWon: true/false to calculate outcome of a match
func calculateLeaderboardScoreExperimentalMatchmaking(identifier: String, scoreOffset: Int64, opponentPlayer: GKPlayer) {
// Iterate through leaderboards
GKLeaderboard.loadLeaderboards { (leaderboards: [GKLeaderboard]?, err: Error?) in
leaderboards?.forEach({ (leaderboard: GKLeaderboard) in
if leaderboard.identifier == identifier {
leaderboard.loadScores { (scores: [GKScore]?, err: Error?) in
// Iterate through scores
var currentScore: GKScore = GKScore.init()
currentScore.value = -1
var opponetScore: GKScore = GKScore.init()
currentScore.value = 0
// Get scores of self and opponent
scores?.forEach({ (score: GKScore) in
if score.player == self.localPlayer {
currentScore = score
opponetScore = score
}
if score.player == opponentPlayer {
opponetScore = score
}
})
// do silly calculation
currentScore.value += scoreOffset + (opponetScore.value - currentScore.value)
GKScore.report([currentScore]) { (error) in
guard error == nil else {
print(error?.localizedDescription ?? "")
return
}
}
}
}
})
}
}
func match(_ match: GKMatch, didReceive data: Data, fromRemotePlayer player: GKPlayer) { func match(_ match: GKMatch, didReceive data: Data, fromRemotePlayer player: GKPlayer) {
if myMatch != match { return } if myMatch != match { return }
@ -275,6 +204,15 @@ final class GameCenterManager: NSObject, GKMatchmakerViewControllerDelegate, GKG
os_log("Notification erhalten", log: LOG, type: .info) os_log("Notification erhalten", log: LOG, type: .info)
NotificationCenter.default.post(name: Notification.Name(rawValue: notification.name), object: nil) NotificationCenter.default.post(name: Notification.Name(rawValue: notification.name), object: nil)
} }
if let eloData = try? jsonDecoder.decode(EloDataForPeer.self, from: data) {
print("Recieved elo data: \(eloData.scoreToReport)")
let score = GKScore(leaderboardIdentifier: EloHelper.IDENTIFIER, player: self.localPlayer)
score.value = eloData.scoreToReport
GKScore.report([score], withCompletionHandler: { error in
os_log("New Scores reported to EloSystem", log: self.LOG, type: .info)
})
}
MultiplayerNetwork.sharedInstance.isSending = false MultiplayerNetwork.sharedInstance.isSending = false
} }

View File

@ -60,6 +60,12 @@ class MultiplayerNetwork{
sendData(data: encoded) sendData(data: encoded)
} }
func sendEloData(scoreToReport: GKScore) {
let encoder = JSONEncoder()
let encoded = (try? encoder.encode(EloDataForPeer(scoreToReport: scoreToReport.value)))!
sendData(data: encoded)
}
func sendNotificationToPlayer(name: String) { func sendNotificationToPlayer(name: String) {
let encoder = JSONEncoder() let encoder = JSONEncoder()
let encoded = (try? encoder.encode(NotificationModel(name: name)))! let encoded = (try? encoder.encode(NotificationModel(name: name)))!

View File

@ -160,6 +160,14 @@ class RoundCalculatorService {
winner == GameCenterManager.sharedInstance.hostingPlayer?.displayName ? GameCenterManager.sharedInstance.sendStateToPeers(state: State(state: 4)) : GameCenterManager.sharedInstance.sendStateToPeers(state: State(state: 5)) winner == GameCenterManager.sharedInstance.hostingPlayer?.displayName ? GameCenterManager.sharedInstance.sendStateToPeers(state: State(state: 4)) : GameCenterManager.sharedInstance.sendStateToPeers(state: State(state: 5))
GameCenterManager.sharedInstance.winner = winner GameCenterManager.sharedInstance.winner = winner
GameCenterManager.sharedInstance.gameEnded = true GameCenterManager.sharedInstance.gameEnded = true
// Update EloSystem
if winner == GameCenterManager.sharedInstance.hostingPlayer?.displayName {
EloHelper.updateEloScore(winner: GameCenterManager.sharedInstance.hostingPlayer!, hatDenNikoGemacht: GameCenterManager.sharedInstance.peerPlayer!)
} else {
EloHelper.updateEloScore(winner: GameCenterManager.sharedInstance.peerPlayer!, hatDenNikoGemacht: GameCenterManager.sharedInstance.hostingPlayer!)
}
return return
} }
currentRound += 1 currentRound += 1