diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 73f6849..0000000 --- a/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -cmake_install.cmake -CMakeCache.txt -CMakeFiles -Makefile -Moserware.Skills.dll -TestResult.xml -UnitTests.dll - -bin -deploy -deploy/* -obj -*.suo -*.cache -*.tmp - -_Resharper.* - -*.resharper -*.resharper.user \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 419d94d..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,79 +0,0 @@ -cmake_minimum_required(VERSION 2.6) -set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules) -enable_language(CSharp) - -csharp_add_library(Moserware.Skills - Skills/Elo/DuellingEloCalculator.cs - Skills/Elo/EloRating.cs - Skills/Elo/FideEloCalculator.cs - Skills/Elo/FideKFactor.cs - Skills/Elo/GaussianEloCalculator.cs - Skills/Elo/GaussianKFactor.cs - Skills/Elo/KFactor.cs - Skills/Elo/TwoPlayerEloCalculator.cs - Skills/FactorGraphs/Factor.cs - Skills/FactorGraphs/FactorGraph.cs - Skills/FactorGraphs/FactorGraphLayer.cs - Skills/FactorGraphs/FactorList.cs - Skills/FactorGraphs/Message.cs - Skills/FactorGraphs/Schedule.cs - Skills/FactorGraphs/Variable.cs - Skills/FactorGraphs/VariableFactory.cs - Skills/GameInfo.cs - Skills/Guard.cs - Skills/ISupportPartialPlay.cs - Skills/ISupportPartialUpdate.cs - Skills/Numerics/GaussianDistribution.cs - Skills/Numerics/Matrix.cs - Skills/Numerics/Range.cs - Skills/PairwiseComparison.cs - Skills/PartialPlay.cs - Skills/Player.cs - Skills/PlayersRange.cs - Skills/Properties/AssemblyInfo.cs - Skills/RankSorter.cs - Skills/Rating.cs - Skills/SkillCalculator.cs - Skills/Team.cs - Skills/TeamsRange.cs - Skills/TrueSkill/DrawMargin.cs - Skills/TrueSkill/FactorGraphTrueSkillCalculator.cs - Skills/TrueSkill/TrueSkillFactorGraph.cs - Skills/TrueSkill/TruncatedGaussianCorrectionFunctions.cs - Skills/TrueSkill/TwoTeamTrueSkillCalculator.cs - Skills/TrueSkill/TwoPlayerTrueSkillCalculator.cs - Skills/TrueSkill/Factors/GaussianFactor.cs - Skills/TrueSkill/Factors/GaussianGreaterThanFactor.cs - Skills/TrueSkill/Factors/GaussianLikelihoodFactor.cs - Skills/TrueSkill/Factors/GaussianPriorFactor.cs - Skills/TrueSkill/Factors/GaussianWeightedSumFactor.cs - Skills/TrueSkill/Factors/GaussianWithinFactor.cs - Skills/TrueSkill/Layers/IteratedTeamDifferencesInnerLayer.cs - Skills/TrueSkill/Layers/PlayerPerformancesToTeamPerformancesLayer.cs - Skills/TrueSkill/Layers/PlayerPriorValuesToSkillsLayer.cs - Skills/TrueSkill/Layers/PlayerSkillsToPerformancesLayer.cs - Skills/TrueSkill/Layers/TeamDifferencesComparisonLayer.cs - Skills/TrueSkill/Layers/TeamPerformancesToTeamPerformanceDifferencesLayer.cs - Skills/TrueSkill/Layers/TrueSkillFactorGraphLayer.cs - Skills/TrueSkillCalculator.cs -) - -# find_program(NUNIT_CONSOLE nunit-console) - -csharp_add_library(UnitTests - UnitTests/Elo/GaussianEloCalculatorTest.cs - UnitTests/Elo/DuellingEloTest.cs - UnitTests/Elo/EloAssert.cs - UnitTests/Elo/FideEloCalculatorTest.cs - UnitTests/Numerics/MatrixTests.cs - UnitTests/Numerics/GaussianDistributionTests.cs - UnitTests/Properties/AssemblyInfo.cs - UnitTests/RankSorterTest.cs - UnitTests/TrueSkill/DrawMarginTest.cs - UnitTests/TrueSkill/FactorGraphTrueSkillCalculatorTests.cs - UnitTests/TrueSkill/TrueSkillCalculatorTests.cs - UnitTests/TrueSkill/TwoPlayerTrueSkillCalculatorTest.cs - UnitTests/TrueSkill/TwoTeamTrueSkillCalculatorTest.cs - REFERENCES Moserware.Skills nunit.framework -) -add_dependencies(UnitTests Moserware.Skills) diff --git a/README b/README new file mode 100644 index 0000000..ce4c9d6 --- /dev/null +++ b/README @@ -0,0 +1,9 @@ +This is a PHP port of the Moserware.Skills project that's available at + +http://github.com/moserware/Skills + +For more details on how the algorithm works, see + +http://www.moserware.com/2010/03/computing-your-skill.html + +For details on how to use this project, see the accompanying unit tests with this project \ No newline at end of file diff --git a/README.txt b/README.txt deleted file mode 100644 index c7a875e..0000000 --- a/README.txt +++ /dev/null @@ -1,4 +0,0 @@ -The code for the TrueSkill calculator is in the "Skills" folder and its unit -tests are in the "UnitTests" folder. - -For more details, see the "README" file in each of those folders. \ No newline at end of file diff --git a/Skills.sln b/Skills.sln deleted file mode 100644 index 7e5e0f9..0000000 --- a/Skills.sln +++ /dev/null @@ -1,26 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Skills", "Skills\Skills.csproj", "{15AD1345-984C-48ED-AF9A-2EAB44E5AA2B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{6F80946D-AC8B-4063-8588-96841C18BF0A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {15AD1345-984C-48ED-AF9A-2EAB44E5AA2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {15AD1345-984C-48ED-AF9A-2EAB44E5AA2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {15AD1345-984C-48ED-AF9A-2EAB44E5AA2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {15AD1345-984C-48ED-AF9A-2EAB44E5AA2B}.Release|Any CPU.Build.0 = Release|Any CPU - {6F80946D-AC8B-4063-8588-96841C18BF0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6F80946D-AC8B-4063-8588-96841C18BF0A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6F80946D-AC8B-4063-8588-96841C18BF0A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6F80946D-AC8B-4063-8588-96841C18BF0A}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/Skills/Elo/DuellingEloCalculator.cs b/Skills/Elo/DuellingEloCalculator.cs deleted file mode 100644 index d135608..0000000 --- a/Skills/Elo/DuellingEloCalculator.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Moserware.Skills.Elo -{ - public class DuellingEloCalculator : SkillCalculator - { - private readonly TwoPlayerEloCalculator _TwoPlayerEloCalc; - - public DuellingEloCalculator(TwoPlayerEloCalculator twoPlayerEloCalculator) - : base(SupportedOptions.None, TeamsRange.AtLeast(2), PlayersRange.AtLeast(1)) - { - _TwoPlayerEloCalc = twoPlayerEloCalculator; - } - - public override IDictionary CalculateNewRatings(GameInfo gameInfo, IEnumerable> teams, params int[] teamRanks) - { - // On page 6 of the TrueSkill paper, the authors write: - // "When we had to process a team game or a game with more than two teams we used - // the so-called *duelling* heuristic: For each player, compute the Δ's in comparison - // to all other players based on the team outcome of the player and every other player and - // perform an update with the average of the Δ's." - // This implements that algorithm. - - ValidateTeamCountAndPlayersCountPerTeam(teams); - RankSorter.Sort(ref teams, ref teamRanks); - - var teamsList = teams.ToList(); - - var deltas = new Dictionary>(); - - for(int ixCurrentTeam = 0; ixCurrentTeam < teamsList.Count; ixCurrentTeam++) - { - for(int ixOtherTeam = 0; ixOtherTeam < teamsList.Count; ixOtherTeam++) - { - if(ixOtherTeam == ixCurrentTeam) - { - // Shouldn't duel against ourself ;) - continue; - } - - var currentTeam = teamsList[ixCurrentTeam]; - var otherTeam = teamsList[ixOtherTeam]; - - // Remember that bigger numbers mean worse rank (e.g. other-current is what we want) - var comparison = (PairwiseComparison) Math.Sign(teamRanks[ixOtherTeam] - teamRanks[ixCurrentTeam]); - - foreach(var currentTeamPlayerRatingPair in currentTeam) - { - foreach(var otherTeamPlayerRatingPair in otherTeam) - { - UpdateDuels(gameInfo, deltas, - currentTeamPlayerRatingPair.Key, currentTeamPlayerRatingPair.Value, - otherTeamPlayerRatingPair.Key, otherTeamPlayerRatingPair.Value, - comparison); - - } - } - } - } - - var result = new Dictionary(); - - foreach(var currentTeam in teamsList) - { - foreach(var currentTeamPlayerPair in currentTeam) - { - var currentPlayerAverageDuellingDelta = deltas[currentTeamPlayerPair.Key].Values.Average(); - result[currentTeamPlayerPair.Key] = new EloRating(currentTeamPlayerPair.Value.Mean + currentPlayerAverageDuellingDelta); - } - } - - return result; - } - - private void UpdateDuels(GameInfo gameInfo, - IDictionary> duels, - TPlayer player1, Rating player1Rating, - TPlayer player2, Rating player2Rating, - PairwiseComparison weakToStrongComparison) - { - - var duelOutcomes = _TwoPlayerEloCalc.CalculateNewRatings(gameInfo, - Teams.Concat( - new Team(player1, player1Rating), - new Team(player2, player2Rating)), - (weakToStrongComparison == PairwiseComparison.Win) ? new int[] { 1, 2 } - : (weakToStrongComparison == PairwiseComparison.Lose) ? new int[] { 2, 1 } - : new int[] { 1, 1}); - - - UpdateDuelInfo(duels, player1, player1Rating, duelOutcomes[player1], player2); - UpdateDuelInfo(duels, player2, player2Rating, duelOutcomes[player2], player1); - } - - private static void UpdateDuelInfo(IDictionary> duels, - TPlayer self, Rating selfBeforeRating, Rating selfAfterRating, - TPlayer opponent ) - { - IDictionary selfToOpponentDuelDeltas; - - if(!duels.TryGetValue(self, out selfToOpponentDuelDeltas)) - { - selfToOpponentDuelDeltas = new Dictionary(); - duels[self] = selfToOpponentDuelDeltas; - } - - selfToOpponentDuelDeltas[opponent] = selfAfterRating.Mean - selfBeforeRating.Mean; - } - - public override double CalculateMatchQuality(GameInfo gameInfo, IEnumerable> teams) - { - // HACK! Need a better algorithm, this is just to have something there and it isn't good - double minQuality = 1.0; - - var teamList = teams.ToList(); - - for(int ixCurrentTeam = 0; ixCurrentTeam < teamList.Count; ixCurrentTeam++) - { - EloRating currentTeamAverageRating = new EloRating(teamList[ixCurrentTeam].Values.Average(r => r.Mean)); - var currentTeam = new Team(new Player(ixCurrentTeam), currentTeamAverageRating); - - for(int ixOtherTeam = ixCurrentTeam + 1; ixOtherTeam < teamList.Count; ixOtherTeam++) - { - EloRating otherTeamAverageRating = new EloRating(teamList[ixOtherTeam].Values.Average(r => r.Mean)); - var otherTeam = new Team(new Player(ixOtherTeam), otherTeamAverageRating); - - minQuality = Math.Min(minQuality, - _TwoPlayerEloCalc.CalculateMatchQuality(gameInfo, - Teams.Concat(currentTeam, otherTeam))); - } - } - - return minQuality; - } - } -} \ No newline at end of file diff --git a/Skills/Elo/EloRating.cs b/Skills/Elo/EloRating.cs deleted file mode 100644 index e408c43..0000000 --- a/Skills/Elo/EloRating.cs +++ /dev/null @@ -1,14 +0,0 @@ - -namespace Moserware.Skills.Elo -{ - /// - /// An Elo rating represented by a single number (mean). - /// - public class EloRating : Rating - { - public EloRating(double rating) - : base(rating, 0) - { - } - } -} diff --git a/Skills/Elo/FideEloCalculator.cs b/Skills/Elo/FideEloCalculator.cs deleted file mode 100644 index cc3623e..0000000 --- a/Skills/Elo/FideEloCalculator.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; - -namespace Moserware.Skills.Elo -{ - // Including ELO's scheme as a simple comparison. - // See http://en.wikipedia.org/wiki/Elo_rating_system#Theory - // for more details - public class FideEloCalculator : TwoPlayerEloCalculator - { - public FideEloCalculator() - : this(new FideKFactor()) - { - } - - public FideEloCalculator(FideKFactor kFactor) - : base(kFactor) - { - } - - protected override double GetPlayerWinProbability(GameInfo gameInfo, double playerRating, double opponentRating) - { - double ratingDifference = opponentRating - playerRating; - - return 1.0 - / - ( - 1.0 + Math.Pow(10.0, ratingDifference / (2 * gameInfo.Beta)) - ); - } - } -} \ No newline at end of file diff --git a/Skills/Elo/FideKFactor.cs b/Skills/Elo/FideKFactor.cs deleted file mode 100644 index f3c16b2..0000000 --- a/Skills/Elo/FideKFactor.cs +++ /dev/null @@ -1,36 +0,0 @@ - -namespace Moserware.Skills.Elo -{ - // see http://ratings.fide.com/calculator_rtd.phtml for details - public class FideKFactor : KFactor - { - public FideKFactor() - { - } - - public override double GetValueForRating(double rating) - { - if (rating < 2400) - { - return 15; - } - - return 10; - } - - /// - /// Indicates someone who has played less than 30 games. - /// - public class Provisional : FideKFactor - { - public Provisional() - { - } - - public override double GetValueForRating(double rating) - { - return 25; - } - } - } -} diff --git a/Skills/Elo/GaussianEloCalculator.cs b/Skills/Elo/GaussianEloCalculator.cs deleted file mode 100644 index 9e9a012..0000000 --- a/Skills/Elo/GaussianEloCalculator.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using Moserware.Numerics; - -namespace Moserware.Skills.Elo -{ - public class GaussianEloCalculator : TwoPlayerEloCalculator - { - // From the paper - private static readonly KFactor StableKFactor = new KFactor(24); - - public GaussianEloCalculator() - : base(StableKFactor) - { - } - - protected override double GetPlayerWinProbability(GameInfo gameInfo, double playerRating, double opponentRating) - { - double ratingDifference = playerRating - opponentRating; - - // See equation 1.1 in the TrueSkill paper - return GaussianDistribution.CumulativeTo( - ratingDifference - / - (Math.Sqrt(2) * gameInfo.Beta)); - } - } -} diff --git a/Skills/Elo/GaussianKFactor.cs b/Skills/Elo/GaussianKFactor.cs deleted file mode 100644 index f2fa62a..0000000 --- a/Skills/Elo/GaussianKFactor.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Moserware.Skills.Elo -{ - public class GaussianKFactor : KFactor - { - // From paper - const double StableDynamicsKFactor = 24.0; - - public GaussianKFactor() - : base(StableDynamicsKFactor) - { - } - - public GaussianKFactor(GameInfo gameInfo, double latestGameWeightingFactor) - : base(latestGameWeightingFactor * gameInfo.Beta * Math.Sqrt(Math.PI)) - { - } - } -} diff --git a/Skills/Elo/KFactor.cs b/Skills/Elo/KFactor.cs deleted file mode 100644 index c8be739..0000000 --- a/Skills/Elo/KFactor.cs +++ /dev/null @@ -1,22 +0,0 @@ - -namespace Moserware.Skills.Elo -{ - public class KFactor - { - private double _Value; - - protected KFactor() - { - } - - public KFactor(double exactKFactor) - { - _Value = exactKFactor; - } - - public virtual double GetValueForRating(double rating) - { - return _Value; - } - } -} diff --git a/Skills/Elo/TwoPlayerEloCalculator.cs b/Skills/Elo/TwoPlayerEloCalculator.cs deleted file mode 100644 index 943db47..0000000 --- a/Skills/Elo/TwoPlayerEloCalculator.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Moserware.Skills.Elo -{ - public abstract class TwoPlayerEloCalculator : SkillCalculator - { - protected readonly KFactor _KFactor; - - protected TwoPlayerEloCalculator(KFactor kFactor) - : base(SupportedOptions.None, TeamsRange.Exactly(2), PlayersRange.Exactly(1)) - { - _KFactor = kFactor; - } - - public override IDictionary CalculateNewRatings(GameInfo gameInfo, IEnumerable> teams, params int[] teamRanks) - { - ValidateTeamCountAndPlayersCountPerTeam(teams); - RankSorter.Sort(ref teams, ref teamRanks); - - var result = new Dictionary(); - bool isDraw = (teamRanks[0] == teamRanks[1]); - - var player1 = teams.First().First(); - var player2 = teams.Last().First(); - - var player1Rating = player1.Value.Mean; - var player2Rating = player2.Value.Mean; - - result[player1.Key] = CalculateNewRating(gameInfo, player1Rating, player2Rating, isDraw ? PairwiseComparison.Draw : PairwiseComparison.Win); - result[player2.Key] = CalculateNewRating(gameInfo, player2Rating, player1Rating, isDraw ? PairwiseComparison.Draw : PairwiseComparison.Lose); - - return result; - } - - protected virtual EloRating CalculateNewRating(GameInfo gameInfo, double selfRating, double opponentRating, PairwiseComparison selfToOpponentComparison) - { - double expectedProbability = GetPlayerWinProbability(gameInfo, selfRating, opponentRating); - double actualProbability = GetScoreFromComparison(selfToOpponentComparison); - double k = _KFactor.GetValueForRating(selfRating); - double ratingChange = k * (actualProbability - expectedProbability); - double newRating = selfRating + ratingChange; - - return new EloRating(newRating); - } - - private static double GetScoreFromComparison(PairwiseComparison comparison) - { - switch (comparison) - { - case PairwiseComparison.Win: - return 1; - case PairwiseComparison.Draw: - return 0.5; - case PairwiseComparison.Lose: - return 0; - default: - throw new NotSupportedException(); - } - } - - protected abstract double GetPlayerWinProbability(GameInfo gameInfo, double playerRating, double opponentRating); - - public override double CalculateMatchQuality(GameInfo gameInfo, IEnumerable> teams) - { - ValidateTeamCountAndPlayersCountPerTeam(teams); - double player1Rating = teams.First().First().Value.Mean; - double player2Rating = teams.Last().First().Value.Mean; - double ratingDifference = player1Rating - player2Rating; - - // The TrueSkill paper mentions that they used s1 - s2 (rating difference) to - // determine match quality. I convert that to a percentage as a delta from 50% - // using the cumulative density function of the specific curve being used - double deltaFrom50Percent = Math.Abs(GetPlayerWinProbability(gameInfo, player1Rating, player2Rating) - 0.5); - return (0.5 - deltaFrom50Percent) / 0.5; - } - } -} diff --git a/Skills/FactorGraphs/Factor.cs b/Skills/FactorGraphs/Factor.cs deleted file mode 100644 index 6e7caf2..0000000 --- a/Skills/FactorGraphs/Factor.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; - -namespace Moserware.Skills.FactorGraphs -{ - public abstract class Factor - { - private readonly List> _Messages = new List>(); - - private readonly Dictionary, Variable> _MessageToVariableBinding = - new Dictionary, Variable>(); - - private readonly string _Name; - private readonly List> _Variables = new List>(); - - protected Factor(string name) - { - _Name = "Factor[" + name + "]"; - } - - /// Returns the log-normalization constant of that factor - public virtual double LogNormalization - { - get { return 0; } - } - - /// Returns the number of messages that the factor has - public int NumberOfMessages - { - get { return _Messages.Count; } - } - - protected ReadOnlyCollection> Variables - { - get { return _Variables.AsReadOnly(); } - } - - protected ReadOnlyCollection> Messages - { - get { return _Messages.AsReadOnly(); } - } - - /// Update the message and marginal of the i-th variable that the factor is connected to - public virtual double UpdateMessage(int messageIndex) - { - Guard.ArgumentIsValidIndex(messageIndex, _Messages.Count, "messageIndex"); - return UpdateMessage(_Messages[messageIndex], _MessageToVariableBinding[_Messages[messageIndex]]); - } - - protected virtual double UpdateMessage(Message message, Variable variable) - { - throw new NotImplementedException(); - } - - /// Resets the marginal of the variables a factor is connected to - public virtual void ResetMarginals() - { - foreach (var currentVariable in _MessageToVariableBinding.Values) - { - currentVariable.ResetToPrior(); - } - } - - /// Sends the ith message to the marginal and returns the log-normalization constant - public virtual double SendMessage(int messageIndex) - { - Guard.ArgumentIsValidIndex(messageIndex, _Messages.Count, "messageIndex"); - - Message message = _Messages[messageIndex]; - Variable variable = _MessageToVariableBinding[message]; - return SendMessage(message, variable); - } - - protected abstract double SendMessage(Message message, Variable variable); - - public abstract Message CreateVariableToMessageBinding(Variable variable); - - protected Message CreateVariableToMessageBinding(Variable variable, Message message) - { - int index = _Messages.Count; - _Messages.Add(message); - _MessageToVariableBinding[message] = variable; - _Variables.Add(variable); - - return message; - } - - public override string ToString() - { - return _Name ?? base.ToString(); - } - } -} \ No newline at end of file diff --git a/Skills/FactorGraphs/FactorGraph.cs b/Skills/FactorGraphs/FactorGraph.cs deleted file mode 100644 index a7e93c5..0000000 --- a/Skills/FactorGraphs/FactorGraph.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Moserware.Skills.FactorGraphs -{ - public class FactorGraph - where TSelf : FactorGraph - where TVariable : Variable - { - public VariableFactory VariableFactory { get; protected set; } - } -} \ No newline at end of file diff --git a/Skills/FactorGraphs/FactorGraphLayer.cs b/Skills/FactorGraphs/FactorGraphLayer.cs deleted file mode 100644 index fd76b5b..0000000 --- a/Skills/FactorGraphs/FactorGraphLayer.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Moserware.Skills.FactorGraphs -{ - public abstract class FactorGraphLayerBase - { - public abstract IEnumerable> UntypedFactors { get; } - public abstract void BuildLayer(); - - public virtual Schedule CreatePriorSchedule() - { - return null; - } - - public virtual Schedule CreatePosteriorSchedule() - { - return null; - } - - // HACK - - public abstract void SetRawInputVariablesGroups(object value); - public abstract object GetRawOutputVariablesGroups(); - } - - public abstract class FactorGraphLayer - : FactorGraphLayerBase - where TParentGraph : FactorGraph - where TBaseVariable : Variable - where TInputVariable : TBaseVariable - where TFactor : Factor - where TOutputVariable : TBaseVariable - { - private readonly List _LocalFactors = new List(); - private readonly List> _OutputVariablesGroups = new List>(); - private IList> _InputVariablesGroups = new List>(); - - protected FactorGraphLayer(TParentGraph parentGraph) - { - ParentFactorGraph = parentGraph; - } - - protected IList> InputVariablesGroups - { - get { return _InputVariablesGroups; } - } - - // HACK - - public TParentGraph ParentFactorGraph { get; private set; } - - public IList> OutputVariablesGroups - { - get { return _OutputVariablesGroups; } - } - - public IList LocalFactors - { - get { return _LocalFactors; } - } - - public override IEnumerable> UntypedFactors - { - get { return _LocalFactors.Cast>(); } - } - - public override void SetRawInputVariablesGroups(object value) - { - var newList = value as IList>; - if (newList == null) - { - // TODO: message - throw new ArgumentException(); - } - - _InputVariablesGroups = newList; - } - - public override object GetRawOutputVariablesGroups() - { - return _OutputVariablesGroups; - } - - protected Schedule ScheduleSequence( - IEnumerable itemsToSequence, - string nameFormat, - params object[] args) - where TSchedule : Schedule - - { - string formattedName = String.Format(nameFormat, args); - return new ScheduleSequence(formattedName, itemsToSequence); - } - - protected void AddLayerFactor(TFactor factor) - { - _LocalFactors.Add(factor); - } - - // Helper utility - protected double Square(double x) - { - return x*x; - } - } -} \ No newline at end of file diff --git a/Skills/FactorGraphs/FactorList.cs b/Skills/FactorGraphs/FactorList.cs deleted file mode 100644 index 07540f7..0000000 --- a/Skills/FactorGraphs/FactorList.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Moserware.Skills.FactorGraphs -{ - /// - /// Helper class for computing the factor graph's normalization constant. - /// - public class FactorList - { - private readonly List> _List = new List>(); - - public double LogNormalization - { - get - { - _List.ForEach(f => f.ResetMarginals()); - - double sumLogZ = 0.0; - - for (int i = 0; i < _List.Count; i++) - { - Factor f = _List[i]; - for (int j = 0; j < f.NumberOfMessages; j++) - { - sumLogZ += f.SendMessage(j); - } - } - - double sumLogS = _List.Aggregate(0.0, (acc, fac) => acc + fac.LogNormalization); - - return sumLogZ + sumLogS; - } - } - - public int Count - { - get { return _List.Count; } - } - - public Factor AddFactor(Factor factor) - { - _List.Add(factor); - return factor; - } - } -} \ No newline at end of file diff --git a/Skills/FactorGraphs/Message.cs b/Skills/FactorGraphs/Message.cs deleted file mode 100644 index 85f8c90..0000000 --- a/Skills/FactorGraphs/Message.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; - -namespace Moserware.Skills.FactorGraphs -{ - public class Message - { - private readonly string _NameFormat; - private readonly object[] _NameFormatArgs; - - public Message() - : this(default(T), null, null) - { - } - - public Message(T value, string nameFormat, params object[] args) - - { - _NameFormat = nameFormat; - _NameFormatArgs = args; - Value = value; - } - - public T Value { get; set; } - - public override string ToString() - { - return (_NameFormat == null) ? base.ToString() : String.Format(_NameFormat, _NameFormatArgs); - } - } -} \ No newline at end of file diff --git a/Skills/FactorGraphs/Schedule.cs b/Skills/FactorGraphs/Schedule.cs deleted file mode 100644 index 7e43f09..0000000 --- a/Skills/FactorGraphs/Schedule.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Moserware.Skills.FactorGraphs -{ - public abstract class Schedule - { - private readonly string _Name; - - protected Schedule(string name) - { - _Name = name; - } - - public abstract double Visit(int depth, int maxDepth); - - public double Visit() - { - return Visit(-1, 0); - } - - public override string ToString() - { - return _Name; - } - } - - public class ScheduleStep : Schedule - { - private readonly Factor _Factor; - private readonly int _Index; - - public ScheduleStep(string name, Factor factor, int index) - : base(name) - { - _Factor = factor; - _Index = index; - } - - public override double Visit(int depth, int maxDepth) - { - double delta = _Factor.UpdateMessage(_Index); - return delta; - } - } - - public class ScheduleSequence : ScheduleSequence> - { - public ScheduleSequence(string name, IEnumerable> schedules) - : base(name, schedules) - { - } - } - - public class ScheduleSequence : Schedule - where TSchedule : Schedule - { - private readonly IEnumerable _Schedules; - - public ScheduleSequence(string name, IEnumerable schedules) - : base(name) - { - _Schedules = schedules; - } - - public override double Visit(int depth, int maxDepth) - { - double maxDelta = 0; - - foreach (TSchedule currentSchedule in _Schedules) - { - maxDelta = Math.Max(currentSchedule.Visit(depth + 1, maxDepth), maxDelta); - } - - return maxDelta; - } - } - - public class ScheduleLoop : Schedule - { - private readonly double _MaxDelta; - private readonly Schedule _ScheduleToLoop; - - public ScheduleLoop(string name, Schedule scheduleToLoop, double maxDelta) - : base(name) - { - _ScheduleToLoop = scheduleToLoop; - _MaxDelta = maxDelta; - } - - public override double Visit(int depth, int maxDepth) - { - int totalIterations = 1; - double delta = _ScheduleToLoop.Visit(depth + 1, maxDepth); - while (delta > _MaxDelta) - { - delta = _ScheduleToLoop.Visit(depth + 1, maxDepth); - totalIterations++; - } - - return delta; - } - } -} \ No newline at end of file diff --git a/Skills/FactorGraphs/Variable.cs b/Skills/FactorGraphs/Variable.cs deleted file mode 100644 index 80a3bc7..0000000 --- a/Skills/FactorGraphs/Variable.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; - -namespace Moserware.Skills.FactorGraphs -{ - public class Variable - { - private readonly string _Name; - private readonly TValue _Prior; - - public Variable(string name, TValue prior) - { - _Name = "Variable[" + name + "]"; - _Prior = prior; - ResetToPrior(); - } - - public virtual TValue Value { get; set; } - - public void ResetToPrior() - { - Value = _Prior; - } - - public override string ToString() - { - return _Name; - } - } - - public class DefaultVariable : Variable - { - public DefaultVariable() - : base("Default", default(TValue)) - { - } - - public override TValue Value - { - get { return default(TValue); } - set { throw new NotSupportedException(); } - } - } - - public class KeyedVariable : Variable - { - public KeyedVariable(TKey key, string name, TValue prior) - : base(name, prior) - { - Key = key; - } - - public TKey Key { get; private set; } - } -} \ No newline at end of file diff --git a/Skills/FactorGraphs/VariableFactory.cs b/Skills/FactorGraphs/VariableFactory.cs deleted file mode 100644 index d07b5ff..0000000 --- a/Skills/FactorGraphs/VariableFactory.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Moserware.Skills.FactorGraphs -{ - public class VariableFactory - { - // using a Func to encourage fresh copies in case it's overwritten - private readonly Func _VariablePriorInitializer; - - public VariableFactory(Func variablePriorInitializer) - { - _VariablePriorInitializer = variablePriorInitializer; - } - - public Variable CreateBasicVariable(string nameFormat, params object[] args) - { - var newVar = new Variable( - String.Format(nameFormat, args), - _VariablePriorInitializer()); - - return newVar; - } - - public KeyedVariable CreateKeyedVariable(TKey key, string nameFormat, params object[] args) - { - var newVar = new KeyedVariable( - key, - String.Format(nameFormat, args), - _VariablePriorInitializer()); - - return newVar; - } - } -} \ No newline at end of file diff --git a/Skills/GameInfo.cs b/Skills/GameInfo.cs deleted file mode 100644 index cfdddc4..0000000 --- a/Skills/GameInfo.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace Moserware.Skills -{ - /// - /// Parameters about the game for calculating the TrueSkill. - /// - public class GameInfo - { - private const double DefaultBeta = DefaultInitialMean/6.0; - private const double DefaultDrawProbability = 0.10; - private const double DefaultDynamicsFactor = DefaultInitialMean/300.0; - private const double DefaultInitialMean = 25.0; - private const double DefaultInitialStandardDeviation = DefaultInitialMean/3.0; - - public GameInfo(double initialMean, double initialStandardDeviation, double beta, double dynamicFactor, - double drawProbability) - { - InitialMean = initialMean; - InitialStandardDeviation = initialStandardDeviation; - Beta = beta; - DynamicsFactor = dynamicFactor; - DrawProbability = drawProbability; - } - - public double InitialMean { get; set; } - public double InitialStandardDeviation { get; set; } - public double Beta { get; set; } - - public double DynamicsFactor { get; set; } - public double DrawProbability { get; set; } - - public Rating DefaultRating - { - get { return new Rating(InitialMean, InitialStandardDeviation); } - } - - public static GameInfo DefaultGameInfo - { - get - { - // We return a fresh copy since we have public setters that can mutate state - return new GameInfo(DefaultInitialMean, - DefaultInitialStandardDeviation, - DefaultBeta, - DefaultDynamicsFactor, - DefaultDrawProbability); - } - } - } -} \ No newline at end of file diff --git a/Skills/Guard.cs b/Skills/Guard.cs deleted file mode 100644 index deda637..0000000 --- a/Skills/Guard.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; - -namespace Moserware.Skills -{ - /// - /// Verifies argument contracts. - /// - /// These are used until .NET 4.0 ships with Contracts. For more information, - /// see http://www.moserware.com/2008/01/borrowing-ideas-from-3-interesting.html - internal static class Guard - { - public static void ArgumentNotNull(object value, string parameterName) - { - if (value == null) - { - throw new ArgumentNullException(parameterName); - } - } - - public static void ArgumentIsValidIndex(int index, int count, string parameterName) - { - if ((index < 0) || (index >= count)) - { - throw new ArgumentOutOfRangeException(parameterName); - } - } - - public static void ArgumentInRangeInclusive(double value, double min, double max, string parameterName) - { - if ((value < min) || (value > max)) - { - throw new ArgumentOutOfRangeException(parameterName); - } - } - } -} \ No newline at end of file diff --git a/Skills/ISupportPartialPlay.cs b/Skills/ISupportPartialPlay.cs deleted file mode 100644 index 9b781a6..0000000 --- a/Skills/ISupportPartialPlay.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Moserware.Skills -{ - /// - /// Indicates support for allowing partial play (where a player only plays a part of the time). - /// - public interface ISupportPartialPlay - { - /// - /// Indicates the percent of the time the player should be weighted where 0.0 indicates the player didn't play and 1.0 indicates the player played 100% of the time. - /// - double PartialPlayPercentage { get; } - } -} \ No newline at end of file diff --git a/Skills/ISupportPartialUpdate.cs b/Skills/ISupportPartialUpdate.cs deleted file mode 100644 index f202c61..0000000 --- a/Skills/ISupportPartialUpdate.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Moserware.Skills -{ - public interface ISupportPartialUpdate - { - /// - /// Indicated how much of a skill update a player should receive where 0.0 represents no update and 1.0 represents 100% of the update. - /// - double PartialUpdatePercentage { get; } - } -} \ No newline at end of file diff --git a/Skills/License.txt b/Skills/License.txt deleted file mode 100644 index f6c1a69..0000000 --- a/Skills/License.txt +++ /dev/null @@ -1,37 +0,0 @@ -The core ideas used in this Moserware.Skills project were described in -"TrueSkill (TM): A Bayesian Skill Rating System" available at -http://research.microsoft.com/apps/pubs/default.aspx?id=67956 - -The authors of the above paper have asked for a link to that article -as attribution in derived works. - -Some concepts were based on sample F# code that was written by Ralf Herbrich -Copyright (c) 2007, 2008 Microsoft Research Ltd, available at -http://blogs.technet.com/apg/archive/2008/06/16/trueskill-in-f.aspx - -All the C# code in this Moserware.Skills project is -Copyright (c) 2010 Jeff Moser - -Redistribution and use in source and binary forms, with or without modification, are -permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, this list - of conditions and the following disclaimer in the documentation and/or other materials - provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY JEFF MOSER ``AS IS'' AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JEFF MOSER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation are those of the -authors and should not be interpreted as representing official policies, either expressed -or implied, of Jeff Moser. \ No newline at end of file diff --git a/Skills/Numerics/GaussianDistribution.cs b/Skills/Numerics/GaussianDistribution.cs deleted file mode 100644 index d303b1b..0000000 --- a/Skills/Numerics/GaussianDistribution.cs +++ /dev/null @@ -1,240 +0,0 @@ -using System; - -namespace Moserware.Numerics -{ - public class GaussianDistribution - { - // Intentionally, we're not going to derive related things, but set them all at once - // to get around some NaN issues - - private GaussianDistribution() - { - } - - public GaussianDistribution(double mean, double standardDeviation) - { - Mean = mean; - StandardDeviation = standardDeviation; - Variance = Square(StandardDeviation); - Precision = 1.0/Variance; - PrecisionMean = Precision*Mean; - } - - public double Mean { get; private set; } - public double StandardDeviation { get; private set; } - - // Precision and PrecisionMean are used because they make multiplying and dividing simpler - // (the the accompanying math paper for more details) - - public double Precision { get; private set; } - - public double PrecisionMean { get; private set; } - - private double Variance { get; set; } - - public double NormalizationConstant - { - get - { - // Great derivation of this is at http://www.astro.psu.edu/~mce/A451_2/A451/downloads/notes0.pdf - return 1.0/(Math.Sqrt(2*Math.PI)*StandardDeviation); - } - } - - public GaussianDistribution Clone() - { - var result = new GaussianDistribution(); - result.Mean = Mean; - result.StandardDeviation = StandardDeviation; - result.Variance = Variance; - result.Precision = Precision; - result.PrecisionMean = PrecisionMean; - return result; - } - - public static GaussianDistribution FromPrecisionMean(double precisionMean, double precision) - { - var gaussianDistribution = new GaussianDistribution(); - gaussianDistribution.Precision = precision; - gaussianDistribution.PrecisionMean = precisionMean; - gaussianDistribution.Variance = 1.0/precision; - gaussianDistribution.StandardDeviation = Math.Sqrt(gaussianDistribution.Variance); - gaussianDistribution.Mean = gaussianDistribution.PrecisionMean/gaussianDistribution.Precision; - return gaussianDistribution; - } - - // Although we could use equations from // For details, see http://www.tina-vision.net/tina-knoppix/tina-memo/2003-003.pdf - // for multiplication, the precision mean ones are easier to write :) - public static GaussianDistribution operator *(GaussianDistribution left, GaussianDistribution right) - { - return FromPrecisionMean(left.PrecisionMean + right.PrecisionMean, left.Precision + right.Precision); - } - - /// Computes the absolute difference between two Gaussians - public static double AbsoluteDifference(GaussianDistribution left, GaussianDistribution right) - { - return Math.Max( - Math.Abs(left.PrecisionMean - right.PrecisionMean), - Math.Sqrt(Math.Abs(left.Precision - right.Precision))); - } - - /// Computes the absolute difference between two Gaussians - public static double operator -(GaussianDistribution left, GaussianDistribution right) - { - return AbsoluteDifference(left, right); - } - - public static double LogProductNormalization(GaussianDistribution left, GaussianDistribution right) - { - if ((left.Precision == 0) || (right.Precision == 0)) - { - return 0; - } - - double varianceSum = left.Variance + right.Variance; - double meanDifference = left.Mean - right.Mean; - - double logSqrt2Pi = Math.Log(Math.Sqrt(2*Math.PI)); - return -logSqrt2Pi - (Math.Log(varianceSum)/2.0) - (Square(meanDifference)/(2.0*varianceSum)); - } - - - public static GaussianDistribution operator /(GaussianDistribution numerator, GaussianDistribution denominator) - { - return FromPrecisionMean(numerator.PrecisionMean - denominator.PrecisionMean, - numerator.Precision - denominator.Precision); - } - - public static double LogRatioNormalization(GaussianDistribution numerator, GaussianDistribution denominator) - { - if ((numerator.Precision == 0) || (denominator.Precision == 0)) - { - return 0; - } - - double varianceDifference = denominator.Variance - numerator.Variance; - double meanDifference = numerator.Mean - denominator.Mean; - - double logSqrt2Pi = Math.Log(Math.Sqrt(2*Math.PI)); - - return Math.Log(denominator.Variance) + logSqrt2Pi - Math.Log(varianceDifference)/2.0 + - Square(meanDifference)/(2*varianceDifference); - } - - private static double Square(double x) - { - return x*x; - } - - public static double At(double x) - { - return At(x, 0, 1); - } - - public static double At(double x, double mean, double standardDeviation) - { - // See http://mathworld.wolfram.com/NormalDistribution.html - // 1 -(x-mean)^2 / (2*stdDev^2) - // P(x) = ------------------- * e - // stdDev * sqrt(2*pi) - - double multiplier = 1.0/(standardDeviation*Math.Sqrt(2*Math.PI)); - double expPart = Math.Exp((-1.0*Math.Pow(x - mean, 2.0))/(2*(standardDeviation*standardDeviation))); - double result = multiplier*expPart; - return result; - } - - public static double CumulativeTo(double x, double mean, double standardDeviation) - { - double invsqrt2 = -0.707106781186547524400844362104; - double result = ErrorFunctionCumulativeTo(invsqrt2*x); - return 0.5*result; - } - - public static double CumulativeTo(double x) - { - return CumulativeTo(x, 0, 1); - } - - private static double ErrorFunctionCumulativeTo(double x) - { - // Derived from page 265 of Numerical Recipes 3rd Edition - double z = Math.Abs(x); - - double t = 2.0/(2.0 + z); - double ty = 4*t - 2; - - double[] coefficients = { - -1.3026537197817094, 6.4196979235649026e-1, - 1.9476473204185836e-2, -9.561514786808631e-3, -9.46595344482036e-4, - 3.66839497852761e-4, 4.2523324806907e-5, -2.0278578112534e-5, - -1.624290004647e-6, 1.303655835580e-6, 1.5626441722e-8, -8.5238095915e-8, - 6.529054439e-9, 5.059343495e-9, -9.91364156e-10, -2.27365122e-10, - 9.6467911e-11, 2.394038e-12, -6.886027e-12, 8.94487e-13, 3.13092e-13, - -1.12708e-13, 3.81e-16, 7.106e-15, -1.523e-15, -9.4e-17, 1.21e-16, -2.8e-17 - }; - - int ncof = coefficients.Length; - double d = 0.0; - double dd = 0.0; - - - for (int j = ncof - 1; j > 0; j--) - { - double tmp = d; - d = ty*d - dd + coefficients[j]; - dd = tmp; - } - - double ans = t*Math.Exp(-z*z + 0.5*(coefficients[0] + ty*d) - dd); - return x >= 0.0 ? ans : (2.0 - ans); - } - - - private static double InverseErrorFunctionCumulativeTo(double p) - { - // From page 265 of numerical recipes - - if (p >= 2.0) - { - return -100; - } - if (p <= 0.0) - { - return 100; - } - - double pp = (p < 1.0) ? p : 2 - p; - double t = Math.Sqrt(-2*Math.Log(pp/2.0)); // Initial guess - double x = -0.70711*((2.30753 + t*0.27061)/(1.0 + t*(0.99229 + t*0.04481)) - t); - - for (int j = 0; j < 2; j++) - { - double err = ErrorFunctionCumulativeTo(x) - pp; - x += err/(1.12837916709551257*Math.Exp(-(x*x)) - x*err); // Halley - } - - return p < 1.0 ? x : -x; - } - - public static double InverseCumulativeTo(double x, double mean, double standardDeviation) - { - // From numerical recipes, page 320 - return mean - Math.Sqrt(2)*standardDeviation*InverseErrorFunctionCumulativeTo(2*x); - } - - public static double InverseCumulativeTo(double x) - { - return InverseCumulativeTo(x, 0, 1); - } - - - public override string ToString() - { - // Debug help - return String.Format("μ={0:0.0000}, σ={1:0.0000}", - Mean, - StandardDeviation); - } - } -} \ No newline at end of file diff --git a/Skills/Numerics/Matrix.cs b/Skills/Numerics/Matrix.cs deleted file mode 100644 index feadcb9..0000000 --- a/Skills/Numerics/Matrix.cs +++ /dev/null @@ -1,524 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Moserware.Numerics -{ - /// - /// Represents an MxN matrix with double precision values. - /// - internal class Matrix - { - // Anything smaller than this will be assumed to be rounding error in terms of equality matching - private const int FractionalDigitsToRoundTo = 10; - private static readonly double ErrorTolerance = Math.Pow(0.1, FractionalDigitsToRoundTo); // e.g. 1/10^10 - - protected double[][] _MatrixRowValues; - // Note: some properties like Determinant, Inverse, etc are properties instead - // of methods to make the syntax look nicer even though this sort of goes against - // Framework Design Guidelines that properties should be "cheap" since it could take - // a long time to compute these properties if the matrices are "big." - - protected Matrix() - { - } - - public Matrix(int rows, int columns, params double[] allRowValues) - { - Rows = rows; - Columns = columns; - - _MatrixRowValues = new double[rows][]; - - int currentIndex = 0; - for (int currentRow = 0; currentRow < Rows; currentRow++) - { - _MatrixRowValues[currentRow] = new double[Columns]; - - for (int currentColumn = 0; currentColumn < Columns; currentColumn++) - { - if ((allRowValues != null) && (currentIndex < allRowValues.Length)) - { - _MatrixRowValues[currentRow][currentColumn] = allRowValues[currentIndex++]; - } - } - } - } - - public Matrix(double[][] rowValues) - { - if (!rowValues.All(row => row.Length == rowValues[0].Length)) - { - throw new ArgumentException("All rows must be the same length!"); - } - - Rows = rowValues.Length; - Columns = rowValues[0].Length; - _MatrixRowValues = rowValues; - } - - protected Matrix(int rows, int columns, double[][] matrixRowValues) - { - Rows = rows; - Columns = columns; - _MatrixRowValues = matrixRowValues; - } - - public Matrix(int rows, int columns, IEnumerable> columnValues) - : this(rows, columns) - { - int columnIndex = 0; - - foreach (var currentColumn in columnValues) - { - int rowIndex = 0; - foreach (double currentColumnValue in currentColumn) - { - _MatrixRowValues[rowIndex++][columnIndex] = currentColumnValue; - } - columnIndex++; - } - } - - public int Rows { get; protected set; } - public int Columns { get; protected set; } - - public double this[int row, int column] - { - get { return _MatrixRowValues[row][column]; } - } - - public Matrix Transpose - { - get - { - // Just flip everything - var transposeMatrix = new double[Columns][]; - for (int currentRowTransposeMatrix = 0; - currentRowTransposeMatrix < Columns; - currentRowTransposeMatrix++) - { - var transposeMatrixCurrentRowColumnValues = new double[Rows]; - transposeMatrix[currentRowTransposeMatrix] = transposeMatrixCurrentRowColumnValues; - - for (int currentColumnTransposeMatrix = 0; - currentColumnTransposeMatrix < Rows; - currentColumnTransposeMatrix++) - { - transposeMatrixCurrentRowColumnValues[currentColumnTransposeMatrix] = - _MatrixRowValues[currentColumnTransposeMatrix][currentRowTransposeMatrix]; - } - } - - return new Matrix(Columns, Rows, transposeMatrix); - } - } - - private bool IsSquare - { - get { return (Rows == Columns) && Rows > 0; } - } - - public double Determinant - { - get - { - // Basic argument checking - if (!IsSquare) - { - throw new NotSupportedException("Matrix must be square!"); - } - - if (Rows == 1) - { - // Really happy path :) - return _MatrixRowValues[0][0]; - } - - if (Rows == 2) - { - // Happy path! - // Given: - // | a b | - // | c d | - // The determinant is ad - bc - double a = _MatrixRowValues[0][0]; - double b = _MatrixRowValues[0][1]; - double c = _MatrixRowValues[1][0]; - double d = _MatrixRowValues[1][1]; - return a*d - b*c; - } - - // I use the Laplace expansion here since it's straightforward to implement. - // It's O(n^2) and my implementation is especially poor performing, but the - // core idea is there. Perhaps I should replace it with a better algorithm - // later. - // See http://en.wikipedia.org/wiki/Laplace_expansion for details - - double result = 0.0; - - // I expand along the first row - for (int currentColumn = 0; currentColumn < Columns; currentColumn++) - { - double firstRowColValue = _MatrixRowValues[0][currentColumn]; - double cofactor = GetCofactor(0, currentColumn); - double itemToAdd = firstRowColValue*cofactor; - result += itemToAdd; - } - - return result; - } - } - - public Matrix Adjugate - { - get - { - if (!IsSquare) - { - throw new ArgumentException("Matrix must be square!"); - } - - // See http://en.wikipedia.org/wiki/Adjugate_matrix - if (Rows == 2) - { - // Happy path! - // Adjugate of: - // | a b | - // | c d | - // is - // | d -b | - // | -c a | - - double a = _MatrixRowValues[0][0]; - double b = _MatrixRowValues[0][1]; - double c = _MatrixRowValues[1][0]; - double d = _MatrixRowValues[1][1]; - - return new SquareMatrix(d, -b, - -c, a); - } - - // The idea is that it's the transpose of the cofactors - var result = new double[Columns][]; - - for (int currentColumn = 0; currentColumn < Columns; currentColumn++) - { - result[currentColumn] = new double[Rows]; - - for (int currentRow = 0; currentRow < Rows; currentRow++) - { - result[currentColumn][currentRow] = GetCofactor(currentRow, currentColumn); - } - } - - return new Matrix(result); - } - } - - public Matrix Inverse - { - get - { - if ((Rows == 1) && (Columns == 1)) - { - return new SquareMatrix(1.0/_MatrixRowValues[0][0]); - } - - // Take the simple approach: - // http://en.wikipedia.org/wiki/Cramer%27s_rule#Finding_inverse_matrix - return (1.0/Determinant)*Adjugate; - } - } - - public static Matrix operator *(double scalarValue, Matrix matrix) - { - int rows = matrix.Rows; - int columns = matrix.Columns; - var newValues = new double[rows][]; - - for (int currentRow = 0; currentRow < rows; currentRow++) - { - var newRowColumnValues = new double[columns]; - newValues[currentRow] = newRowColumnValues; - - for (int currentColumn = 0; currentColumn < columns; currentColumn++) - { - newRowColumnValues[currentColumn] = scalarValue*matrix._MatrixRowValues[currentRow][currentColumn]; - } - } - - return new Matrix(rows, columns, newValues); - } - - public static Matrix operator +(Matrix left, Matrix right) - { - if ((left.Rows != right.Rows) || (left.Columns != right.Columns)) - { - throw new ArgumentException("Matrices must be of the same size"); - } - - // simple addition of each item - - var resultMatrix = new double[left.Rows][]; - - for (int currentRow = 0; currentRow < left.Rows; currentRow++) - { - var rowColumnValues = new double[right.Columns]; - resultMatrix[currentRow] = rowColumnValues; - for (int currentColumn = 0; currentColumn < right.Columns; currentColumn++) - { - rowColumnValues[currentColumn] = left._MatrixRowValues[currentRow][currentColumn] - + - right._MatrixRowValues[currentRow][currentColumn]; - } - } - - return new Matrix(left.Rows, right.Columns, resultMatrix); - } - - public static Matrix operator *(Matrix left, Matrix right) - { - // Just your standard matrix multiplication. - // See http://en.wikipedia.org/wiki/Matrix_multiplication for details - - if (left.Columns != right.Rows) - { - throw new ArgumentException("The width of the left matrix must match the height of the right matrix", - "right"); - } - - int resultRows = left.Rows; - int resultColumns = right.Columns; - - var resultMatrix = new double[resultRows][]; - - for (int currentRow = 0; currentRow < resultRows; currentRow++) - { - resultMatrix[currentRow] = new double[resultColumns]; - - for (int currentColumn = 0; currentColumn < resultColumns; currentColumn++) - { - double productValue = 0; - - for (int vectorIndex = 0; vectorIndex < left.Columns; vectorIndex++) - { - double leftValue = left._MatrixRowValues[currentRow][vectorIndex]; - double rightValue = right._MatrixRowValues[vectorIndex][currentColumn]; - double vectorIndexProduct = leftValue*rightValue; - productValue += vectorIndexProduct; - } - - resultMatrix[currentRow][currentColumn] = productValue; - } - } - - return new Matrix(resultRows, resultColumns, resultMatrix); - } - - private Matrix GetMinorMatrix(int rowToRemove, int columnToRemove) - { - // See http://en.wikipedia.org/wiki/Minor_(linear_algebra) - - // I'm going to use a horribly naïve algorithm... because I can :) - var result = new double[Rows - 1][]; - int resultRow = 0; - - for (int currentRow = 0; currentRow < Rows; currentRow++) - { - if (currentRow == rowToRemove) - { - continue; - } - - result[resultRow] = new double[Columns - 1]; - - int resultColumn = 0; - - for (int currentColumn = 0; currentColumn < Columns; currentColumn++) - { - if (currentColumn == columnToRemove) - { - continue; - } - - result[resultRow][resultColumn] = _MatrixRowValues[currentRow][currentColumn]; - resultColumn++; - } - - resultRow++; - } - - return new Matrix(Rows - 1, Columns - 1, result); - } - - private double GetCofactor(int rowToRemove, int columnToRemove) - { - // See http://en.wikipedia.org/wiki/Cofactor_(linear_algebra) for details - // REVIEW: should things be reversed since I'm 0 indexed? - int sum = rowToRemove + columnToRemove; - bool isEven = (sum%2 == 0); - - if (isEven) - { - return GetMinorMatrix(rowToRemove, columnToRemove).Determinant; - } - else - { - return -1.0*GetMinorMatrix(rowToRemove, columnToRemove).Determinant; - } - } - - // Equality stuff - // See http://msdn.microsoft.com/en-us/library/ms173147.aspx - - public static bool operator ==(Matrix a, Matrix b) - { - // If both are null, or both are same instance, return true. - if (ReferenceEquals(a, b)) - { - return true; - } - - // If one is null, but not both, return false. - if (((object) a == null) || ((object) b == null)) - { - return false; - } - - if ((a.Rows != b.Rows) || (a.Columns != b.Columns)) - { - return false; - } - - for (int currentRow = 0; currentRow < a.Rows; currentRow++) - { - for (int currentColumn = 0; currentColumn < a.Columns; currentColumn++) - { - double delta = - Math.Abs(a._MatrixRowValues[currentRow][currentColumn] - - b._MatrixRowValues[currentRow][currentColumn]); - - if (delta > ErrorTolerance) - { - return false; - } - } - } - - return true; - } - - public static bool operator !=(Matrix a, Matrix b) - { - return !(a == b); - } - - public override int GetHashCode() - { - double result = Rows; - result += 2*Columns; - - unchecked - { - for (int currentRow = 0; currentRow < Rows; currentRow++) - { - bool eventRow = (currentRow%2) == 0; - double multiplier = eventRow ? 1.0 : 2.0; - - for (int currentColumn = 0; currentColumn < Columns; currentColumn++) - { - double cellValue = _MatrixRowValues[currentRow][currentColumn]; - double roundedValue = Math.Round(cellValue, FractionalDigitsToRoundTo); - result += multiplier*roundedValue; - } - } - } - - // Ok, now convert that double to an int - byte[] resultBytes = BitConverter.GetBytes(result); - - var finalBytes = new byte[4]; - for (int i = 0; i < 4; i++) - { - finalBytes[i] = (byte) (resultBytes[i] ^ resultBytes[i + 4]); - } - - int hashCode = BitConverter.ToInt32(finalBytes, 0); - return hashCode; - } - - public override bool Equals(object obj) - { - var other = obj as Matrix; - if (other == null) - { - return base.Equals(obj); - } - - return this == other; - } - } - - internal class DiagonalMatrix : Matrix - { - public DiagonalMatrix(IList diagonalValues) - : base(diagonalValues.Count, diagonalValues.Count) - { - for (int i = 0; i < diagonalValues.Count; i++) - { - _MatrixRowValues[i][i] = diagonalValues[i]; - } - } - } - - internal class Vector : Matrix - { - public Vector(IList vectorValues) - : base(vectorValues.Count, 1, new IEnumerable[] {vectorValues}) - { - } - } - - internal class SquareMatrix : Matrix - { - public SquareMatrix(params double[] allValues) - { - Rows = (int) Math.Sqrt(allValues.Length); - Columns = Rows; - - int allValuesIndex = 0; - - _MatrixRowValues = new double[Rows][]; - for (int currentRow = 0; currentRow < Rows; currentRow++) - { - var currentRowValues = new double[Columns]; - _MatrixRowValues[currentRow] = currentRowValues; - - for (int currentColumn = 0; currentColumn < Columns; currentColumn++) - { - currentRowValues[currentColumn] = allValues[allValuesIndex++]; - } - } - } - } - - internal class IdentityMatrix : DiagonalMatrix - { - public IdentityMatrix(int rows) - : base(CreateDiagonal(rows)) - { - } - - private static double[] CreateDiagonal(int rows) - { - var result = new double[rows]; - for (int i = 0; i < rows; i++) - { - result[i] = 1.0; - } - - return result; - } - } -} \ No newline at end of file diff --git a/Skills/Numerics/Range.cs b/Skills/Numerics/Range.cs deleted file mode 100644 index 7d5b581..0000000 --- a/Skills/Numerics/Range.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; - -namespace Moserware.Skills.Numerics -{ - // The whole purpose of this class is to make the code for the SkillCalculator(s) - // look a little cleaner - - public abstract class Range where T : Range, new() - { - private static readonly T _Instance = new T(); - - protected Range(int min, int max) - { - if (min > max) - { - throw new ArgumentOutOfRangeException(); - } - - Min = min; - Max = max; - } - - public int Min { get; private set; } - public int Max { get; private set; } - protected abstract T Create(int min, int max); - - // REVIEW: It's probably bad form to have access statics via a derived class, but the syntax looks better :-) - - public static T Inclusive(int min, int max) - { - return _Instance.Create(min, max); - } - - public static T Exactly(int value) - { - return _Instance.Create(value, value); - } - - public static T AtLeast(int minimumValue) - { - return _Instance.Create(minimumValue, int.MaxValue); - } - - public bool IsInRange(int value) - { - return (Min <= value) && (value <= Max); - } - } -} \ No newline at end of file diff --git a/Skills/PairwiseComparison.cs b/Skills/PairwiseComparison.cs deleted file mode 100644 index 7848eb3..0000000 --- a/Skills/PairwiseComparison.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Moserware.Skills -{ - /// - /// Represents a comparison between two players. - /// - /// - /// The actual values for the enum were chosen so that the also correspond to the multiplier for updates to means. - /// - public enum PairwiseComparison - { - Win = 1, - Draw = 0, - Lose = -1 - } -} \ No newline at end of file diff --git a/Skills/PartialPlay.cs b/Skills/PartialPlay.cs deleted file mode 100644 index b24d08f..0000000 --- a/Skills/PartialPlay.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Moserware.Skills -{ - internal static class PartialPlay - { - public static double GetPartialPlayPercentage(object player) - { - // If the player doesn't support the interface, assume 1.0 == 100% - var partialPlay = player as ISupportPartialPlay; - if (partialPlay == null) - { - return 1.0; - } - - double partialPlayPercentage = partialPlay.PartialPlayPercentage; - - // HACK to get around bug near 0 - const double smallestPercentage = 0.0001; - if (partialPlayPercentage < smallestPercentage) - { - partialPlayPercentage = smallestPercentage; - } - - return partialPlayPercentage; - } - } -} \ No newline at end of file diff --git a/Skills/Player.cs b/Skills/Player.cs deleted file mode 100644 index 74bcad6..0000000 --- a/Skills/Player.cs +++ /dev/null @@ -1,129 +0,0 @@ -namespace Moserware.Skills -{ - /// - /// Represents a player who has a . - /// - public class Player : ISupportPartialPlay, ISupportPartialUpdate - { - private const double DefaultPartialPlayPercentage = 1.0; // = 100% play time - private const double DefaultPartialUpdatePercentage = 1.0; // = receive 100% update - private readonly T _Id; - private readonly double _PartialPlayPercentage; - - private readonly double _PartialUpdatePercentage; - - /// - /// Constructs a player. - /// - /// The identifier for the player, such as a name. - public Player(T id) - : this(id, DefaultPartialPlayPercentage, DefaultPartialUpdatePercentage) - { - } - - /// - /// Constructs a player. - /// - /// The identifier for the player, such as a name. - /// The weight percentage to give this player when calculating a new rank. - public Player(T id, double partialPlayPercentage) - : this(id, partialPlayPercentage, DefaultPartialUpdatePercentage) - { - } - - /// - /// Constructs a player. - /// - /// The identifier for the player, such as a name. - /// The weight percentage to give this player when calculating a new rank. - /// /// Indicated how much of a skill update a player should receive where 0 represents no update and 1.0 represents 100% of the update. - public Player(T id, double partialPlayPercentage, double partialUpdatePercentage) - { - // If they don't want to give a player an id, that's ok... - Guard.ArgumentInRangeInclusive(partialPlayPercentage, 0, 1.0, "partialPlayPercentage"); - Guard.ArgumentInRangeInclusive(partialUpdatePercentage, 0, 1.0, "partialUpdatePercentage"); - _Id = id; - _PartialPlayPercentage = partialPlayPercentage; - _PartialUpdatePercentage = partialUpdatePercentage; - } - - /// - /// The identifier for the player, such as a name. - /// - public T Id - { - get { return _Id; } - } - - #region ISupportPartialPlay Members - - /// - /// Indicates the percent of the time the player should be weighted where 0.0 indicates the player didn't play and 1.0 indicates the player played 100% of the time. - /// - public double PartialPlayPercentage - { - get { return _PartialPlayPercentage; } - } - - #endregion - - #region ISupportPartialUpdate Members - - /// - /// Indicated how much of a skill update a player should receive where 0.0 represents no update and 1.0 represents 100% of the update. - /// - public double PartialUpdatePercentage - { - get { return _PartialUpdatePercentage; } - } - - #endregion - - public override string ToString() - { - if (Id != null) - { - return Id.ToString(); - } - - return base.ToString(); - } - } - - /// - /// Represents a player who has a . - /// - public class Player : Player - { - /// - /// Constructs a player. - /// - /// The identifier for the player, such as a name. - public Player(object id) - : base(id) - { - } - - /// - /// Constructs a player. - /// - /// The identifier for the player, such as a name. - /// The weight percentage to give this player when calculating a new rank. - /// Indicated how much of a skill update a player should receive where 0 represents no update and 1.0 represents 100% of the update. - public Player(object id, double partialPlayPercentage) - : base(id, partialPlayPercentage) - { - } - - /// - /// Constructs a player. - /// - /// The identifier for the player, such as a name. - /// The weight percentage to give this player when calculating a new rank. - /// Indicated how much of a skill update a player should receive where 0 represents no update and 1.0 represents 100% of the update. - public Player(object id, double partialPlayPercentage, double partialUpdatePercentage) - : base(id, partialPlayPercentage, partialUpdatePercentage) - { - } - } -} \ No newline at end of file diff --git a/Skills/PlayersRange.cs b/Skills/PlayersRange.cs deleted file mode 100644 index b3c2963..0000000 --- a/Skills/PlayersRange.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Moserware.Skills.Numerics; - -namespace Moserware.Skills -{ - public class PlayersRange : Range - { - public PlayersRange() - : base(int.MinValue, int.MinValue) - { - } - - private PlayersRange(int min, int max) - : base(min, max) - { - } - - protected override PlayersRange Create(int min, int max) - { - return new PlayersRange(min, max); - } - } -} \ No newline at end of file diff --git a/Skills/Properties/AssemblyInfo.cs b/Skills/Properties/AssemblyInfo.cs deleted file mode 100644 index 5f3b598..0000000 --- a/Skills/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. - -[assembly: AssemblyTitle("Moserware.Skills")] -[assembly: AssemblyDescription("Implementation of the TrueSkill algorithm.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Jeff Moser")] -[assembly: AssemblyProduct("TrueSkill Calculator")] -[assembly: AssemblyCopyright("Copyright © Jeff Moser 2010")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. - -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM - -[assembly: Guid("4326f9ed-f234-42ed-bee0-84f7757ab28f")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: InternalsVisibleTo("UnitTests")] diff --git a/Skills/README.txt b/Skills/README.txt deleted file mode 100644 index fcc9b11..0000000 --- a/Skills/README.txt +++ /dev/null @@ -1,58 +0,0 @@ -Hi there! - -Thanks for downloading this code and opening up this file. The goal of this -project is to provide an annotated reference implementation of Microsoft's -TrueSkill algorithm. - -I describe the philosophy and the buildup of the math involved in my blog post -"Computing Your Skill" available at moserware.com. - -In addition, there is a math paper that goes along with the blog post that explains -most of the more technical concepts. - -This project isn't intended to win performance tests, it's meant to be read -and understood. If you see ways to improve its clarity, please submit a patch. - -If you just want to use the TrueSkill algorithm, simply use the TrueSkillCalculator -class and enjoy. If you need examples, please look in the UnitTests\TrueSkill folder. - -If you want to understand the inner workings of the algorithm and implement it -yourself, look in the Skills\TrueSkill folder. There are three separate -implementations of the algorithm in increasing levels of difficulty: - - 1. TwoPlayerTrueSkillCalculator.cs is the easiest to follow and implement. It uses - the simple equations directly from the TrueSkill website. - 2. TwoTeamTrueSkillCalculator.cs is slightly more complicated than the two player - version and supports two teams that have at least one player each. It extends - the equations on the website and incorporates some things implied in the paper. - 3. FactorGraphTrueSkillCalculator.cs is a wholly different animal than the first two - and it is at least an order of magnitude more complex. It implements the complete - TrueSkill algorithm and builds up a "factor graph" composed of several layers. - Each layer is composed of "factors", "variables", and "messages" between the two. - - Work happens on the factor graph according to a "schedule" which can either be - a single step (e.g. sending a message from a factor to a variable) or a sequence of - steps (e.g. everything that happens in a "layer") or a loop where the schedule runs - until values start to stabilize (e.g. the bottom layer is approximated and runs until - it converges) - -TrueSkill is more general than the popular Elo algorithm. As a comparison, I implemented -the Elo algorithm using the both the bell curve (Gaussian) and curve that the FIDE chess -league uses (logistic curve). I specifically implemented them in a way to show how the -only difference among these Elo implementations is the curve. I also implemented the -"duelling" Elo calculator as implied in the paper. - -Everything else was implemented to support these classes. Note that a "player" can be an -arbitrary class. However, if that player class supports the "ISupportPartialPlay" or -"ISupportPartialUpdate" interfaces, you can add these extra parameters. The only calculator -that uses this info is the factor graph implementation. See those files for more details. - -I use this code personally to rank around 45 people, so it's important that it's accurate. -Please let me know if you find errors. Bug fix patches are strongly encouraged! Also, feel -free to fork the project for different language implementations. - -I'd love to hear from you via comments on the "Computing Your Skill" blog post. - -Have fun and enjoy! - -Jeff Moser \ No newline at end of file diff --git a/Skills/RankSorter.cs b/Skills/RankSorter.cs deleted file mode 100644 index c3dc1f6..0000000 --- a/Skills/RankSorter.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Moserware.Skills -{ - /// - /// Helper class to sort ranks in non-decreasing order. - /// - internal static class RankSorter - { - /// - /// Performs an in-place sort of the in according to the in non-decreasing order. - /// - /// The types of items to sort. - /// The items to sort according to the order specified by . - /// The ranks for each item where 1 is first place. - public static void Sort(ref IEnumerable teams, ref int[] teamRanks) - { - Guard.ArgumentNotNull(teams, "teams"); - Guard.ArgumentNotNull(teamRanks, "teamRanks"); - - int lastObserverdRank = 0; - bool needToSort = false; - - foreach (int currentRank in teamRanks) - { - // We're expecting ranks to go up (e.g. 1, 2, 2, 3, ...) - // If it goes down, then we've got to sort it. - if (currentRank < lastObserverdRank) - { - needToSort = true; - break; - } - - lastObserverdRank = currentRank; - } - - if (!needToSort) - { - // Don't bother doing more work, it's already in a good order - return; - } - - // Get the existing items as an indexable list. - List itemsInList = teams.ToList(); - - // item -> rank - var itemToRank = new Dictionary(); - - for (int i = 0; i < itemsInList.Count; i++) - { - T currentItem = itemsInList[i]; - int currentItemRank = teamRanks[i]; - itemToRank[currentItem] = currentItemRank; - } - - // Now we need a place for our results... - var sortedItems = new T[teamRanks.Length]; - var sortedRanks = new int[teamRanks.Length]; - - // where are we in the result? - int currentIndex = 0; - - // Let LINQ-to-Objects to the actual sorting - foreach (var sortedKeyValuePair in itemToRank.OrderBy(pair => pair.Value)) - { - sortedItems[currentIndex] = sortedKeyValuePair.Key; - sortedRanks[currentIndex++] = sortedKeyValuePair.Value; - } - - // And we're done - teams = sortedItems; - teamRanks = sortedRanks; - } - } -} \ No newline at end of file diff --git a/Skills/Rating.cs b/Skills/Rating.cs deleted file mode 100644 index 429f51f..0000000 --- a/Skills/Rating.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using Moserware.Numerics; - -namespace Moserware.Skills -{ - /// - /// Container for a player's rating. - /// - public class Rating - { - private const int ConservativeStandardDeviationMultiplier = 3; - private readonly double _ConservativeStandardDeviationMultiplier; - private readonly double _Mean; - private readonly double _StandardDeviation; - - /// - /// Constructs a rating. - /// - /// The statistical mean value of the rating (also known as μ). - /// The standard deviation of the rating (also known as σ). - public Rating(double mean, double standardDeviation) - : this(mean, standardDeviation, ConservativeStandardDeviationMultiplier) - { - } - - /// - /// Constructs a rating. - /// - /// The statistical mean value of the rating (also known as μ). - /// The standard deviation (the spread) of the rating (also known as σ). - /// The number of s to subtract from the to achieve a conservative rating. - public Rating(double mean, double standardDeviation, double conservativeStandardDeviationMultiplier) - { - _Mean = mean; - _StandardDeviation = standardDeviation; - _ConservativeStandardDeviationMultiplier = conservativeStandardDeviationMultiplier; - } - - /// - /// The statistical mean value of the rating (also known as μ). - /// - public double Mean - { - get { return _Mean; } - } - - /// - /// The standard deviation (the spread) of the rating. This is also known as σ. - /// - public double StandardDeviation - { - get { return _StandardDeviation; } - } - - /// - /// A conservative estimate of skill based on the mean and standard deviation. - /// - public double ConservativeRating - { - get { return _Mean - ConservativeStandardDeviationMultiplier*_StandardDeviation; } - } - - public static Rating GetPartialUpdate(Rating prior, Rating fullPosterior, double updatePercentage) - { - var priorGaussian = new GaussianDistribution(prior.Mean, prior.StandardDeviation); - var posteriorGaussian = new GaussianDistribution(fullPosterior.Mean, fullPosterior.StandardDeviation); - - // From a clarification email from Ralf Herbrich: - // "the idea is to compute a linear interpolation between the prior and posterior skills of each player - // ... in the canonical space of parameters" - - double precisionDifference = posteriorGaussian.Precision - priorGaussian.Precision; - double partialPrecisionDifference = updatePercentage*precisionDifference; - - double precisionMeanDifference = posteriorGaussian.PrecisionMean - priorGaussian.PrecisionMean; - double partialPrecisionMeanDifference = updatePercentage*precisionMeanDifference; - - GaussianDistribution partialPosteriorGaussion = GaussianDistribution.FromPrecisionMean( - priorGaussian.PrecisionMean + partialPrecisionMeanDifference, - priorGaussian.Precision + partialPrecisionDifference); - - return new Rating(partialPosteriorGaussion.Mean, partialPosteriorGaussion.StandardDeviation, - prior._ConservativeStandardDeviationMultiplier); - } - - public override string ToString() - { - // As a debug helper, display a localized rating: - return String.Format( - "μ={0:0.0000}, σ={1:0.0000}", - Mean, StandardDeviation); - } - } -} \ No newline at end of file diff --git a/Skills/SkillCalculator.cs b/Skills/SkillCalculator.cs deleted file mode 100644 index 6f70b6e..0000000 --- a/Skills/SkillCalculator.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Moserware.Skills -{ - /// - /// Base class for all skill calculator implementations. - /// - public abstract class SkillCalculator - { - [Flags] - public enum SupportedOptions - { - None = 0x00, - PartialPlay = 0x01, - PartialUpdate = 0x02, - } - - private readonly SupportedOptions _SupportedOptions; - private readonly PlayersRange _PlayersPerTeamAllowed; - private readonly TeamsRange _TotalTeamsAllowed; - - protected SkillCalculator(SupportedOptions supportedOptions, TeamsRange totalTeamsAllowed, PlayersRange playerPerTeamAllowed) - { - _SupportedOptions = supportedOptions; - _TotalTeamsAllowed = totalTeamsAllowed; - _PlayersPerTeamAllowed = playerPerTeamAllowed; - } - - /// - /// Calculates new ratings based on the prior ratings and team ranks. - /// - /// The underlying type of the player. - /// Parameters for the game. - /// A mapping of team players and their ratings. - /// The ranks of the teams where 1 is first place. For a tie, repeat the number (e.g. 1, 2, 2) - /// All the players and their new ratings. - public abstract IDictionary CalculateNewRatings(GameInfo gameInfo, - IEnumerable - > - teams, - params int[] teamRanks); - - /// - /// Calculates the match quality as the likelihood of all teams drawing. - /// - /// The underlying type of the player. - /// Parameters for the game. - /// A mapping of team players and their ratings. - /// The quality of the match between the teams as a percentage (0% = bad, 100% = well matched). - public abstract double CalculateMatchQuality(GameInfo gameInfo, - IEnumerable> teams); - - public bool IsSupported(SupportedOptions option) - { - return (_SupportedOptions & option) == option; - } - - /// - /// Helper function to square the . - /// - /// * - protected static double Square(double value) - { - return value*value; - } - - protected void ValidateTeamCountAndPlayersCountPerTeam(IEnumerable> teams) - { - ValidateTeamCountAndPlayersCountPerTeam(teams, _TotalTeamsAllowed, _PlayersPerTeamAllowed); - } - - private static void ValidateTeamCountAndPlayersCountPerTeam( - IEnumerable> teams, TeamsRange totalTeams, PlayersRange playersPerTeam) - { - Guard.ArgumentNotNull(teams, "teams"); - int countOfTeams = 0; - foreach (var currentTeam in teams) - { - if (!playersPerTeam.IsInRange(currentTeam.Count)) - { - throw new ArgumentException(); - } - countOfTeams++; - } - - if (!totalTeams.IsInRange(countOfTeams)) - { - throw new ArgumentException(); - } - } - } -} \ No newline at end of file diff --git a/Skills/Skills.csproj b/Skills/Skills.csproj deleted file mode 100644 index 4290fde..0000000 --- a/Skills/Skills.csproj +++ /dev/null @@ -1,117 +0,0 @@ - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {15AD1345-984C-48ED-AF9A-2EAB44E5AA2B} - Library - Properties - Moserware.Skills - Moserware.Skills - v3.5 - 512 - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - true - - - - - 3.5 - - - 3.5 - - - 3.5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Skills/Skills.suo b/Skills/Skills.suo deleted file mode 100644 index 2d273c6..0000000 Binary files a/Skills/Skills.suo and /dev/null differ diff --git a/Skills/Team.cs b/Skills/Team.cs deleted file mode 100644 index 85cfad6..0000000 --- a/Skills/Team.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Moserware.Skills -{ - /// - /// Helper class for working with a single team. - /// - public class Team - { - private readonly Dictionary _PlayerRatings = new Dictionary(); - - /// - /// Constructs a new team. - /// - public Team() - { - } - - /// - /// Constructs a and populates it with the specified . - /// - /// The player to add. - /// The rating of the . - public Team(TPlayer player, Rating rating) - { - AddPlayer(player, rating); - } - - /// - /// Adds the to the team. - /// - /// The player to add. - /// The rating of the . - /// The instance of the team (for chaining convenience). - public Team AddPlayer(TPlayer player, Rating rating) - { - _PlayerRatings[player] = rating; - return this; - } - - /// - /// Returns the as a simple dictionary. - /// - /// The as a simple dictionary. - public IDictionary AsDictionary() - { - return _PlayerRatings; - } - } - - /// - /// Helper class for working with a single team. - /// - public class Team : Team - { - /// - /// Constructs a new team. - /// - public Team() - { - } - - /// - /// Constructs a and populates it with the specified . - /// - /// The player to add. - /// The rating of the . - public Team(Player player, Rating rating) - : base(player, rating) - { - } - } - - /// - /// Helper class for working with multiple teams. - /// - public static class Teams - { - /// - /// Concatenates multiple teams into a list of teams. - /// - /// The teams to concatenate together. - /// A sequence of teams. - public static IEnumerable> Concat(params Team[] teams) - { - return teams.Select(t => t.AsDictionary()); - } - } -} \ No newline at end of file diff --git a/Skills/TeamsRange.cs b/Skills/TeamsRange.cs deleted file mode 100644 index 22c40e7..0000000 --- a/Skills/TeamsRange.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Moserware.Skills.Numerics; - -namespace Moserware.Skills -{ - public class TeamsRange : Range - { - public TeamsRange() - : base(int.MinValue, int.MinValue) - { - } - - private TeamsRange(int min, int max) - : base(min, max) - { - } - - protected override TeamsRange Create(int min, int max) - { - return new TeamsRange(min, max); - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/DrawMargin.cs b/Skills/TrueSkill/DrawMargin.cs deleted file mode 100644 index 25335d5..0000000 --- a/Skills/TrueSkill/DrawMargin.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using Moserware.Numerics; - -namespace Moserware.Skills.TrueSkill -{ - internal static class DrawMargin - { - public static double GetDrawMarginFromDrawProbability(double drawProbability, double beta) - { - // Derived from TrueSkill technical report (MSR-TR-2006-80), page 6 - - // draw probability = 2 * CDF(margin/(sqrt(n1+n2)*beta)) -1 - - // implies - // - // margin = inversecdf((draw probability + 1)/2) * sqrt(n1+n2) * beta - // n1 and n2 are the number of players on each team - double margin = GaussianDistribution.InverseCumulativeTo(.5*(drawProbability + 1), 0, 1)*Math.Sqrt(1 + 1)* - beta; - return margin; - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/FactorGraphTrueSkillCalculator.cs b/Skills/TrueSkill/FactorGraphTrueSkillCalculator.cs deleted file mode 100644 index 8a291a0..0000000 --- a/Skills/TrueSkill/FactorGraphTrueSkillCalculator.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Moserware.Numerics; - -namespace Moserware.Skills.TrueSkill -{ - /// - /// Calculates TrueSkill using a full factor graph. - /// - internal class FactorGraphTrueSkillCalculator : SkillCalculator - { - public FactorGraphTrueSkillCalculator() - : base(SupportedOptions.PartialPlay | SupportedOptions.PartialUpdate, TeamsRange.AtLeast(2), PlayersRange.AtLeast(1)) - { - } - - public override IDictionary CalculateNewRatings(GameInfo gameInfo, - IEnumerable> teams, - params int[] teamRanks) - { - Guard.ArgumentNotNull(gameInfo, "gameInfo"); - ValidateTeamCountAndPlayersCountPerTeam(teams); - - RankSorter.Sort(ref teams, ref teamRanks); - - var factorGraph = new TrueSkillFactorGraph(gameInfo, teams, teamRanks); - factorGraph.BuildGraph(); - factorGraph.RunSchedule(); - - double probabilityOfOutcome = factorGraph.GetProbabilityOfRanking(); - - return factorGraph.GetUpdatedRatings(); - } - - - public override double CalculateMatchQuality(GameInfo gameInfo, - IEnumerable> teams) - { - // We need to create the A matrix which is the player team assigments. - List> teamAssignmentsList = teams.ToList(); - Matrix skillsMatrix = GetPlayerCovarianceMatrix(teamAssignmentsList); - Vector meanVector = GetPlayerMeansVector(teamAssignmentsList); - Matrix meanVectorTranspose = meanVector.Transpose; - - Matrix playerTeamAssignmentsMatrix = CreatePlayerTeamAssignmentMatrix(teamAssignmentsList, meanVector.Rows); - Matrix playerTeamAssignmentsMatrixTranspose = playerTeamAssignmentsMatrix.Transpose; - - double betaSquared = Square(gameInfo.Beta); - - Matrix start = meanVectorTranspose * playerTeamAssignmentsMatrix; - Matrix aTa = (betaSquared * playerTeamAssignmentsMatrixTranspose) * playerTeamAssignmentsMatrix; - Matrix aTSA = playerTeamAssignmentsMatrixTranspose * skillsMatrix * playerTeamAssignmentsMatrix; - Matrix middle = aTa + aTSA; - - Matrix middleInverse = middle.Inverse; - - Matrix end = playerTeamAssignmentsMatrixTranspose * meanVector; - - Matrix expPartMatrix = -0.5 * (start * middleInverse * end); - double expPart = expPartMatrix.Determinant; - - double sqrtPartNumerator = aTa.Determinant; - double sqrtPartDenominator = middle.Determinant; - double sqrtPart = sqrtPartNumerator / sqrtPartDenominator; - - double result = Math.Exp(expPart) * Math.Sqrt(sqrtPart); - - return result; - } - - private static Vector GetPlayerMeansVector( - IEnumerable> teamAssignmentsList) - { - // A simple vector of all the player means. - return new Vector(GetPlayerRatingValues(teamAssignmentsList, rating => rating.Mean)); - } - - private static Matrix GetPlayerCovarianceMatrix( - IEnumerable> teamAssignmentsList) - { - // This is a square matrix whose diagonal values represent the variance (square of standard deviation) of all - // players. - return - new DiagonalMatrix(GetPlayerRatingValues(teamAssignmentsList, rating => Square(rating.StandardDeviation))); - } - - // Helper function that gets a list of values for all player ratings - private static IList GetPlayerRatingValues( - IEnumerable> teamAssignmentsList, Func playerRatingFunction) - { - var playerRatingValues = new List(); - - foreach (var currentTeam in teamAssignmentsList) - { - foreach (Rating currentRating in currentTeam.Values) - { - playerRatingValues.Add(playerRatingFunction(currentRating)); - } - } - - return playerRatingValues; - } - - private static Matrix CreatePlayerTeamAssignmentMatrix( - IList> teamAssignmentsList, int totalPlayers) - { - // The team assignment matrix is often referred to as the "A" matrix. It's a matrix whose rows represent the players - // and the columns represent teams. At Matrix[row, column] represents that player[row] is on team[col] - // Positive values represent an assignment and a negative value means that we subtract the value of the next - // team since we're dealing with pairs. This means that this matrix always has teams - 1 columns. - // The only other tricky thing is that values represent the play percentage. - - // For example, consider a 3 team game where team1 is just player1, team 2 is player 2 and player 3, and - // team3 is just player 4. Furthermore, player 2 and player 3 on team 2 played 25% and 75% of the time - // (e.g. partial play), the A matrix would be: - - // A = this 4x2 matrix: - // | 1.00 0.00 | - // | -0.25 0.25 | - // | -0.75 0.75 | - // | 0.00 -1.00 | - - var playerAssignments = new List>(); - int totalPreviousPlayers = 0; - - for (int i = 0; i < teamAssignmentsList.Count - 1; i++) - { - IDictionary currentTeam = teamAssignmentsList[i]; - - // Need to add in 0's for all the previous players, since they're not - // on this team - var currentRowValues = new List(new double[totalPreviousPlayers]); - playerAssignments.Add(currentRowValues); - - foreach (var currentRating in currentTeam) - { - currentRowValues.Add(PartialPlay.GetPartialPlayPercentage(currentRating.Key)); - // indicates the player is on the team - totalPreviousPlayers++; - } - - IDictionary nextTeam = teamAssignmentsList[i + 1]; - foreach (var nextTeamPlayerPair in nextTeam) - { - // Add a -1 * playing time to represent the difference - currentRowValues.Add(-1 * PartialPlay.GetPartialPlayPercentage(nextTeamPlayerPair.Key)); - } - } - - var playerTeamAssignmentsMatrix = new Matrix(totalPlayers, teamAssignmentsList.Count - 1, playerAssignments); - - return playerTeamAssignmentsMatrix; - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/Factors/GaussianFactor.cs b/Skills/TrueSkill/Factors/GaussianFactor.cs deleted file mode 100644 index 3417627..0000000 --- a/Skills/TrueSkill/Factors/GaussianFactor.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Moserware.Numerics; -using Moserware.Skills.FactorGraphs; - -namespace Moserware.Skills.TrueSkill.Factors -{ - public abstract class GaussianFactor : Factor - { - protected GaussianFactor(string name) - : base(name) - { - } - - /// Sends the factor-graph message with and returns the log-normalization constant - protected override double SendMessage(Message message, - Variable variable) - { - GaussianDistribution marginal = variable.Value; - GaussianDistribution messageValue = message.Value; - double logZ = GaussianDistribution.LogProductNormalization(marginal, messageValue); - variable.Value = marginal*messageValue; - return logZ; - } - - public override Message CreateVariableToMessageBinding( - Variable variable) - { - return CreateVariableToMessageBinding(variable, - new Message( - GaussianDistribution.FromPrecisionMean(0, 0), - "message from {0} to {1}", this, variable)); - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/Factors/GaussianGreaterThanFactor.cs b/Skills/TrueSkill/Factors/GaussianGreaterThanFactor.cs deleted file mode 100644 index 31c4d4a..0000000 --- a/Skills/TrueSkill/Factors/GaussianGreaterThanFactor.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using Moserware.Numerics; -using Moserware.Skills.FactorGraphs; - -namespace Moserware.Skills.TrueSkill.Factors -{ - /// - /// Factor representing a team difference that has exceeded the draw margin. - /// - /// See the accompanying math paper for more details. - public class GaussianGreaterThanFactor : GaussianFactor - { - private readonly double _Epsilon; - - public GaussianGreaterThanFactor(double epsilon, Variable variable) - : base(String.Format("{0} > {1:0.000}", variable, epsilon)) - { - _Epsilon = epsilon; - CreateVariableToMessageBinding(variable); - } - - public override double LogNormalization - { - get - { - GaussianDistribution marginal = Variables[0].Value; - GaussianDistribution message = Messages[0].Value; - GaussianDistribution messageFromVariable = marginal/message; - return -GaussianDistribution.LogProductNormalization(messageFromVariable, message) - + - Math.Log( - GaussianDistribution.CumulativeTo((messageFromVariable.Mean - _Epsilon)/ - messageFromVariable.StandardDeviation)); - } - } - - protected override double UpdateMessage(Message message, - Variable variable) - { - GaussianDistribution oldMarginal = variable.Value.Clone(); - GaussianDistribution oldMessage = message.Value.Clone(); - GaussianDistribution messageFromVar = oldMarginal/oldMessage; - - double c = messageFromVar.Precision; - double d = messageFromVar.PrecisionMean; - - double sqrtC = Math.Sqrt(c); - - double dOnSqrtC = d/sqrtC; - - double epsilsonTimesSqrtC = _Epsilon*sqrtC; - d = messageFromVar.PrecisionMean; - - double denom = 1.0 - TruncatedGaussianCorrectionFunctions.WExceedsMargin(dOnSqrtC, epsilsonTimesSqrtC); - - double newPrecision = c/denom; - double newPrecisionMean = (d + - sqrtC* - TruncatedGaussianCorrectionFunctions.VExceedsMargin(dOnSqrtC, epsilsonTimesSqrtC))/ - denom; - - GaussianDistribution newMarginal = GaussianDistribution.FromPrecisionMean(newPrecisionMean, newPrecision); - - GaussianDistribution newMessage = oldMessage*newMarginal/oldMarginal; - - /// Update the message and marginal - message.Value = newMessage; - - variable.Value = newMarginal; - - /// Return the difference in the new marginal - return newMarginal - oldMarginal; - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/Factors/GaussianLikelihoodFactor.cs b/Skills/TrueSkill/Factors/GaussianLikelihoodFactor.cs deleted file mode 100644 index 95d4a6d..0000000 --- a/Skills/TrueSkill/Factors/GaussianLikelihoodFactor.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using Moserware.Numerics; -using Moserware.Skills.FactorGraphs; - -namespace Moserware.Skills.TrueSkill.Factors -{ - /// - /// Connects two variables and adds uncertainty. - /// - /// See the accompanying math paper for more details. - public class GaussianLikelihoodFactor : GaussianFactor - { - private readonly double _Precision; - - public GaussianLikelihoodFactor(double betaSquared, Variable variable1, - Variable variable2) - : base(String.Format("Likelihood of {0} going to {1}", variable2, variable1)) - { - _Precision = 1.0/betaSquared; - CreateVariableToMessageBinding(variable1); - CreateVariableToMessageBinding(variable2); - } - - public override double LogNormalization - { - get { return GaussianDistribution.LogRatioNormalization(Variables[0].Value, Messages[0].Value); } - } - - private double UpdateHelper(Message message1, Message message2, - Variable variable1, Variable variable2) - { - GaussianDistribution message1Value = message1.Value.Clone(); - GaussianDistribution message2Value = message2.Value.Clone(); - - GaussianDistribution marginal1 = variable1.Value.Clone(); - GaussianDistribution marginal2 = variable2.Value.Clone(); - - double a = _Precision/(_Precision + marginal2.Precision - message2Value.Precision); - - GaussianDistribution newMessage = GaussianDistribution.FromPrecisionMean( - a*(marginal2.PrecisionMean - message2Value.PrecisionMean), - a*(marginal2.Precision - message2Value.Precision)); - - GaussianDistribution oldMarginalWithoutMessage = marginal1/message1Value; - - GaussianDistribution newMarginal = oldMarginalWithoutMessage*newMessage; - - /// Update the message and marginal - - message1.Value = newMessage; - variable1.Value = newMarginal; - - /// Return the difference in the new marginal - return newMarginal - marginal1; - } - - public override double UpdateMessage(int messageIndex) - { - switch (messageIndex) - { - case 0: - return UpdateHelper(Messages[0], Messages[1], - Variables[0], Variables[1]); - case 1: - return UpdateHelper(Messages[1], Messages[0], - Variables[1], Variables[0]); - default: - throw new ArgumentOutOfRangeException(); - } - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/Factors/GaussianPriorFactor.cs b/Skills/TrueSkill/Factors/GaussianPriorFactor.cs deleted file mode 100644 index 3921880..0000000 --- a/Skills/TrueSkill/Factors/GaussianPriorFactor.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using Moserware.Numerics; -using Moserware.Skills.FactorGraphs; - -namespace Moserware.Skills.TrueSkill.Factors -{ - /// - /// Supplies the factor graph with prior information. - /// - /// See the accompanying math paper for more details. - public class GaussianPriorFactor : GaussianFactor - { - private readonly GaussianDistribution _NewMessage; - - public GaussianPriorFactor(double mean, double variance, Variable variable) - : base(String.Format("Prior value going to {0}", variable)) - { - _NewMessage = new GaussianDistribution(mean, Math.Sqrt(variance)); - CreateVariableToMessageBinding(variable, - new Message( - GaussianDistribution.FromPrecisionMean(0, 0), "message from {0} to {1}", - this, variable)); - } - - protected override double UpdateMessage(Message message, - Variable variable) - { - GaussianDistribution oldMarginal = variable.Value.Clone(); - Message oldMessage = message; - GaussianDistribution newMarginal = - GaussianDistribution.FromPrecisionMean( - oldMarginal.PrecisionMean + _NewMessage.PrecisionMean - oldMessage.Value.PrecisionMean, - oldMarginal.Precision + _NewMessage.Precision - oldMessage.Value.Precision); - variable.Value = newMarginal; - message.Value = _NewMessage; - return oldMarginal - newMarginal; - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/Factors/GaussianWeightedSumFactor.cs b/Skills/TrueSkill/Factors/GaussianWeightedSumFactor.cs deleted file mode 100644 index d836574..0000000 --- a/Skills/TrueSkill/Factors/GaussianWeightedSumFactor.cs +++ /dev/null @@ -1,251 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using Moserware.Numerics; -using Moserware.Skills.FactorGraphs; - -namespace Moserware.Skills.TrueSkill.Factors -{ - /// - /// Factor that sums together multiple Gaussians. - /// - /// See the accompanying math paper for more details. - public class GaussianWeightedSumFactor : GaussianFactor - { - private readonly List _VariableIndexOrdersForWeights = new List(); - - // This following is used for convenience, for example, the first entry is [0, 1, 2] - // corresponding to v[0] = a1*v[1] + a2*v[2] - private readonly double[][] _Weights; - private readonly double[][] _WeightsSquared; - - public GaussianWeightedSumFactor(Variable sumVariable, - Variable[] variablesToSum) - : this(sumVariable, - variablesToSum, - variablesToSum.Select(v => 1.0).ToArray()) // By default, set the weight to 1.0 - { - } - - public GaussianWeightedSumFactor(Variable sumVariable, - Variable[] variablesToSum, double[] variableWeights) - : base(CreateName(sumVariable, variablesToSum, variableWeights)) - { - _Weights = new double[variableWeights.Length + 1][]; - _WeightsSquared = new double[_Weights.Length][]; - - // The first weights are a straightforward copy - // v_0 = a_1*v_1 + a_2*v_2 + ... + a_n * v_n - _Weights[0] = new double[variableWeights.Length]; - Array.Copy(variableWeights, _Weights[0], variableWeights.Length); - _WeightsSquared[0] = _Weights[0].Select(w => w*w).ToArray(); - - // 0..n-1 - _VariableIndexOrdersForWeights.Add(Enumerable.Range(0, 1 + variablesToSum.Length).ToArray()); - - - // The rest move the variables around and divide out the constant. - // For example: - // v_1 = (-a_2 / a_1) * v_2 + (-a3/a1) * v_3 + ... + (1.0 / a_1) * v_0 - // By convention, we'll put the v_0 term at the end - - for (int weightsIndex = 1; weightsIndex < _Weights.Length; weightsIndex++) - { - var currentWeights = new double[variableWeights.Length]; - _Weights[weightsIndex] = currentWeights; - - var variableIndices = new int[variableWeights.Length + 1]; - variableIndices[0] = weightsIndex; - - var currentWeightsSquared = new double[variableWeights.Length]; - _WeightsSquared[weightsIndex] = currentWeightsSquared; - - // keep a single variable to keep track of where we are in the array. - // This is helpful since we skip over one of the spots - int currentDestinationWeightIndex = 0; - - for (int currentWeightSourceIndex = 0; - currentWeightSourceIndex < variableWeights.Length; - currentWeightSourceIndex++) - { - if (currentWeightSourceIndex == (weightsIndex - 1)) - { - continue; - } - - double currentWeight = (-variableWeights[currentWeightSourceIndex]/variableWeights[weightsIndex - 1]); - - if (variableWeights[weightsIndex - 1] == 0) - { - // HACK: Getting around division by zero - currentWeight = 0; - } - - currentWeights[currentDestinationWeightIndex] = currentWeight; - currentWeightsSquared[currentDestinationWeightIndex] = currentWeight*currentWeight; - - variableIndices[currentDestinationWeightIndex + 1] = currentWeightSourceIndex + 1; - currentDestinationWeightIndex++; - } - - // And the final one - double finalWeight = 1.0/variableWeights[weightsIndex - 1]; - - if (variableWeights[weightsIndex - 1] == 0) - { - // HACK: Getting around division by zero - finalWeight = 0; - } - currentWeights[currentDestinationWeightIndex] = finalWeight; - currentWeightsSquared[currentDestinationWeightIndex] = finalWeight*finalWeight; - variableIndices[variableIndices.Length - 1] = 0; - _VariableIndexOrdersForWeights.Add(variableIndices); - } - - CreateVariableToMessageBinding(sumVariable); - - foreach (var currentVariable in variablesToSum) - { - CreateVariableToMessageBinding(currentVariable); - } - } - - public override double LogNormalization - { - get - { - ReadOnlyCollection> vars = Variables; - ReadOnlyCollection> messages = Messages; - - double result = 0.0; - - // We start at 1 since offset 0 has the sum - for (int i = 1; i < vars.Count; i++) - { - result += GaussianDistribution.LogRatioNormalization(vars[i].Value, messages[i].Value); - } - - return result; - } - } - - private double UpdateHelper(double[] weights, double[] weightsSquared, - IList> messages, - IList> variables) - { - // Potentially look at http://mathworld.wolfram.com/NormalSumDistribution.html for clues as - // to what it's doing - - GaussianDistribution message0 = messages[0].Value.Clone(); - GaussianDistribution marginal0 = variables[0].Value.Clone(); - - // The math works out so that 1/newPrecision = sum of a_i^2 /marginalsWithoutMessages[i] - double inverseOfNewPrecisionSum = 0.0; - double anotherInverseOfNewPrecisionSum = 0.0; - double weightedMeanSum = 0.0; - double anotherWeightedMeanSum = 0.0; - - for (int i = 0; i < weightsSquared.Length; i++) - { - // These flow directly from the paper - - inverseOfNewPrecisionSum += weightsSquared[i]/ - (variables[i + 1].Value.Precision - messages[i + 1].Value.Precision); - - GaussianDistribution diff = (variables[i + 1].Value/messages[i + 1].Value); - anotherInverseOfNewPrecisionSum += weightsSquared[i]/diff.Precision; - - weightedMeanSum += weights[i] - * - (variables[i + 1].Value.PrecisionMean - messages[i + 1].Value.PrecisionMean) - / - (variables[i + 1].Value.Precision - messages[i + 1].Value.Precision); - - anotherWeightedMeanSum += weights[i]*diff.PrecisionMean/diff.Precision; - } - - double newPrecision = 1.0/inverseOfNewPrecisionSum; - double anotherNewPrecision = 1.0/anotherInverseOfNewPrecisionSum; - - double newPrecisionMean = newPrecision*weightedMeanSum; - double anotherNewPrecisionMean = anotherNewPrecision*anotherWeightedMeanSum; - - GaussianDistribution newMessage = GaussianDistribution.FromPrecisionMean(newPrecisionMean, newPrecision); - GaussianDistribution oldMarginalWithoutMessage = marginal0/message0; - - GaussianDistribution newMarginal = oldMarginalWithoutMessage*newMessage; - - /// Update the message and marginal - - messages[0].Value = newMessage; - variables[0].Value = newMarginal; - - /// Return the difference in the new marginal - return newMarginal - marginal0; - } - - public override double UpdateMessage(int messageIndex) - { - ReadOnlyCollection> allMessages = Messages; - ReadOnlyCollection> allVariables = Variables; - - Guard.ArgumentIsValidIndex(messageIndex, allMessages.Count, "messageIndex"); - - var updatedMessages = new List>(); - var updatedVariables = new List>(); - - int[] indicesToUse = _VariableIndexOrdersForWeights[messageIndex]; - - // The tricky part here is that we have to put the messages and variables in the same - // order as the weights. Thankfully, the weights and messages share the same index numbers, - // so we just need to make sure they're consistent - for (int i = 0; i < allMessages.Count; i++) - { - updatedMessages.Add(allMessages[indicesToUse[i]]); - updatedVariables.Add(allVariables[indicesToUse[i]]); - } - - return UpdateHelper(_Weights[messageIndex], _WeightsSquared[messageIndex], updatedMessages, updatedVariables); - } - - private static string CreateName(Variable sumVariable, - IList> variablesToSum, double[] weights) - { - var sb = new StringBuilder(); - sb.Append(sumVariable.ToString()); - sb.Append(" = "); - for (int i = 0; i < variablesToSum.Count; i++) - { - bool isFirst = (i == 0); - - if (isFirst && (weights[i] < 0)) - { - sb.Append("-"); - } - - sb.Append(Math.Abs(weights[i]).ToString("0.00")); - sb.Append("*["); - sb.Append(variablesToSum[i]); - sb.Append("]"); - - bool isLast = (i == variablesToSum.Count - 1); - - if (!isLast) - { - if (weights[i + 1] >= 0) - { - sb.Append(" + "); - } - else - { - sb.Append(" - "); - } - } - } - - return sb.ToString(); - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/Factors/GaussianWithinFactor.cs b/Skills/TrueSkill/Factors/GaussianWithinFactor.cs deleted file mode 100644 index 71113b7..0000000 --- a/Skills/TrueSkill/Factors/GaussianWithinFactor.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using Moserware.Numerics; -using Moserware.Skills.FactorGraphs; - -namespace Moserware.Skills.TrueSkill.Factors -{ - /// - /// Factor representing a team difference that has not exceeded the draw margin. - /// - /// See the accompanying math paper for more details. - public class GaussianWithinFactor : GaussianFactor - { - private readonly double _Epsilon; - - public GaussianWithinFactor(double epsilon, Variable variable) - : base(String.Format("{0} <= {1:0.000}", variable, epsilon)) - { - _Epsilon = epsilon; - CreateVariableToMessageBinding(variable); - } - - public override double LogNormalization - { - get - { - GaussianDistribution marginal = Variables[0].Value; - GaussianDistribution message = Messages[0].Value; - GaussianDistribution messageFromVariable = marginal/message; - double mean = messageFromVariable.Mean; - double std = messageFromVariable.StandardDeviation; - double z = GaussianDistribution.CumulativeTo((_Epsilon - mean)/std) - - - GaussianDistribution.CumulativeTo((-_Epsilon - mean)/std); - - return -GaussianDistribution.LogProductNormalization(messageFromVariable, message) + Math.Log(z); - } - } - - protected override double UpdateMessage(Message message, - Variable variable) - { - GaussianDistribution oldMarginal = variable.Value.Clone(); - GaussianDistribution oldMessage = message.Value.Clone(); - GaussianDistribution messageFromVariable = oldMarginal/oldMessage; - - double c = messageFromVariable.Precision; - double d = messageFromVariable.PrecisionMean; - - double sqrtC = Math.Sqrt(c); - double dOnSqrtC = d/sqrtC; - - double epsilonTimesSqrtC = _Epsilon*sqrtC; - d = messageFromVariable.PrecisionMean; - - double denominator = 1.0 - TruncatedGaussianCorrectionFunctions.WWithinMargin(dOnSqrtC, epsilonTimesSqrtC); - double newPrecision = c/denominator; - double newPrecisionMean = (d + - sqrtC* - TruncatedGaussianCorrectionFunctions.VWithinMargin(dOnSqrtC, epsilonTimesSqrtC))/ - denominator; - - GaussianDistribution newMarginal = GaussianDistribution.FromPrecisionMean(newPrecisionMean, newPrecision); - GaussianDistribution newMessage = oldMessage*newMarginal/oldMarginal; - - /// Update the message and marginal - message.Value = newMessage; - variable.Value = newMarginal; - - /// Return the difference in the new marginal - return newMarginal - oldMarginal; - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/Layers/IteratedTeamDifferencesInnerLayer.cs b/Skills/TrueSkill/Layers/IteratedTeamDifferencesInnerLayer.cs deleted file mode 100644 index 94da9ba..0000000 --- a/Skills/TrueSkill/Layers/IteratedTeamDifferencesInnerLayer.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Moserware.Numerics; -using Moserware.Skills.FactorGraphs; -using Moserware.Skills.TrueSkill.Factors; - -namespace Moserware.Skills.TrueSkill.Layers -{ - // The whole purpose of this is to do a loop on the bottom - internal class IteratedTeamDifferencesInnerLayer : - TrueSkillFactorGraphLayer - , GaussianWeightedSumFactor, Variable> - { - private readonly TeamDifferencesComparisonLayer _TeamDifferencesComparisonLayer; - - private readonly TeamPerformancesToTeamPerformanceDifferencesLayer - _TeamPerformancesToTeamPerformanceDifferencesLayer; - - public IteratedTeamDifferencesInnerLayer(TrueSkillFactorGraph parentGraph, - TeamPerformancesToTeamPerformanceDifferencesLayer - teamPerformancesToPerformanceDifferences, - TeamDifferencesComparisonLayer teamDifferencesComparisonLayer) - : base(parentGraph) - { - _TeamPerformancesToTeamPerformanceDifferencesLayer = teamPerformancesToPerformanceDifferences; - _TeamDifferencesComparisonLayer = teamDifferencesComparisonLayer; - } - - public override IEnumerable> UntypedFactors - { - get - { - return - _TeamPerformancesToTeamPerformanceDifferencesLayer.UntypedFactors.Concat( - _TeamDifferencesComparisonLayer.UntypedFactors); - } - } - - public override void BuildLayer() - { - _TeamPerformancesToTeamPerformanceDifferencesLayer.SetRawInputVariablesGroups(InputVariablesGroups); - _TeamPerformancesToTeamPerformanceDifferencesLayer.BuildLayer(); - - _TeamDifferencesComparisonLayer.SetRawInputVariablesGroups( - _TeamPerformancesToTeamPerformanceDifferencesLayer.GetRawOutputVariablesGroups()); - _TeamDifferencesComparisonLayer.BuildLayer(); - } - - public override Schedule CreatePriorSchedule() - { - Schedule loop = null; - - switch (InputVariablesGroups.Count) - { - case 0: - case 1: - throw new InvalidOperationException(); - case 2: - loop = CreateTwoTeamInnerPriorLoopSchedule(); - break; - default: - loop = CreateMultipleTeamInnerPriorLoopSchedule(); - break; - } - - // When dealing with differences, there are always (n-1) differences, so add in the 1 - int totalTeamDifferences = _TeamPerformancesToTeamPerformanceDifferencesLayer.LocalFactors.Count; - int totalTeams = totalTeamDifferences + 1; - - var innerSchedule = new ScheduleSequence( - "inner schedule", - new[] - { - loop, - new ScheduleStep( - "teamPerformanceToPerformanceDifferenceFactors[0] @ 1", - _TeamPerformancesToTeamPerformanceDifferencesLayer.LocalFactors[0], 1), - new ScheduleStep( - String.Format("teamPerformanceToPerformanceDifferenceFactors[teamTeamDifferences = {0} - 1] @ 2", - totalTeamDifferences), - _TeamPerformancesToTeamPerformanceDifferencesLayer.LocalFactors[totalTeamDifferences - 1], 2) - } - ); - - return innerSchedule; - } - - private Schedule CreateTwoTeamInnerPriorLoopSchedule() - { - return ScheduleSequence( - new[] - { - new ScheduleStep( - "send team perf to perf differences", - _TeamPerformancesToTeamPerformanceDifferencesLayer.LocalFactors[0], - 0), - new ScheduleStep( - "send to greater than or within factor", - _TeamDifferencesComparisonLayer.LocalFactors[0], - 0) - }, - "loop of just two teams inner sequence"); - } - - private Schedule CreateMultipleTeamInnerPriorLoopSchedule() - { - int totalTeamDifferences = _TeamPerformancesToTeamPerformanceDifferencesLayer.LocalFactors.Count; - - var forwardScheduleList = new List>(); - - for (int i = 0; i < totalTeamDifferences - 1; i++) - { - Schedule currentForwardSchedulePiece = - ScheduleSequence( - new Schedule[] - { - new ScheduleStep( - String.Format("team perf to perf diff {0}", - i), - _TeamPerformancesToTeamPerformanceDifferencesLayer.LocalFactors[i], 0), - new ScheduleStep( - String.Format("greater than or within result factor {0}", - i), - _TeamDifferencesComparisonLayer.LocalFactors[i], - 0), - new ScheduleStep( - String.Format("team perf to perf diff factors [{0}], 2", - i), - _TeamPerformancesToTeamPerformanceDifferencesLayer.LocalFactors[i], 2) - }, "current forward schedule piece {0}", i); - - forwardScheduleList.Add(currentForwardSchedulePiece); - } - - var forwardSchedule = - new ScheduleSequence( - "forward schedule", - forwardScheduleList); - - var backwardScheduleList = new List>(); - - for (int i = 0; i < totalTeamDifferences - 1; i++) - { - var currentBackwardSchedulePiece = new ScheduleSequence( - "current backward schedule piece", - new Schedule[] - { - new ScheduleStep( - String.Format("teamPerformanceToPerformanceDifferenceFactors[totalTeamDifferences - 1 - {0}] @ 0", - i), - _TeamPerformancesToTeamPerformanceDifferencesLayer.LocalFactors[ - totalTeamDifferences - 1 - i], 0), - new ScheduleStep( - String.Format("greaterThanOrWithinResultFactors[totalTeamDifferences - 1 - {0}] @ 0", - i), - _TeamDifferencesComparisonLayer.LocalFactors[totalTeamDifferences - 1 - i], 0), - new ScheduleStep( - String.Format("teamPerformanceToPerformanceDifferenceFactors[totalTeamDifferences - 1 - {0}] @ 1", - i), - _TeamPerformancesToTeamPerformanceDifferencesLayer.LocalFactors[ - totalTeamDifferences - 1 - i], 1) - } - ); - backwardScheduleList.Add(currentBackwardSchedulePiece); - } - - var backwardSchedule = - new ScheduleSequence( - "backward schedule", - backwardScheduleList); - - var forwardBackwardScheduleToLoop = - new ScheduleSequence( - "forward Backward Schedule To Loop", - new Schedule[] - { - forwardSchedule, backwardSchedule - }); - - const double initialMaxDelta = 0.0001; - - var loop = new ScheduleLoop( - String.Format("loop with max delta of {0}", - initialMaxDelta), - forwardBackwardScheduleToLoop, - initialMaxDelta); - - return loop; - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/Layers/PlayerPerformancesToTeamPerformancesLayer.cs b/Skills/TrueSkill/Layers/PlayerPerformancesToTeamPerformancesLayer.cs deleted file mode 100644 index 2aae362..0000000 --- a/Skills/TrueSkill/Layers/PlayerPerformancesToTeamPerformancesLayer.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Moserware.Numerics; -using Moserware.Skills.FactorGraphs; -using Moserware.Skills.TrueSkill.Factors; - -namespace Moserware.Skills.TrueSkill.Layers -{ - internal class PlayerPerformancesToTeamPerformancesLayer : - TrueSkillFactorGraphLayer - , GaussianWeightedSumFactor, - Variable> - { - public PlayerPerformancesToTeamPerformancesLayer(TrueSkillFactorGraph parentGraph) - : base(parentGraph) - { - } - - public override void BuildLayer() - { - foreach (var currentTeam in InputVariablesGroups) - { - Variable teamPerformance = CreateOutputVariable(currentTeam); - AddLayerFactor(CreatePlayerToTeamSumFactor(currentTeam, teamPerformance)); - - // REVIEW: Does it make sense to have groups of one? - OutputVariablesGroups.Add(new[] {teamPerformance}); - } - } - - public override Schedule CreatePriorSchedule() - { - return ScheduleSequence( - from weightedSumFactor in LocalFactors - select new ScheduleStep("Perf to Team Perf Step", weightedSumFactor, 0), - "all player perf to team perf schedule"); - } - - protected GaussianWeightedSumFactor CreatePlayerToTeamSumFactor( - IList> teamMembers, Variable sumVariable) - { - return new GaussianWeightedSumFactor(sumVariable, teamMembers.ToArray(), - teamMembers.Select(v => PartialPlay.GetPartialPlayPercentage(v.Key)). - ToArray()); - } - - public override Schedule CreatePosteriorSchedule() - { - return ScheduleSequence(from currentFactor in LocalFactors - from currentIteration in - Enumerable.Range(1, currentFactor.NumberOfMessages - 1) - select new ScheduleStep( - "team sum perf @" + currentIteration, - currentFactor, - currentIteration), - "all of the team's sum iterations"); - } - - private Variable CreateOutputVariable( - IList> team) - { - string teamMemberNames = String.Join(", ", team.Select(teamMember => teamMember.Key.ToString()).ToArray()); - - return ParentFactorGraph.VariableFactory.CreateBasicVariable("Team[{0}]'s performance", teamMemberNames); - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/Layers/PlayerPriorValuesToSkillsLayer.cs b/Skills/TrueSkill/Layers/PlayerPriorValuesToSkillsLayer.cs deleted file mode 100644 index cec4c27..0000000 --- a/Skills/TrueSkill/Layers/PlayerPriorValuesToSkillsLayer.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Moserware.Numerics; -using Moserware.Skills.FactorGraphs; -using Moserware.Skills.TrueSkill.Factors; - -namespace Moserware.Skills.TrueSkill.Layers -{ - // We intentionally have no Posterior schedule since the only purpose here is to - internal class PlayerPriorValuesToSkillsLayer : - TrueSkillFactorGraphLayer - , GaussianPriorFactor, - KeyedVariable> - { - private readonly IEnumerable> _Teams; - - public PlayerPriorValuesToSkillsLayer(TrueSkillFactorGraph parentGraph, - IEnumerable> teams) - : base(parentGraph) - { - _Teams = teams; - } - - public override void BuildLayer() - { - foreach (var currentTeam in _Teams) - { - var currentTeamSkills = new List>(); - - foreach (var currentTeamPlayer in currentTeam) - { - KeyedVariable playerSkill = - CreateSkillOutputVariable(currentTeamPlayer.Key); - AddLayerFactor(CreatePriorFactor(currentTeamPlayer.Key, currentTeamPlayer.Value, playerSkill)); - currentTeamSkills.Add(playerSkill); - } - - OutputVariablesGroups.Add(currentTeamSkills); - } - } - - public override Schedule CreatePriorSchedule() - { - return ScheduleSequence( - from prior in LocalFactors - select new ScheduleStep("Prior to Skill Step", prior, 0), - "All priors"); - } - - private GaussianPriorFactor CreatePriorFactor(TPlayer player, Rating priorRating, - Variable skillsVariable) - { - return new GaussianPriorFactor(priorRating.Mean, - Square(priorRating.StandardDeviation) + - Square(ParentFactorGraph.GameInfo.DynamicsFactor), skillsVariable); - } - - private KeyedVariable CreateSkillOutputVariable(TPlayer key) - { - return ParentFactorGraph.VariableFactory.CreateKeyedVariable(key, "{0}'s skill", key); - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/Layers/PlayerSkillsToPerformancesLayer.cs b/Skills/TrueSkill/Layers/PlayerSkillsToPerformancesLayer.cs deleted file mode 100644 index 1899092..0000000 --- a/Skills/TrueSkill/Layers/PlayerSkillsToPerformancesLayer.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Moserware.Numerics; -using Moserware.Skills.FactorGraphs; -using Moserware.Skills.TrueSkill.Factors; - -namespace Moserware.Skills.TrueSkill.Layers -{ - internal class PlayerSkillsToPerformancesLayer : - TrueSkillFactorGraphLayer - , GaussianLikelihoodFactor, - KeyedVariable> - { - public PlayerSkillsToPerformancesLayer(TrueSkillFactorGraph parentGraph) - : base(parentGraph) - { - } - - public override void BuildLayer() - { - foreach (var currentTeam in InputVariablesGroups) - { - var currentTeamPlayerPerformances = new List>(); - - foreach (var playerSkillVariable in currentTeam) - { - KeyedVariable playerPerformance = - CreateOutputVariable(playerSkillVariable.Key); - AddLayerFactor(CreateLikelihood(playerSkillVariable, playerPerformance)); - currentTeamPlayerPerformances.Add(playerPerformance); - } - - OutputVariablesGroups.Add(currentTeamPlayerPerformances); - } - } - - private GaussianLikelihoodFactor CreateLikelihood(KeyedVariable playerSkill, - KeyedVariable playerPerformance) - { - return new GaussianLikelihoodFactor(Square(ParentFactorGraph.GameInfo.Beta), playerPerformance, playerSkill); - } - - private KeyedVariable CreateOutputVariable(TPlayer key) - { - return ParentFactorGraph.VariableFactory.CreateKeyedVariable(key, "{0}'s performance", key); - } - - public override Schedule CreatePriorSchedule() - { - return ScheduleSequence( - from likelihood in LocalFactors - select new ScheduleStep("Skill to Perf step", likelihood, 0), - "All skill to performance sending"); - } - - public override Schedule CreatePosteriorSchedule() - { - return ScheduleSequence( - from likelihood in LocalFactors - select new ScheduleStep("name", likelihood, 1), - "All skill to performance sending"); - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/Layers/TeamDifferencesComparisonLayer.cs b/Skills/TrueSkill/Layers/TeamDifferencesComparisonLayer.cs deleted file mode 100644 index 748aec0..0000000 --- a/Skills/TrueSkill/Layers/TeamDifferencesComparisonLayer.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Moserware.Numerics; -using Moserware.Skills.FactorGraphs; -using Moserware.Skills.TrueSkill.Factors; - -namespace Moserware.Skills.TrueSkill.Layers -{ - internal class TeamDifferencesComparisonLayer : - TrueSkillFactorGraphLayer - , GaussianFactor, DefaultVariable> - { - private readonly double _Epsilon; - private readonly int[] _TeamRanks; - - public TeamDifferencesComparisonLayer(TrueSkillFactorGraph parentGraph, int[] teamRanks) - : base(parentGraph) - { - _TeamRanks = teamRanks; - GameInfo gameInfo = ParentFactorGraph.GameInfo; - _Epsilon = DrawMargin.GetDrawMarginFromDrawProbability(gameInfo.DrawProbability, gameInfo.Beta); - } - - public override void BuildLayer() - { - for (int i = 0; i < InputVariablesGroups.Count; i++) - { - bool isDraw = (_TeamRanks[i] == _TeamRanks[i + 1]); - Variable teamDifference = InputVariablesGroups[i][0]; - - GaussianFactor factor = - isDraw - ? (GaussianFactor) new GaussianWithinFactor(_Epsilon, teamDifference) - : new GaussianGreaterThanFactor(_Epsilon, teamDifference); - - AddLayerFactor(factor); - } - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/Layers/TeamPerformancesToTeamPerformanceDifferencesLayer.cs b/Skills/TrueSkill/Layers/TeamPerformancesToTeamPerformanceDifferencesLayer.cs deleted file mode 100644 index cec409d..0000000 --- a/Skills/TrueSkill/Layers/TeamPerformancesToTeamPerformanceDifferencesLayer.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Moserware.Numerics; -using Moserware.Skills.FactorGraphs; -using Moserware.Skills.TrueSkill.Factors; - -namespace Moserware.Skills.TrueSkill.Layers -{ - internal class TeamPerformancesToTeamPerformanceDifferencesLayer : - TrueSkillFactorGraphLayer - , GaussianWeightedSumFactor, Variable> - { - public TeamPerformancesToTeamPerformanceDifferencesLayer(TrueSkillFactorGraph parentGraph) - : base(parentGraph) - { - } - - public override void BuildLayer() - { - for (int i = 0; i < InputVariablesGroups.Count - 1; i++) - { - Variable strongerTeam = InputVariablesGroups[i][0]; - Variable weakerTeam = InputVariablesGroups[i + 1][0]; - - Variable currentDifference = CreateOutputVariable(); - AddLayerFactor(CreateTeamPerformanceToDifferenceFactor(strongerTeam, weakerTeam, currentDifference)); - - // REVIEW: Does it make sense to have groups of one? - OutputVariablesGroups.Add(new[] {currentDifference}); - } - } - - private GaussianWeightedSumFactor CreateTeamPerformanceToDifferenceFactor( - Variable strongerTeam, Variable weakerTeam, - Variable output) - { - return new GaussianWeightedSumFactor(output, new[] {strongerTeam, weakerTeam}, new[] {1.0, -1.0}); - } - - private Variable CreateOutputVariable() - { - return ParentFactorGraph.VariableFactory.CreateBasicVariable("Team performance difference"); - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/Layers/TrueSkillFactorGraphLayer.cs b/Skills/TrueSkill/Layers/TrueSkillFactorGraphLayer.cs deleted file mode 100644 index fedb182..0000000 --- a/Skills/TrueSkill/Layers/TrueSkillFactorGraphLayer.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Moserware.Numerics; -using Moserware.Skills.FactorGraphs; - -namespace Moserware.Skills.TrueSkill.Layers -{ - internal abstract class TrueSkillFactorGraphLayer - : - FactorGraphLayer - , GaussianDistribution, Variable, TInputVariable, - TFactor, TOutputVariable> - where TInputVariable : Variable - where TFactor : Factor - where TOutputVariable : Variable - { - public TrueSkillFactorGraphLayer(TrueSkillFactorGraph parentGraph) - : base(parentGraph) - { - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/TrueSkillFactorGraph.cs b/Skills/TrueSkill/TrueSkillFactorGraph.cs deleted file mode 100644 index ffe5d0a..0000000 --- a/Skills/TrueSkill/TrueSkillFactorGraph.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Moserware.Numerics; -using Moserware.Skills.FactorGraphs; -using Moserware.Skills.TrueSkill.Layers; - -namespace Moserware.Skills.TrueSkill -{ - public class TrueSkillFactorGraph : - FactorGraph, GaussianDistribution, Variable> - { - private readonly List> _Layers; - private readonly PlayerPriorValuesToSkillsLayer _PriorLayer; - - public TrueSkillFactorGraph(GameInfo gameInfo, IEnumerable> teams, int[] teamRanks) - { - _PriorLayer = new PlayerPriorValuesToSkillsLayer(this, teams); - GameInfo = gameInfo; - VariableFactory = - new VariableFactory(() => GaussianDistribution.FromPrecisionMean(0, 0)); - - _Layers = new List> - { - _PriorLayer, - new PlayerSkillsToPerformancesLayer(this), - new PlayerPerformancesToTeamPerformancesLayer(this), - new IteratedTeamDifferencesInnerLayer( - this, - new TeamPerformancesToTeamPerformanceDifferencesLayer(this), - new TeamDifferencesComparisonLayer(this, teamRanks)) - }; - } - - public GameInfo GameInfo { get; private set; } - - public void BuildGraph() - { - object lastOutput = null; - - foreach (var currentLayer in _Layers) - { - if (lastOutput != null) - { - currentLayer.SetRawInputVariablesGroups(lastOutput); - } - - currentLayer.BuildLayer(); - - lastOutput = currentLayer.GetRawOutputVariablesGroups(); - } - } - - public void RunSchedule() - { - Schedule fullSchedule = CreateFullSchedule(); - double fullScheduleDelta = fullSchedule.Visit(); - } - - public double GetProbabilityOfRanking() - { - var factorList = new FactorList(); - - foreach (var currentLayer in _Layers) - { - foreach (var currentFactor in currentLayer.UntypedFactors) - { - factorList.AddFactor(currentFactor); - } - } - - double logZ = factorList.LogNormalization; - return Math.Exp(logZ); - } - - private Schedule CreateFullSchedule() - { - var fullSchedule = new List>(); - - foreach (var currentLayer in _Layers) - { - Schedule currentPriorSchedule = currentLayer.CreatePriorSchedule(); - if (currentPriorSchedule != null) - { - fullSchedule.Add(currentPriorSchedule); - } - } - - // Casting to IEnumerable to get the LINQ Reverse() - IEnumerable> allLayers = _Layers; - - foreach (var currentLayer in allLayers.Reverse()) - { - Schedule currentPosteriorSchedule = currentLayer.CreatePosteriorSchedule(); - if (currentPosteriorSchedule != null) - { - fullSchedule.Add(currentPosteriorSchedule); - } - } - - return new ScheduleSequence("Full schedule", fullSchedule); - } - - public IDictionary GetUpdatedRatings() - { - var result = new Dictionary(); - foreach (var currentTeam in _PriorLayer.OutputVariablesGroups) - { - foreach (var currentPlayer in currentTeam) - { - result[currentPlayer.Key] = new Rating(currentPlayer.Value.Mean, - currentPlayer.Value.StandardDeviation); - } - } - - return result; - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/TruncatedGaussianCorrectionFunctions.cs b/Skills/TrueSkill/TruncatedGaussianCorrectionFunctions.cs deleted file mode 100644 index 252ee6a..0000000 --- a/Skills/TrueSkill/TruncatedGaussianCorrectionFunctions.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System; -using Moserware.Numerics; - -namespace Moserware.Skills.TrueSkill -{ - internal static class TruncatedGaussianCorrectionFunctions - { - // These functions from the bottom of page 4 of the TrueSkill paper. - - /// - /// The "V" function where the team performance difference is greater than the draw margin. - /// - /// In the reference F# implementation, this is referred to as "the additive - /// correction of a single-sided truncated Gaussian with unit variance." - /// - /// In the paper, it's referred to as just "ε". - /// - public static double VExceedsMargin(double teamPerformanceDifference, double drawMargin, double c) - { - return VExceedsMargin(teamPerformanceDifference/c, drawMargin/c); - //return GaussianDistribution.At((teamPerformanceDifference - drawMargin) / c) / GaussianDistribution.CumulativeTo((teamPerformanceDifference - drawMargin) / c); - } - - public static double VExceedsMargin(double teamPerformanceDifference, double drawMargin) - { - double denominator = GaussianDistribution.CumulativeTo(teamPerformanceDifference - drawMargin); - - if (denominator < 2.222758749e-162) - { - return -teamPerformanceDifference + drawMargin; - } - - return GaussianDistribution.At(teamPerformanceDifference - drawMargin)/denominator; - } - - /// - /// The "W" function where the team performance difference is greater than the draw margin. - /// - /// In the reference F# implementation, this is referred to as "the multiplicative - /// correction of a single-sided truncated Gaussian with unit variance." - /// - /// - /// - /// - public static double WExceedsMargin(double teamPerformanceDifference, double drawMargin, double c) - { - return WExceedsMargin(teamPerformanceDifference/c, drawMargin/c); - //var vWin = VExceedsMargin(teamPerformanceDifference, drawMargin, c); - //return vWin * (vWin + (teamPerformanceDifference - drawMargin) / c); - } - - public static double WExceedsMargin(double teamPerformanceDifference, double drawMargin) - { - double denominator = GaussianDistribution.CumulativeTo(teamPerformanceDifference - drawMargin); - - if (denominator < 2.222758749e-162) - { - if (teamPerformanceDifference < 0.0) - { - return 1.0; - } - return 0.0; - } - - double vWin = VExceedsMargin(teamPerformanceDifference, drawMargin); - return vWin*(vWin + teamPerformanceDifference - drawMargin); - } - - // the additive correction of a double-sided truncated Gaussian with unit variance - public static double VWithinMargin(double teamPerformanceDifference, double drawMargin, double c) - { - return VWithinMargin(teamPerformanceDifference/c, drawMargin/c); - //var teamPerformanceDifferenceAbsoluteValue = Math.Abs(teamPerformanceDifference); - //return (GaussianDistribution.At((-drawMargin - teamPerformanceDifferenceAbsoluteValue) / c) - GaussianDistribution.At((drawMargin - teamPerformanceDifferenceAbsoluteValue) / c)) - // / - // (GaussianDistribution.CumulativeTo((drawMargin - teamPerformanceDifferenceAbsoluteValue) / c) - GaussianDistribution.CumulativeTo((-drawMargin - teamPerformanceDifferenceAbsoluteValue) / c)); - } - - // My original: - //public static double VWithinMargin(double teamPerformanceDifference, double drawMargin) - //{ - // var teamPerformanceDifferenceAbsoluteValue = Math.Abs(teamPerformanceDifference); - // return (GaussianDistribution.At(-drawMargin - teamPerformanceDifferenceAbsoluteValue) - GaussianDistribution.At(drawMargin - teamPerformanceDifferenceAbsoluteValue)) - // / - // (GaussianDistribution.CumulativeTo(drawMargin - teamPerformanceDifferenceAbsoluteValue) - GaussianDistribution.CumulativeTo(-drawMargin - teamPerformanceDifferenceAbsoluteValue)); - //} - - // from F#: - public static double VWithinMargin(double teamPerformanceDifference, double drawMargin) - { - double teamPerformanceDifferenceAbsoluteValue = Math.Abs(teamPerformanceDifference); - double denominator = - GaussianDistribution.CumulativeTo(drawMargin - teamPerformanceDifferenceAbsoluteValue) - - GaussianDistribution.CumulativeTo(-drawMargin - teamPerformanceDifferenceAbsoluteValue); - if (denominator < 2.222758749e-162) - { - if (teamPerformanceDifference < 0.0) - { - return -teamPerformanceDifference - drawMargin; - } - - return -teamPerformanceDifference + drawMargin; - } - - double numerator = GaussianDistribution.At(-drawMargin - teamPerformanceDifferenceAbsoluteValue) - - GaussianDistribution.At(drawMargin - teamPerformanceDifferenceAbsoluteValue); - - if (teamPerformanceDifference < 0.0) - { - return -numerator/denominator; - } - - return numerator/denominator; - } - - // the multiplicative correction of a double-sided truncated Gaussian with unit variance - public static double WWithinMargin(double teamPerformanceDifference, double drawMargin, double c) - { - return WWithinMargin(teamPerformanceDifference/c, drawMargin/c); - //var teamPerformanceDifferenceAbsoluteValue = Math.Abs(teamPerformanceDifference); - //var vDraw = VWithinMargin(teamPerformanceDifferenceAbsoluteValue, drawMargin, c); - - //return (vDraw * vDraw) - // + - // ( - // ( - // ( - // ((drawMargin - teamPerformanceDifferenceAbsoluteValue) / c) - // * - // GaussianDistribution.At((drawMargin - teamPerformanceDifferenceAbsoluteValue) / c) - // ) - // + - // ( - // ((drawMargin + teamPerformanceDifferenceAbsoluteValue) / c) - // * - // GaussianDistribution.At((drawMargin + teamPerformanceDifferenceAbsoluteValue) / c) - // ) - // ) - // / - // ( - // GaussianDistribution.CumulativeTo((drawMargin - teamPerformanceDifferenceAbsoluteValue) / c) - // - - // GaussianDistribution.CumulativeTo((-drawMargin - teamPerformanceDifferenceAbsoluteValue) / c) - // ) - // ); - } - - // My original: - //public static double WWithinMargin(double teamPerformanceDifference, double drawMargin) - //{ - // var teamPerformanceDifferenceAbsoluteValue = Math.Abs(teamPerformanceDifference); - // var vDraw = VWithinMargin(teamPerformanceDifferenceAbsoluteValue, drawMargin); - // return (vDraw * vDraw) - // + - // ( - // ((drawMargin - teamPerformanceDifferenceAbsoluteValue) * GaussianDistribution.At(drawMargin - teamPerformanceDifferenceAbsoluteValue) + (drawMargin + teamPerformanceDifferenceAbsoluteValue) * GaussianDistribution.At(drawMargin + teamPerformanceDifferenceAbsoluteValue)) - // / - // (GaussianDistribution.CumulativeTo(drawMargin - teamPerformanceDifferenceAbsoluteValue) - GaussianDistribution.CumulativeTo(-drawMargin - teamPerformanceDifferenceAbsoluteValue)) - // ); - //} - - // From F#: - public static double WWithinMargin(double teamPerformanceDifference, double drawMargin) - { - double teamPerformanceDifferenceAbsoluteValue = Math.Abs(teamPerformanceDifference); - double denominator = GaussianDistribution.CumulativeTo(drawMargin - teamPerformanceDifferenceAbsoluteValue) - - - GaussianDistribution.CumulativeTo(-drawMargin - teamPerformanceDifferenceAbsoluteValue); - - if (denominator < 2.222758749e-162) - { - return 1.0; - } - - double vt = VWithinMargin(teamPerformanceDifferenceAbsoluteValue, drawMargin); - - return vt*vt + - ( - (drawMargin - teamPerformanceDifferenceAbsoluteValue) - * - GaussianDistribution.At( - drawMargin - teamPerformanceDifferenceAbsoluteValue) - - (-drawMargin - teamPerformanceDifferenceAbsoluteValue) - * - GaussianDistribution.At(-drawMargin - teamPerformanceDifferenceAbsoluteValue))/denominator; - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/TwoPlayerTrueSkillCalculator.cs b/Skills/TrueSkill/TwoPlayerTrueSkillCalculator.cs deleted file mode 100644 index e494474..0000000 --- a/Skills/TrueSkill/TwoPlayerTrueSkillCalculator.cs +++ /dev/null @@ -1,150 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Moserware.Skills.Numerics; - -namespace Moserware.Skills.TrueSkill -{ - /// - /// Calculates the new ratings for only two players. - /// - /// - /// When you only have two players, a lot of the math simplifies. The main purpose of this class - /// is to show the bare minimum of what a TrueSkill implementation should have. - /// - public class TwoPlayerTrueSkillCalculator : SkillCalculator - { - public TwoPlayerTrueSkillCalculator() - : base(SupportedOptions.None, Range.Exactly(2), Range.Exactly(1)) - { - } - - /// - public override IDictionary CalculateNewRatings(GameInfo gameInfo, - IEnumerable - > - teams, params int[] teamRanks) - { - // Basic argument checking - Guard.ArgumentNotNull(gameInfo, "gameInfo"); - ValidateTeamCountAndPlayersCountPerTeam(teams); - - // Make sure things are in order - RankSorter.Sort(ref teams, ref teamRanks); - - // Get the teams as a list to make it easier to index - List> teamList = teams.ToList(); - - // Since we verified that each team has one player, we know the player is the first one - IDictionary winningTeam = teamList[0]; - TPlayer winner = winningTeam.Keys.First(); - Rating winnerPreviousRating = winningTeam[winner]; - - IDictionary losingTeam = teamList[1]; - TPlayer loser = losingTeam.Keys.First(); - Rating loserPreviousRating = losingTeam[loser]; - - bool wasDraw = (teamRanks[0] == teamRanks[1]); - - var results = new Dictionary(); - results[winner] = CalculateNewRating(gameInfo, winnerPreviousRating, loserPreviousRating, - wasDraw ? PairwiseComparison.Draw : PairwiseComparison.Win); - results[loser] = CalculateNewRating(gameInfo, loserPreviousRating, winnerPreviousRating, - wasDraw ? PairwiseComparison.Draw : PairwiseComparison.Lose); - - // And we're done! - return results; - } - - private static Rating CalculateNewRating(GameInfo gameInfo, Rating selfRating, Rating opponentRating, - PairwiseComparison comparison) - { - double drawMargin = DrawMargin.GetDrawMarginFromDrawProbability(gameInfo.DrawProbability, gameInfo.Beta); - - double c = - Math.Sqrt( - Square(selfRating.StandardDeviation) - + - Square(opponentRating.StandardDeviation) - + - 2*Square(gameInfo.Beta)); - - double winningMean = selfRating.Mean; - double losingMean = opponentRating.Mean; - - switch (comparison) - { - case PairwiseComparison.Win: - case PairwiseComparison.Draw: - // NOP - break; - case PairwiseComparison.Lose: - winningMean = opponentRating.Mean; - losingMean = selfRating.Mean; - break; - } - - double meanDelta = winningMean - losingMean; - - double v; - double w; - double rankMultiplier; - - if (comparison != PairwiseComparison.Draw) - { - // non-draw case - v = TruncatedGaussianCorrectionFunctions.VExceedsMargin(meanDelta, drawMargin, c); - w = TruncatedGaussianCorrectionFunctions.WExceedsMargin(meanDelta, drawMargin, c); - rankMultiplier = (int) comparison; - } - else - { - v = TruncatedGaussianCorrectionFunctions.VWithinMargin(meanDelta, drawMargin, c); - w = TruncatedGaussianCorrectionFunctions.WWithinMargin(meanDelta, drawMargin, c); - rankMultiplier = 1; - } - - double meanMultiplier = (Square(selfRating.StandardDeviation) + Square(gameInfo.DynamicsFactor))/c; - - double varianceWithDynamics = Square(selfRating.StandardDeviation) + Square(gameInfo.DynamicsFactor); - double stdDevMultiplier = varianceWithDynamics/Square(c); - - double newMean = selfRating.Mean + (rankMultiplier*meanMultiplier*v); - double newStdDev = Math.Sqrt(varianceWithDynamics*(1 - w*stdDevMultiplier)); - - return new Rating(newMean, newStdDev); - } - - /// - public override double CalculateMatchQuality(GameInfo gameInfo, - IEnumerable> teams) - { - Guard.ArgumentNotNull(gameInfo, "gameInfo"); - ValidateTeamCountAndPlayersCountPerTeam(teams); - - Rating player1Rating = teams.First().Values.First(); - Rating player2Rating = teams.Last().Values.First(); - - // We just use equation 4.1 found on page 8 of the TrueSkill 2006 paper: - double betaSquared = Square(gameInfo.Beta); - double player1SigmaSquared = Square(player1Rating.StandardDeviation); - double player2SigmaSquared = Square(player2Rating.StandardDeviation); - - // This is the square root part of the equation: - double sqrtPart = - Math.Sqrt( - (2*betaSquared) - / - (2*betaSquared + player1SigmaSquared + player2SigmaSquared)); - - // This is the exponent part of the equation: - double expPart = - Math.Exp( - (-1*Square(player1Rating.Mean - player2Rating.Mean)) - / - (2*(2*betaSquared + player1SigmaSquared + player2SigmaSquared))); - - return sqrtPart*expPart; - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkill/TwoTeamTrueSkillCalculator.cs b/Skills/TrueSkill/TwoTeamTrueSkillCalculator.cs deleted file mode 100644 index 8a493b8..0000000 --- a/Skills/TrueSkill/TwoTeamTrueSkillCalculator.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Moserware.Skills.Numerics; - -namespace Moserware.Skills.TrueSkill -{ - /// - /// Calculates new ratings for only two teams where each team has 1 or more players. - /// - /// - /// When you only have two teams, the math is still simple: no factor graphs are used yet. - /// - public class TwoTeamTrueSkillCalculator : SkillCalculator - { - public TwoTeamTrueSkillCalculator() - : base(SupportedOptions.None, Range.Exactly(2), Range.AtLeast(1)) - { - } - - /// - public override IDictionary CalculateNewRatings(GameInfo gameInfo, - IEnumerable - > - teams, params int[] teamRanks) - { - Guard.ArgumentNotNull(gameInfo, "gameInfo"); - ValidateTeamCountAndPlayersCountPerTeam(teams); - - RankSorter.Sort(ref teams, ref teamRanks); - - IDictionary team1 = teams.First(); - IDictionary team2 = teams.Last(); - - bool wasDraw = (teamRanks[0] == teamRanks[1]); - - var results = new Dictionary(); - - UpdatePlayerRatings(gameInfo, - results, - team1, - team2, - wasDraw ? PairwiseComparison.Draw : PairwiseComparison.Win); - - UpdatePlayerRatings(gameInfo, - results, - team2, - team1, - wasDraw ? PairwiseComparison.Draw : PairwiseComparison.Lose); - - return results; - } - - private static void UpdatePlayerRatings(GameInfo gameInfo, - IDictionary newPlayerRatings, - IDictionary selfTeam, - IDictionary otherTeam, - PairwiseComparison selfToOtherTeamComparison) - { - double drawMargin = DrawMargin.GetDrawMarginFromDrawProbability(gameInfo.DrawProbability, gameInfo.Beta); - double betaSquared = Square(gameInfo.Beta); - double tauSquared = Square(gameInfo.DynamicsFactor); - - int totalPlayers = selfTeam.Count() + otherTeam.Count(); - - double selfMeanSum = selfTeam.Values.Sum(r => r.Mean); - double otherTeamMeanSum = otherTeam.Values.Sum(r => r.Mean); - - double c = Math.Sqrt(selfTeam.Values.Sum(r => Square(r.StandardDeviation)) - + - otherTeam.Values.Sum(r => Square(r.StandardDeviation)) - + - totalPlayers*betaSquared); - - double winningMean = selfMeanSum; - double losingMean = otherTeamMeanSum; - - switch (selfToOtherTeamComparison) - { - case PairwiseComparison.Win: - case PairwiseComparison.Draw: - // NOP - break; - case PairwiseComparison.Lose: - winningMean = otherTeamMeanSum; - losingMean = selfMeanSum; - break; - } - - double meanDelta = winningMean - losingMean; - - double v; - double w; - double rankMultiplier; - - if (selfToOtherTeamComparison != PairwiseComparison.Draw) - { - // non-draw case - v = TruncatedGaussianCorrectionFunctions.VExceedsMargin(meanDelta, drawMargin, c); - w = TruncatedGaussianCorrectionFunctions.WExceedsMargin(meanDelta, drawMargin, c); - rankMultiplier = (int) selfToOtherTeamComparison; - } - else - { - // assume draw - v = TruncatedGaussianCorrectionFunctions.VWithinMargin(meanDelta, drawMargin, c); - w = TruncatedGaussianCorrectionFunctions.WWithinMargin(meanDelta, drawMargin, c); - rankMultiplier = 1; - } - - foreach (var teamPlayerRatingPair in selfTeam) - { - Rating previousPlayerRating = teamPlayerRatingPair.Value; - - double meanMultiplier = (Square(previousPlayerRating.StandardDeviation) + tauSquared)/c; - double stdDevMultiplier = (Square(previousPlayerRating.StandardDeviation) + tauSquared)/Square(c); - - double playerMeanDelta = (rankMultiplier*meanMultiplier*v); - double newMean = previousPlayerRating.Mean + playerMeanDelta; - - double newStdDev = - Math.Sqrt((Square(previousPlayerRating.StandardDeviation) + tauSquared)*(1 - w*stdDevMultiplier)); - - newPlayerRatings[teamPlayerRatingPair.Key] = new Rating(newMean, newStdDev); - } - } - - /// - public override double CalculateMatchQuality(GameInfo gameInfo, - IEnumerable> teams) - { - Guard.ArgumentNotNull(gameInfo, "gameInfo"); - ValidateTeamCountAndPlayersCountPerTeam(teams); - - // We've verified that there's just two teams - ICollection team1 = teams.First().Values; - int team1Count = team1.Count(); - - ICollection team2 = teams.Last().Values; - int team2Count = team2.Count(); - - int totalPlayers = team1Count + team2Count; - - double betaSquared = Square(gameInfo.Beta); - - double team1MeanSum = team1.Sum(r => r.Mean); - double team1StdDevSquared = team1.Sum(r => Square(r.StandardDeviation)); - - double team2MeanSum = team2.Sum(r => r.Mean); - double team2SigmaSquared = team2.Sum(r => Square(r.StandardDeviation)); - - // This comes from equation 4.1 in the TrueSkill paper on page 8 - // The equation was broken up into the part under the square root sign and - // the exponential part to make the code easier to read. - - double sqrtPart - = Math.Sqrt( - (totalPlayers*betaSquared) - / - (totalPlayers*betaSquared + team1StdDevSquared + team2SigmaSquared) - ); - - double expPart - = Math.Exp( - (-1*Square(team1MeanSum - team2MeanSum)) - / - (2*(totalPlayers*betaSquared + team1StdDevSquared + team2SigmaSquared)) - ); - - return expPart*sqrtPart; - } - } -} \ No newline at end of file diff --git a/Skills/TrueSkillCalculator.cs b/Skills/TrueSkillCalculator.cs deleted file mode 100644 index 9f3d05d..0000000 --- a/Skills/TrueSkillCalculator.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections.Generic; -using Moserware.Skills.TrueSkill; - -namespace Moserware.Skills -{ - /// - /// Calculates a TrueSkill rating using . - /// - public static class TrueSkillCalculator - { - // Keep a singleton around - private static readonly SkillCalculator _Calculator - = new FactorGraphTrueSkillCalculator(); - - /// - /// Calculates new ratings based on the prior ratings and team ranks. - /// - /// Parameters for the game. - /// A mapping of team players and their ratings. - /// The ranks of the teams where 1 is first place. For a tie, repeat the number (e.g. 1, 2, 2) - /// All the players and their new ratings. - public static IDictionary CalculateNewRatings(GameInfo gameInfo, - IEnumerable - > teams, - params int[] teamRanks) - { - // Just punt the work to the full implementation - return _Calculator.CalculateNewRatings(gameInfo, teams, teamRanks); - } - - /// - /// Calculates the match quality as the likelihood of all teams drawing. - /// - /// The underlying type of the player. - /// Parameters for the game. - /// A mapping of team players and their ratings. - /// The match quality as a percentage (between 0.0 and 1.0). - public static double CalculateMatchQuality(GameInfo gameInfo, - IEnumerable> teams) - { - // Just punt the work to the full implementation - return _Calculator.CalculateMatchQuality(gameInfo, teams); - } - } -} \ No newline at end of file diff --git a/Skills/bin/Debug/Moserware.Skills.dll b/Skills/bin/Debug/Moserware.Skills.dll deleted file mode 100644 index b00b103..0000000 Binary files a/Skills/bin/Debug/Moserware.Skills.dll and /dev/null differ diff --git a/Skills/bin/Debug/Moserware.Skills.pdb b/Skills/bin/Debug/Moserware.Skills.pdb deleted file mode 100644 index 67814ce..0000000 Binary files a/Skills/bin/Debug/Moserware.Skills.pdb and /dev/null differ diff --git a/Skills/bin/Release/Moserware.Skills.dll b/Skills/bin/Release/Moserware.Skills.dll deleted file mode 100644 index 0b7a2dc..0000000 Binary files a/Skills/bin/Release/Moserware.Skills.dll and /dev/null differ diff --git a/Skills/bin/Release/Moserware.Skills.pdb b/Skills/bin/Release/Moserware.Skills.pdb deleted file mode 100644 index 64309d5..0000000 Binary files a/Skills/bin/Release/Moserware.Skills.pdb and /dev/null differ diff --git a/UnitTests/Elo/DuellingEloTest.cs b/UnitTests/Elo/DuellingEloTest.cs deleted file mode 100644 index baf3ff1..0000000 --- a/UnitTests/Elo/DuellingEloTest.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Moserware.Skills; -using Moserware.Skills.Elo; -using NUnit.Framework; - -namespace UnitTests.Elo -{ - [TestFixture] - public class DuellingEloTest - { - private const double ErrorTolerance = 0.1; - - [Test] - public void TwoOnTwoDuellingTest() - { - var calculator = new DuellingEloCalculator(new GaussianEloCalculator()); - - var player1 = new Player(1); - var player2 = new Player(2); - - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team() - .AddPlayer(player1, gameInfo.DefaultRating) - .AddPlayer(player2, gameInfo.DefaultRating); - - var player3 = new Player(3); - var player4 = new Player(4); - - var team2 = new Team() - .AddPlayer(player3, gameInfo.DefaultRating) - .AddPlayer(player4, gameInfo.DefaultRating); - - var teams = Teams.Concat(team1, team2); - var newRatingsWinLose = calculator.CalculateNewRatings(gameInfo, teams, 1, 2); - - // TODO: Verify? - AssertRating(37, newRatingsWinLose[player1]); - AssertRating(37, newRatingsWinLose[player2]); - AssertRating(13, newRatingsWinLose[player3]); - AssertRating(13, newRatingsWinLose[player4]); - - var quality = calculator.CalculateMatchQuality(gameInfo, teams); - Assert.AreEqual(1.0, quality, 0.001); - } - - private static void AssertRating(double expected, Rating actual) - { - Assert.AreEqual(expected, actual.Mean, ErrorTolerance); - } - } -} diff --git a/UnitTests/Elo/EloAssert.cs b/UnitTests/Elo/EloAssert.cs deleted file mode 100644 index 2242120..0000000 --- a/UnitTests/Elo/EloAssert.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Moserware.Skills; -using Moserware.Skills.Elo; -using NUnit.Framework; - -namespace UnitTests.Elo -{ - internal static class EloAssert - { - private const double ErrorTolerance = 0.1; - - public static void AssertChessRating(TwoPlayerEloCalculator calculator, - double player1BeforeRating, - double player2BeforeRating, - PairwiseComparison player1Result, - double player1AfterRating, - double player2AfterRating) - { - var player1 = new Player(1); - var player2 = new Player(2); - - var teams = Teams.Concat( - new Team(player1, new EloRating(player1BeforeRating)), - new Team(player2, new EloRating(player2BeforeRating))); - - var chessGameInfo = new GameInfo(1200, 0, 200, 0, 0); - - var result = calculator.CalculateNewRatings(chessGameInfo, teams, - (player1Result == PairwiseComparison.Win) ? new[] { 1, 2 } : - (player1Result == PairwiseComparison.Lose) ? new[] { 2, 1 } : - new[] { 1, 1 }); - - - Assert.AreEqual(player1AfterRating, result[player1].Mean, ErrorTolerance); - Assert.AreEqual(player2AfterRating, result[player2].Mean, ErrorTolerance); - } - } -} diff --git a/UnitTests/Elo/FideEloCalculatorTest.cs b/UnitTests/Elo/FideEloCalculatorTest.cs deleted file mode 100644 index 56febae..0000000 --- a/UnitTests/Elo/FideEloCalculatorTest.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Moserware.Skills; -using Moserware.Skills.Elo; -using NUnit.Framework; - -namespace UnitTests.Elo -{ - [TestFixture] - public class FideEloCalculatorTest - { - [Test] - public void FideProvisionalEloCalculatorTests() - { - // verified against http://ratings.fide.com/calculator_rtd.phtml - var calc = new FideEloCalculator(new FideKFactor.Provisional()); - - EloAssert.AssertChessRating(calc, 1200, 1500, PairwiseComparison.Win, 1221.25, 1478.75); - EloAssert.AssertChessRating(calc, 1200, 1500, PairwiseComparison.Draw, 1208.75, 1491.25); - EloAssert.AssertChessRating(calc, 1200, 1500, PairwiseComparison.Lose, 1196.25, 1503.75); - } - - [Test] - public void FideNonProvisionalEloCalculatorTests() - { - // verified against http://ratings.fide.com/calculator_rtd.phtml - var calc = new FideEloCalculator(); - - EloAssert.AssertChessRating(calc, 1200, 1200, PairwiseComparison.Win, 1207.5, 1192.5); - EloAssert.AssertChessRating(calc, 1200, 1200, PairwiseComparison.Draw, 1200, 1200); - EloAssert.AssertChessRating(calc, 1200, 1200, PairwiseComparison.Lose, 1192.5, 1207.5); - - EloAssert.AssertChessRating(calc, 2600, 2500, PairwiseComparison.Win, 2603.6, 2496.4); - EloAssert.AssertChessRating(calc, 2600, 2500, PairwiseComparison.Draw, 2598.6, 2501.4); - EloAssert.AssertChessRating(calc, 2600, 2500, PairwiseComparison.Lose, 2593.6, 2506.4); - } - } -} \ No newline at end of file diff --git a/UnitTests/Elo/GaussianEloCalculatorTest.cs b/UnitTests/Elo/GaussianEloCalculatorTest.cs deleted file mode 100644 index a6325e5..0000000 --- a/UnitTests/Elo/GaussianEloCalculatorTest.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Moserware.Skills; -using Moserware.Skills.Elo; -using NUnit.Framework; - -namespace UnitTests.Elo -{ - [TestFixture] - public class GaussianEloCalculatorTest - { - [Test] - public void GaussianEloCalculatorTests() - { - const double defaultKFactor = 24.0; - var calc = new GaussianEloCalculator(); - - EloAssert.AssertChessRating(calc, 1200, 1200, PairwiseComparison.Win, 1212, 1188); - EloAssert.AssertChessRating(calc, 1200, 1200, PairwiseComparison.Draw, 1200, 1200); - EloAssert.AssertChessRating(calc, 1200, 1200, PairwiseComparison.Lose, 1188, 1212); - - // verified using TrueSkill paper equation - EloAssert.AssertChessRating(calc, 1200, 1000, PairwiseComparison.Win, 1200 + ((1 - 0.76024993890652326884) * defaultKFactor), 1000 - (1 - 0.76024993890652326884) * defaultKFactor); - EloAssert.AssertChessRating(calc, 1200, 1000, PairwiseComparison.Draw, 1200 - (0.76024993890652326884 - 0.5) * defaultKFactor, 1000 + (0.76024993890652326884 - 0.5) * defaultKFactor); - EloAssert.AssertChessRating(calc, 1200, 1000, PairwiseComparison.Lose, 1200 - 0.76024993890652326884 * defaultKFactor, 1000 + 0.76024993890652326884 * defaultKFactor); - } - } -} diff --git a/UnitTests/Numerics/GaussianDistributionTests.cs b/UnitTests/Numerics/GaussianDistributionTests.cs deleted file mode 100644 index c1e3b12..0000000 --- a/UnitTests/Numerics/GaussianDistributionTests.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using Moserware.Numerics; -using NUnit.Framework; - -namespace UnitTests.Numerics -{ - [TestFixture] - public class GaussianDistributionTests - { - private const double ErrorTolerance = 0.000001; - - [Test] - public void CumulativeToTests() - { - // Verified with WolframAlpha - // (e.g. http://www.wolframalpha.com/input/?i=CDF%5BNormalDistribution%5B0%2C1%5D%2C+0.5%5D ) - Assert.AreEqual(0.691462, GaussianDistribution.CumulativeTo(0.5), ErrorTolerance); - } - - [Test] - public void AtTests() - { - // Verified with WolframAlpha - // (e.g. http://www.wolframalpha.com/input/?i=PDF%5BNormalDistribution%5B0%2C1%5D%2C+0.5%5D ) - Assert.AreEqual(0.352065, GaussianDistribution.At(0.5), ErrorTolerance); - } - - [Test] - public void MultiplicationTests() - { - // I verified this against the formula at http://www.tina-vision.net/tina-knoppix/tina-memo/2003-003.pdf - var standardNormal = new GaussianDistribution(0, 1); - var shiftedGaussian = new GaussianDistribution(2, 3); - - var product = standardNormal * shiftedGaussian; - - Assert.AreEqual(0.2, product.Mean, ErrorTolerance); - Assert.AreEqual(3.0 / Math.Sqrt(10), product.StandardDeviation, ErrorTolerance); - - var m4s5 = new GaussianDistribution(4, 5); - var m6s7 = new GaussianDistribution(6, 7); - - var product2 = m4s5 * m6s7; - Func square = x => x*x; - - var expectedMean = (4 * square(7) + 6 * square(5)) / (square(5) + square(7)); - Assert.AreEqual(expectedMean, product2.Mean, ErrorTolerance); - - var expectedSigma = Math.Sqrt(((square(5) * square(7)) / (square(5) + square(7)))); - Assert.AreEqual(expectedSigma, product2.StandardDeviation, ErrorTolerance); - } - - [Test] - public void DivisionTests() - { - // Since the multiplication was worked out by hand, we use the same numbers but work backwards - var product = new GaussianDistribution(0.2, 3.0 / Math.Sqrt(10)); - var standardNormal = new GaussianDistribution(0, 1); - - var productDividedByStandardNormal = product / standardNormal; - Assert.AreEqual(2.0, productDividedByStandardNormal.Mean, ErrorTolerance); - Assert.AreEqual(3.0, productDividedByStandardNormal.StandardDeviation, ErrorTolerance); - - Func square = x => x * x; - var product2 = new GaussianDistribution((4 * square(7) + 6 * square(5)) / (square(5) + square(7)), Math.Sqrt(((square(5) * square(7)) / (square(5) + square(7))))); - var m4s5 = new GaussianDistribution(4,5); - var product2DividedByM4S5 = product2 / m4s5; - Assert.AreEqual(6.0, product2DividedByM4S5.Mean, ErrorTolerance); - Assert.AreEqual(7.0, product2DividedByM4S5.StandardDeviation, ErrorTolerance); - } - - [Test] - public void LogProductNormalizationTests() - { - // Verified with Ralf Herbrich's F# implementation - var standardNormal = new GaussianDistribution(0, 1); - var lpn = GaussianDistribution.LogProductNormalization(standardNormal, standardNormal); - Assert.AreEqual(-1.2655121234846454, lpn, ErrorTolerance); - - var m1s2 = new GaussianDistribution(1, 2); - var m3s4 = new GaussianDistribution(3, 4); - var lpn2 = GaussianDistribution.LogProductNormalization(m1s2, m3s4); - Assert.AreEqual(-2.5168046699816684, lpn2, ErrorTolerance); - } - - [Test] - public void LogRatioNormalizationTests() - { - // Verified with Ralf Herbrich's F# implementation - var m1s2 = new GaussianDistribution(1, 2); - var m3s4 = new GaussianDistribution(3, 4); - var lrn = GaussianDistribution.LogRatioNormalization(m1s2, m3s4); - Assert.AreEqual(2.6157405972171204, lrn, ErrorTolerance); - } - - [Test] - public void AbsoluteDifferenceTests() - { - // Verified with Ralf Herbrich's F# implementation - var standardNormal = new GaussianDistribution(0, 1); - var absDiff = GaussianDistribution.AbsoluteDifference(standardNormal, standardNormal); - Assert.AreEqual(0.0, absDiff, ErrorTolerance); - - var m1s2 = new GaussianDistribution(1, 2); - var m3s4 = new GaussianDistribution(3, 4); - var absDiff2 = GaussianDistribution.AbsoluteDifference(m1s2, m3s4); - Assert.AreEqual(0.4330127018922193, absDiff2, ErrorTolerance); - } - } -} \ No newline at end of file diff --git a/UnitTests/Numerics/MatrixTests.cs b/UnitTests/Numerics/MatrixTests.cs deleted file mode 100644 index 41e4497..0000000 --- a/UnitTests/Numerics/MatrixTests.cs +++ /dev/null @@ -1,196 +0,0 @@ -using Moserware.Numerics; -using NUnit.Framework; - -namespace UnitTests.Numerics -{ - [TestFixture] - public class MatrixTests - { - [Test] - public void TwoByTwoDeterminantTests() - { - var a = new SquareMatrix(1, 2, - 3, 4); - Assert.AreEqual(-2, a.Determinant); - - var b = new SquareMatrix(3, 4, - 5, 6); - Assert.AreEqual(-2, b.Determinant); - - var c = new SquareMatrix(1, 1, - 1, 1); - Assert.AreEqual(0, c.Determinant); - - var d = new SquareMatrix(12, 15, - 17, 21); - Assert.AreEqual(12 * 21 - 15 * 17, d.Determinant); - } - - [Test] - public void ThreeByThreeDeterminantTests() - { - var a = new SquareMatrix(1, 2, 3, - 4, 5, 6, - 7, 8, 9); - Assert.AreEqual(0, a.Determinant); - - var π = new SquareMatrix(3, 1, 4, - 1, 5, 9, - 2, 6, 5); - - // Verified against http://www.wolframalpha.com/input/?i=determinant+%7B%7B3%2C1%2C4%7D%2C%7B1%2C5%2C9%7D%2C%7B2%2C6%2C5%7D%7D - Assert.AreEqual(-90, π.Determinant); - } - - [Test] - public void FourByFourDeterminantTests() - { - var a = new SquareMatrix( 1, 2, 3, 4, - 5, 6, 7, 8, - 9, 10, 11, 12, - 13, 14, 15, 16); - - Assert.AreEqual(0, a.Determinant); - - var π = new SquareMatrix(3, 1, 4, 1, - 5, 9, 2, 6, - 5, 3, 5, 8, - 9, 7, 9, 3); - - // Verified against http://www.wolframalpha.com/input/?i=determinant+%7B+%7B3%2C1%2C4%2C1%7D%2C+%7B5%2C9%2C2%2C6%7D%2C+%7B5%2C3%2C5%2C8%7D%2C+%7B9%2C7%2C9%2C3%7D%7D - Assert.AreEqual(98, π.Determinant); - } - - [Test] - public void EightByEightDeterminantTests() - { - var a = new SquareMatrix( 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 32, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 60, 61, 62, 63, 64); - - Assert.AreEqual(0, a.Determinant); - - var π = new SquareMatrix(3, 1, 4, 1, 5, 9, 2, 6, - 5, 3, 5, 8, 9, 7, 9, 3, - 2, 3, 8, 4, 6, 2, 6, 4, - 3, 3, 8, 3, 2, 7, 9, 5, - 0, 2, 8, 8, 4, 1, 9, 7, - 1, 6, 9, 3, 9, 9, 3, 7, - 5, 1, 0, 5, 8, 2, 0, 9, - 7, 4, 9, 4, 4, 5, 9, 2); - - // Verified against http://www.wolframalpha.com/input/?i=det+%7B%7B3%2C1%2C4%2C1%2C5%2C9%2C2%2C6%7D%2C%7B5%2C3%2C5%2C8%2C9%2C7%2C9%2C3%7D%2C%7B2%2C3%2C8%2C4%2C6%2C2%2C6%2C4%7D%2C%7B3%2C3%2C8%2C3%2C2%2C7%2C9%2C5%7D%2C%7B0%2C2%2C8%2C8%2C4%2C1%2C9%2C7%7D%2C%7B1%2C6%2C9%2C3%2C9%2C9%2C3%2C7%7D%2C%7B5%2C1%2C0%2C5%2C8%2C2%2C0%2C9%7D%2C%7B7%2C4%2C9%2C4%2C4%2C5%2C9%2C2%7D%7D - Assert.AreEqual(1378143, π.Determinant); - } - - [Test] - public void EqualsTest() - { - var a = new SquareMatrix(1, 2, - 3, 4); - - var b = new SquareMatrix(1, 2, - 3, 4); - - Assert.IsTrue(a == b); - Assert.AreEqual(a, b); - - var c = new Matrix(2, 3, - 1, 2, 3, - 4, 5, 6); - - var d = new Matrix(2, 3, - 1, 2, 3, - 4, 5, 6); - - Assert.IsTrue(c == d); - Assert.AreEqual(c, d); - - var e = new Matrix(3, 2, - 1, 4, - 2, 5, - 3, 6); - - var f = e.Transpose; - Assert.IsTrue(d == f); - Assert.AreEqual(d, f); - Assert.AreEqual(d.GetHashCode(), f.GetHashCode()); - - // Test rounding (thanks to nsp on GitHub for finding this case) - var g = new SquareMatrix(1, 2.00000000000001, - 3, 4); - - var h = new SquareMatrix(1, 2, - 3, 4); - - Assert.IsTrue(g == h); - Assert.AreEqual(g, h); - Assert.AreEqual(g.GetHashCode(), h.GetHashCode()); - } - - [Test] - public void AdjugateTests() - { - // From Wikipedia: http://en.wikipedia.org/wiki/Adjugate_matrix - - var a = new SquareMatrix(1, 2, - 3, 4); - - var b = new SquareMatrix( 4, -2, - -3, 1); - - Assert.AreEqual(b, a.Adjugate); - - - var c = new SquareMatrix(-3, 2, -5, - -1, 0, -2, - 3, -4, 1); - - var d = new SquareMatrix(-8, 18, -4, - -5, 12, -1, - 4, -6, 2); - - Assert.AreEqual(d, c.Adjugate); - } - - [Test] - public void InverseTests() - { - // see http://www.mathwords.com/i/inverse_of_a_matrix.htm - var a = new SquareMatrix(4, 3, - 3, 2); - - var b = new SquareMatrix(-2, 3, - 3, -4); - - var aInverse = a.Inverse; - Assert.AreEqual(b, aInverse); - - var identity2x2 = new IdentityMatrix(2); - - var aaInverse = a * aInverse; - Assert.IsTrue(identity2x2 == aaInverse); - - var c = new SquareMatrix(1, 2, 3, - 0, 4, 5, - 1, 0, 6); - - var cInverse = c.Inverse; - var d = (1.0 / 22) * new SquareMatrix(24, -12, -2, - 5, 3, -5, - -4, 2, 4); - - - Assert.IsTrue(d == cInverse); - var identity3x3 = new IdentityMatrix(3); - - var ccInverse = c * cInverse; - Assert.IsTrue(identity3x3 == ccInverse); - } - } -} \ No newline at end of file diff --git a/UnitTests/Properties/AssemblyInfo.cs b/UnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index 5e1fe1e..0000000 --- a/UnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("UnitTests")] -[assembly: AssemblyDescription("Unit tests for Moserware.Skills")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Jeff Moser")] -[assembly: AssemblyProduct("UnitTests")] -[assembly: AssemblyCopyright("Copyright © Jeff Moser 2010")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("ddd7d430-f9c0-45c8-9576-70418d766e1f")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/UnitTests/README.txt b/UnitTests/README.txt deleted file mode 100644 index d8eea37..0000000 --- a/UnitTests/README.txt +++ /dev/null @@ -1,23 +0,0 @@ -These tests were written using NUnit 2.5.2 that is available for download -at: - -http://sourceforge.net/projects/nunit/files/NUnit%20Version%202/NUnit-2.5.2.9222.msi/download - -If you have a different version or setup, you'll need to update the path under -the UnitTests project properties by right clicking on UnitTests and then -click "properties" and then click the "debug" tab. The "start external program" -points to the NUnit test runner. - -I did it this way so you didn't need more than the express version of -Visual Studio to run. If you have a fancy test runner already, feel -free to use that. - -Additionally, it should be easy to update the tests to your tool -of choice. - -Finally, realize that these tests test *all* of the calculators -implementations. For that reason, they create a new instance of -a particular calculator. If you're using this code in your application, -you can just use the convenience helper class of "TrueSkillCalculator" -that has static methods. If you do that, you won't have to worry -about creating your own instances. \ No newline at end of file diff --git a/UnitTests/RankSorterTest.cs b/UnitTests/RankSorterTest.cs deleted file mode 100644 index 82ed457..0000000 --- a/UnitTests/RankSorterTest.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using Moserware.Skills; -using NUnit.Framework; - -namespace UnitTests -{ - [TestFixture] - public class RankSorterTest - { - [Test] - public void SortAlreadySortedTest() - { - IEnumerable people = new[] { "One", "Two", "Three" }; - int[] ranks = new[] { 1, 2, 3 }; - - RankSorter.Sort(ref people, ref ranks); - - CollectionAssert.AreEqual(new[] { "One", "Two", "Three" }, people); - CollectionAssert.AreEqual(new[] { 1, 2, 3 }, ranks); - } - - [Test] - public void SortUnsortedTest() - { - IEnumerable people = new[] { "Five", "Two1", "Two2", "One", "Four" }; - int[] ranks = new[] { 5, 2, 2, 1, 4 }; - - RankSorter.Sort(ref people, ref ranks); - - CollectionAssert.AreEqual(new[] { "One", "Two1", "Two2", "Four", "Five" }, people); - CollectionAssert.AreEqual(new[] { 1, 2, 2, 4, 5 }, ranks); - } - } -} \ No newline at end of file diff --git a/UnitTests/TrueSkill/DrawMarginTest.cs b/UnitTests/TrueSkill/DrawMarginTest.cs deleted file mode 100644 index 831c9c6..0000000 --- a/UnitTests/TrueSkill/DrawMarginTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Moserware.Skills.TrueSkill; -using NUnit.Framework; - -namespace UnitTests.TrueSkill -{ - [TestFixture] - public class DrawMarginTest - { - private const double ErrorTolerance = .000001; - - [Test] - public void GetDrawMarginFromDrawProbabilityTest() - { - double beta = 25.0 / 6.0; - // The expected values were compared against Ralf Herbrich's implementation in F# - AssertDrawMargin(0.10, beta, 0.74046637542690541); - AssertDrawMargin(0.25, beta, 1.87760059883033); - AssertDrawMargin(0.33, beta, 2.5111010132487492); - } - - private static void AssertDrawMargin(double drawProbability, double beta, double expected) - { - double actual = DrawMargin.GetDrawMarginFromDrawProbability(drawProbability, beta); - Assert.AreEqual(expected, actual, ErrorTolerance); - } - } -} \ No newline at end of file diff --git a/UnitTests/TrueSkill/FactorGraphTrueSkillCalculatorTests.cs b/UnitTests/TrueSkill/FactorGraphTrueSkillCalculatorTests.cs deleted file mode 100644 index 1f15f23..0000000 --- a/UnitTests/TrueSkill/FactorGraphTrueSkillCalculatorTests.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Moserware.Skills.TrueSkill; -using NUnit.Framework; - -namespace UnitTests.TrueSkill -{ - [TestFixture] - public class FactorGraphTrueSkillCalculatorTests - { - [Test] - public void FullFactorGraphCalculatorTests() - { - var calculator = new FactorGraphTrueSkillCalculator(); - - // We can test all classes - TrueSkillCalculatorTests.TestAllTwoPlayerScenarios(calculator); - TrueSkillCalculatorTests.TestAllTwoTeamScenarios(calculator); - TrueSkillCalculatorTests.TestAllMultipleTeamScenarios(calculator); - - TrueSkillCalculatorTests.TestPartialPlayScenarios(calculator); - } - } -} \ No newline at end of file diff --git a/UnitTests/TrueSkill/TrueSkillCalculatorTests.cs b/UnitTests/TrueSkill/TrueSkillCalculatorTests.cs deleted file mode 100644 index 33c0307..0000000 --- a/UnitTests/TrueSkill/TrueSkillCalculatorTests.cs +++ /dev/null @@ -1,987 +0,0 @@ -using Moserware.Skills; -using NUnit.Framework; - -namespace UnitTests.TrueSkill -{ - public static class TrueSkillCalculatorTests - { - private const double ErrorTolerance = 0.085; - - // These are the roll-up ones - - public static void TestAllTwoPlayerScenarios(SkillCalculator calculator) - { - TwoPlayerTestNotDrawn(calculator); - TwoPlayerTestDrawn(calculator); - OneOnOneMassiveUpsetDrawTest(calculator); - - TwoPlayerChessTestNotDrawn(calculator); - } - - public static void TestAllTwoTeamScenarios(SkillCalculator calculator) - { - OneOnTwoSimpleTest(calculator); - OneOnTwoDrawTest(calculator); - OneOnTwoSomewhatBalanced(calculator); - OneOnThreeDrawTest(calculator); - OneOnThreeSimpleTest(calculator); - OneOnSevenSimpleTest(calculator); - - TwoOnTwoSimpleTest(calculator); - TwoOnTwoUnbalancedDrawTest(calculator); - TwoOnTwoDrawTest(calculator); - TwoOnTwoUpsetTest(calculator); - - ThreeOnTwoTests(calculator); - - FourOnFourSimpleTest(calculator); - } - - public static void TestAllMultipleTeamScenarios(SkillCalculator calculator) - { - ThreeTeamsOfOneNotDrawn(calculator); - ThreeTeamsOfOneDrawn(calculator); - FourTeamsOfOneNotDrawn(calculator); - FiveTeamsOfOneNotDrawn(calculator); - EightTeamsOfOneDrawn(calculator); - EightTeamsOfOneUpset(calculator); - SixteenTeamsOfOneNotDrawn(calculator); - - TwoOnFourOnTwoWinDraw(calculator); - } - - public static void TestPartialPlayScenarios(SkillCalculator calculator) - { - OneOnTwoBalancedPartialPlay(calculator); - } - - //------------------- Actual Tests --------------------------- - // If you see more than 3 digits of precision in the decimal point, then the expected values calculated from - // F# RalfH's implementation with the same input. It didn't support teams, so team values all came from the - // online calculator at http://atom.research.microsoft.com/trueskill/rankcalculator.aspx - // - // All match quality expected values came from the online calculator - - // In both cases, there may be some discrepancy after the first decimal point. I think this is due to my implementation - // using slightly higher precision in GaussianDistribution. - - //------------------------------------------------------------------------------ - // Two Player Tests - //------------------------------------------------------------------------------ - - private static void TwoPlayerTestNotDrawn(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team(player1, gameInfo.DefaultRating); - var team2 = new Team(player2, gameInfo.DefaultRating); - var teams = Teams.Concat(team1, team2); - - var newRatings = calculator.CalculateNewRatings(gameInfo, teams, 1, 2); - - var player1NewRating = newRatings[player1]; - AssertRating(29.39583201999924, 7.171475587326186, player1NewRating); - - var player2NewRating = newRatings[player2]; - AssertRating(20.60416798000076, 7.171475587326186, player2NewRating); - - AssertMatchQuality(0.447, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void TwoPlayerTestDrawn(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team(player1, gameInfo.DefaultRating); - var team2 = new Team(player2, gameInfo.DefaultRating); - - var teams = Teams.Concat(team1, team2); - var newRatings = calculator.CalculateNewRatings(gameInfo, teams, 1, 1); - - var player1NewRating = newRatings[player1]; - AssertRating(25.0, 6.4575196623173081, player1NewRating); - - var player2NewRating = newRatings[player2]; - AssertRating(25.0, 6.4575196623173081, player2NewRating); - - AssertMatchQuality(0.447, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void TwoPlayerChessTestNotDrawn(SkillCalculator calculator) - { - // Inspired by a real bug :-) - var player1 = new Player(1); - var player2 = new Player(2); - var gameInfo = new GameInfo(1200.0, 1200.0 / 3.0, 200.0, 1200.0 / 300.0, 0.03); - - var team1 = new Team(player1, new Rating(1301.0007, 42.9232)); - var team2 = new Team(player2, new Rating(1188.7560, 42.5570)); - - var newRatings = calculator.CalculateNewRatings(gameInfo, Teams.Concat(team1, team2), 1, 2); - - var player1NewRating = newRatings[player1]; - AssertRating(1304.7820836053318, 42.843513887848658, player1NewRating); - - var player2NewRating = newRatings[player2]; - AssertRating(1185.0383099003536, 42.485604606897752, player2NewRating); - } - - private static void OneOnOneMassiveUpsetDrawTest(SkillCalculator calculator) - { - var player1 = new Player(1); - - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team() - .AddPlayer(player1, gameInfo.DefaultRating); - - var player2 = new Player(2); - - var team2 = new Team() - .AddPlayer(player2, new Rating(50, 12.5)); - - var teams = Teams.Concat(team1, team2); - - var newRatingsWinLose = calculator.CalculateNewRatings(gameInfo, teams, 1, 1); - - // Winners - AssertRating(31.662, 7.137, newRatingsWinLose[player1]); - - // Losers - AssertRating(35.010, 7.910, newRatingsWinLose[player2]); - - AssertMatchQuality(0.110, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - //------------------------------------------------------------------------------ - // Two Team Tests - //------------------------------------------------------------------------------ - - private static void TwoOnTwoSimpleTest(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team() - .AddPlayer(player1, gameInfo.DefaultRating) - .AddPlayer(player2, gameInfo.DefaultRating); - - var player3 = new Player(3); - var player4 = new Player(4); - - var team2 = new Team() - .AddPlayer(player3, gameInfo.DefaultRating) - .AddPlayer(player4, gameInfo.DefaultRating); - - var teams = Teams.Concat(team1, team2); - var newRatingsWinLose = calculator.CalculateNewRatings(gameInfo, teams, 1, 2); - - // Winners - AssertRating(28.108, 7.774, newRatingsWinLose[player1]); - AssertRating(28.108, 7.774, newRatingsWinLose[player2]); - - // Losers - AssertRating(21.892, 7.774, newRatingsWinLose[player3]); - AssertRating(21.892, 7.774, newRatingsWinLose[player4]); - - AssertMatchQuality(0.447, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void TwoOnTwoDrawTest(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team() - .AddPlayer(player1, gameInfo.DefaultRating) - .AddPlayer(player2, gameInfo.DefaultRating); - - var player3 = new Player(3); - var player4 = new Player(4); - - var team2 = new Team() - .AddPlayer(player3, gameInfo.DefaultRating) - .AddPlayer(player4, gameInfo.DefaultRating); - - var teams = Teams.Concat(team1, team2); - var newRatingsWinLose = calculator.CalculateNewRatings(gameInfo, teams, 1, 1); - - // Winners - AssertRating(25, 7.455, newRatingsWinLose[player1]); - AssertRating(25, 7.455, newRatingsWinLose[player2]); - - // Losers - AssertRating(25, 7.455, newRatingsWinLose[player3]); - AssertRating(25, 7.455, newRatingsWinLose[player4]); - - AssertMatchQuality(0.447, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void TwoOnTwoUnbalancedDrawTest(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team() - .AddPlayer(player1, new Rating(15, 8)) - .AddPlayer(player2, new Rating(20, 6)); - - var player3 = new Player(3); - var player4 = new Player(4); - - var team2 = new Team() - .AddPlayer(player3, new Rating(25, 4)) - .AddPlayer(player4, new Rating(30, 3)); - - var teams = Teams.Concat(team1, team2); - var newRatingsWinLose = calculator.CalculateNewRatings(gameInfo, teams, 1, 1); - - // Winners - AssertRating(21.570, 6.556, newRatingsWinLose[player1]); - AssertRating(23.696, 5.418, newRatingsWinLose[player2]); - - // Losers - AssertRating(23.357, 3.833, newRatingsWinLose[player3]); - AssertRating(29.075, 2.931, newRatingsWinLose[player4]); - - AssertMatchQuality(0.214, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void TwoOnTwoUpsetTest(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team() - .AddPlayer(player1, new Rating(20, 8)) - .AddPlayer(player2, new Rating(25, 6)); - - var player3 = new Player(3); - var player4 = new Player(4); - - var team2 = new Team() - .AddPlayer(player3, new Rating(35, 7)) - .AddPlayer(player4, new Rating(40, 5)); - - var teams = Teams.Concat(team1, team2); - var newRatingsWinLose = calculator.CalculateNewRatings(gameInfo, teams, 1, 2); - - // Winners - AssertRating(29.698, 7.008, newRatingsWinLose[player1]); - AssertRating(30.455, 5.594, newRatingsWinLose[player2]); - - // Losers - AssertRating(27.575, 6.346, newRatingsWinLose[player3]); - AssertRating(36.211, 4.768, newRatingsWinLose[player4]); - - AssertMatchQuality(0.084, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void FourOnFourSimpleTest(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - var player3 = new Player(3); - var player4 = new Player(4); - - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team() - .AddPlayer(player1, gameInfo.DefaultRating) - .AddPlayer(player2, gameInfo.DefaultRating) - .AddPlayer(player3, gameInfo.DefaultRating) - .AddPlayer(player4, gameInfo.DefaultRating); - - var player5 = new Player(5); - var player6 = new Player(6); - var player7 = new Player(7); - var player8 = new Player(8); - - var team2 = new Team() - .AddPlayer(player5, gameInfo.DefaultRating) - .AddPlayer(player6, gameInfo.DefaultRating) - .AddPlayer(player7, gameInfo.DefaultRating) - .AddPlayer(player8, gameInfo.DefaultRating); - - - var teams = Teams.Concat(team1, team2); - - var newRatingsWinLose = calculator.CalculateNewRatings(gameInfo, teams, 1, 2); - - // Winners - AssertRating(27.198, 8.059, newRatingsWinLose[player1]); - AssertRating(27.198, 8.059, newRatingsWinLose[player2]); - AssertRating(27.198, 8.059, newRatingsWinLose[player3]); - AssertRating(27.198, 8.059, newRatingsWinLose[player4]); - - // Losers - AssertRating(22.802, 8.059, newRatingsWinLose[player5]); - AssertRating(22.802, 8.059, newRatingsWinLose[player6]); - AssertRating(22.802, 8.059, newRatingsWinLose[player7]); - AssertRating(22.802, 8.059, newRatingsWinLose[player8]); - - AssertMatchQuality(0.447, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void OneOnTwoSimpleTest(SkillCalculator calculator) - { - var player1 = new Player(1); - - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team() - .AddPlayer(player1, gameInfo.DefaultRating); - - var player2 = new Player(2); - var player3 = new Player(3); - - var team2 = new Team() - .AddPlayer(player2, gameInfo.DefaultRating) - .AddPlayer(player3, gameInfo.DefaultRating); - - var teams = Teams.Concat(team1, team2); - var newRatingsWinLose = calculator.CalculateNewRatings(gameInfo, teams, 1, 2); - - // Winners - AssertRating(33.730, 7.317, newRatingsWinLose[player1]); - - // Losers - AssertRating(16.270, 7.317, newRatingsWinLose[player2]); - AssertRating(16.270, 7.317, newRatingsWinLose[player3]); - - AssertMatchQuality(0.135, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void OneOnTwoSomewhatBalanced(SkillCalculator calculator) - { - var player1 = new Player(1); - - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team() - .AddPlayer(player1, new Rating(40, 6)); - - var player2 = new Player(2); - var player3 = new Player(3); - - var team2 = new Team() - .AddPlayer(player2, new Rating(20, 7)) - .AddPlayer(player3, new Rating(25, 8)); - - var teams = Teams.Concat(team1, team2); - var newRatingsWinLose = calculator.CalculateNewRatings(gameInfo, teams, 1, 2); - - // Winners - AssertRating(42.744, 5.602, newRatingsWinLose[player1]); - - // Losers - AssertRating(16.266, 6.359, newRatingsWinLose[player2]); - AssertRating(20.123, 7.028, newRatingsWinLose[player3]); - - AssertMatchQuality(0.478, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void OneOnThreeSimpleTest(SkillCalculator calculator) - { - var player1 = new Player(1); - - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team() - .AddPlayer(player1, gameInfo.DefaultRating); - - var player2 = new Player(2); - var player3 = new Player(3); - var player4 = new Player(4); - - var team2 = new Team() - .AddPlayer(player2, gameInfo.DefaultRating) - .AddPlayer(player3, gameInfo.DefaultRating) - .AddPlayer(player4, gameInfo.DefaultRating); - - var teams = Teams.Concat(team1, team2); - var newRatingsWinLose = calculator.CalculateNewRatings(gameInfo, teams, 1, 2); - - // Winners - AssertRating(36.337, 7.527, newRatingsWinLose[player1]); - - // Losers - AssertRating(13.663, 7.527, newRatingsWinLose[player2]); - AssertRating(13.663, 7.527, newRatingsWinLose[player3]); - AssertRating(13.663, 7.527, newRatingsWinLose[player4]); - - AssertMatchQuality(0.012, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void OneOnTwoDrawTest(SkillCalculator calculator) - { - var player1 = new Player(1); - - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team() - .AddPlayer(player1, gameInfo.DefaultRating); - - var player2 = new Player(2); - var player3 = new Player(3); - - var team2 = new Team() - .AddPlayer(player2, gameInfo.DefaultRating) - .AddPlayer(player3, gameInfo.DefaultRating); - - var teams = Teams.Concat(team1, team2); - var newRatingsWinLose = calculator.CalculateNewRatings(gameInfo, teams, 1, 1); - - // Winners - AssertRating(31.660, 7.138, newRatingsWinLose[player1]); - - // Losers - AssertRating(18.340, 7.138, newRatingsWinLose[player2]); - AssertRating(18.340, 7.138, newRatingsWinLose[player3]); - - AssertMatchQuality(0.135, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void OneOnThreeDrawTest(SkillCalculator calculator) - { - var player1 = new Player(1); - - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team() - .AddPlayer(player1, gameInfo.DefaultRating); - - var player2 = new Player(2); - var player3 = new Player(3); - var player4 = new Player(4); - - var team2 = new Team() - .AddPlayer(player2, gameInfo.DefaultRating) - .AddPlayer(player3, gameInfo.DefaultRating) - .AddPlayer(player4, gameInfo.DefaultRating); - - var teams = Teams.Concat(team1, team2); - var newRatingsWinLose = calculator.CalculateNewRatings(gameInfo, teams, 1, 1); - - // Winners - AssertRating(34.990, 7.455, newRatingsWinLose[player1]); - - // Losers - AssertRating(15.010, 7.455, newRatingsWinLose[player2]); - AssertRating(15.010, 7.455, newRatingsWinLose[player3]); - AssertRating(15.010, 7.455, newRatingsWinLose[player4]); - - AssertMatchQuality(0.012, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void OneOnSevenSimpleTest(SkillCalculator calculator) - { - var player1 = new Player(1); - - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team() - .AddPlayer(player1, gameInfo.DefaultRating); - - var player2 = new Player(2); - var player3 = new Player(3); - var player4 = new Player(4); - var player5 = new Player(5); - var player6 = new Player(6); - var player7 = new Player(7); - var player8 = new Player(8); - - var team2 = new Team() - .AddPlayer(player2, gameInfo.DefaultRating) - .AddPlayer(player3, gameInfo.DefaultRating) - .AddPlayer(player4, gameInfo.DefaultRating) - .AddPlayer(player5, gameInfo.DefaultRating) - .AddPlayer(player6, gameInfo.DefaultRating) - .AddPlayer(player7, gameInfo.DefaultRating) - .AddPlayer(player8, gameInfo.DefaultRating); - - var teams = Teams.Concat(team1, team2); - var newRatingsWinLose = calculator.CalculateNewRatings(gameInfo, teams, 1, 2); - - // Winners - AssertRating(40.582, 7.917, newRatingsWinLose[player1]); - - // Losers - AssertRating(9.418, 7.917, newRatingsWinLose[player2]); - AssertRating(9.418, 7.917, newRatingsWinLose[player3]); - AssertRating(9.418, 7.917, newRatingsWinLose[player4]); - AssertRating(9.418, 7.917, newRatingsWinLose[player5]); - AssertRating(9.418, 7.917, newRatingsWinLose[player6]); - AssertRating(9.418, 7.917, newRatingsWinLose[player7]); - AssertRating(9.418, 7.917, newRatingsWinLose[player8]); - - AssertMatchQuality(0.000, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void ThreeOnTwoTests(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - var player3 = new Player(3); - - var team1 = new Team() - .AddPlayer(player1, new Rating(28, 7)) - .AddPlayer(player2, new Rating(27, 6)) - .AddPlayer(player3, new Rating(26, 5)); - - - var player4 = new Player(4); - var player5 = new Player(5); - - var team2 = new Team() - .AddPlayer(player4, new Rating(30, 4)) - .AddPlayer(player5, new Rating(31, 3)); - - var gameInfo = GameInfo.DefaultGameInfo; - - var teams = Teams.Concat(team1, team2); - var newRatingsWinLoseExpected = calculator.CalculateNewRatings(gameInfo, teams, 1, 2); - - // Winners - AssertRating(28.658, 6.770, newRatingsWinLoseExpected[player1]); - AssertRating(27.484, 5.856, newRatingsWinLoseExpected[player2]); - AssertRating(26.336, 4.917, newRatingsWinLoseExpected[player3]); - - // Losers - AssertRating(29.785, 3.958, newRatingsWinLoseExpected[player4]); - AssertRating(30.879, 2.983, newRatingsWinLoseExpected[player5]); - - var newRatingsWinLoseUpset = calculator.CalculateNewRatings(gameInfo, Teams.Concat(team1, team2), 2, 1); - - // Winners - AssertRating(32.012, 3.877, newRatingsWinLoseUpset[player4]); - AssertRating(32.132, 2.949, newRatingsWinLoseUpset[player5]); - - // Losers - AssertRating(21.840, 6.314, newRatingsWinLoseUpset[player1]); - AssertRating(22.474, 5.575, newRatingsWinLoseUpset[player2]); - AssertRating(22.857, 4.757, newRatingsWinLoseUpset[player3]); - - AssertMatchQuality(0.254, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - //------------------------------------------------------------------------------ - // Multiple Teams Tests - //------------------------------------------------------------------------------ - - private static void TwoOnFourOnTwoWinDraw(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team() - .AddPlayer(player1, new Rating(40,4)) - .AddPlayer(player2, new Rating(45,3)); - - var player3 = new Player(3); - var player4 = new Player(4); - var player5 = new Player(5); - var player6 = new Player(6); - - var team2 = new Team() - .AddPlayer(player3, new Rating(20, 7)) - .AddPlayer(player4, new Rating(19, 6)) - .AddPlayer(player5, new Rating(30, 9)) - .AddPlayer(player6, new Rating(10, 4)); - - var player7 = new Player(7); - var player8 = new Player(8); - - var team3 = new Team() - .AddPlayer(player7, new Rating(50,5)) - .AddPlayer(player8, new Rating(30,2)); - - - var teams = Teams.Concat(team1, team2, team3); - var newRatingsWinLose = calculator.CalculateNewRatings(gameInfo, teams, 1, 2, 2); - - // Winners - AssertRating(40.877, 3.840, newRatingsWinLose[player1]); - AssertRating(45.493, 2.934, newRatingsWinLose[player2]); - AssertRating(19.609, 6.396, newRatingsWinLose[player3]); - AssertRating(18.712, 5.625, newRatingsWinLose[player4]); - AssertRating(29.353, 7.673, newRatingsWinLose[player5]); - AssertRating(9.872, 3.891, newRatingsWinLose[player6]); - AssertRating(48.830, 4.590, newRatingsWinLose[player7]); - AssertRating(29.813, 1.976, newRatingsWinLose[player8]); - - AssertMatchQuality(0.367, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void ThreeTeamsOfOneNotDrawn(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - var player3 = new Player(3); - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team(player1, gameInfo.DefaultRating); - var team2 = new Team(player2, gameInfo.DefaultRating); - var team3 = new Team(player3, gameInfo.DefaultRating); - - var teams = Teams.Concat(team1, team2, team3); - var newRatings = calculator.CalculateNewRatings(gameInfo, teams, 1, 2, 3); - - var player1NewRating = newRatings[player1]; - AssertRating(31.675352419172107, 6.6559853776206905, player1NewRating); - - var player2NewRating = newRatings[player2]; - AssertRating(25.000000000003912, 6.2078966412243233, player2NewRating); - - var player3NewRating = newRatings[player3]; - AssertRating(18.324647580823971, 6.6559853776218318, player3NewRating); - - AssertMatchQuality(0.200, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void ThreeTeamsOfOneDrawn(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - var player3 = new Player(3); - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team(player1, gameInfo.DefaultRating); - var team2 = new Team(player2, gameInfo.DefaultRating); - var team3 = new Team(player3, gameInfo.DefaultRating); - - var teams = Teams.Concat(team1, team2, team3); - var newRatings = calculator.CalculateNewRatings(gameInfo, teams, 1, 1, 1); - - var player1NewRating = newRatings[player1]; - AssertRating(25.000, 5.698, player1NewRating); - - var player2NewRating = newRatings[player2]; - AssertRating(25.000, 5.695, player2NewRating); - - var player3NewRating = newRatings[player3]; - AssertRating(25.000, 5.698, player3NewRating); - - AssertMatchQuality(0.200, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void FourTeamsOfOneNotDrawn(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - var player3 = new Player(3); - var player4 = new Player(4); - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team(player1, gameInfo.DefaultRating); - var team2 = new Team(player2, gameInfo.DefaultRating); - var team3 = new Team(player3, gameInfo.DefaultRating); - var team4 = new Team(player4, gameInfo.DefaultRating); - - var teams = Teams.Concat(team1, team2, team3, team4); - - var newRatings = calculator.CalculateNewRatings(gameInfo, teams, 1, 2, 3, 4); - - var player1NewRating = newRatings[player1]; - AssertRating(33.206680965631264, 6.3481091698077057, player1NewRating); - - var player2NewRating = newRatings[player2]; - AssertRating(27.401454693843323, 5.7871629348447584, player2NewRating); - - var player3NewRating = newRatings[player3]; - AssertRating(22.598545306188374, 5.7871629348413451, player3NewRating); - - var player4NewRating = newRatings[player4]; - AssertRating(16.793319034361271, 6.3481091698144967, player4NewRating); - - AssertMatchQuality(0.089, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void FiveTeamsOfOneNotDrawn(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - var player3 = new Player(3); - var player4 = new Player(4); - var player5 = new Player(5); - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team(player1, gameInfo.DefaultRating); - var team2 = new Team(player2, gameInfo.DefaultRating); - var team3 = new Team(player3, gameInfo.DefaultRating); - var team4 = new Team(player4, gameInfo.DefaultRating); - var team5 = new Team(player5, gameInfo.DefaultRating); - - var teams = Teams.Concat(team1, team2, team3, team4, team5); - var newRatings = calculator.CalculateNewRatings(gameInfo, teams, 1, 2, 3, 4, 5); - - var player1NewRating = newRatings[player1]; - AssertRating(34.363135705841188, 6.1361528798112692, player1NewRating); - - var player2NewRating = newRatings[player2]; - AssertRating(29.058448805636779, 5.5358352402833413, player2NewRating); - - var player3NewRating = newRatings[player3]; - AssertRating(25.000000000031758, 5.4200805474429847, player3NewRating); - - var player4NewRating = newRatings[player4]; - AssertRating(20.941551194426314, 5.5358352402709672, player4NewRating); - - var player5NewRating = newRatings[player5]; - AssertRating(15.636864294158848, 6.136152879829349, player5NewRating); - - AssertMatchQuality(0.040, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void EightTeamsOfOneDrawn(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - var player3 = new Player(3); - var player4 = new Player(4); - var player5 = new Player(5); - var player6 = new Player(6); - var player7 = new Player(7); - var player8 = new Player(8); - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team(player1, gameInfo.DefaultRating); - var team2 = new Team(player2, gameInfo.DefaultRating); - var team3 = new Team(player3, gameInfo.DefaultRating); - var team4 = new Team(player4, gameInfo.DefaultRating); - var team5 = new Team(player5, gameInfo.DefaultRating); - var team6 = new Team(player6, gameInfo.DefaultRating); - var team7 = new Team(player7, gameInfo.DefaultRating); - var team8 = new Team(player8, gameInfo.DefaultRating); - - var teams = Teams.Concat(team1, team2, team3, team4, team5, team6, team7, team8); - var newRatings = calculator.CalculateNewRatings(gameInfo, teams, 1, 1, 1, 1, 1, 1, 1, 1); - - var player1NewRating = newRatings[player1]; - AssertRating(25.000, 4.592, player1NewRating); - - var player2NewRating = newRatings[player2]; - AssertRating(25.000, 4.583, player2NewRating); - - var player3NewRating = newRatings[player3]; - AssertRating(25.000, 4.576, player3NewRating); - - var player4NewRating = newRatings[player4]; - AssertRating(25.000, 4.573, player4NewRating); - - var player5NewRating = newRatings[player5]; - AssertRating(25.000, 4.573, player5NewRating); - - var player6NewRating = newRatings[player6]; - AssertRating(25.000, 4.576, player6NewRating); - - var player7NewRating = newRatings[player7]; - AssertRating(25.000, 4.583, player7NewRating); - - var player8NewRating = newRatings[player8]; - AssertRating(25.000, 4.592, player8NewRating); - - AssertMatchQuality(0.004, calculator.CalculateMatchQuality(gameInfo, teams)); - } - - private static void EightTeamsOfOneUpset(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - var player3 = new Player(3); - var player4 = new Player(4); - var player5 = new Player(5); - var player6 = new Player(6); - var player7 = new Player(7); - var player8 = new Player(8); - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team(player1, new Rating(10, 8)); - var team2 = new Team(player2, new Rating(15, 7)); - var team3 = new Team(player3, new Rating(20, 6)); - var team4 = new Team(player4, new Rating(25, 5)); - var team5 = new Team(player5, new Rating(30, 4)); - var team6 = new Team(player6, new Rating(35, 3)); - var team7 = new Team(player7, new Rating(40, 2)); - var team8 = new Team(player8, new Rating(45, 1)); - - var teams = Teams.Concat(team1, team2, team3, team4, team5, team6, team7, team8); - var newRatings = calculator.CalculateNewRatings(gameInfo, teams, 1, 2, 3, 4, 5, 6, 7, 8); - - var player1NewRating = newRatings[player1]; - AssertRating(35.135, 4.506, player1NewRating); - - var player2NewRating = newRatings[player2]; - AssertRating(32.585, 4.037, player2NewRating); - - var player3NewRating = newRatings[player3]; - AssertRating(31.329, 3.756, player3NewRating); - - var player4NewRating = newRatings[player4]; - AssertRating(30.984, 3.453, player4NewRating); - - var player5NewRating = newRatings[player5]; - AssertRating(31.751, 3.064, player5NewRating); - - var player6NewRating = newRatings[player6]; - AssertRating(34.051, 2.541, player6NewRating); - - var player7NewRating = newRatings[player7]; - AssertRating(38.263, 1.849, player7NewRating); - - var player8NewRating = newRatings[player8]; - AssertRating(44.118, 0.983, player8NewRating); - - AssertMatchQuality(0.000, calculator.CalculateMatchQuality(gameInfo,teams)); - } - - private static void SixteenTeamsOfOneNotDrawn(SkillCalculator calculator) - { - var player1 = new Player(1); - var player2 = new Player(2); - var player3 = new Player(3); - var player4 = new Player(4); - var player5 = new Player(5); - var player6 = new Player(6); - var player7 = new Player(7); - var player8 = new Player(8); - var player9 = new Player(9); - var player10 = new Player(10); - var player11 = new Player(11); - var player12 = new Player(12); - var player13 = new Player(13); - var player14 = new Player(14); - var player15 = new Player(15); - var player16 = new Player(16); - var gameInfo = GameInfo.DefaultGameInfo; - - var team1 = new Team(player1, gameInfo.DefaultRating); - var team2 = new Team(player2, gameInfo.DefaultRating); - var team3 = new Team(player3, gameInfo.DefaultRating); - var team4 = new Team(player4, gameInfo.DefaultRating); - var team5 = new Team(player5, gameInfo.DefaultRating); - var team6 = new Team(player6, gameInfo.DefaultRating); - var team7 = new Team(player7, gameInfo.DefaultRating); - var team8 = new Team(player8, gameInfo.DefaultRating); - var team9 = new Team(player9, gameInfo.DefaultRating); - var team10 = new Team(player10, gameInfo.DefaultRating); - var team11 = new Team(player11, gameInfo.DefaultRating); - var team12 = new Team(player12, gameInfo.DefaultRating); - var team13 = new Team(player13, gameInfo.DefaultRating); - var team14 = new Team(player14, gameInfo.DefaultRating); - var team15 = new Team(player15, gameInfo.DefaultRating); - var team16 = new Team(player16, gameInfo.DefaultRating); - - var newRatings = - calculator.CalculateNewRatings( - gameInfo, - Teams.Concat( - team1, team2, team3, team4, team5, - team6, team7, team8, team9, team10, - team11, team12, team13, team14, team15, - team16), - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); - - var player1NewRating = newRatings[player1]; - AssertRating(40.53945776946920, 5.27581643889050, player1NewRating); - - var player2NewRating = newRatings[player2]; - AssertRating(36.80951229454210, 4.71121217610266, player2NewRating); - - var player3NewRating = newRatings[player3]; - AssertRating(34.34726355544460, 4.52440328139991, player3NewRating); - - var player4NewRating = newRatings[player4]; - AssertRating(32.33614722608720, 4.43258628279632, player4NewRating); - - var player5NewRating = newRatings[player5]; - AssertRating(30.55048814671730, 4.38010805034365, player5NewRating); - - var player6NewRating = newRatings[player6]; - AssertRating(28.89277312234790, 4.34859291776483, player6NewRating); - - var player7NewRating = newRatings[player7]; - AssertRating(27.30952161972210, 4.33037679041216, player7NewRating); - - var player8NewRating = newRatings[player8]; - AssertRating(25.76571046519540, 4.32197078088701, player8NewRating); - - var player9NewRating = newRatings[player9]; - AssertRating(24.23428953480470, 4.32197078088703, player9NewRating); - - var player10NewRating = newRatings[player10]; - AssertRating(22.69047838027800, 4.33037679041219, player10NewRating); - - var player11NewRating = newRatings[player11]; - AssertRating(21.10722687765220, 4.34859291776488, player11NewRating); - - var player12NewRating = newRatings[player12]; - AssertRating(19.44951185328290, 4.38010805034375, player12NewRating); - - var player13NewRating = newRatings[player13]; - AssertRating(17.66385277391300, 4.43258628279643, player13NewRating); - - var player14NewRating = newRatings[player14]; - AssertRating(15.65273644455550, 4.52440328139996, player14NewRating); - - var player15NewRating = newRatings[player15]; - AssertRating(13.19048770545810, 4.71121217610273, player15NewRating); - - var player16NewRating = newRatings[player16]; - AssertRating(9.46054223053080, 5.27581643889032, player16NewRating); - } - - //------------------------------------------------------------------------------ - // Partial Play Tests - //------------------------------------------------------------------------------ - - private static void OneOnTwoBalancedPartialPlay(SkillCalculator calculator) - { - var gameInfo = GameInfo.DefaultGameInfo; - - var p1 = new Player(1); - var team1 = new Team(p1, gameInfo.DefaultRating); - - var p2 = new Player(2, 0.0); - var p3 = new Player(3, 1.00); - - var team2 = new Team() - .AddPlayer(p2, gameInfo.DefaultRating) - .AddPlayer(p3, gameInfo.DefaultRating); - - var teams = Teams.Concat(team1, team2); - var newRatings = calculator.CalculateNewRatings(gameInfo, teams, 1, 2); - var matchQuality = calculator.CalculateMatchQuality(gameInfo, teams); - - } - - //------------------------------------------------------------------------------ - // Helpers - //------------------------------------------------------------------------------ - - private static void AssertRating(double expectedMean, double expectedStandardDeviation, Rating actual) - { - Assert.AreEqual(expectedMean, actual.Mean, ErrorTolerance); - Assert.AreEqual(expectedStandardDeviation, actual.StandardDeviation, ErrorTolerance); - } - - private static void AssertMatchQuality(double expectedMatchQuality, double actualMatchQuality) - { - Assert.AreEqual(expectedMatchQuality, actualMatchQuality, 0.0005); - } - } -} \ No newline at end of file diff --git a/UnitTests/TrueSkill/TwoPlayerTrueSkillCalculatorTest.cs b/UnitTests/TrueSkill/TwoPlayerTrueSkillCalculatorTest.cs deleted file mode 100644 index 89bd38c..0000000 --- a/UnitTests/TrueSkill/TwoPlayerTrueSkillCalculatorTest.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Moserware.Skills.TrueSkill; -using NUnit.Framework; - -namespace UnitTests.TrueSkill -{ - [TestFixture] - public class TwoPlayerTrueSkillCalculatorTest - { - [Test] - public void TwoPlayerTrueSkillCalculatorTests() - { - var calculator = new TwoPlayerTrueSkillCalculator(); - - // We only support two players - TrueSkillCalculatorTests.TestAllTwoPlayerScenarios(calculator); - - // TODO: Assert failures for larger teams - } - } -} \ No newline at end of file diff --git a/UnitTests/TrueSkill/TwoTeamTrueSkillCalculatorTest.cs b/UnitTests/TrueSkill/TwoTeamTrueSkillCalculatorTest.cs deleted file mode 100644 index af334b5..0000000 --- a/UnitTests/TrueSkill/TwoTeamTrueSkillCalculatorTest.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Moserware.Skills.TrueSkill; -using NUnit.Framework; - -namespace UnitTests.TrueSkill -{ - [TestFixture] - public class TwoTeamTrueSkillCalculatorTest - { - [Test] - public void TwoTeamTrueSkillCalculatorTests() - { - var calculator = new TwoTeamTrueSkillCalculator(); - - // This calculator supports up to two teams with many players each - TrueSkillCalculatorTests.TestAllTwoPlayerScenarios(calculator); - TrueSkillCalculatorTests.TestAllTwoTeamScenarios(calculator); - } - } -} \ No newline at end of file diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj deleted file mode 100644 index ffde503..0000000 --- a/UnitTests/UnitTests.csproj +++ /dev/null @@ -1,80 +0,0 @@ - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {6F80946D-AC8B-4063-8588-96841C18BF0A} - Library - Properties - UnitTests - UnitTests - v3.5 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - 3.5 - - - 3.5 - - - 3.5 - - - - - - - - - - - - - - - - - - - - - - {15AD1345-984C-48ED-AF9A-2EAB44E5AA2B} - Skills - - - - - - - - \ No newline at end of file diff --git a/UnitTests/UnitTests.csproj.user b/UnitTests/UnitTests.csproj.user deleted file mode 100644 index 68c3597..0000000 --- a/UnitTests/UnitTests.csproj.user +++ /dev/null @@ -1,9 +0,0 @@ - - - Program - C:\Program Files (x86)\NUnit 2.5.2\bin\net-2.0\nunit.exe - - - UnitTests.dll - - \ No newline at end of file diff --git a/cmake/modules/CMakeCSharpCompiler.cmake.in b/cmake/modules/CMakeCSharpCompiler.cmake.in deleted file mode 100644 index 7d17148..0000000 --- a/cmake/modules/CMakeCSharpCompiler.cmake.in +++ /dev/null @@ -1,7 +0,0 @@ -set(CMAKE_CSharp_COMPILER "@CMAKE_CSharp_COMPILER@") -set(CMAKE_CSharp_COMPILER_LOADED @CMAKE_CSharp_COMPILER_LOADED@) -set(GAC_DIR @GAC_DIR@) - -set(_csc_default_lib_path @_csc_default_lib_path@ CACHE INTERNAL "") - -set(CMAKE_CSharp_COMPILER_ENV_VAR "CSC") diff --git a/cmake/modules/CMakeCSharpInformation.cmake b/cmake/modules/CMakeCSharpInformation.cmake deleted file mode 100644 index ecb6585..0000000 --- a/cmake/modules/CMakeCSharpInformation.cmake +++ /dev/null @@ -1,321 +0,0 @@ -# copyright (c) 2007, 2009 Arno Rehn arno@arnorehn.de -# copyright (c) 2008 Helio castro helio@kde.org -# -# Redistribution and use is allowed according to the terms of the GPL license. - -# This file adds support for the C# language to cmake. -# -# It adds the following functions: -# -# csharp_add_executable ( [UNSAFE] [WINEXE] [REFERENCES ] -# [COMPILE_FLAGS ] -# [COMPILE_DEFINITIONS ] ) -# -# csharp_add_library ( [UNSAFE] [REFERENCES ] -# [COMPILE_FLAGS ] -# [COMPILE_DEFINITIONS ] ) -# -# install_assembly ( DESTINATION -# [PACKAGE ] ) -# The assembly destination directory is only used if we compile with Visual C# and thus can't use gacutil. -# If a package is specified and a file called .pc.cmake exists in the current source directory, -# this function will configure the template file. All occurences of @assembly@ will be replaced with -# the path to the assembly. The resulting .pc file will be installed to -# /lib/pkgconfig/ . If you want to have a different basename for the template file, -# set the 'pkg-config_template_basename' property of the target with set_property. -# -# Example: -# ------------------------------ -# cmake code: -# ------------------------------ -# csharp_add_library(foo foo.cs) -# install_assembly(foo DESTINATION lib) -# -# ------------------------------ -# contents of foo.pc.cmake file: -# ------------------------------ -# Name: Foo -# Description: Foo library -# Version: 1.0 -# Libs: -r:@assembly@ - -# ----- support macros ----- -macro(GET_LIBRARY_OUTPUT_DIR var) - if (NOT LIBRARY_OUTPUT_PATH) - set(${var} ${CMAKE_CURRENT_BINARY_DIR}) - else (NOT LIBRARY_OUTPUT_PATH) - set(${var} ${LIBRARY_OUTPUT_PATH}) - endif (NOT LIBRARY_OUTPUT_PATH) -endmacro(GET_LIBRARY_OUTPUT_DIR) - -macro(GET_EXECUTABLE_OUTPUT_DIR var) - if (NOT EXECUTABLE_OUTPUT_PATH) - set(${var} ${CMAKE_CURRENT_BINARY_DIR}) - else (NOT EXECUTABLE_OUTPUT_PATH) - set(${var} ${EXECUTABLE_OUTPUT_PATH}) - endif (NOT EXECUTABLE_OUTPUT_PATH) -endmacro(GET_EXECUTABLE_OUTPUT_DIR) - -# This does just not always work... why?! -# macro(MAKE_PROPER_FILE_LIST var) -# foreach(file ${ARGN}) -# if (IS_ABSOLUTE "${file}") -# file(GLOB globbed "${file}") -# else (IS_ABSOLUTE "${file}") -# file(GLOB globbed "${CMAKE_CURRENT_SOURCE_DIR}/${file}") -# endif (IS_ABSOLUTE "${file}") -# -# foreach (glob ${globbed}) -# file(TO_NATIVE_PATH "${glob}" native) -# list(APPEND proper_file_list "${native}") -# endforeach (glob ${globbed}) -# endforeach(file ${ARGN}) -# endmacro(MAKE_PROPER_FILE_LIST) - -# ----- actual functions ----- - -# ----- add an executable ----- -function(csharp_add_executable target) - set(current "s") - set(dotnet_target "exe") - - foreach (arg ${ARGN}) - file(TO_NATIVE_PATH ${arg} native_path) - - if (arg STREQUAL "UNSAFE") - set (unsafe "/unsafe") - elseif (arg STREQUAL "WINEXE") - set (dotnet_target "winexe") - elseif (arg STREQUAL "REFERENCES") - set (current "r") - elseif (arg STREQUAL "COMPILE_FLAGS") - set (current "flags") - elseif (arg STREQUAL "COMPILE_DEFINITIONS") - set (current "defs") - else (arg STREQUAL "UNSAFE") - if (current STREQUAL "s") - # source file - list(APPEND sources ${native_path}) - elseif (current STREQUAL "r") - # reference - if (TARGET ${arg}) - # this is an existing target - get the target assembly - get_property(prop TARGET ${arg} PROPERTY _assembly) - list(APPEND references "/r:${prop}") - list(APPEND deps ${arg}) - else (TARGET ${arg}) - # something different (e.g. assembly name in the gac) - list(APPEND references "/r:${native_path}") - endif (TARGET ${arg}) - elseif (current STREQUAL "flags") - list(APPEND _csc_opts "${arg}") - elseif (current STREQUAL "defs") - list(APPEND _csc_opts "/define:${arg}") - endif (current STREQUAL "s") - endif (arg STREQUAL "UNSAFE") - endforeach (arg ${ARGN}) - - if (CMAKE_BUILD_TYPE STREQUAL "Debug") - list(APPEND _csc_opts "/define:DEBUG") - list(APPEND _csc_opts "/debug") - endif (CMAKE_BUILD_TYPE STREQUAL "Debug") - - get_executable_output_dir(outdir) - if (NOT IS_ABSOLUTE "${outdir}") - message(FATAL_ERROR "Directory \"${outdir}\" is not an absolute path!") - endif (NOT IS_ABSOLUTE "${outdir}") - - file(RELATIVE_PATH relative_path "${CMAKE_BINARY_DIR}" "${outdir}/${target}.exe") - file(TO_NATIVE_PATH "${outdir}/${target}" native_target) - - # inlined - this doesn't work as a macro :( - foreach(file ${sources}) - file(TO_CMAKE_PATH "${file}" cmake_file) - - if (IS_ABSOLUTE "${cmake_file}") - file(GLOB globbed "${cmake_file}") - else (IS_ABSOLUTE "${cmake_file}") - file(GLOB globbed "${CMAKE_CURRENT_SOURCE_DIR}/${cmake_file}") - endif (IS_ABSOLUTE "${cmake_file}") - - foreach (glob ${globbed}) - file(TO_CMAKE_PATH "${glob}" cmake_path) - list(APPEND cmake_file_list "${cmake_path}") - endforeach (glob ${globbed}) - if (NOT globbed) - list(APPEND cmake_file_list "${cmake_file}") - endif (NOT globbed) - list(APPEND compiler_file_list ${file}) - endforeach(file ${sources}) - - get_directory_property(compile_definitions COMPILE_DEFINITIONS) - foreach (def ${compile_definitions}) - # macros with values aren't supported by C# - if (NOT def MATCHES ".*=.*") - list(APPEND _csc_opts "/define:${def}") - endif (NOT def MATCHES ".*=.*") - endforeach (def ${compile_definitions}) - - get_directory_property(link_dirs LINK_DIRECTORIES) - foreach (dir ${link_dirs}) - list(APPEND _csc_opts "/lib:${dir}") - endforeach (dir ${link_dirs}) - - add_custom_command(OUTPUT "${outdir}/${target}.stubexe" - COMMAND "${CMAKE_COMMAND}" -E make_directory "${outdir}" # create the output dir - COMMAND "${CMAKE_CSharp_COMPILER}" /nologo /target:${dotnet_target} "/out:${native_target}.exe" # build the executable - ${_csc_opts} ${unsafe} ${references} ${compiler_file_list} - COMMAND "${CMAKE_COMMAND}" -E touch "${outdir}/${target}.stubexe" # create the stub so that DEPENDS will work - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" # working directory is the source directory, so we don't have to care about relative paths - DEPENDS ${cmake_file_list} - COMMENT "Building ${relative_path}" VERBATIM) # nice comment - add_custom_target(${target} ALL DEPENDS "${outdir}/${target}.stubexe" SOURCES ${cmake_file_list}) # create the actual target - if (deps) - add_dependencies(${target} ${deps}) - endif(deps) -endfunction(csharp_add_executable) - -# ----- add a library ----- -function(csharp_add_library target) - set(current "s") - - foreach (arg ${ARGN}) - file(TO_NATIVE_PATH ${arg} native_path) - - if (arg STREQUAL "UNSAFE") - set (unsafe "/unsafe") - elseif (arg STREQUAL "REFERENCES") - set (current "r") - elseif (arg STREQUAL "COMPILE_FLAGS") - set (current "flags") - elseif (arg STREQUAL "COMPILE_DEFINITIONS") - set (current "defs") - else (arg STREQUAL "UNSAFE") - if (current STREQUAL "s") - # source file - list(APPEND sources ${native_path}) - elseif (current STREQUAL "r") - # reference - if (TARGET ${arg}) - # this is an existing target - get the target assembly - get_property(prop TARGET ${arg} PROPERTY _assembly) - list(APPEND references "/r:${prop}") - list(APPEND deps ${arg}) - else (TARGET ${arg}) - # something different (e.g. assembly name in the gac) - list(APPEND references "/r:${native_path}") - endif (TARGET ${arg}) - elseif (current STREQUAL "flags") - list(APPEND _csc_opts "${arg}") - elseif (current STREQUAL "defs") - list(APPEND _csc_opts "/define:${arg}") - endif (current STREQUAL "s") - endif (arg STREQUAL "UNSAFE") - endforeach (arg ${ARGN}) - - if (CMAKE_BUILD_TYPE STREQUAL "Debug") - list(APPEND _csc_opts "/define:DEBUG") - list(APPEND _csc_opts "/debug") - endif (CMAKE_BUILD_TYPE STREQUAL "Debug") - - get_library_output_dir(outdir) - if (NOT IS_ABSOLUTE "${outdir}") - message(FATAL_ERROR "Directory \"${outdir}\" is not an absolute path!") - endif (NOT IS_ABSOLUTE "${outdir}") - - file(RELATIVE_PATH relative_path "${CMAKE_BINARY_DIR}" "${outdir}/${target}.dll") - file(TO_NATIVE_PATH "${outdir}/${target}" native_target) - - # inlined - this doesn't work as a macro :( - foreach(file ${sources}) - file(TO_CMAKE_PATH "${file}" cmake_file) - - if (IS_ABSOLUTE "${cmake_file}") - file(GLOB globbed "${cmake_file}") - else (IS_ABSOLUTE "${cmake_file}") - file(GLOB globbed "${CMAKE_CURRENT_SOURCE_DIR}/${cmake_file}") - endif (IS_ABSOLUTE "${cmake_file}") - - foreach (glob ${globbed}) - file(TO_CMAKE_PATH "${glob}" cmake_path) - list(APPEND cmake_file_list "${cmake_path}") - endforeach (glob ${globbed}) - if (NOT globbed) - list(APPEND cmake_file_list "${cmake_file}") - endif (NOT globbed) - list(APPEND compiler_file_list ${file}) - endforeach(file ${sources}) - -# message("CMake File List for target ${target}: ${cmake_file_list}") - - get_directory_property(compile_definitions COMPILE_DEFINITIONS) - foreach (def ${compile_definitions}) - # macros with values aren't supported by C# - if (NOT def MATCHES ".*=.*") - list(APPEND _csc_opts "/define:${def}") - endif (NOT def MATCHES ".*=.*") - endforeach (def ${compile_definitions}) - - get_directory_property(link_dirs LINK_DIRECTORIES) - foreach (dir ${link_dirs}) - list(APPEND _csc_opts "/lib:${dir}") - endforeach (dir ${link_dirs}) - - add_custom_command(OUTPUT "${outdir}/${target}.dll" - COMMAND "${CMAKE_COMMAND}" -E make_directory "${outdir}" # create the output dir - COMMAND "${CMAKE_CSharp_COMPILER}" /nologo /target:library "/out:${native_target}.dll" # build the executable - ${_csc_opts} ${unsafe} ${references} ${compiler_file_list} - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" # working directory is the source directory, so we don't have to care about relative paths - DEPENDS ${cmake_file_list} - COMMENT "Building ${relative_path}" VERBATIM) # nice comment - add_custom_target(${target} ALL DEPENDS "${outdir}/${target}.dll" SOURCES ${cmake_file_list}) # create the actual target - set_property(TARGET ${target} PROPERTY _assembly "${native_target}.dll") - if (deps) - add_dependencies(${target} ${deps}) - endif(deps) -endfunction(csharp_add_library) - -# ----- install a library assembly ----- -function(install_assembly target DESTINATION destination_dir) - # retrieve the absolute path of the generated assembly - get_property(filename TARGET ${target} PROPERTY _assembly) - get_property(pc_file TARGET ${target} PROPERTY pkg-config_template_basename) - if (NOT pc_file) - set (pc_file ${target}) - endif (NOT pc_file) - - if (NOT filename) - message(FATAL_ERROR "Couldn't retrieve the assembly filename for target ${target}! Are you sure the target is a .NET library assembly?") - endif (NOT filename) - - if (NOT MONO_FOUND) - install(FILES "${filename}" DESTINATION ${destination_dir}) - if (EXISTS "${filename}.config") - install(FILES "${filename}.config" DESTINATION ${destination_dir}) - endif (EXISTS "${filename}.config") - return() - endif (NOT MONO_FOUND) - - if (ARGV3 STREQUAL "PACKAGE" AND ARGV4) - set (package_option "-package ${ARGV4}") - - if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${pc_file}.pc.cmake") - set(assembly "${GAC_DIR}/${ARGV4}/${target}.dll") - configure_file ("${CMAKE_CURRENT_SOURCE_DIR}/${pc_file}.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/${pc_file}.pc") - - if (NOT LIB_INSTALL_DIR) - set (LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib) - endif (NOT LIB_INSTALL_DIR) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${pc_file}.pc" DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) - endif (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${pc_file}.pc.cmake") - - endif (ARGV3 STREQUAL "PACKAGE" AND ARGV4) - - # So we have the mono runtime and we can use gacutil (it has the -root option, which the MS version doesn't have). - install(CODE "execute_process(COMMAND ${GACUTIL_EXECUTABLE} -i ${filename} ${package_option} -root ${CMAKE_CURRENT_BINARY_DIR}/tmp_gac)") - file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/tmp_gac/mono) - file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tmp_gac/mono) - install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tmp_gac/mono/ DESTINATION ${GAC_DIR} ) -endfunction(install_assembly) - -set(CMAKE_CSharp_INFORMATION_LOADED 1) diff --git a/cmake/modules/CMakeDetermineCSharpCompiler.cmake b/cmake/modules/CMakeDetermineCSharpCompiler.cmake deleted file mode 100644 index b0fdd61..0000000 --- a/cmake/modules/CMakeDetermineCSharpCompiler.cmake +++ /dev/null @@ -1,85 +0,0 @@ -# copyright (c) 2007, 2009 Arno Rehn arno@arnorehn.de -# copyright (c) 2008 Helio castro helio@kde.org -# -# Redistribution and use is allowed according to the terms of the GPL license. - -# determine the compiler to use for C# programs -# NOTE, a generator may set CMAKE_CSharp_COMPILER before -# loading this file to force a compiler. - -if(NOT CMAKE_CSharp_COMPILER) - # prefer the environment variable CSC - if($ENV{CSC} MATCHES ".+") - if (EXISTS $ENV{CSC}) - message(STATUS "Found compiler set in environment variable CSC: $ENV{CSC}.") - set(CMAKE_CSharp_COMPILER $ENV{CSC}) - else (EXISTS $ENV{CSC}) - message(SEND_ERROR "Could not find compiler set in environment variable CSC:\n$ENV{CSC}.") - endif (EXISTS $ENV{CSC}) - endif($ENV{CSC} MATCHES ".+") - - # if no compiler has been specified yet, then look for one - if (NOT CMAKE_CSharp_COMPILER) - find_package(Mono) - set (CMAKE_CSharp_COMPILER "${GMCS_EXECUTABLE}") - - # still not found, try csc.exe - if (NOT CMAKE_CSharp_COMPILER) - get_filename_component(dotnet_path "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\.NETFramework;InstallRoot]" PATH) - find_program(CMAKE_CSharp_COMPILER NAMES csc PATHS "${dotnet_path}/Framework/v2.0.50727") - file(TO_NATIVE_PATH "${dotnet_path}/Framework/v2.0.50727" native_path) - message(STATUS "Looking for csc: ${CMAKE_CSharp_COMPILER}") - - # give up - if (NOT CMAKE_CSharp_COMPILER) - message (STATUS "Couldn't find a valid C# compiler. Set either CMake_CSharp_COMPILER or the CSC environment variable to a valid path.") - endif (NOT CMAKE_CSharp_COMPILER) - endif (NOT CMAKE_CSharp_COMPILER) - endif (NOT CMAKE_CSharp_COMPILER) - -endif(NOT CMAKE_CSharp_COMPILER) - -# now try to find the gac location -if (CMAKE_CSharp_COMPILER AND NOT GAC_DIR AND MONO_FOUND) - find_package(PkgConfig) - - if (PKG_CONFIG_FOUND) - pkg_search_module(MONO_CECIL mono-cecil) - if(MONO_CECIL_FOUND) - execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} mono-cecil --variable=assemblies_dir OUTPUT_VARIABLE GAC_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) - endif(MONO_CECIL_FOUND) - - pkg_search_module(CECIL cecil) - if(CECIL_FOUND) - execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} cecil --variable=assemblies_dir OUTPUT_VARIABLE GAC_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) - endif(CECIL_FOUND) - - if (NOT GAC_DIR) - execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} mono --variable=libdir OUTPUT_VARIABLE MONO_LIB_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) - if (MONO_LIB_DIR) - set (GAC_DIR "${MONO_LIB_DIR}/mono") - message (STATUS "Could not find cecil, guessing GAC dir from mono prefix: ${GAC_DIR}") - endif (MONO_LIB_DIR) - endif (NOT GAC_DIR) - endif (PKG_CONFIG_FOUND) - - if (NOT GAC_DIR) - set (GAC_DIR "/usr/lib/mono") - message(STATUS "Could not find cecil or mono. Using default GAC dir: ${GAC_DIR}") - endif (NOT GAC_DIR) -endif (CMAKE_CSharp_COMPILER AND NOT GAC_DIR AND MONO_FOUND) - -# Create a cache entry so the user can modify this. -set(GAC_DIR "${GAC_DIR}" CACHE PATH "Location of the GAC") -message(STATUS "Using GAC dir: ${GAC_DIR}") - -mark_as_advanced(CMAKE_CSharp_COMPILER) - -if (CMAKE_CSharp_COMPILER) - set (CMAKE_CSharp_COMPILER_LOADED 1) -endif (CMAKE_CSharp_COMPILER) - -# configure variables set in this file for fast reload later on -configure_file(${CMAKE_SOURCE_DIR}/cmake/modules/CMakeCSharpCompiler.cmake.in - ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeCSharpCompiler.cmake IMMEDIATE @ONLY) -set(CMAKE_CSharp_COMPILER_ENV_VAR "CSC") diff --git a/cmake/modules/FindMono.cmake b/cmake/modules/FindMono.cmake deleted file mode 100644 index 7a87c09..0000000 --- a/cmake/modules/FindMono.cmake +++ /dev/null @@ -1,36 +0,0 @@ -# - Try to find the mono, mcs, gmcs and gacutil -# -# defines -# -# MONO_FOUND - system has mono, mcs, gmcs and gacutil -# MONO_PATH - where to find 'mono' -# GMCS_PATH - where to find 'gmcs' -# GACUTIL_PATH - where to find 'gacutil' -# -# copyright (c) 2007 Arno Rehn arno@arnorehn.de -# -# Redistribution and use is allowed according to the terms of the GPL license. - -FIND_PROGRAM (MONO_EXECUTABLE mono) -FIND_PROGRAM (GMCS_EXECUTABLE gmcs) -FIND_PROGRAM (GACUTIL_EXECUTABLE gacutil) - -SET (MONO_FOUND FALSE CACHE INTERNAL "") - -IF (MONO_EXECUTABLE AND GMCS_EXECUTABLE AND GACUTIL_EXECUTABLE) - SET (MONO_FOUND TRUE CACHE INTERNAL "") -ENDIF (MONO_EXECUTABLE AND GMCS_EXECUTABLE AND GACUTIL_EXECUTABLE) - -IF (NOT Mono_FIND_QUIETLY) - MESSAGE(STATUS "Path of mono: ${MONO_EXECUTABLE}") - MESSAGE(STATUS "Path of gmcs: ${GMCS_EXECUTABLE}") - MESSAGE(STATUS "Path of gacutil: ${GACUTIL_EXECUTABLE}") -ENDIF (NOT Mono_FIND_QUIETLY) - -IF (NOT MONO_FOUND) - IF (Mono_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "Could not find one or more of the following programs: mono, gmcs, gacutil") - ENDIF (Mono_FIND_REQUIRED) -ENDIF (NOT MONO_FOUND) - -MARK_AS_ADVANCED(MONO_EXECUTABLE GMCS_EXECUTABLE GACUTIL_EXECUTABLE)