-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path06.DesignAParkingLot.py
More file actions
206 lines (172 loc) · 6.68 KB
/
06.DesignAParkingLot.py
File metadata and controls
206 lines (172 loc) · 6.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# Design and implement an object-oriented Parking Lot system that supports multiple levels
# and slot sizes. The system should allow vehicles to park, unpark, and display the parking status
# — all within a single-file.
import threading
import time
import uuid
from enum import Enum
from typing import Optional, Dict, List
class VehicleType(Enum):
MOTORCYCLE = 1
CAR = 2
BUS = 3
VEHICLE_TO_MIN_SLOT = {
VehicleType.MOTORCYCLE: 1,
VehicleType.CAR: 2,
VehicleType.BUS: 3,
}
class Vehicle:
def __init__(self, plate:str, vtype: VehicleType):
self.plate = plate
self.vtype = vtype
def __repr__(self):
return f"Vehicle({self.plate}, {self.vtype.name})"
class ParkingSlot:
def __init__(self, slot_id:str, size: int, level_id: int):
self.slot_id = slot_id
self.size = size
self.level_id = level_id
self.occupied_by: Optional[Vehicle] = None
def is_free(self):
return self.occupied_by is None
def fits(self, vehicle: Vehicle) -> bool:
return self.is_free() and self.size >= VEHICLE_TO_MIN_SLOT[vehicle.vtype]
def __repr__(self):
occ = self.occupied_by.plate if self.occupied_by else "Free"
return f"Slot({self.slot_id}, size = {self.size}, occ = {occ})"
class Level:
def __init__(self, level_id: int, slots: List[ParkingSlot]):
self.level_id = level_id
self.slots = slots
class Ticket:
def __init__(self, ticket_id: str, plate: str, slot_id: str, parked_at: float):
self.ticket_id = ticket_id
self.plate = plate
self.slot_id = slot_id
self.parked_at = parked_at
def __repr__(self):
return f"Ticket({self.ticket_id}, plate = {self.plate}, slot = {self.slot_id}, at = {self.parked_at})"
class ParkingLot:
def __init__(self, levels: List[Level]):
self.levels = levels
self.ticket_map: Dict[str, Ticket] = {}
self.plate_map: Dict[str, str] = {}
self.lock = threading.Lock()
@classmethod
def create_demo_lot(cls, level_count = 2, slots_per_level = 10):
levels = []
for li in range(level_count):
slots = []
for si in range(slots_per_level):
if si < 2:
size = 1
elif si < slots_per_level - 2:
size = 2
else:
size = 3
slot_id = f"L{li + 1}-S{si + 1}"
slots.append(ParkingSlot(slot_id, size, li + 1))
levels.append(Level(li + 1, slots))
return cls(levels)
def _generate_ticket_id(self) -> str:
return str(uuid.uuid4())[:8]
def park_vehicle(self, vehicle: Vehicle) -> Optional[Ticket]:
with self.lock:
if vehicle.plate in self.plate_map:
tid = self.plate_map[vehicle.plate]
return self.ticket_map.get(tid)
for lvl in self.levels:
for slot in lvl.slots:
if slot.fits(vehicle):
slot.occupied_by = vehicle
ticket_id = self._generate_ticket_id()
t = Ticket(ticket_id, vehicle.plate, slot.slot_id, time.time())
self.ticket_map[ticket_id] = t
self.plate_map[vehicle.plate] = ticket_id
return t
return None
def leave(self, ticket_id: str) -> Optional[dict]:
with self.lock:
t = self.ticket_map.get(ticket_id)
if not t:
return None
for lvl in self.levels:
for slot in lvl.slots:
if slot.slot_id == t.slot_id:
slot.occupied_by = None
break
parked_seconds = time.time() - t.parked_at
hours = int((parked_seconds + 3599) // 3600)
fee = hours * 10 if hours > 0 else 10
del self.ticket_map[ticket_id]
if t.plate in self.plate_map:
del self.plate_map[t.plate]
return {"ticket": t, "fee": fee, "duration_seconds": int(parked_seconds)}
def status(self) -> dict:
with self.lock:
total = 0
free = 0
per_level = {}
for lvl in self.levels:
lvl_total = len(lvl.slots)
lvl_free = sum(1 for s in lvl.slots if s.is_free())
per_level[lvl.level_id] = {"total": lvl_total, "free": lvl_free}
total += lvl_total
free += lvl_free
occupied = total - free
return {"total_slots": total, "free_slots": free, "occupied": occupied, "per_level": per_level}
def find_by_plate(self, plate: str) -> Optional[Ticket]:
with self.lock:
tid = self.plate_map.get(plate)
if tid:
return self.ticket_map.get(tid)
return None
def dump_slots(self) -> str:
with self.lock:
lines = []
for lvl in self.levels:
lines.append(f"Level {lvl.level_id}:")
for s in lvl.slots:
occ = s.occupied_by.plate if s.occupied_by else "Free"
lines.append(f" {s.slot_id} size = {s.size} -> {occ}")
return "\n".join(lines)
def demo_scenario():
pl = ParkingLot.create_demo_lot(level_count=2, slots_per_level=8)
print("=== Parking Lot Created ===")
print(pl.status())
print()
# Park vehicles
cars = [
Vehicle("KA01AA1111", VehicleType.CAR),
Vehicle("KA01BB2222", VehicleType.CAR),
Vehicle("KA01CC3333", VehicleType.MOTORCYCLE),
Vehicle("KA01DD4444", VehicleType.BUS),
]
tickets = []
for v in cars:
t = pl.park_vehicle(v)
if t:
print(f"Parked {v.plate} as {v.vtype.name} -> ticket {t.ticket_id} slot {t.slot_id}")
tickets.append(t)
else:
print(f"No space for {v.plate} ({v.vtype.name})")
print()
print("Status after parking:", pl.status())
print()
print("Slot dump:\n", pl.dump_slots())
print()
# Leave one car (simulate some time)
time.sleep(1.2) # small pause to create non-zero duration
if tickets:
t0 = tickets[0]
res = pl.leave(t0.ticket_id)
if res:
print(f"Vehicle {res['ticket'].plate} left. Fee: {res['fee']} Duration(s): {res['duration_seconds']}")
else:
print("Invalid ticket for leaving.")
print()
print("Final status:", pl.status())
print()
print("Final slot dump:\n", pl.dump_slots())
if __name__ == "__main__":
demo_scenario()