Example #1
0
        /// <summary>
        /// Calculates the winning <see cref="Runner"/>.
        /// </summary>
        /// <returns></returns>
        public Runner RunRace()
        {
            if (!_canRunRace || _raceMargin == 0)
            {
                return(null);
            }

            // Calculate the chances of each of the runners winning, based on their margin.
            var runnersAndChances = Runners.Select(r => new { Runner = r, Chances = r.GetMargin() / _raceMargin });

            // Now, randomize the runners' chances using a randomization margin.
            var runnersAndRanomizedChances = runnersAndChances.Select(r => new { Runner = r.Runner, Chances = r.Chances + Convert.ToDecimal(_random.NextDouble() * 2 - 1) * _randomMargin });

            // Re-normalize the chances so that they stack up to 100%.
            Decimal totalRandomizedChances = runnersAndRanomizedChances.Sum(r => r.Chances);
            var     runnersAndNormalizedRandomizedChances = runnersAndRanomizedChances
                                                            .Select(r => new { Runner = r.Runner, Chances = r.Chances / totalRandomizedChances })
                                                            .ToList();

            // Calculate a random outcome for the race.
            Decimal raceOutcome = Convert.ToDecimal(_random.NextDouble());

            // Now, iterate over the runners to see if their chances of winning covers the random race outcome.
            Decimal cumulativeChances = 0M;

            foreach (var runnerChances in runnersAndNormalizedRandomizedChances.OrderBy(r => r.Chances))
            {
                Decimal runnerMinChance = cumulativeChances;
                Decimal runnerMaxChance = cumulativeChances + runnerChances.Chances;


                if (raceOutcome > runnerMinChance && raceOutcome <= runnerMaxChance)
                {
                    // If the runners chances covers the outcome, we have a winner.
                    // Note that this case covers 1.
                    return(runnerChances.Runner);
                }
                else if (cumulativeChances == 0 && raceOutcome == 0)
                {
                    // If the race outcome was zero and this is the first runner, then this one wins.
                    return(runnerChances.Runner);
                }

                // If we didn't find a winner, increase the cumulative chances and continue.
                cumulativeChances += runnerChances.Chances;
            }

            // If we reach this point, its likely that the runnerMaxChance was still less than
            // the race outcome due to accuracy issues. If this is the case, then last runner
            // would have been the winner.
            return(runnersAndNormalizedRandomizedChances.OrderBy(r => r.Chances).Last().Runner);
        }