Created
April 1, 2019 03:25
-
-
Save samwhaleIV/a27e09dd25101ef9ea0aab8c2eaa7d0f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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