Skip to content

Instantly share code, notes, and snippets.

@natafaye
Created February 10, 2022 02:45
Show Gist options
  • Save natafaye/89d3128c8cf535d05d59fc4b54aecef4 to your computer and use it in GitHub Desktop.
Save natafaye/89d3128c8cf535d05d59fc4b54aecef4 to your computer and use it in GitHub Desktop.
/*
*
*
* This code was written specifically for a CodeWars kata, and is different than the code
* we worked through in class because it is designed for a different purpose.
* It shows that there is not one right way to break things into classes/objects (or functions)
* it depends on what your goals are and what you need it to do!
*
* Also, this isn't doctrine on how to solve this problem,
* it's just a solution to this CodeWars kata that I made, using classes, for fun :)
* https://www.codewars.com/kata/58905bfa1decb981da00009e
*
*
*/
/********** BUILDING **********/
class Building {
constructor(queues) {
this.queues = queues; // an array of arrays of numbers (passengers)
}
/* Is there no one calling the elevator */
get isEmpty() { return !this.queues.some(q => q.length) }
/* Top floor with someone calling the elevator */
get topCallingFloor() {
let top = this.queues.length - 1 - this.queues.slice().reverse().findIndex(q => q.length)
if(top >= this.queues.length) top = -1;
return top;
}
/* Bottom floor with someone calling the elevator */
get bottomCallingFloor() {
let bottom = this.queues.findIndex(q => q.length);
if(bottom < 0) bottom = this.queues.length;
return bottom;
}
/* Checks if a floor has anyone calling the elevator in that direction */
isCalling(floor, goingUp, canSwitchDirection) {
return this.queues[floor].some(p => canSwitchDirection || p > floor === goingUp);
}
/* Takes an amount of passengers from the floor that are going in that direction */
getPassengers(floor, amount, goingUp, canSwitchDirection) {
const queue = this.queues[floor];
const passengers = [];
// packaged in a function so we can run it again in the other direction if we need to
const tryGettingPassengers = () => {
for(let i = 0; i < queue.length; i++) {
// If the elevator is full, stop loading passengers
if(!amount) return passengers;
// Load a passenger if they're going in the right direction
if(goingUp && queue[i] > floor || !goingUp && queue[i] < floor) {
passengers.push(queue.splice(i, 1).pop());
amount--;
i--; // Adjust index for removed passenger
}
}
}
// Use the function to get the passengers (maybe trying a second time in the other direction)
tryGettingPassengers();
if(!passengers.length && canSwitchDirection) {
goingUp = !goingUp;
tryGettingPassengers();
}
return passengers;
}
}
/********** ELEVATOR **********/
class Elevator {
constructor(capacity) {
this.floor = 0;
this.goingUp = true;
this.capacity = capacity;
this.contents = []; // an array of numbers (passengers)
this.floorLog = [0];
}
/* Is the elevator empty */
get isEmpty() { return !this.contents.length }
/* How much space is available for passengers */
get availableSpace() { return this.capacity - this.contents.length }
/* Is the elevator on the ground floor */
get isOnGroundFloor() { return this.floor === 0 }
/* An arry of all floors the elevator stopped on */
get stoppedFloors() { return this.floorLog }
/* The top floor requested by passengers in the elevator */
get topRequestedFloor() {
if(this.isEmpty) return -1;
return this.contents[this.contents.length - 1];
}
/* The bottom floor requested by passengers in the elevator */
get bottomRequestedFloor() {
if(this.isEmpty) return Number.POSITIVE_INFINITY;
return this.contents[0];
}
/* Checks if there is anyone on the elevator requesting the floor */
isRequested(floor) {
return this.contents.includes(floor);
}
/* The farthest in the current direction the elevator should go */
getBoundary(building) { return (this.goingUp) ? Math.max(building.topCallingFloor, this.topRequestedFloor) :
Math.min(building.bottomCallingFloor, this.bottomRequestedFloor) }
/* Loads passengers on the elevator from the building on the current floor of the elevator */
load(building) {
const canSwitchDirection = this.floor === this.getBoundary(building);
const passengers = building.getPassengers(this.floor, this.availableSpace, this.goingUp, canSwitchDirection);
this.contents.push(...passengers);
this.contents.sort();
}
/* Unloads all passengers on the elevator that want to get off at the current floor */
unload() {
this.contents = this.contents.filter(p => p !== this.floor);
}
/* Takes the elevator to the next floor it should go to */
/* This one method took 90% of the work */
goToNextFloor(building) {
// If we just finished, go to the bottom
if(building.isEmpty && this.isEmpty) {
// Only actually move if we're not already there
if(this.floor !== 0) {
this.floor = 0;
this.floorLog.push(this.floor);
}
return;
}
let boundary = this.getBoundary(building);
// If we're now past the limit (meaning we've unloaded or loaded everything in this direction), turn around
if(this.floor > boundary === this.goingUp || this.floor === boundary) {
this.goingUp = !this.goingUp
boundary = this.getBoundary(building); // update the boundary for the new direction
}
let canSwitchDirection; // declared outside the loop for scope
do {
// Move up or down
(this.goingUp) ? this.floor++ : this.floor--;
// If we've reached the end of what we need to do in this direction, we can pick up passengers in either direction
canSwitchDirection = this.floor === boundary;
// Keep moving if not a requested or calling floor
} while(!this.isRequested(this.floor) && !building.isCalling(this.floor, this.goingUp, canSwitchDirection))
this.floorLog.push(this.floor);
}
}
/********** TOP LEVEL FUNCTION **********/
const theLift = (queues, capacity) => {
// Set up objects
const elevator = new Elevator(capacity);
const building = new Building(queues);
// Loop as long as there's anything to do
while(!elevator.isEmpty || !building.isEmpty || !elevator.isOnGroundFloor) {
elevator.unload();
elevator.load(building);
elevator.goToNextFloor(building);
}
// Return the floors the elevator stopped at
return elevator.stoppedFloors;
}
let building1, building2, building3, elevator1, elevator2, elevator3;
/*** NOTE: This is not using Mocha/Chai, this is using CodeWars' testing library, based on Mocha ***/
// A function for creating the buildings before each test
function createBuildings() {
building1 = new Building([
[], // G
[5,4], // 1
[1,1], // 2
[], // 3
[2,6], // 4
[], // 5
[], // 6
]);
building2 = new Building([
[3], // G
[], // 1
[5,5,3], // 2
[], // 3
[], // 4
[], // 5
[3], // 6
]);
building3 = new Building([
[], // G
[], // 1
[], // 2
[], // 3
[], // 4
[], // 5
[], // 6
]);
}
/********** BUILDING **********/
describe("Building", function() {
before(createBuildings) // runs at the beginning of this section
describe("#isEmpty", function() {
it("should check if empty", function() {
Test.assertEquals(building1.isEmpty, false)
Test.assertEquals(building2.isEmpty, false)
Test.assertEquals(building3.isEmpty, true)
})
})
describe("#topCallingFloor", function() {
it("should return the top floor with a passenger waiting", function() {
Test.assertEquals(building1.topCallingFloor, 4)
Test.assertEquals(building2.topCallingFloor, 6)
Test.assertEquals(building3.topCallingFloor, -1)
})
})
describe("#bottomCallingFloor", function() {
it("should return the bottom floor with a passenger waiting", function() {
Test.assertEquals(building1.bottomCallingFloor, 1)
Test.assertEquals(building2.bottomCallingFloor, 0)
Test.assertEquals(building3.bottomCallingFloor, 7)
})
})
describe("#isCalling", function() {
it("should return true if there is a passenger calling the elevator on that floor in that direction", function() {
Test.assertEquals(building1.isCalling(1, true), true)
Test.assertEquals(building1.isCalling(4, true), true)
Test.assertEquals(building1.isCalling(4, false), true)
Test.assertEquals(building1.isCalling(2, false), true)
})
it("should return false if there isn't a passenger calling the elevator on that floor in that direction", function() {
Test.assertEquals(building1.isCalling(0, true), false)
Test.assertEquals(building1.isCalling(2, true), false)
Test.assertEquals(building1.isCalling(3, true), false)
Test.assertEquals(building1.isCalling(5, true), false)
Test.assertEquals(building1.isCalling(6, true), false)
Test.assertEquals(building1.isCalling(0, false), false)
Test.assertEquals(building1.isCalling(1, false), false)
Test.assertEquals(building1.isCalling(3, false), false)
Test.assertEquals(building1.isCalling(5, false), false)
Test.assertEquals(building1.isCalling(6, false), false)
})
})
describe("#getPassengers", function() {
it("should fill with passengers correctly going up", function() {
Test.assertDeepEquals(building1.getPassengers(1, 5, true), [5, 4])
Test.assertDeepEquals(building1.getPassengers(2, 5, true), [])
Test.assertDeepEquals(building1.getPassengers(3, 5, true), [])
Test.assertDeepEquals(building1.getPassengers(4, 5, true), [6])
})
it("should fill with passengers correctly going down", function() {
Test.assertDeepEquals(building1.getPassengers(4, 5, false), [2])
Test.assertDeepEquals(building1.getPassengers(3, 5, false), [])
Test.assertDeepEquals(building1.getPassengers(2, 5, false), [1, 1])
Test.assertDeepEquals(building1.getPassengers(1, 5, false), [])
})
it("should be removing passengers from queues", function() {
building1.getPassengers(1, 5, true)
building1.getPassengers(2, 5, true)
building1.getPassengers(3, 5, true)
building1.getPassengers(4, 5, true)
building1.getPassengers(4, 5, false)
building1.getPassengers(3, 5, false)
building1.getPassengers(2, 5, false)
building1.getPassengers(1, 5, false)
Test.assertEquals(building1.isEmpty, true)
})
it("should only give within the amount of passengers", function() {
Test.assertDeepEquals(building2.getPassengers(0, 0, true), [])
Test.assertDeepEquals(building2.getPassengers(2, 2, true), [5, 5])
Test.assertDeepEquals(building2.getPassengers(3, 2, true), [])
Test.assertDeepEquals(building2.getPassengers(6, 3, false), [3])
})
})
})
/********** ELEVATOR **********/
describe("Elevator", function() {
before(function() {
elevator1 = new Elevator(1);
elevator2 = new Elevator(5);
elevator3 = new Elevator(0);
})
describe("#isEmpty", function() {
before(createBuildings) // Recreates the building for this test
it("should check if empty", function() {
elevator1.contents = [4];
elevator2.contents = [1, 3, 5];
elevator3.contents = [];
Test.assertEquals(elevator1.isEmpty, false)
Test.assertEquals(elevator2.isEmpty, false)
Test.assertEquals(elevator3.isEmpty, true)
})
})
describe("#availableSpace", function() {
it("should return the space left in the elevator for passengers", function() {
elevator1.contents = [4];
elevator2.contents = [1, 3, 5];
elevator3.contents = [];
Test.assertEquals(elevator1.availableSpace, 0)
Test.assertEquals(elevator2.availableSpace, 2)
Test.assertEquals(elevator3.availableSpace, 0)
})
})
describe("#topRequestedFloor", function() {
it("should return the top floor requested by a passenger", function() {
Test.assertEquals(elevator1.topRequestedFloor, 4)
Test.assertEquals(elevator2.topRequestedFloor, 5)
Test.assertEquals(elevator3.topRequestedFloor, -1)
})
})
describe("#bottomRequestedFloor", function() {
it("should return the bottom floor requested by a passenger", function() {
Test.assertEquals(elevator1.bottomRequestedFloor, 4)
Test.assertEquals(elevator2.bottomRequestedFloor, 1)
Test.assertEquals(elevator3.bottomRequestedFloor, Number.POSITIVE_INFINITY)
})
})
describe("#load", function() {
before(createBuildings) // Recreates the building for each test
it("should fill with passengers correctly going up", function() {
elevator2.goingUp = true;
elevator2.contents = [];
elevator2.floor = 0;
elevator2.load(building1)
Test.assertDeepEquals(elevator2.contents, [])
elevator2.floor = 1;
elevator2.load(building1)
Test.assertDeepEquals(elevator2.contents, [4, 5])
elevator2.floor = 2;
elevator2.load(building1)
Test.assertDeepEquals(elevator2.contents, [4, 5])
elevator2.floor = 3;
elevator2.load(building1)
Test.assertDeepEquals(elevator2.contents, [4, 5])
elevator2.floor = 4;
elevator2.load(building1)
Test.assertDeepEquals(elevator2.contents, [4, 5, 6])
})
it("should fill with passengers correctly going down", function() {
elevator2.goingUp = false;
elevator2.contents = [];
elevator2.floor = 4;
elevator2.load(building1)
Test.assertDeepEquals(elevator2.contents, [2])
elevator2.floor = 3;
elevator2.load(building1)
Test.assertDeepEquals(elevator2.contents, [2])
elevator2.floor = 2;
elevator2.load(building1)
Test.assertDeepEquals(elevator2.contents, [1, 1, 2])
elevator2.floor = 1;
elevator2.load(building1)
Test.assertDeepEquals(elevator2.contents, [1, 1, 2])
elevator2.floor = 0;
elevator2.load(building1)
Test.assertDeepEquals(elevator2.contents, [1, 1, 2])
})
it("should only fill up to the capacity", function() {
elevator3.goingUp = true;
elevator3.contents = [];
elevator3.floor = 0
elevator3.load(building2);
Test.assertDeepEquals(elevator3.contents, [])
elevator1.goingUp = true;
elevator1.contents = [];
elevator1.floor = 0;
elevator1.load(building2);
Test.assertDeepEquals(elevator1.contents, [3])
elevator1.floor = 2;
elevator1.load(building2);
Test.assertDeepEquals(elevator1.contents, [3])
elevator1.contents = [];
elevator1.load(building2);
Test.assertDeepEquals(elevator1.contents, [5])
elevator2.goingUp = true;
elevator2.contents = [1, 2, 3];
elevator2.floor = 2;
elevator2.load(building2);
Test.assertDeepEquals(elevator2.contents, [1, 2, 3, 3, 5])
})
})
describe("#unload", function() {
it("should unload passengers correctly", function() {
elevator2.contents = [1, 1, 2, 4];
elevator2.floor = 0;
elevator2.unload();
Test.assertDeepEquals(elevator2.contents, [1, 1, 2, 4])
elevator2.floor = 1;
elevator2.unload();
Test.assertDeepEquals(elevator2.contents, [2, 4])
elevator2.floor = 4;
elevator2.unload();
Test.assertDeepEquals(elevator2.contents, [2])
elevator2.floor = 2;
elevator2.unload();
Test.assertDeepEquals(elevator2.contents, [])
})
})
describe("#goToNextFloor", function() {
before(createBuildings) // Recreates the buildings for each test
it("should go up when there's no reason to go down", function() {
elevator2.goingUp = false;
elevator2.floor = 0;
elevator2.goToNextFloor(building1);
Test.assertEquals(elevator2.goingUp, true)
Test.assertEquals(elevator2.floor, 1)
})
it("should stop on a floor while going up if there's someone there waiting to go up", function() {
elevator2.goingUp = true;
elevator2.floor = 0;
elevator2.goToNextFloor(building1);
Test.assertEquals(elevator2.goingUp, true)
Test.assertEquals(elevator2.floor, 1)
elevator2.goToNextFloor(building1);
Test.assertEquals(elevator2.goingUp, true)
Test.assertEquals(elevator2.floor, 4)
})
it("should go down when there's no reason to go up", function() {
elevator2.goingUp = true;
elevator2.floor = 6;
elevator2.goToNextFloor(building1);
Test.assertEquals(elevator2.goingUp, false)
Test.assertEquals(elevator2.floor, 4)
elevator2.goingUp = true;
elevator2.floor = 5;
elevator2.goToNextFloor(building1);
Test.assertEquals(elevator2.goingUp, false);
Test.assertEquals(elevator2.floor, 4);
})
it("should stop on a floor while going down if there's someone there waiting to go down", function() {
elevator2.goingUp = false;
elevator2.floor = 6;
elevator2.goToNextFloor(building1);
Test.assertEquals(elevator2.goingUp, false);
Test.assertEquals(elevator2.floor, 4);
elevator2.goToNextFloor(building1);
Test.assertEquals(elevator2.goingUp, false);
Test.assertEquals(elevator2.floor, 2);
})
it("should stop on a floor if there's someone who needs to get off there", function() {
elevator2.goingUp = true;
elevator2.floor = 0;
elevator2.contents = [1, 3, 5];
elevator2.goToNextFloor(building3);
Test.assertEquals(elevator2.goingUp, true);
Test.assertEquals(elevator2.floor, 1);
elevator2.goToNextFloor(building3);
Test.assertEquals(elevator2.goingUp, true);
Test.assertEquals(elevator2.floor, 3);
elevator2.goToNextFloor(building3);
Test.assertEquals(elevator2.goingUp, true);
Test.assertEquals(elevator2.floor, 5);
})
it("should go to the bottom if there's no one waiting and no one inside", function() {
elevator2.goingUp = true;
elevator2.floor = 3;
elevator2.contents = [];
elevator2.goToNextFloor(building3);
Test.assertEquals(elevator2.goingUp, true);
Test.assertEquals(elevator2.floor, 0);
})
})
})
/********** CODEWARS TESTS **********/
describe("Example Tests", function() {
it("up", function() {
var queues = [
[], // G
[], // 1
[5,5,5], // 2
[], // 3
[], // 4
[], // 5
[], // 6
];
var result = theLift(queues,5);
Test.assertDeepEquals(result, [0,2,5,0]);
});
it("down", function() {
var queues = [
[], // G
[], // 1
[1,1], // 2
[], // 3
[], // 4
[], // 5
[], // 6
];
var result = theLift(queues,5);
Test.assertDeepEquals(result, [0,2,1,0]);
});
it("up and up", function() {
var queues = [
[], // G
[3], // 1
[4], // 2
[], // 3
[5], // 4
[], // 5
[], // 6
];
var result = theLift(queues,5);
Test.assertDeepEquals(result, [0,1,2,3,4,5,0]);
});
it("down and down", function() {
var queues = [
[], // G
[0], // 1
[], // 2
[], // 3
[2], // 4
[3], // 5
[], // 6
];
var result = theLift(queues,5);
Test.assertDeepEquals(result, [0,5,4,3,2,1,0]);
});
it("up and down", function() {
var queues = [
[], // G
[5,4], // 1
[1,1], // 2
[], // 3
[2,6], // 4
[], // 5
[], // 6
];
var result = theLift(queues, 5);
Test.assertDeepEquals(result, [0,1,4,5,6,4,2,1,0]);
});
it("everyone down", function() {
var queues = [
[],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
var result = theLift(queues, 5);
Test.assertDeepEquals(result, [0,6,5,4,3,2,1,0,5,4,3,2,1,0,4,3,2,1,0,3,2,1,0,1,0]);
})
it("up down up down", function() {
var queues = [ [ 1, 4, 4, 1 ], [ 3, 2 ], [ 3, 4, 0, 3 ], [], [] ]
var result = theLift(queues, 4);
Test.assertDeepEquals(result, [0,1,2,3,4,2,0,2,3,4,0]);
})
it("not enough room", function() {
var queues = [ [], [], [ 4, 4, 4, 4 ], [], [ 2, 2, 2, 2 ], [], [] ]
var result = theLift(queues, 2);
Test.assertDeepEquals(result, [0,2,4,2,4,2,0]);
})
});
class Elevator {
constructor(building, capacity) {
this.floor = 0;
this.building = building;
this.capacity = capacity;
this.contents = [];
}
goToNextFloor() {
this.building
}
load() {
}
unload() {
}
}
class Building {
constructor(queues) {
this.queues = queues;
}
isEmpty() {
return this.queues.find( floor => floor.length !== 0) === undefined;
}
}
var theLift = function(queues, capacity) {
const building = new Building(queues);
const elevator = new Elevator(building, capacity);
// the elevator to pick people up and take them where they need to go until the building is empty
while( !building.isEmpty() ) {
elevator.goToNextFloor();
elevator.unload();
elevator.load();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment