diff --git a/GoldWars/GoldWars.xcodeproj/project.pbxproj b/GoldWars/GoldWars.xcodeproj/project.pbxproj index 9063e7d..741ce81 100644 --- a/GoldWars/GoldWars.xcodeproj/project.pbxproj +++ b/GoldWars/GoldWars.xcodeproj/project.pbxproj @@ -44,6 +44,7 @@ 9EC86B9F245C88A300796EF3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC86B9E245C88A300796EF3 /* Modal.swift */; }; 9EEDE02D246FCD770096C735 /* SpinningLogoEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EEDE02C246FCD770096C735 /* SpinningLogoEntity.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 */; }; ABC0C3732481509300387B8F /* MapUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC0C3722481509300387B8F /* MapUtils.swift */; }; 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 = ""; }; 9EEDE02C246FCD770096C735 /* SpinningLogoEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpinningLogoEntity.swift; sourceTree = ""; }; AB21D7D4246C748A00B09CBA /* MapFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapFactory.swift; sourceTree = ""; }; + AB671B242494ECF0003FBE8D /* EloHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EloHelper.swift; sourceTree = ""; }; ABA03D9F244BD54F00A66916 /* Base.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Base.swift; sourceTree = ""; }; ABC0C3722481509300387B8F /* MapUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapUtils.swift; sourceTree = ""; }; C04783ED2468583F004961FB /* intro-music.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = "intro-music.mp3"; sourceTree = ""; }; @@ -182,6 +184,7 @@ 9E0E459624796262009817A6 /* GameCenterManager.swift */, C04783EF24685995004961FB /* SettingsScene.swift */, 3EAD889424801B6A0048A10A /* RoundTimer.swift */, + AB671B242494ECF0003FBE8D /* EloHelper.swift */, ); path = GoldWars; sourceTree = ""; @@ -407,6 +410,7 @@ 9EC2FBA72476B1EC00ABF11F /* PlayerInfoComponent.swift in Sources */, 9EEDE02D246FCD770096C735 /* SpinningLogoEntity.swift in Sources */, 9E174C86245DD91500209FF0 /* ButtonComponent.swift in Sources */, + AB671B252494ECF0003FBE8D /* EloHelper.swift in Sources */, 11036113244B3E30008610AF /* MenuScene.swift in Sources */, C099579C246C5E5C0016AA22 /* DataService.swift in Sources */, AB21D7D5246C748A00B09CBA /* MapFactory.swift in Sources */, diff --git a/GoldWars/GoldWars/EloHelper.swift b/GoldWars/GoldWars/EloHelper.swift new file mode 100644 index 0000000..0b08cde --- /dev/null +++ b/GoldWars/GoldWars/EloHelper.swift @@ -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) + }) + + } + + } +} diff --git a/GoldWars/GoldWars/GameCenterManager.swift b/GoldWars/GoldWars/GameCenterManager.swift index a6e949c..a29fdd2 100644 --- a/GoldWars/GoldWars/GameCenterManager.swift +++ b/GoldWars/GoldWars/GameCenterManager.swift @@ -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) { if myMatch != match { return } @@ -275,6 +204,15 @@ final class GameCenterManager: NSObject, GKMatchmakerViewControllerDelegate, GKG os_log("Notification erhalten", log: LOG, type: .info) 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 } diff --git a/GoldWars/GoldWars/MultiplayerNetwork.swift b/GoldWars/GoldWars/MultiplayerNetwork.swift index 0327243..596fd1f 100644 --- a/GoldWars/GoldWars/MultiplayerNetwork.swift +++ b/GoldWars/GoldWars/MultiplayerNetwork.swift @@ -60,6 +60,12 @@ class MultiplayerNetwork{ 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) { let encoder = JSONEncoder() let encoded = (try? encoder.encode(NotificationModel(name: name)))! diff --git a/GoldWars/GoldWars/RoundCalculatorService.swift b/GoldWars/GoldWars/RoundCalculatorService.swift index c1d1904..4aacba7 100644 --- a/GoldWars/GoldWars/RoundCalculatorService.swift +++ b/GoldWars/GoldWars/RoundCalculatorService.swift @@ -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)) GameCenterManager.sharedInstance.winner = winner 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 } currentRound += 1