Skip to content
Kay Chan edited this page Nov 5, 2020 · 1 revision

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());
  },
}
Clone this wiki locally