-
-
Save tmeers/8701826 to your computer and use it in GitHub Desktop.
Get number of heats based on LaneCount and RacerCount: totalHeats | |
Add a Race per Den | |
For each Heat found above: | |
Select random racers for the lineup based on a "Chaotic-Rotation Method" | |
Each Racer must race at least 3 times | |
Each racer must race the same number of times | |
Racers should be held in a race through as many heats as possible | |
Racers cannot compete against themselves | |
Racers should compete against as many different opponents as possible | |
Given data: | |
Den (Id, Name) | |
List of Racers (RacerId, ScoutId, CarNumber, Weight) | |
Number of Lanes (4 lanes is the easiest) | |
Data to Generate | |
-Number of Heats based on Lane and Racer counts | |
3 lanes is almost 1 lane per racer, unless it's not | |
4 lanes is 1 heat per racer | |
6 lanes is the most complex though I'm sure it's simple math (that I don't know) | |
-Race | |
id | |
denId | |
-Heats | |
id | |
raceId | |
- Contestants | |
heatId | |
racerId | |
laneNumber | |
As a note: The lane count is static and won't be changed for the entire process.
Here is the main working portion of the code:
for (int i = 0; i <= (totalHeats - 1); i++)
{
var heat = new Heat();
heat.Id = heatId;
heat.RaceId = 1;
heat.Contestants = new List<Contestant>();
foreach (var lane in FillLineup(racers, heat.Id))
{
var _lane = lane;
Contestant _contestant = new Contestant();
_contestant.HeatId = heat.Id;
_contestant.Car = racers.FirstOrDefault(x => x.Id == _lane.Car).Name;
_contestant.Lane = _lane.LaneNumber;
heat.Contestants.Add(_contestant);
}
Heats.Add(heat);
heatId++;
}
This loops the number of heats and for each heat calls FillLineup(int, int)
This FillLineup does the following:
Gets a list of all racers that have not completed 4 races, ordered randomly
Fills a list of Lanes for each lane on a track.
Loops the list of lanes
Selects the first racer in the list of Racers
Checks to see how many Heats it's been in previously
If less than 4 heats, schedule the racer for the current lane
If has been in 4 heats mark the racer as finished so it can't be selected during the next heat
Return the List<Lane> to be added as contestants for the current heat
Expected data
With 4 lanes and 5 racers there should be 5 heats and each should have all 4 lanes filled, and one racer with a BYE for each heat. That racer should be in the next heat.
With 4 lanes and 6 racers there should be 6 heats and each should have all 4 lanes filled, and two racers with a BYE for each heat.
With 4 lanes and 7 racers there should be 8 heats and each should have all 4 lanes filled for 4 heats, and 3 lanes filled for the other 4, and no racers with a BYE for any heat.
With 4 lanes and 8 racers there should be 8 heats and each should have all 4 lanes filled, and no racer with a BYE for each heat.
With 4 lanes and 9 racers there should be 9 heats and each should have all 4 lanes filled, and one racer with a BYE for each heat. That racer should be in the next heat.
Here is my take on the problem:
https://github.com/pizzapanther/Race-Manager/blob/master/js/ctrl-race.js#L64-L189
I did things a little differently because I made some insights which I think could be good, but I'm not the best algorithm writer so it definitely needs improvement.
First instead of calculating heats I just make sure every racer races the correct amount of times and calculate races from that. I try to rotate them on the track with different opponents randomly. Also I noticed it is easier to calculate races if the number of racers is a multiple of the available tracks. So I fill in dummy racers to make this possible and hide them when displaying races. I also check to make sure a race has at least two real racers.
The downsides of this method is that because it is random it can fail a lot so I have to be lenient on the track requirement. Thus I allow a racer to end up on a track a max of two times.
I think I could get rid of this compromise of two times on a track if my algorithm was better. This reminds me of the 8-queens problem where you have to write an algorithm that is able to go back when a path fails. http://en.wikipedia.org/wiki/Eight_queens_puzzle
I failed that question on an Amazon interview so I guess I should have learned it now :-)
Lastly, I used Mario Kart scoring for each race which gives the 1st place racer the most points. At the end, I total everything and you get a winner.
The app is in the Chrome Store now: https://chrome.google.com/webstore/detail/race-manager/flgdapbfafoghifpocjbmhboaiikncoh
I'll probably create an Android Bundle too.
A sample console application that will generate heats for 5 racers on a 4 lane track.
This generates the output of:
You can see that not all heats are full, which they should be in this case. When you hit 4 lanes and 7 racers you start getting incomplete tracks and 8 heats. But with 5 racers and 4 lanes you should get a similar output to this: