Parking Lot
Build a multi-floor parking lot. Vehicles arrive, get a ticket and a spot, and pay when they leave. Two decisions will change in real life, so they must be changeable at runtime without touching the rest of the code: which free spot a vehicle gets, and how the fee is computed.
Implement the class ParkingLot:
ParkingLot(floors):floorsis an array of floors, and each floor is an array of spot types, in order, for example[["bike", "compact", "large"], ["compact", "compact", "large"]]. A spot is identified by its floor index and its index on the floor, so"1-2"is floor 1, third spot. At the start, the spot policy is"smallest-first"and the pricing is"hourly".park(plate, vehicle):vehicleis"bike","car"or"truck". Picks a free spot that fits and returns a new ticket id:1for the first successful park, then2,3and so on. Returns-1if nothing fits, if the vehicle type is unknown, or if a vehicle with thisplateis already parked. A failedparkdoes not use up a ticket id.leave(ticketId, minutes): the vehicle leaves afterminutesminutes (an integer,>= 0). Frees its spot and returns the fee in cents. Returns-1for an unknown ticket or a ticket that has already been used. Ticket ids are never reused.available(floor, spotType): how many free spots of exactly that type the floor has (0for a floor or type that does not exist).spotOf(ticketId): the spot of a vehicle that is still parked, as"floor-index"(for example"0-2"), or""if the ticket is unknown or already used.useSpotPolicy(name)andusePricing(name): switch the policy, see below. Returntrue, orfalse(and change nothing) if the name is not one of the listed ones.
What fits where
| Vehicle | Spot types it can use |
|---|---|
"bike" | "bike", "compact", "large" |
"car" | "compact", "large" |
"truck" | "large" |
Spot policies choose among the free spots that fit. Spot types are ordered bike < compact < large, and the spot index breaks any remaining tie (lower first).
"smallest-first": the smallest spot type, then the lowest floor, then the lowest index. A car takes a compact spot on any floor before it uses a large one."lowest-floor-first": the lowest floor, then the smallest spot type, then the lowest index. A car takes a large spot on floor 0 before a compact spot on floor 1.
Pricing policies compute the fee from the vehicle type (never the spot type) and the minutes:
"hourly": the first 10 minutes are free. Beyond that, every started hour is charged at a per-vehicle rate: bike 100, car 200, truck 500 cents per hour. So 10 minutes costs 0, 11 minutes is one hour, 60 is one hour, 61 is two hours."flat": one fee per visit, by vehicle only: bike 300, car 500, truck 1200 cents. There is no free period: even 0 minutes is charged.
Switching policies takes effect immediately and only for later calls. A vehicle already parked stays where it is. The fee is computed by the pricing that is in force when leave is called, whichever pricing was active when the vehicle arrived.
Design requirements
The tests cannot see your classes, but the idea of the exercise is visible in how you would answer "now add a third pricing rule" or "now prefer the floor nearest the exit". If park and leave contain if (policy === "hourly") ... else ..., every new rule edits and risks the code that already works. Keep spot choice and pricing each behind their own small seam (an interface, a function, or a lookup of named strategies), keep the lot responsible only for spots and tickets, and make the lot depend on those seams rather than on the concrete rules. Adding a policy should mean adding one new entry and no change to park, leave or the ticket book.
Notes
- A plate is "already parked" from a successful
parkuntil the matchingleave. After that it can park again and gets a new ticket. - A car may park in a large spot when no compact spot is free. The fee still depends on the vehicle, not on the spot.
availablecounts free spots of the exact type, so a free large spot is not counted as a compact one.
Examples
Example 1
Input: ["ParkingLot","park","spotOf","available","leave","available"]
[[[["bike","compact","large"],["compact","compact","large"]]],["CAR1","car"],[1],[0,"compact"],[1,5],[0,"compact"]]
Output: [null,1,"0-1",0,0,1]A car goes into the smallest spot it fits: the compact spot on floor 0, which is spot "0-1". While it is parked that spot is not available. It leaves after 5 minutes, which is inside the free grace period, so the fee is 0 and the spot is free again.
Example 2
Input: ["ParkingLot","park","park","park","park","park","park"]
[[[["bike","compact","large"]]],["T1","truck"],["T2","truck"],["C1","car"],["C2","car"],["B1","bike"],["B2","bike"]]
Output: [null,1,-1,2,-1,3,-1]A truck fits only a large spot, a car a compact or large one, a bike any spot. The second truck finds no large spot. The car takes the compact spot, so the second car finds nothing left it fits. A failed park does not use up a ticket id: the bike still gets 3.
Example 3
Input: ["ParkingLot","park","leave","leave"]
[[[["compact"]]],["A1","car"],[1,90],[1,90]]
Output: [null,1,400,-1]The default pricing is hourly: every started hour is charged at the vehicle's rate, 200 cents for a car. 90 minutes is 2 started hours, so 400. A ticket can be used once, so the second leave is -1.
Each test creates the class with the first operation, then calls the methods in order. Output lists what each call returns; null marks the constructor and methods that return nothing, and their return values aren't checked.
class ParkingLot {
/** `floors` is an array of floors, each an array of spot types: "bike", "compact" or "large". */
constructor(floors) {
}
/** Choose how a free spot is picked: "smallest-first" (the default) or "lowest-floor-first". @return {boolean} false for an unknown name */
useSpotPolicy(name) {
}
/** Choose how fees are computed: "hourly" (the default) or "flat". @return {boolean} false for an unknown name */
usePricing(name) {
}
/** Park a "bike", "car" or "truck" with this plate. @return {number} a ticket id (1, 2, 3, ...) or -1 when nothing fits */
park(plate, vehicle) {
}
/** The vehicle leaves after `minutes`. Frees its spot. @return {number} the fee in cents, or -1 for an unknown or used ticket */
leave(ticketId, minutes) {
}
/** @return {number} how many free spots of exactly this type the floor has */
available(floor, spotType) {
}
/** @return {string} where the ticket's vehicle is parked as "floor-index" (for example "0-2"), or "" if it is not parked */
spotOf(ticketId) {
}
}
Ctrl/⌘ + ' runs the examples, Ctrl/⌘ + Enter submits. Use console.log freely: output shows under Console. In the editor, press Esc then Tab to move focus out.