If you wish to attach one or more files enter the details below. You may also attach files by dragging and dropping them in the message box.
by Claesar » Mon Feb 18, 2019 6:44 pm
nopunin10did wrote: ↑Mon Feb 18, 2019 5:39 pm ... One minor downside of Fibonacci scoring is that someone cheating with multiple accounts can hypothetically more quickly boost a single player's score than with other DIAS systems. For instance, in a classic game that starts with one real account and six sock puppets, the real player can be assigned Russia and the draw declared immediately to grant that player ~40% of the pot. I expect that you may already have safeguards for this sort of behavior. ...
by nopunin10did » Mon Feb 18, 2019 5:39 pm
by Claesar » Mon Feb 18, 2019 4:52 pm
by nopunin10did » Mon Feb 18, 2019 3:47 pm
Code: Select all
public static class FibonacciDrawScoringImplementation { // Will update the PointsAwardedBeforeElo value on each supplied object in // the array. // This input should include exactly one object per position in the map. // e.g. // - Classic, 1900, or Versailles game should be an array of 7 // - Ancient Med should be an array of 5 // // Surrendered positions that were never replaced (those that show up as // "surrendered" in the site interface) SHOULD be included, with PlayerName // set to null. // // When a player surrenders a position, and another player replaces them, // include the REPLACEMENT player here. Any adjustments based on % of // turns played are assumed to occur after Elo and outside the context of // this function. public static void ApplyScoresToPlayersInDraw(PlayerPowerData[] players) { int count = players.Length; if (count < 5) { throw new Exception("Fibonacci scoring supports variants " + "with five or more players"); } // This sorts from least to greatest by SortingScore(). This array // contains pointers to the same objects in "players" rather than a // deep copy. PlayerPowerData[] sortedPlayers = players.OrderBy(p => p.SortingScore()); // To determine the total fibonacci points for a position, we need to // calculate all ties. Therefore, per player, we need the highest and // lowest ranks shared. int currentLowRank = count; int currentLowScore = sortedPlayers[0].SortingScore(); for (int i = 0; i < count; i++) { if (sortedPlayers[i].SortingScore() != currentLowScore) { currentLowRank = count - i; currentLowScore = sortedPlayers[i].SortingScore(); } sortedPlayers[i].LowestRankShared = currentLowRank; } int currentHighRank = 1; int currentHighScore = sortedPlayers[count - 1].SortingScore(); for (int i = count - 1; i >= 0; i--) { if (sortedPlayers[i].SortingScore() != currentHighScore) { currentHighRank = count - i; currentHighScore = sortedPlayers[i].SortingScore(); } sortedPlayers[i].HighestRankShared = currentHighRank; } decimal[] fibonacciRaw = FibonacciSequenceOfSize(count); decimal fibonacciTotal = fibonacciRaw.Sum(); foreach(PlayerPowerData player in sortedPlayers) { int lowIndex = count - player.LowestRankShared; int highIndex = count - player.HighestRankShared; int numberTied = highIndex - lowIndex + 1; decimal sumRawForRange = 0.0; for (int k = lowIndex; k <= highIndex; k++) { sumRawForRange += fibonacciRaw[k]; } // The final pre-Elo score, expressed as a decimal from 0 to 1, // is the average of the fibonacci values for ranks shared by // this player divided by the total of all fibonacciNumbers. player.PointsAwardedBeforeElo = sumRawForRange / numberTied / fibonacciTotal; } } private static decimal[] FibonacciSequenceOfSize(int size) { decimal[] fibonacciNumbers = new decimal[size]; for(int i = 0; i < size; i++) { if (i == 0) { fibonacciNumbers[i] = 0.0; } else if (i == 1 || i == 2) { fibonacciNumbers[i] = 1.0; } else { fibonacciNumbers[i] = fibonacciNumbers[i-1] + fibonacciNumbers[i-2]; } } return fibonacciNumbers; } } // A container for each player / position in the game. public class PlayerPowerData { // A Null player name represents an abandoned position public string PlayerName {get; set;} public int SupplyCentersHeld {get; set;} public int YearEliminated {get; set;} // Expressed as a decimal from 0 to 1. public decimal? PointsAwardedBeforeElo {get; set;} public int HighestRankShared {get; set;} public int LowestRankShared {get; set;} public bool IsSurrenderedPosition() { return string.IsNullOrEmpty(PlayerName); } public int SortingScore() { // Surrendered positions always place last if (IsSurrenderedPosition()) { return int.MinValue; } if (SupplyCentersHeld <= 0) { return YearEliminated; } // This methodology assumes the year number will never be higher than // 10000. return SupplyCentersHeld * 10000; } }
Top