Skip to content

Instantly share code, notes, and snippets.

@KOMON
Created October 13, 2021 22:44
Show Gist options
  • Save KOMON/444c404306918abab77a77f5054069e0 to your computer and use it in GitHub Desktop.
Save KOMON/444c404306918abab77a77f5054069e0 to your computer and use it in GitHub Desktop.
Remitly Take Home assignment
"use strict";
/* eslint-disable */
/*
/*
You are implementing an elevator.
The elevator has 4 actions:
UP_ONE
DOWN_ONE
OPEN_CLOSE
NO_OP
The elevator class has 2 functions:
def requestFloor(n):
// Called when the elevator is summoned to floor n, once per button press. Returns nothing.
def getAction():
// Returns next action to do. See "additional guidance" items 3 & 4.
- Implement requestFloor and getAction
- What is the complexity of requestFloor and getAction?
- How would you measure success?
*/
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
exports.__esModule = true;
exports.expectActions = exports.test = exports.Elevator = void 0;
var assert = require("assert");
var Elevator = /** @class */ (function () {
function Elevator() {
// User requests floor:
// Keep track of that floor
// Keep track of what floor we're on
// getAction will need to emit directionals until we get to the correct floor
// get action will need to OPEN_CLOSE when we're at a requested floor
// getAction will NO_OP if no requests exists
this.currentFloor = 0;
this.currentDirection = 'UP';
this.floorRequests = new Map();
}
Elevator.prototype.requestFloor = function (n) {
if (n === this.currentFloor) {
return;
}
// if floors is empty, assume we're not travelling in a particular direction
if (this.floorRequests.size === 0) {
if (n < this.currentFloor) {
this.currentDirection = 'DOWN';
}
else {
this.currentDirection = 'UP';
}
}
this.floorRequests.set(n, true);
};
Elevator.prototype.searchFloorsForClosest = function () {
// search for the current floor if we can
var _this = this;
// return the current floor if found
if (this.floorRequests.get(this.currentFloor)) {
return this.currentFloor;
}
// if we can't find the current floor
// iterate over all the entries in the map, collecting results above and below currentFloor
// trying to find z or a below
// [ ... x, y, z, <currentFloor?>, a, b, c, ...]
var _a = Array.from(this.floorRequests.keys()).reduce(function (carry, current) {
if (
// we're on the xyz side
current < _this.currentFloor &&
// and this is a better candidate for z
(!carry.nextLower || current > carry.nextLower)) {
return __assign(__assign({}, carry), { nextLower: current });
}
else if (
// we're on the abc side
current > _this.currentFloor &&
// and this is a better candidate for a
(!carry.nextHigher || current < carry.nextHigher)) {
return __assign(__assign({}, carry), { nextHigher: current });
}
return carry;
}, { nextHigher: null, nextLower: null }), nextHigher = _a.nextHigher, nextLower = _a.nextLower;
if (this.currentDirection === 'UP') {
return nextHigher !== null && nextHigher !== void 0 ? nextHigher : nextLower;
}
else {
return nextLower !== null && nextLower !== void 0 ? nextLower : nextHigher;
}
};
Elevator.prototype.getAction = function () {
// if floors is empty, no_op
if (this.floorRequests.size === 0) {
return 'NO_OP';
}
// search the array, looking for either floor we're on, or the next greatest floor
var foundFloor = this.searchFloorsForClosest();
if (foundFloor === this.currentFloor) {
this.floorRequests["delete"](this.currentFloor);
return 'OPEN_CLOSE';
}
else if (foundFloor > this.currentFloor) {
this.currentDirection = 'UP';
this.currentFloor++;
return 'UP_ONE';
}
else if (foundFloor < this.currentFloor) {
this.currentDirection = 'DOWN';
this.currentFloor--;
return 'DOWN_ONE';
}
};
return Elevator;
}());
exports.Elevator = Elevator;
function test() {
try {
var elevator = new Elevator();
elevator.requestFloor(4); // go to the 4th floor
expectActions(elevator, [
{ action: 'UP_ONE', count: 4 },
{ action: 'OPEN_CLOSE', count: 1 },
], 'Go to the 4th floor');
elevator.requestFloor(3);
elevator.requestFloor(30);
elevator.requestFloor(17);
elevator.requestFloor(5);
elevator.requestFloor(8);
expectActions(elevator, [
{ action: 'DOWN_ONE', count: 1 },
{ action: 'OPEN_CLOSE', count: 1 },
], 'Go to the 3rd floor (first wins)');
expectActions(elevator, [
{ action: 'UP_ONE', count: 2 },
{ action: 'OPEN_CLOSE', count: 1 },
], 'Go to the 5th floor');
elevator.requestFloor(2);
expectActions(elevator, [
{ action: 'UP_ONE', count: 3 },
{ action: 'OPEN_CLOSE', count: 1 },
], 'Go to the 8th floor');
expectActions(elevator, [
{ action: 'UP_ONE', count: 9 },
{ action: 'OPEN_CLOSE', count: 1 },
], 'Go to the 17th floor');
expectActions(elevator, [
{ action: 'UP_ONE', count: 13 },
{ action: 'OPEN_CLOSE', count: 1 },
], 'Go to the 30th floor');
expectActions(elevator, [
{ action: 'DOWN_ONE', count: 28 },
{ action: 'OPEN_CLOSE', count: 1 },
], 'Go to the second floor (requested on the 5th floor earlier)');
}
catch (err) {
console.error("Test failed! -- " + err.message);
return;
}
console.log('Test succeeded!');
}
exports.test = test;
function expectActions(elevator, expectations, descriptor) {
expectations.forEach(function (_a) {
var action = _a.action, count = _a.count;
var current = 1;
var currentAction;
while (current < count + 1 && currentAction !== 'NO_OP') {
currentAction = elevator.getAction();
assert.strictEqual(action, currentAction, "" + (descriptor ? descriptor : '') + (descriptor ? ': ' : '') + "Expected " + current + "th " + action + " but got " + currentAction + " instead");
current++;
}
});
}
exports.expectActions = expectActions;
if (require.main === module) {
test();
}
/* eslint-disable */
/*
/*
You are implementing an elevator.
The elevator has 4 actions:
UP_ONE
DOWN_ONE
OPEN_CLOSE
NO_OP
The elevator class has 2 functions:
def requestFloor(n):
// Called when the elevator is summoned to floor n, once per button press. Returns nothing.
def getAction():
// Returns next action to do. See "additional guidance" items 3 & 4.
- Implement requestFloor and getAction
- What is the complexity of requestFloor and getAction?
- How would you measure success?
*/
import * as assert from 'assert';
type Action = 'UP_ONE' | 'DOWN_ONE' | 'OPEN_CLOSE' | 'NO_OP';
type Direction = 'UP' | 'DOWN';
export class Elevator {
// User requests floor:
// Keep track of that floor
// Keep track of what floor we're on
// getAction will need to emit directionals until we get to the correct floor
// get action will need to OPEN_CLOSE when we're at a requested floor
// getAction will NO_OP if no requests exists
private currentFloor = 0;
private currentDirection: Direction = 'UP';
private floorRequests: Map<number, boolean> = new Map();
public requestFloor(n: number) {
if (n === this.currentFloor) {
return;
}
// if floors is empty, assume we're not travelling in a particular direction
if (this.floorRequests.size === 0) {
if (n < this.currentFloor) {
this.currentDirection = 'DOWN';
} else {
this.currentDirection = 'UP';
}
}
this.floorRequests.set(n, true);
}
private searchFloorsForClosest(): number {
// search for the current floor if we can
// return the current floor if found
if (this.floorRequests.get(this.currentFloor)) {
return this.currentFloor;
}
// if we can't find the current floor
// iterate over all the entries in the map, collecting results above and below currentFloor
// trying to find z or a below
// [ ... x, y, z, <currentFloor?>, a, b, c, ...]
const { nextHigher, nextLower } = Array.from(
this.floorRequests.keys(),
).reduce(
(carry, current) => {
if (
// we're on the xyz side
current < this.currentFloor &&
// and this is a better candidate for z
(!carry.nextLower || current > carry.nextLower)
) {
return { ...carry, nextLower: current };
} else if (
// we're on the abc side
current > this.currentFloor &&
// and this is a better candidate for a
(!carry.nextHigher || current < carry.nextHigher)
) {
return { ...carry, nextHigher: current };
}
return carry;
},
{ nextHigher: null, nextLower: null },
);
if (this.currentDirection === 'UP') {
return nextHigher ?? nextLower;
} else {
return nextLower ?? nextHigher;
}
}
public getAction(): Action {
// if floors is empty, no_op
if (this.floorRequests.size === 0) {
return 'NO_OP';
}
// search the array, looking for either floor we're on, or the next greatest floor
const foundFloor = this.searchFloorsForClosest();
if (foundFloor === this.currentFloor) {
this.floorRequests.delete(this.currentFloor);
return 'OPEN_CLOSE';
} else if (foundFloor > this.currentFloor) {
this.currentDirection = 'UP';
this.currentFloor++;
return 'UP_ONE';
} else if (foundFloor < this.currentFloor) {
this.currentDirection = 'DOWN';
this.currentFloor--;
return 'DOWN_ONE';
}
}
}
export function test() {
try {
const elevator = new Elevator();
elevator.requestFloor(4); // go to the 4th floor
expectActions(
elevator,
[
{ action: 'UP_ONE', count: 4 },
{ action: 'OPEN_CLOSE', count: 1 },
],
'Go to the 4th floor',
);
elevator.requestFloor(3);
elevator.requestFloor(30);
elevator.requestFloor(17);
elevator.requestFloor(5);
elevator.requestFloor(8);
expectActions(
elevator,
[
{ action: 'DOWN_ONE', count: 1 },
{ action: 'OPEN_CLOSE', count: 1 },
],
'Go to the 3rd floor (first wins)',
);
expectActions(
elevator,
[
{ action: 'UP_ONE', count: 2 },
{ action: 'OPEN_CLOSE', count: 1 },
],
'Go to the 5th floor',
);
elevator.requestFloor(2);
expectActions(
elevator,
[
{ action: 'UP_ONE', count: 3 },
{ action: 'OPEN_CLOSE', count: 1 },
],
'Go to the 8th floor',
);
expectActions(
elevator,
[
{ action: 'UP_ONE', count: 9 },
{ action: 'OPEN_CLOSE', count: 1 },
],
'Go to the 17th floor',
);
expectActions(
elevator,
[
{ action: 'UP_ONE', count: 13 },
{ action: 'OPEN_CLOSE', count: 1 },
],
'Go to the 30th floor',
);
expectActions(
elevator,
[
{ action: 'DOWN_ONE', count: 28 },
{ action: 'OPEN_CLOSE', count: 1 },
],
'Go to the second floor (requested on the 5th floor earlier)',
);
} catch (err) {
console.error(`Test failed! -- ${err.message}`);
return;
}
console.log('Test succeeded!');
}
export function expectActions(
elevator: Elevator,
expectations: { action: Action; count: number }[],
descriptor?: string,
) {
expectations.forEach(({ action, count }) => {
let current = 1;
let currentAction: Action;
while (current < count + 1 && currentAction !== 'NO_OP') {
currentAction = elevator.getAction();
assert.strictEqual(
action,
currentAction,
`${descriptor ? descriptor : ''}${
descriptor ? ': ' : ''
}Expected ${current}th ${action} but got ${currentAction} instead`,
);
current++;
}
});
}
if (require.main === module) {
test();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment