Last active
November 24, 2022 03:56
-
-
Save mLuby/fa23b780b71a5f1b98fe312420a1052e to your computer and use it in GitHub Desktop.
Help travellers get from one side of a bridge to another with only a lantern to guide them. (Solution is random/brute force, not exhaustive/optimal.)
This file contains 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
// Set up initial constraints. | |
GOAL_TOTAL_TIME = parseInt(process.argv.slice(2)[1]); // if optimal time unknown, set 0. | |
MAX_ATTEMPTS = parseInt(process.argv.slice(2)[2]); // I don't recommend going past 50000. | |
try { INITIAL_AT_RISK_PEOPLE = JSON.parse(process.argv.slice(2)[0]); } finally { | |
if (typeof INITIAL_AT_RISK_PEOPLE === 'undefined' || isNaN(GOAL_TOTAL_TIME) || isNaN(MAX_ATTEMPTS) || !Array.isArray(INITIAL_AT_RISK_PEOPLE)) { | |
console.log("\nInvalid command format. The command should look like this (space-sensitive):\n"); | |
console.log(" node ZombieBridge.js [1,2,5,10] 17 1000\n"); | |
return; // Return early since the command won't work. | |
} else console.log("INITIAL CONSTRAINTS:", {INITIAL_AT_RISK_PEOPLE,GOAL_TOTAL_TIME, MAX_ATTEMPTS}, "\n"); | |
} | |
// Set up variables to track the best solution we find as well as how many attempts we've tried. | |
attemptNumber = 0; | |
bestSolutionSteps = []; | |
bestTotalTimeFound = Infinity // Will decrease over time as better solutions are found. | |
while (bestTotalTimeFound > GOAL_TOTAL_TIME && attemptNumber++ < MAX_ATTEMPTS) { | |
// Whe starting a new attempt we reset the time and steps and we move people and the lantern back | |
// to their starting positions. | |
timeUsed = 0; | |
stepsUsed = [] | |
atRiskPeople = [...INITIAL_AT_RISK_PEOPLE]; | |
safePeople = []; | |
isLanternSafe = false; | |
// Keep doing the following until everybody is safe. | |
while(atRiskPeople.length > 0) { | |
// If the lantern is on the "safe" side, pick one person to walk back to the "at risk" side, | |
// otherwise pick two people to walk from the "at risk" side to the "safe" side. | |
walkers = isLanternSafe ? pick(1, safePeople) : pick(2, atRiskPeople); | |
// Add the time of the slowest walker to the total time for this solution. | |
timeUsed = timeUsed + max(walkers); | |
// Store the text representing the step (and output it) so in case it's the best solution we | |
// can show it at the end. | |
stepsUsed.push(`${pad("#"+attemptNumber, "#"+MAX_ATTEMPTS)} @ ${pad(timeUsed, GOAL_TOTAL_TIME || "999")}min: \t ${pad(atRiskPeople, INITIAL_AT_RISK_PEOPLE)} \t AT RISK \t ${isLanternSafe ? '<' : '-'}-${pad(walkers, INITIAL_AT_RISK_PEOPLE, "-")}-${isLanternSafe ? '-' : '>'} \t SAFE \t ${safePeople}`); | |
console.log(stepsUsed[stepsUsed.length-1]); | |
// Move the walker(s) and the lantern to their new side. | |
walkers.forEach(walker => (isLanternSafe ? atRiskPeople : safePeople).push(walker)); | |
isLanternSafe = !isLanternSafe; | |
} | |
// If this solution was better than the best solution we've found so far, save this one as the | |
// new best solution. | |
if (timeUsed < bestTotalTimeFound) { | |
bestTotalTimeFound = timeUsed; | |
bestSolutionSteps = stepsUsed; | |
} | |
console.log(""); // Add a carriage return/new line to visually separate each solution's steps. | |
} | |
// Now that we've either achieved the goal time or run out of attempts, output the best solution. | |
console.log("BEST SOLUTION FOUND:\n" + bestSolutionSteps.join("\n")); | |
// These are utility functions. Note that pick chooses n items _at random_. | |
function max (pair) { return Math.max(...pair); } | |
function pick (howMany, toRemoveFrom) { var results = []; while (results.length < howMany) { var indexToRemove = Math.floor(Math.random()*toRemoveFrom.length); var itemToRemove = toRemoveFrom[indexToRemove]; toRemoveFrom.splice(indexToRemove,1); results.push(itemToRemove); } return results; } | |
function pad(string, maxStr, char=" ") { while (String(string).length < String(maxStr).length) string = char + string; return string; } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example output from running
node ZombieBridge.js [1,2,5,10] 17 1000
: (also runnable online here.)