Created
December 11, 2020 18:17
-
-
Save johnorourke/d20878e4745eedc29b1b8c097e91e506 to your computer and use it in GitHub Desktop.
Elevator fun
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
const o = { | |
init: function(lifts, floors) { | |
/* | |
aim: get a lift to waiting people quickly, get people to their floor quickly | |
get a lift to waiting people quickly: | |
- nearest lift | |
- which isnt full | |
- already got this floor in its queue | |
- floors in its queue which are in the direction the user wants to go | |
= lowest value = select that lift | |
get people ot their floor quickly | |
- go to nearest floor whose button has been pressed | |
- stay in the current direction of travel as long as you have destinations that way | |
*/ | |
const topFloor = floors.length; | |
// actual floor buttons pressed: | |
const liftFloorQueue = lifts.map(() => []); | |
// current direction: | |
const liftDirections = lifts.map(() => 0); // 1 = up, -1 = down, 0 = idle | |
// floors whose up buttons are pressed: | |
const upButtons = floors.map(() => 0); | |
// floors whose down buttons are preseed: | |
const downButtons = floors.map(() => 0); | |
const weighting = { | |
nearness: 0.5, // the value is no. of floors | |
fullness: 1.5, // the value is 0 to 1 | |
inQueueNess: 1.5, // the value is how far down the queue my floor is | |
goingInDirectionNess: 0.5, // could be very high - eg. if 3 floors queued in wrong direction, value = 3+2+1=6: the value is higher if we have floors to visit in the wrong direction, which are closer to the front of the queue | |
} | |
function chooseLiftToCall(floorNum,direction) { | |
// direction: 1 for up, -1 for down | |
let currentWeight = 1000; // lower weight is better | |
let chosenLift = 0; | |
for(let liftNum in lifts) { | |
const lift = lifts[liftNum]; | |
const queue = [lift.currentFloor()].concat(lift.getPressedFloors()); | |
// weight for nearness: | |
const nearness = Math.abs(lift.currentFloor() - floorNum); | |
// weight for fullness: | |
const fullness = lift.loadFactor(); | |
// weight for this floor already in this lift's queue, higher for further along in queue | |
const inQueueNess = queue.indexOf(floorNum); | |
// weight for floors in queue in the direction user wants to go: | |
// (eg. if lift is on 2, going to 0, and user wants to go DOWN from 1, this is higher | |
let queueLen = queue.length; | |
const goingInDirectionNess = queue.reduce((weight,f)=>{ | |
// could reduce the weight added as we get further down the queue | |
weight += (Math.sign(f - lift.currentFloor()) == direction) ? 0 : queueLen; // add reversed queue position for each floor in wrong direction | |
// eg. if we have a queue of 5 destinations, queueLen goes 5 to 1, and positions 4 and 5 are in the wrong direction, we add 2 and 1 = 3 | |
queueLen--; | |
}, 0); | |
thisWeight = | |
(weighting.nearness * nearness) + | |
(weighting.fullness * fullness) + | |
(weighting.inQueueNess * inQueueNess) + | |
(weighting.goingInDirectionNess * goingInDirectionNess); | |
if(thisWeight < currentWeight) { | |
chosenLift = liftNum; | |
currentWeight = thisWeight; | |
} | |
} | |
return chosenLift; | |
} | |
function getQueue(lift, liftNum) { | |
let fullQueue = []; | |
// first see if we're the best lift for any open calls: | |
upButtons.forEach((button, floorNum) => { | |
if(button && floorNum != lift.currentFloor() && chooseLiftToCall(floorNum, liftDirections[liftNum]) == liftNum) { | |
fullQueue.push(floorNum); | |
} | |
}); | |
downButtons.forEach((button, floorNum) => { | |
if(button && floorNum != lift.currentFloor() && chooseLiftToCall(floorNum, liftDirections[liftNum]) == liftNum) { | |
fullQueue.push(floorNum); | |
} | |
}); | |
fullQueue = fullQueue.concat(lift.getPressedFloors()); // ignore the queue! | |
return fullQueue; | |
} | |
function chooseNextFloor(lift, liftNum, enqueue = false) { | |
// see what's motivating this lift, and how strong the motivations are... | |
console.log('choosing floor for lift '+liftNum); | |
let fullQueue = getQueue(lift, liftNum); | |
// add the current queue to the floor(s) that are calling us: | |
upwardsFloors = fullQueue.filter(floorNum => floorNum > lift.currentFloor()).sort((a,b) => a - b); | |
downwardsFloors = fullQueue.filter(floorNum => floorNum < lift.currentFloor()).sort((a,b) => b - a); | |
if(liftDirections[liftNum] > 0 && upwardsFloors.length > 0) { | |
fullQueue = upwardsFloors.concat(downwardsFloors); | |
} else if(liftDirections[liftNum] < 0 && downwardsFloors.length > 0) { | |
fullQueue = downwardsFloors.concat(upwardsFloors); | |
} else { | |
if(upwardsFloors.length >= downwardsFloors.length) { | |
fullQueue = upwardsFloors.concat(downwardsFloors); | |
liftDirections[liftNum] = 1; | |
} else { | |
fullQueue = downwardsFloors.concat(upwardsFloors); | |
liftDirections[liftNum] = -1; | |
} | |
} | |
if(fullQueue.length == 0) { | |
liftDirections[liftNum] = 0; | |
} | |
console.log(`lift ${liftNum} going dir=${liftDirections[liftNum]}, up to ${upwardsFloors}, down to ${downwardsFloors}`); | |
if(fullQueue.length > 0) { | |
if(enqueue) { | |
lift.goToFloor(fullQueue[0]); | |
} else { | |
lift.destinationQueue = [fullQueue[0]]; | |
lift.checkDestinationQueue(); | |
} | |
} | |
lift.goingUpIndicator(liftDirections[liftNum] >= 0); | |
lift.goingDownIndicator(liftDirections[liftNum] <= 0); | |
console.log(`lift ${liftNum} has new queue ${lift.destinationQueue} and direction ${liftDirections[liftNum]}`); | |
} | |
lifts.forEach((lift, liftNum) => { | |
console.log(Date.now() + ' initialising lift ' + liftNum); | |
lift.on("idle", () => { | |
console.log(`${Date.now()} lift ${liftNum} is idle on floor ${lift.currentFloor()}`); | |
chooseNextFloor(lift, liftNum) | |
}); | |
lift.on("stopped_at_floor", (floorNum) => { | |
console.log(`${Date.now()} lift ${liftNum} just stopped at ${floorNum}, cf=${lift.currentFloor()}`); | |
// optimise the floor queue and choose next floor - queue it, don't overwrite the queue | |
chooseNextFloor(lift, liftNum, true); | |
// clear downButtons or upButtons[floorNum] = false; depending on direction! | |
if(liftDirections[liftNum] == 1) { | |
upButtons[floorNum] = false; | |
} | |
if(liftDirections[liftNum] == -1) { | |
downButtons[floorNum] = false; | |
} | |
}); | |
lift.on("floor_button_pressed", floorNum => { | |
console.log(Date.now() + ' lift '+liftNum+' floor '+floorNum+' was pressed'); | |
liftFloorQueue[liftNum].push(floorNum); // queue it up | |
// optimise the floor queue and set destination: | |
chooseNextFloor(lift, liftNum); | |
}); | |
lift.on("passing_floor", (floorNum, directionName) => { | |
const direction = directionName == "up" ? 1 : -1; | |
console.log(`${Date.now()} lift ${liftNum} about to pass ${floorNum}, cf=${lift.currentFloor()} going ${directionName}`); | |
// decide whether to stop or not | |
const floorButtons = direction == 1 ? upButtons : downButtons; | |
if(floorButtons[floorNum]) { | |
// re-calculate optimal next floor, in case it's this one: | |
chooseNextFloor(lift, liftNum); | |
} | |
}); | |
}); | |
floors.forEach(f => { | |
f.on("up_button_pressed", () => { | |
upButtons[f.floorNum()] = true; | |
selectedLift = chooseLiftToCall(f.floorNum(), 1); // will get called twice - could be optimised | |
chooseNextFloor(lifts[selectedLift], selectedLift, f.floorNum()); | |
console.log(Date.now() + ' floor '+f.floorNum()+' wants to go up - using ' + selectedLift); | |
}); | |
f.on("down_button_pressed", () => { | |
downButtons[f.floorNum()] = true; | |
selectedLift = chooseLiftToCall(f.floorNum(), -1); // will get called twice - could be optimised | |
chooseNextFloor(lifts[selectedLift], selectedLift, f.floorNum()); | |
console.log(Date.now() + ' floor '+f.floorNum()+' wants to go down - using ' + selectedLift); | |
}); | |
}); | |
}, | |
update: function(dt, elevators, floors) { | |
// We normally don't need to do anything here | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment