Skip to content

swiss chris' solution

C. K. Tang edited this page May 30, 2020 · 4 revisions

Solves all levels 1 thru 18 (struggles with some) - using Version 1.6.5 of elevator saga.

I tried to imitate how I imagine elevators should work, without exploring other people's solutions or algorithms: Once going in one direction, an elevator tries to continue going in that direction.

I found there were some limitations to the elevator saga API so I used only what seemed to work as I expected (I stopped using "passing_floor" because when I would make the elevator visit that floor first, the "stopped_at_floor" or the idle event was never triggered. Also, I found that "Note that passengers will press the button again if they fail to enter an elevator" didn't behave as I would expect, leaving passengers waiting forever if a full elevator wasn't able to pick everybody up. I didn't investigate further into the reasons for these unexpected behaviors.).

Kudos to @efouts who's solution I admire for it's simplicity and effectiveness. You obviously did a better job than I did at figuring out how the API works.

And of course a huge thanks to @magwo for the elevator saga. I enjoyed it immensely.

    {
        init: function(elevators, floors) {
            var topFloor = floors.length-1;

            // kept global for easier debugging
            var upFloors = {};
            var downFloors = {};
            for(var floorNum in floors){
                upFloors[floorNum] = 0;
                downFloors[floorNum] = 0;
            }

            /////////// register events ///////////////

            for(var elevatorNum in elevators){
                var elevator = elevators[elevatorNum];
                registerEventsForElevators(elevator);

                // additional fields needed on elevator objects
                elevator.gdtUp = true; // general direction of travel. true: up, false: down
                elevator.elevatorNum = elevatorNum;
            }

            for(var floorNum in floors){
                registerUpButtonPressed(floors[floorNum]);
                registerDownButtonPressed(floors[floorNum]);
            }

            function registerEventsForElevators(elevator){
                elevator.on("idle", function() {

                    // 1. 
                    clearOrAddCurrentFloorWaiting(elevator);
                    // 2. and 3. 
                    setNextDestinationInGDT(elevator, nextFloorInGDT(elevator));
                    // 
                    if(elevator.destinationQueue.length === 0){
                        reverseGdt(elevator);
                        // 4. and 5.
                        setNextDestinationInGDT(elevator, elevator.currentFloor());
                    }
                    // 6. if nothing to do, return to floor 0
                    if(elevator.destinationQueue.length === 0){
                        goToFloor(elevator, 0);
                        elevator.gdtUp = false; // we don't really need this, do we?
                    }

                    // purely for good form, set indicator lights (in case we changed direction last minute)
                    var nextStop = elevator.destinationQueue[0];
                    var currentFloor = elevator.currentFloor();
                    if(nextStop > currentFloor){
                        setOneDirectionIndicator(elevator, true);
                    } else if (nextStop < currentFloor){
                        setOneDirectionIndicator(elevator, false);
                    } 
                });
                
                elevator.on("stopped_at_floor", function(floorNum) {
                    setDirectionIndicatorWhenStoppedAtFloor(elevator);
                });
            }

            function registerUpButtonPressed(floor){
                floor.on("up_button_pressed", function() { 
                    addEntryFloor(upFloors, floor.floorNum());
                });
            }

            function registerDownButtonPressed(floor){
                floor.on("down_button_pressed", function() {
                    addEntryFloor(downFloors, floor.floorNum());
                });
            }

            ////////////// called functions ///////////////

            function addEntryFloor(floorMap, floorNum){
                floorMap[floorNum]++;
            }

            function goToFloor(elevator, floorNum){
                elevator.goToFloor(floorNum);
            }

            function clearUpFloor(floorNum){
                upFloors[floorNum] = 0;
            }

            function clearDownFloor(floorNum){
                downFloors[floorNum] = 0;
            }

            function setOneDirectionIndicator(elevator, gdtUp){
                setGoingUpIndicator(elevator, gdtUp);
                setGoingDownIndicator(elevator, !gdtUp);
            }

            function setGoingUpIndicator(elevator, goingUp){
                elevator.goingUpIndicator(goingUp);
            }

            function setGoingDownIndicator(elevator, goingDown){
                elevator.goingDownIndicator(goingDown);
            }

            function isFloorSelected(floorMap, floorNum){
                return floorMap[floorNum] !== 0;
            }

            function isElevatorFull(elevator){
                var loadFactor = elevator.loadFactor();
                var maxPassengerCount = elevator.maxPassengerCount();
                var neededAvgPassengerSpace = 1.5;
                // e.g. loadFactor = 0.91 and max 10 passengers and neededAvgPassengerSpace = 1:
                // (1-0.91) = 0.09 < (1/10) = 0.1 so elevatorFull;
                var elevatorFull = (1-loadFactor) < (neededAvgPassengerSpace/maxPassengerCount)
                return elevatorFull;
            }

            function setDirectionIndicatorWhenStoppedAtFloor(elevator){
                var currentFloor = elevator.currentFloor();

                if(currentFloor === 0){
                    elevator.gdtUp = true;
                } else if(currentFloor === topFloor){
                    elevator.gdtUp = false;
                } else {
                   // else keep gdtUp
                }

                setOneDirectionIndicator(elevator, elevator.gdtUp);
            }

            function clearOrAddCurrentFloorWaiting(elevator){
                var floorNum = elevator.currentFloor();

                if(isElevatorFull(elevator)){
                    // keep entry waiting flag set for this floor in this direction
                    if(elevator.gdtUp){
                        addEntryFloor(upFloors, floorNum);
                    } else {
                        addEntryFloor(downFloors, floorNum);
                    }
                } else {
                    clearEntryWaiting(elevator.gdtUp, [floorNum]);
                }
            }

            function clearEntryWaiting(gdtUp, floorNums){
                for(var i in floorNums){
                    var floorNum = floorNums[i];

                    if(floorNum === 0){
                        clearUpFloor(floorNum);
                    } else if (floorNum === topFloor){
                        clearDownFloor(floorNum);
                    } else if(gdtUp){
                        clearUpFloor(floorNum);
                    } else{
                        clearDownFloor(floorNum);
                    }                    
                }
            }

            function setNextDestinationInGDT(elevator, searchFrom){
                if(elevator.gdtUp){
                    setNextDestinationAbove(elevator, searchFrom);
                } else {
                    setNextDestinationBelow(elevator, searchFrom);
                }
            }

            function setNextDestinationAbove(elevator, searchFrom){
                var nextDestination = -1;
                var entryFloorOppositeDirection = -1;
                var pressedFloors = elevator.getPressedFloors();

                for (var floorNum = searchFrom; floorNum < floors.length; floorNum++) {
                    if(pressedFloors.indexOf(floorNum) !== -1){
                        clearEntryWaiting(true, [floorNum]);
                        nextDestination = floorNum;
                        break;
                    } 
                    if(!isElevatorFull(elevator)){
                        if(isFloorSelected(upFloors, floorNum)){
                            clearEntryWaiting(true, [floorNum]);
                            nextDestination = floorNum;
                            break;
                        }
                        if(isFloorSelected(downFloors, floorNum)){
                            entryFloorOppositeDirection = floorNum;
                        }
                    }
                };

                if(nextDestination === -1){
                    if(entryFloorOppositeDirection !== -1){
                        clearEntryWaiting(false, [entryFloorOppositeDirection]);
                        reverseGdt(elevator);
                        nextDestination = entryFloorOppositeDirection;
                    }
                }

                if(nextDestination !== -1){
                    goToFloor(elevator, nextDestination);
                }
            }

            function setNextDestinationBelow(elevator, searchFrom){
                var nextDestination = -1;
                var entryFloorOppositeDirection = -1;
                var pressedFloors = elevator.getPressedFloors();

                for (var floorNum = searchFrom; floorNum >= 0; floorNum--) {
                    if(pressedFloors.indexOf(floorNum) !== -1){
                        clearEntryWaiting(false, [floorNum]);
                        nextDestination = floorNum;
                        break;
                    } 
                    if(!isElevatorFull(elevator)){
                        if(isFloorSelected(downFloors, floorNum)){
                            clearEntryWaiting(false, [floorNum]);
                            nextDestination = floorNum;
                            break;
                        }
                        if(isFloorSelected(upFloors, floorNum)){
                            entryFloorOppositeDirection = floorNum;
                        }
                    }
                };

                if(nextDestination === -1){
                    if(entryFloorOppositeDirection !== -1){
                        clearEntryWaiting(true, [entryFloorOppositeDirection]);
                        reverseGdt(elevator);
                        nextDestination = entryFloorOppositeDirection;
                    }
                }

                if(nextDestination !== -1){
                    goToFloor(elevator, nextDestination);
                }
            }

            function nextFloorInGDT(elevator){
                if(elevator.gdtUp){
                    return elevator.currentFloor()+1;
                } else {
                    return elevator.currentFloor()-1;
                }
            }

            function reverseGdt(elevator){
                elevator.gdtUp = !elevator.gdtUp;            
            }
        },

        update: function(dt, elevators, floors) {
            // We normally don't need to do anything here
        }
    }
Clone this wiki locally