Skip to content

Instantly share code, notes, and snippets.

@samwhaleIV
Created April 1, 2019 03:25
Show Gist options
  • Save samwhaleIV/a27e09dd25101ef9ea0aab8c2eaa7d0f to your computer and use it in GitHub Desktop.
Save samwhaleIV/a27e09dd25101ef9ea0aab8c2eaa7d0f to your computer and use it in GitHub Desktop.
const topRacePositions = 3;
const maxHorseRaceCount = 5;
const horseCount = 25;
/*
The numberical constants are very hard-coded into this problem and the algorithm itself.
It could be reworked into making it work with other numbers of horses,
but the requirement of 25 is that 5 horses per race is also the
the square root of 25 in the first place.
The way the algorithm is currently processed is not very dynamic, but I imagine
that it could be updated to support other arities of horses;
Currently there is a 4 line block or so that's repeated with incrementally smaller chunks to narrow
the horse candidates down from 25 to 3, but this arrangement is specifically for 25.
*/
const getHorses = function horseGeneration(horseCount) {
//This function generates named horses with internal speed values that we can use for the problem.
const horseNames = [
"Jim","Sally","David","Maurice","Jack",
"Amanda","Alexa","Trisha","Henry","Harry",
"Bob","Billy","Bryan","Julia","Justine",
"Dino","Dina","Donald","Ronald","Emily",
"George","Edward","Isabelle","Eric","Brendon"
];
const speeds = [];
for(let i = 0;i<25;i++) {
speeds.push(i+1);
}
const horses = {};
for(let i = 0;i<horseCount;i++) {
const name = horseNames.splice(Math.floor(Math.random() * horseNames.length),1)[0];
horses[name] = {
name: name,
speed: speeds.splice(Math.floor(Math.random() * speeds.length),1)[0]
}
}
return horses;
}
const getFastestThreeHorses = function horseSpeedEvaluation(horses) {
//This is the cheating version of the estimation function - only this one can read the horses' speeds.
//(It's a bit more verbose than it needs to be for JS, but I thought the pattern was fun to write by hand, and fast, too)
let f1={speed:-1}, f2={speed:-1}, f3={speed:-1};
for(let key in horses) {
const horse = horses[key];
if(horse.speed > f1.speed) {
f3 = f2;
f2 = f1;
f1 = horse;
} else if(horse.speed > f2.speed) {
f3 = f2;
f2 = horse;
} else if(horse.speed > f3.speed) {
f3 = horse;
}
}
return [f1,f2,f3];
}
//This is used to run a race of 5 horses. The speed is not reported back - the horses' speeds are used to calcuate a FIFO race order. 1st, 2nd, 3rd, etc.
const fireTheStartingPistol = horses => horses.sort((a,b) => b.speed - a.speed);
//This is used to test the true horse values with the calculated ones.
const compareHorseResults = function(solvedResults,estimatedResults,log=false) {
if(solvedResults.length !== estimatedResults.length) {
console.error("There is a mismatch in length between solved results and estimated results.");
return false;
}
for(let i = 0;i<solvedResults.length;i++) {
if(solvedResults[i].name !== estimatedResults[i].name) {
if(log) {
console.log("The estimated results do not match the solved results.");
}
return false;
}
}
if(log) {
console.log("The estimated results are the same as the solved results!");
}
return true;
}
const aggregateRaceResult = function(horse,lookupType,horseManifest) {
for(let key in horse[lookupType]) {
const nextHorse = horseManifest[key];
aggregateRaceResult(nextHorse,lookupType,horseManifest);
for(let key in nextHorse[lookupType]) {
horse[lookupType][key] = true;
}
}
}
const aggregateRaceResults = function(horseList,horseManifest) {
horseList.forEach(horse => {
aggregateRaceResult(horse,"fasterThan",horseManifest);
aggregateRaceResult(horse,"slowerThan",horseManifest);
});
}
const slowestHorseFilter = function(horse) {
return Object.entries(horse.slowerThan).length < topRacePositions;
}
const highestFasterThanComparison = function(a,b) {
return Object.entries(b.fasterThan).length-Object.entries(a.fasterThan).length;
}
const processRaceResult = function(raceResult) {
for(let i2 = 0;i2<raceResult.length;i2++) {
const fastHorse = raceResult[i2];
for(let i3 = i2+1;i3<raceResult.length;i3++) {
const slowerHorse = raceResult[i3];
fastHorse.fasterThan[slowerHorse.name] = true;
slowerHorse.slowerThan[fastHorse.name] = true;
}
}
}
const simulateHorseRaces = function(horseManifest) {
const allHorses = [];
for(let key in horseManifest) {
const horse = horseManifest[key];
horse.fasterThan = {};
horse.slowerThan = {};
allHorses.push(horse);
}
for(let i = 0;i<maxHorseRaceCount;i++) {
const raceHorseStartIndex = i*maxHorseRaceCount;
const raceResult = fireTheStartingPistol(
allHorses.slice(raceHorseStartIndex,raceHorseStartIndex+maxHorseRaceCount)
);
processRaceResult(raceResult);
}
aggregateRaceResults(allHorses,horseManifest);
allHorses.sort(highestFasterThanComparison);
const top15Horses = allHorses.filter(slowestHorseFilter);
const top5Race = fireTheStartingPistol(top15Horses.slice(0,maxHorseRaceCount));
processRaceResult(top5Race);
aggregateRaceResults(top15Horses,horseManifest);
top15Horses.sort(highestFasterThanComparison);
const top6Horses = top15Horses.filter(slowestHorseFilter);
const bottom5Race = fireTheStartingPistol(top6Horses.slice(1));
processRaceResult(bottom5Race);
aggregateRaceResults(top6Horses,horseManifest);
top6Horses.sort(highestFasterThanComparison);
top3Horses = top6Horses.filter(slowestHorseFilter);
return top3Horses;
}
const tests = 1000;
let passed = true;
for(let i = 0;i<tests;i++) {
const raceHorses = getHorses(horseCount);
const solvedHorseResults = getFastestThreeHorses(raceHorses);
const estimatedResults = simulateHorseRaces(raceHorses);
if(!compareHorseResults(solvedHorseResults,estimatedResults)) {
console.log("BEGIN ALL HORSES");
console.log(raceHorses);
console.log("BEGIN ESTIMATED RESULTS");
console.log(estimatedResults);
console.log("BEGIN SOLVED RESULTS");
console.log(solvedResults);
console.log("END ALL");
passed = false;
}
}
if(passed) {
console.log(`The test passed ${tests} iteration${tests !== 1 ? "s" : ""} without failure!`);
} else {
console.log("The above set of race horses failed the estimation heuristic!");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment