-
Notifications
You must be signed in to change notification settings - Fork 319
wwboy6's Solution
Event Driven, Solve all levels (Require many tries for level 13 15)
Event Driven solution (No code in update function) This method would store additional informations in "stats" of floor and elevator. A method "changeDirection" would be added to elevator. The function "searchPassenger" is a bit tricky. It is used when elevator search passenger to serve.
Say a elevator is marked as "going down", and it just stop a certain floor (just after taking passenger or deliver passenger to destination), there are 2 cases of it to search passenger:
Case I: elevator has passenger
- Search downward for nearest floor (sort them from top to bottom and get the first one) with passenger want to go down, and compare it with the destinations of the loaded passengers, to get the nearest target floor.
Case II: elevator has no passenger
- Search downward for nearest floor with passenger want to go down.
- If none, search downward for most distant floor with passenger.
- If none, mark the elevator as "going up", then search upward for nearest floor with passenger want to go up.
- If none, search upward for most distant floor with passenger.
- If none, mark it as idle
All "search" operations are done by "searchPassenger"
- It always search with the same direction of the elevator marked going to. If the elevator is marked going down, it search downward.
- Setting "lockDirection" to true means to search only for the passenger want to go with the same direction of the elevator, and it would return the nearest one. Otherwise, it return most distant passenger.
"isServedUp" and "isServedDown" indicate if an elevator is going to that floor to serve, and so the searching would skip the served passengers.
{
init: function (elevators, floors) {
const controller = this;
for (const floor of floors) {
floor.stats = { upButton: false, downButton: false, isServedUp: false, isServedDown: false };
floor.on('up_button_pressed', function() {
floor.stats.upButton = true;
controller.searchIdleLift(elevators, floor);
});
floor.on('down_button_pressed', function() {
floor.stats.downButton = true;
controller.searchIdleLift(elevators, floor);
});
}
for (const elevator of elevators) {
elevator.stats = { direction: 0, willServeDirection: 0 };
elevator.changeDirection = function (dir) {
this.stats.direction = dir;
switch (dir) {
case 0:
this.goingUpIndicator(true);
this.goingDownIndicator(true);
break;
case 1:
this.goingUpIndicator(true);
this.goingDownIndicator(false);
break;
case -1:
this.goingUpIndicator(false);
this.goingDownIndicator(true);
break;
}
};
elevator.on('idle', function() {
const pressedFloors = this.getPressedFloors();
if (pressedFloors.length === 0) {
if (this.stats.direction === 0) this.changeDirection(1);
// search passengers for current direction
if (controller.searchAndTakePassenger(this, floors, true)) return;
// search most distant passengers for current direction
if (controller.searchAndTakePassenger(this, floors)) return;
// change direction if not found
this.changeDirection(-this.stats.direction);
// search passengers for another direction
if (controller.searchAndTakePassenger(this, floors, true)) return;
// search most distant passengers for another direction
if (controller.searchAndTakePassenger(this, floors)) return;
// go idle if not found
this.changeDirection(0);
} else {
// go next getPressedFloors or searchPassenger
const toServeFloor = controller.searchPassenger(elevator, floors, true);
const toServeFloors = this.loadFactor() < 1 && toServeFloor > 0 && toServeFloor !== elevator.currentFloor() ? [ toServeFloor ] : [];
const floorIndexes = [ ...pressedFloors, ...toServeFloors ].sort((a, b) => (a - b) * this.stats.direction);
controller.serveFloor(elevator, floors[floorIndexes[0]], elevator.stats.direction);
}
});
elevator.on('stopped_at_floor', function(floorNum) {
// change direction to serve specific passengers
this.changeDirection(this.stats.willServeDirection);
// turn off floor button flag
const floor = floors[floorNum];
if (this.stats.willServeDirection === 1) {
floor.stats.upButton = false;
floor.stats.isServedUp = false;
} else {
floor.stats.downButton = false;
floor.stats.isServedDown = false;
}
});
}
},
update: function(dt, elevators, floors) {
// We normally don't need to do anything here
},
searchIdleLift: function (elevators, floor) {
for (const elevator of elevators) {
if (elevator.stats.direction === 0) {
elevator.changeDirection(Math.sign(floor.floorNum() - elevator.currentFloor()));
let serveDir;
if (floor.stats.upButton && floor.stats.downButton) {
serveDir = elevator.stats.direction;
} else {
serveDir = floor.stats.upButton ? 1 : -1;
}
this.serveFloor(elevator, floor, serveDir);
break;
}
}
},
searchPassenger: function (elevator, floors, lockDirection) {
const dir = elevator.stats.direction;
const up = !lockDirection || dir !== -1;
const down = !lockDirection || dir !== 1;
const nonemptyFloorIndexes = floors.filter(floor =>
((up && floor.stats.upButton && !floor.stats.isServedUp) || (down && floor.stats.downButton && !floor.stats.isServedDown))
&& (floor.floorNum() - elevator.currentFloor()) * dir >= 0)
.map(floor => floor.floorNum());
if (nonemptyFloorIndexes.length === 0) return -1;
// when lockDirection === false, search for most distant passengers
const sortDirection = lockDirection ? dir : -dir;
return nonemptyFloorIndexes.sort((a, b) => (a - b) * sortDirection)[0];
},
searchAndTakePassenger: function (elevator, floors, lockDirection) {
const floorIndex = this.searchPassenger(elevator, floors, lockDirection);
if (floorIndex === -1) return false;
// go and serve that floor
const floor = floors[floorIndex];
// serve for same direction first
let dir = elevator.stats.direction;
if (this.checkServeFloorWithDirection(elevator, floor, dir)) return true;
if (lockDirection) return false;
// try another direction to serve
dir = -dir;
if (this.checkServeFloorWithDirection(elevator, floor, dir)) return true;
console.error('[searchAndTakePassenger] unexpected error');
return false;
},
checkServeFloorWithDirection: function (elevator, floor, dir) {
let serveDir;
if (dir === 1 && floor.stats.upButton) {
serveDir = 1;
} else if (dir === -1 && floor.stats.downButton) {
serveDir = -1;
} else {
return false;
}
this.serveFloor(elevator, floor, serveDir);
return true;
},
serveFloor: function (elevator, floor, dir) {
elevator.stats.willServeDirection = dir;
if (dir === 1) {
floor.stats.isServedUp = true;
} else {
floor.stats.isServedDown = true;
}
elevator.goToFloor(floor.floorNum());
},
}
Play it yourself at play.elevatorsaga.com