Skip to content

Instantly share code, notes, and snippets.

@maydaytx
Last active July 30, 2019 18:20
Show Gist options
  • Save maydaytx/53c24cb86d82b226c00e to your computer and use it in GitHub Desktop.
Save maydaytx/53c24cb86d82b226c00e to your computer and use it in GitHub Desktop.
Randomly select bowlers to exclude each week
void Main()
{
const int weeks = 12;
const int bowlersPerWeek = 4;
var bowlers = new[]
{
new Bowler("Bob", 5, 8),
new Bowler("Ben", 2),
new Bowler("Janna", 5),
new Bowler("Jonny", 0, 8),
new Bowler("Jeremy", 0, 3),
new Bowler("Patrick", 3)
};
var maximumNumberOfWeeksPerBowler = (int) Math.Ceiling(1m * weeks * bowlersPerWeek / bowlers.Length);
do
{
GenerateSchedule(weeks, bowlersPerWeek, maximumNumberOfWeeksPerBowler, bowlers);
} while (bowlers.Any(x => x.Schedule.Count(y => y.Value) > maximumNumberOfWeeksPerBowler));
bowlers.Select(x => x.Schedule.Count(y => y.Value)).ToArray().Dump();
var schedule = new string[weeks, bowlers.Length];
for (var i = 0; i < weeks; ++i)
for (var j = 0; j < bowlers.Length; ++j)
schedule[i, j] = bowlers[j].Schedule.Get(i) == true ? "x" : "";
schedule.Dump();
}
private static void GenerateSchedule(int weeks, int bowlersPerWeek, int maximumNumberOfWeeksPerBowler, IReadOnlyCollection<Bowler> bowlers)
{
foreach (var bowler in bowlers)
bowler.Schedule.Clear();
var bowlersToExcludePerWeek = bowlers.Count - bowlersPerWeek;
var random = new Random();
for (var week = 0; week < weeks; ++week)
{
var bowlerWeights = bowlers
.Select(x => new {Bowler = x, ExclusionWeight = x.GetExclusionWeight(week, maximumNumberOfWeeksPerBowler)})
.ToList();
for (var count = 0; count < bowlersToExcludePerWeek; ++count)
{
int index;
if (bowlerWeights.Count == 1)
{
index = 0;
}
else
{
var totalWeight = bowlerWeights.Sum(x => x.ExclusionWeight);
var runningWeight = 0d;
var cumulativeWeights = bowlerWeights
.Select(x => runningWeight += (x.ExclusionWeight / totalWeight))
.ToList();
index = cumulativeWeights.BinarySearch(random.NextDouble());
if (index < 0)
index = (index * -1) - 1;
}
bowlerWeights[index].Bowler.Schedule.Add(week, false);
bowlerWeights.RemoveAt(index);
}
foreach (var bowler in bowlerWeights.Select(x => x.Bowler))
bowler.Schedule.Add(week, true);
}
}
private class Bowler
{
public string Name { get; }
public CoolDictionary<int, bool> Schedule { get; } = new CoolDictionary<int, bool>();
private readonly int[] _excludedWeeks;
public Bowler(string name, params int[] excludedWeeks)
{
Name = name;
_excludedWeeks = excludedWeeks;
}
public double GetExclusionWeight(int week, int maximumNumberOfWeeksPerBowler)
{
var bowlerWeeks = Schedule.Count(x => x.Value);
if (_excludedWeeks.Contains(week))
return 100000;
if (bowlerWeeks == maximumNumberOfWeeksPerBowler)
return 500;
if (Schedule.Get(week - 1) == false)
{
if (Schedule.Get(week - 2) == true)
return maximumNumberOfWeeksPerBowler * 2; //prioritize two weeks off in a row
if (Schedule.Get(week - 3) == false)
return 1 + bowlerWeeks; //de-prioritize >2 weeks off in a row
}
return 1 + bowlerWeeks * 2;
}
}
private class CoolDictionary<T, V> : Dictionary<T, V> where V : struct
{
public V? Get(T key)
{
if (ContainsKey(key))
return this[key];
return null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment