Move images and solution under docs

This commit is contained in:
Ido777
2025-05-11 15:03:38 +03:00
committed by Ido Ophir
parent cb28c0d58c
commit 91b67a57f0
145 changed files with 90 additions and 135 deletions

View File

@@ -0,0 +1,206 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/ido777/system-design-primer-update)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Design a call center"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and assumptions\n",
"\n",
"* What levels of employees are in the call center?\n",
" * Operator, supervisor, director\n",
"* Can we assume operators always get the initial calls?\n",
" * Yes\n",
"* If there is no available operators or the operator can't handle the call, does the call go to the supervisors?\n",
" * Yes\n",
"* If there is no available supervisors or the supervisor can't handle the call, does the call go to the directors?\n",
" * Yes\n",
"* Can we assume the directors can handle all calls?\n",
" * Yes\n",
"* What happens if nobody can answer the call?\n",
" * It gets queued\n",
"* Do we need to handle 'VIP' calls where we put someone to the front of the line?\n",
" * No\n",
"* Can we assume inputs are valid or do we have to validate them?\n",
" * Assume they're valid"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Solution"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting call_center.py\n"
]
}
],
"source": [
"%%writefile call_center.py\n",
"from abc import ABCMeta, abstractmethod\n",
"from collections import deque\n",
"from enum import Enum\n",
"\n",
"\n",
"class Rank(Enum):\n",
"\n",
" OPERATOR = 0\n",
" SUPERVISOR = 1\n",
" DIRECTOR = 2\n",
"\n",
"\n",
"class Employee(metaclass=ABCMeta):\n",
"\n",
" def __init__(self, employee_id, name, rank, call_center):\n",
" self.employee_id = employee_id\n",
" self.name = name\n",
" self.rank = rank\n",
" self.call = None\n",
" self.call_center = call_center\n",
"\n",
" def take_call(self, call):\n",
" \"\"\"Assume the employee will always successfully take the call.\"\"\"\n",
" self.call = call\n",
" self.call.employee = self\n",
" self.call.state = CallState.IN_PROGRESS\n",
"\n",
" def complete_call(self):\n",
" self.call.state = CallState.COMPLETE\n",
" self.call_center.notify_call_completed(self.call)\n",
"\n",
" @abstractmethod\n",
" def escalate_call(self):\n",
" pass\n",
"\n",
" def _escalate_call(self):\n",
" self.call.state = CallState.READY\n",
" call = self.call\n",
" self.call = None\n",
" self.call_center.notify_call_escalated(call)\n",
"\n",
"\n",
"class Operator(Employee):\n",
"\n",
" def __init__(self, employee_id, name):\n",
" super(Operator, self).__init__(employee_id, name, Rank.OPERATOR)\n",
"\n",
" def escalate_call(self):\n",
" self.call.rank = Rank.SUPERVISOR\n",
" self._escalate_call()\n",
"\n",
"\n",
"class Supervisor(Employee):\n",
"\n",
" def __init__(self, employee_id, name):\n",
" super(Operator, self).__init__(employee_id, name, Rank.SUPERVISOR)\n",
"\n",
" def escalate_call(self):\n",
" self.call.rank = Rank.DIRECTOR\n",
" self._escalate_call()\n",
"\n",
"\n",
"class Director(Employee):\n",
"\n",
" def __init__(self, employee_id, name):\n",
" super(Operator, self).__init__(employee_id, name, Rank.DIRECTOR)\n",
"\n",
" def escalate_call(self):\n",
" raise NotImplemented('Directors must be able to handle any call')\n",
"\n",
"\n",
"class CallState(Enum):\n",
"\n",
" READY = 0\n",
" IN_PROGRESS = 1\n",
" COMPLETE = 2\n",
"\n",
"\n",
"class Call(object):\n",
"\n",
" def __init__(self, rank):\n",
" self.state = CallState.READY\n",
" self.rank = rank\n",
" self.employee = None\n",
"\n",
"\n",
"class CallCenter(object):\n",
"\n",
" def __init__(self, operators, supervisors, directors):\n",
" self.operators = operators\n",
" self.supervisors = supervisors\n",
" self.directors = directors\n",
" self.queued_calls = deque()\n",
"\n",
" def dispatch_call(self, call):\n",
" if call.rank not in (Rank.OPERATOR, Rank.SUPERVISOR, Rank.DIRECTOR):\n",
" raise ValueError('Invalid call rank: {}'.format(call.rank))\n",
" employee = None\n",
" if call.rank == Rank.OPERATOR:\n",
" employee = self._dispatch_call(call, self.operators)\n",
" if call.rank == Rank.SUPERVISOR or employee is None:\n",
" employee = self._dispatch_call(call, self.supervisors)\n",
" if call.rank == Rank.DIRECTOR or employee is None:\n",
" employee = self._dispatch_call(call, self.directors)\n",
" if employee is None:\n",
" self.queued_calls.append(call)\n",
"\n",
" def _dispatch_call(self, call, employees):\n",
" for employee in employees:\n",
" if employee.call is None:\n",
" employee.take_call(call)\n",
" return employee\n",
" return None\n",
"\n",
" def notify_call_escalated(self, call): # ...\n",
" def notify_call_completed(self, call): # ...\n",
" def dispatch_queued_call_to_newly_freed_employee(self, call, employee): # ..."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

View File

@@ -0,0 +1,142 @@
from abc import ABCMeta, abstractmethod
from collections import deque
from enum import Enum
class Rank(Enum):
OPERATOR = 0
SUPERVISOR = 1
DIRECTOR = 2
class Employee(metaclass=ABCMeta):
def __init__(self, employee_id, name, rank):
self.employee_id = employee_id
self.name = name
self.rank = rank
self.call = None
self.call_center = None
def take_call(self, call):
"""Assume the employee will always successfully take the call."""
self.call = call
self.call.employee = self
self.call.state = CallState.IN_PROGRESS
def complete_call(self):
assert self.call_center is not None
self.call.state = CallState.COMPLETE
self.call_center.notify_call_completed(self.call)
@abstractmethod
def escalate_call(self):
pass
def _escalate_call(self):
assert self.call_center is not None
self.call.state = CallState.READY
call = self.call
self.call = None
self.call_center.notify_call_escalated(call)
def set_call_center(self, call_center):
self.call_center = call_center
class Operator(Employee):
def __init__(self, employee_id, name):
super(Operator, self).__init__(employee_id, name, Rank.OPERATOR)
def escalate_call(self):
self.call.level = Rank.SUPERVISOR
self._escalate_call()
class Supervisor(Employee):
def __init__(self, employee_id, name):
super(Supervisor, self).__init__(employee_id, name, Rank.SUPERVISOR)
def escalate_call(self):
self.call.level = Rank.DIRECTOR
self._escalate_call()
class Director(Employee):
def __init__(self, employee_id, name):
super(Director, self).__init__(employee_id, name, Rank.DIRECTOR)
def escalate_call(self):
raise NotImplementedError('Directors must be able to handle any call')
class CallState(Enum):
READY = 0
IN_PROGRESS = 1
COMPLETE = 2
class Call(object):
def __init__(self, rank):
self.state = CallState.READY
self.rank = rank
self.employee = None
class CallCenter(object):
def __init__(self, operators, supervisors, directors):
self.operators = []
self.supervisors = []
self.directors = []
self.queued_calls = deque()
for employee in operators + supervisors + directors:
self.add_employee(employee)
def dispatch_call(self, call):
if call.rank not in (Rank.OPERATOR, Rank.SUPERVISOR, Rank.DIRECTOR):
raise ValueError('Invalid call rank: {}'.format(call.rank))
employee = None
if call.rank == Rank.OPERATOR:
employee = self._dispatch_call(call, self.operators)
if call.rank == Rank.SUPERVISOR or employee is None:
employee = self._dispatch_call(call, self.supervisors)
if call.rank == Rank.DIRECTOR or employee is None:
employee = self._dispatch_call(call, self.directors)
if employee is None:
self.queued_calls.append(call)
def _dispatch_call(self, call, employees):
for employee in employees:
if employee.call is None:
employee.take_call(call)
return employee
return None
def add_employee(self, employee: Employee):
if isinstance(employee, Operator):
self.operators.append(employee)
elif isinstance(employee, Supervisor):
self.supervisors.append(employee)
elif isinstance(employee, Director):
self.directors.append(employee)
else:
raise ValueError('Invalid employee type: {}'.format(type(employee)))
employee.set_call_center(self)
def notify_call_escalated(self, call):
pass
def notify_call_completed(self, call):
pass
def dispatch_queued_call_to_newly_freed_employee(self, call, employee):
pass

View File

@@ -0,0 +1,76 @@
# test_call_center.py - Generated by CodiumAI
import pytest
from call_center.call_center import Operator, Supervisor, Director, Call, CallCenter, Rank
"""
Code Analysis:
- The CallCenter class is used to manage calls in a call center.
- It takes three parameters: operators, supervisors, and directors.
- The queued_calls attribute is a deque object, which is a double-ended queue.
- The dispatch_call() method is used to assign a call to an employee based on the call's rank.
- If an employee is not available, the call is added to the queued_calls deque.
- The _dispatch_call() method is used to assign a call to an employee.
- The notify_call_escalated() and notify_call_completed() methods are used to notify the call center when a call is escalated or completed.
- The dispatch_queued_call_to_newly_freed_employee() method is used to assign a queued call to an employee who has just become available.
"""
"""
Test Plan:
- test_dispatch_call(): tests that the dispatch_call() method assigns the call to the correct employee based on the call's rank
- test_dispatch_call_with_no_available_employee(): tests that the dispatch_call() method adds the call to the queued_calls deque if no employee is available
- test_add_employee(): tests that the add_employee() method adds the employee to the correct list based on the employee's type
"""
class TestCallCenter():
def setup_method(self, method):
self.operators = [Operator(1, 'John'), Operator(2, 'Jane')]
self.supervisors = [Supervisor(3, 'Bob')]
self.directors = [Director(4, 'Alice')]
self.call_center = CallCenter(self.operators, self.supervisors, self.directors)
def test_dispatch_call(self):
call = Call(Rank.OPERATOR)
self.call_center.dispatch_call(call)
assert self.operators[0].call == call
call = Call(Rank.SUPERVISOR)
self.call_center.dispatch_call(call)
assert self.supervisors[0].call == call
call = Call(Rank.DIRECTOR)
self.call_center.dispatch_call(call)
assert self.directors[0].call == call
def test_dispatch_call_with_no_available_employee(self):
for employee in self.operators + self.supervisors + self.directors:
employee.take_call(Call(Rank.OPERATOR))
assert employee.call is not None
call = Call(Rank.OPERATOR)
self.call_center.dispatch_call(call)
assert len(self.call_center.queued_calls) == 1
def test_add_employee(self):
operator = Operator(5, 'Tom')
supervisor = Supervisor(6, 'Jerry')
director = Director(7, 'Sally')
self.call_center.add_employee(operator)
assert operator in self.call_center.operators
self.call_center.add_employee(supervisor)
assert supervisor in self.call_center.supervisors
self.call_center.add_employee(director)
assert director in self.call_center.directors

View File

@@ -0,0 +1,186 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook was prepared by [Abhijeet Antin](https://github.com/abhijeet14antin). Source and license info is on [GitHub](https://github.com/ido777/system-design-primer-update)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Design a circular array"
]
},
{
"cell_type": "markdown",
"metadata": {
"jp-MarkdownHeadingCollapsed": true
},
"source": [
"## Constraints and assumptions\n",
"\n",
"* What are we queueing?\n",
" * We are queueing integers\n",
"* Can we assume inputs are valid or do we have to validate them?\n",
" * Assume they're valid\n",
"* Can we assume this fits memory?\n",
" * Yes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Solution"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting circular_array.py\n"
]
}
],
"source": [
"%%writefile circular_array.py\n",
"\n",
"class CircularArray(object):\n",
"\n",
" def __init__(self, MAX_SIZE):\n",
" self.MAX_SIZE = MAX_SIZE\n",
" self.size = 0\n",
" self.array = [None] * MAX_SIZE\n",
" self.front = 0\n",
" self.back = 0\n",
"\n",
" def push(self, value):\n",
" \"\"\"Push the value to the back of the circular array\n",
" \"\"\"\n",
" if self.size == self.MAX_SIZE:\n",
" raise IndexError('Array is full')\n",
" self.array[self.back] = value\n",
" self.back += 1\n",
" self.size += 1\n",
" \n",
" # wrap around\n",
" if self.back == self.MAX_SIZE:\n",
" self.back = 0\n",
"\n",
" def pop(self):\n",
" \"\"\"Pop a value from the front of the circular array\n",
" \"\"\"\n",
" if self.size == 0:\n",
" raise IndexError('Array is empty')\n",
" ret = self.array[self.front]\n",
" self.front += 1\n",
" self.size -= 1\n",
"\n",
" # wrap around\n",
" if self.front == self.MAX_SIZE:\n",
" self.front = 0\n",
" return ret\n",
" \n",
" def print_array(self):\n",
" print('Array: ', end = ' ')\n",
" if self.size == 0:\n",
" print('empty')\n",
" return\n",
" i = self.front\n",
" while True:\n",
" print(self.array[i], end = ' ')\n",
" i += 1\n",
" if i == self.MAX_SIZE:\n",
" i = 0\n",
" if i == self.back:\n",
" print()\n",
" break\n",
"\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Array: empty\n",
"Array: 1 2 3 4 5 \n",
"Array: 3 4 5 \n",
"Array: 3 4 5 6 \n"
]
}
],
"source": [
"from circular_array import CircularArray\n",
"ca = CircularArray(5)\n",
"ca.print_array()\n",
"ca.push(1)\n",
"ca.push(2)\n",
"ca.push(3)\n",
"ca.push(4)\n",
"ca.push(5)\n",
"ca.print_array()\n",
"\n",
"ca.pop()\n",
"ca.pop()\n",
"ca.print_array()\n",
"\n",
"ca.push(6)\n",
"ca.print_array()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -0,0 +1,53 @@
class CircularArray(object):
def __init__(self, MAX_SIZE):
self.MAX_SIZE = MAX_SIZE
self.size = 0
self.array = [None] * MAX_SIZE
self.front = 0
self.back = 0
def push(self, value):
"""Push the value to the back of the circular array
"""
if self.size == self.MAX_SIZE:
raise IndexError('Array is full')
self.array[self.back] = value
self.back += 1
self.size += 1
# wrap around
if self.back == self.MAX_SIZE:
self.back = 0
def pop(self):
"""Pop a value from the front of the circular array
"""
if self.size == 0:
raise IndexError('Array is empty')
ret = self.array[self.front]
self.front += 1
self.size -= 1
# wrap around
if self.front == self.MAX_SIZE:
self.front = 0
return ret
def print_array(self):
print('Array: ', end = ' ')
if self.size == 0:
print('empty')
return
i = self.front
while True:
print(self.array[i], end = ' ')
i += 1
if i == self.MAX_SIZE:
i = 0
if i == self.back:
print()
break

View File

@@ -0,0 +1,195 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/ido777/system-design-primer-update)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Design a deck of cards"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and assumptions\n",
"\n",
"* Is this a generic deck of cards for games like poker and black jack?\n",
" * Yes, design a generic deck then extend it to black jack\n",
"* Can we assume the deck has 52 cards (2-10, Jack, Queen, King, Ace) and 4 suits?\n",
" * Yes\n",
"* Can we assume inputs are valid or do we have to validate them?\n",
" * Assume they're valid"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Solution"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting deck_of_cards.py\n"
]
}
],
"source": [
"%%writefile deck_of_cards.py\n",
"from abc import ABCMeta, abstractmethod\n",
"from enum import Enum\n",
"import sys\n",
"\n",
"\n",
"class Suit(Enum):\n",
"\n",
" HEART = 0\n",
" DIAMOND = 1\n",
" CLUBS = 2\n",
" SPADE = 3\n",
"\n",
"\n",
"class Card(metaclass=ABCMeta):\n",
"\n",
" def __init__(self, value, suit):\n",
" self.value = value\n",
" self.suit = suit\n",
" self.is_available = True\n",
"\n",
" @property\n",
" @abstractmethod\n",
" def value(self):\n",
" pass\n",
"\n",
" @value.setter\n",
" @abstractmethod\n",
" def value(self, other):\n",
" pass\n",
"\n",
"\n",
"class BlackJackCard(Card):\n",
"\n",
" def __init__(self, value, suit):\n",
" super(BlackJackCard, self).__init__(value, suit)\n",
"\n",
" def is_ace(self):\n",
" return self._value == 1\n",
"\n",
" def is_face_card(self):\n",
" \"\"\"Jack = 11, Queen = 12, King = 13\"\"\"\n",
" return 10 < self._value <= 13\n",
"\n",
" @property\n",
" def value(self):\n",
" if self.is_ace() == 1:\n",
" return 1\n",
" elif self.is_face_card():\n",
" return 10\n",
" else:\n",
" return self._value\n",
"\n",
" @value.setter\n",
" def value(self, new_value):\n",
" if 1 <= new_value <= 13:\n",
" self._value = new_value\n",
" else:\n",
" raise ValueError('Invalid card value: {}'.format(new_value))\n",
"\n",
"\n",
"class Hand(object):\n",
"\n",
" def __init__(self, cards):\n",
" self.cards = cards\n",
"\n",
" def add_card(self, card):\n",
" self.cards.append(card)\n",
"\n",
" def score(self):\n",
" total_value = 0\n",
" for card in self.cards:\n",
" total_value += card.value\n",
" return total_value\n",
"\n",
"\n",
"class BlackJackHand(Hand):\n",
"\n",
" BLACKJACK = 21\n",
"\n",
" def __init__(self, cards):\n",
" super(BlackJackHand, self).__init__(cards)\n",
"\n",
" def score(self):\n",
" min_over = sys.MAXSIZE\n",
" max_under = -sys.MAXSIZE\n",
" for score in self.possible_scores():\n",
" if self.BLACKJACK < score < min_over:\n",
" min_over = score\n",
" elif max_under < score <= self.BLACKJACK:\n",
" max_under = score\n",
" return max_under if max_under != -sys.MAXSIZE else min_over\n",
"\n",
" def possible_scores(self):\n",
" \"\"\"Return a list of possible scores, taking Aces into account.\"\"\"\n",
" # ...\n",
"\n",
"\n",
"class Deck(object):\n",
"\n",
" def __init__(self, cards):\n",
" self.cards = cards\n",
" self.deal_index = 0\n",
"\n",
" def remaining_cards(self):\n",
" return len(self.cards) - deal_index\n",
"\n",
" def deal_card():\n",
" try:\n",
" card = self.cards[self.deal_index]\n",
" card.is_available = False\n",
" self.deal_index += 1\n",
" except IndexError:\n",
" return None\n",
" return card\n",
"\n",
" def shuffle(self): # ..."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

View File

@@ -0,0 +1,117 @@
from abc import ABCMeta, abstractmethod
from enum import Enum
import sys
class Suit(Enum):
HEART = 0
DIAMOND = 1
CLUBS = 2
SPADE = 3
class Card(metaclass=ABCMeta):
def __init__(self, value, suit):
self.value = value
self.suit = suit
self.is_available = True
@property
@abstractmethod
def value(self):
pass
@value.setter
@abstractmethod
def value(self, other):
pass
class BlackJackCard(Card):
def __init__(self, value, suit):
super(BlackJackCard, self).__init__(value, suit)
def is_ace(self):
return True if self._value == 1 else False
def is_face_card(self):
"""Jack = 11, Queen = 12, King = 13"""
return True if 10 < self._value <= 13 else False
@property
def value(self):
if self.is_ace() == 1:
return 1
elif self.is_face_card():
return 10
else:
return self._value
@value.setter
def value(self, new_value):
if 1 <= new_value <= 13:
self._value = new_value
else:
raise ValueError('Invalid card value: {}'.format(new_value))
class Hand(object):
def __init__(self, cards):
self.cards = cards
def add_card(self, card):
self.cards.append(card)
def score(self):
total_value = 0
for card in self.cards:
total_value += card.value
return total_value
class BlackJackHand(Hand):
BLACKJACK = 21
def __init__(self, cards):
super(BlackJackHand, self).__init__(cards)
def score(self):
min_over = sys.MAXSIZE
max_under = -sys.MAXSIZE
for score in self.possible_scores():
if self.BLACKJACK < score < min_over:
min_over = score
elif max_under < score <= self.BLACKJACK:
max_under = score
return max_under if max_under != -sys.MAXSIZE else min_over
def possible_scores(self):
"""Return a list of possible scores, taking Aces into account."""
pass
class Deck(object):
def __init__(self, cards):
self.cards = cards
self.deal_index = 0
def remaining_cards(self):
return len(self.cards) - self.deal_index
def deal_card(self):
try:
card = self.cards[self.deal_index]
card.is_available = False
self.deal_index += 1
except IndexError:
return None
return card
def shuffle(self):
pass

View File

@@ -0,0 +1,121 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/ido777/system-design-primer-update)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Design a hash map"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and assumptions\n",
"\n",
"* For simplicity, are the keys integers only?\n",
" * Yes\n",
"* For collision resolution, can we use chaining?\n",
" * Yes\n",
"* Do we have to worry about load factors?\n",
" * No\n",
"* Can we assume inputs are valid or do we have to validate them?\n",
" * Assume they're valid\n",
"* Can we assume this fits memory?\n",
" * Yes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Solution"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting hash_map.py\n"
]
}
],
"source": [
"%%writefile hash_map.py\n",
"class Item(object):\n",
"\n",
" def __init__(self, key, value):\n",
" self.key = key\n",
" self.value = value\n",
"\n",
"\n",
"class HashTable(object):\n",
"\n",
" def __init__(self, size):\n",
" self.size = size\n",
" self.table = [[] for _ in range(self.size)]\n",
"\n",
" def _hash_function(self, key):\n",
" return key % self.size\n",
"\n",
" def set(self, key, value):\n",
" hash_index = self._hash_function(key)\n",
" for item in self.table[hash_index]:\n",
" if item.key == key:\n",
" item.value = value\n",
" return\n",
" self.table[hash_index].append(Item(key, value))\n",
"\n",
" def get(self, key):\n",
" hash_index = self._hash_function(key)\n",
" for item in self.table[hash_index]:\n",
" if item.key == key:\n",
" return item.value\n",
" raise KeyError('Key not found')\n",
"\n",
" def remove(self, key):\n",
" hash_index = self._hash_function(key)\n",
" for index, item in enumerate(self.table[hash_index]):\n",
" if item.key == key:\n",
" del self.table[hash_index][index]\n",
" return\n",
" raise KeyError('Key not found')"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

View File

@@ -0,0 +1,38 @@
class Item(object):
def __init__(self, key, value):
self.key = key
self.value = value
class HashTable(object):
def __init__(self, size):
self.size = size
self.table = [[] for _ in range(self.size)]
def _hash_function(self, key):
return key % self.size
def set(self, key, value):
hash_index = self._hash_function(key)
for item in self.table[hash_index]:
if item.key == key:
item.value = value
return
self.table[hash_index].append(Item(key, value))
def get(self, key):
hash_index = self._hash_function(key)
for item in self.table[hash_index]:
if item.key == key:
return item.value
raise KeyError('Key not found')
def remove(self, key):
hash_index = self._hash_function(key)
for index, item in enumerate(self.table[hash_index]):
if item.key == key:
del self.table[hash_index][index]
return
raise KeyError('Key not found')

View File

@@ -0,0 +1,141 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/ido777/system-design-primer-update)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Design an LRU cache"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and assumptions\n",
"\n",
"* What are we caching?\n",
" * We are caching the results of web queries\n",
"* Can we assume inputs are valid or do we have to validate them?\n",
" * Assume they're valid\n",
"* Can we assume this fits memory?\n",
" * Yes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Solution"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting lru_cache.py\n"
]
}
],
"source": [
"%%writefile lru_cache.py\n",
"class Node(object):\n",
"\n",
" def __init__(self, results):\n",
" self.results = results\n",
" self.prev = None\n",
" self.next = None\n",
"\n",
"\n",
"class LinkedList(object):\n",
"\n",
" def __init__(self):\n",
" self.head = None\n",
" self.tail = None\n",
"\n",
" def move_to_front(self, node): # ...\n",
" def append_to_front(self, node): # ...\n",
" def remove_from_tail(self): # ...\n",
"\n",
"\n",
"class Cache(object):\n",
"\n",
" def __init__(self, MAX_SIZE):\n",
" self.MAX_SIZE = MAX_SIZE\n",
" self.size = 0\n",
" self.lookup = {} # key: query, value: node\n",
" self.linked_list = LinkedList()\n",
"\n",
" def get(self, query)\n",
" \"\"\"Get the stored query result from the cache.\n",
" \n",
" Accessing a node updates its position to the front of the LRU list.\n",
" \"\"\"\n",
" node = self.lookup.get(query)\n",
" if node is None:\n",
" return None\n",
" self.linked_list.move_to_front(node)\n",
" return node.results\n",
"\n",
" def set(self, results, query):\n",
" \"\"\"Set the result for the given query key in the cache.\n",
" \n",
" When updating an entry, updates its position to the front of the LRU list.\n",
" If the entry is new and the cache is at capacity, removes the oldest entry\n",
" before the new entry is added.\n",
" \"\"\"\n",
" node = self.lookup.get(query)\n",
" if node is not None:\n",
" # Key exists in cache, update the value\n",
" node.results = results\n",
" self.linked_list.move_to_front(node)\n",
" else:\n",
" # Key does not exist in cache\n",
" if self.size == self.MAX_SIZE:\n",
" # Remove the oldest entry from the linked list and lookup\n",
" self.lookup.pop(self.linked_list.tail.query, None)\n",
" self.linked_list.remove_from_tail()\n",
" else:\n",
" self.size += 1\n",
" # Add the new key and value\n",
" new_node = Node(results)\n",
" self.linked_list.append_to_front(new_node)\n",
" self.lookup[query] = new_node"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

View File

@@ -0,0 +1,66 @@
class Node(object):
def __init__(self, results):
self.results = results
self.next = next
class LinkedList(object):
def __init__(self):
self.head = None
self.tail = None
def move_to_front(self, node):
pass
def append_to_front(self, node):
pass
def remove_from_tail(self):
pass
class Cache(object):
def __init__(self, MAX_SIZE):
self.MAX_SIZE = MAX_SIZE
self.size = 0
self.lookup = {} # key: query, value: node
self.linked_list = LinkedList()
def get(self, query):
"""Get the stored query result from the cache.
Accessing a node updates its position to the front of the LRU list.
"""
node = self.lookup.get(query)
if node is None:
return None
self.linked_list.move_to_front(node)
return node.results
def set(self, results, query):
"""Set the result for the given query key in the cache.
When updating an entry, updates its position to the front of the LRU list.
If the entry is new and the cache is at capacity, removes the oldest entry
before the new entry is added.
"""
node = self.lookup.get(query)
if node is not None:
# Key exists in cache, update the value
node.results = results
self.linked_list.move_to_front(node)
else:
# Key does not exist in cache
if self.size == self.MAX_SIZE:
# Remove the oldest entry from the linked list and lookup
self.lookup.pop(self.linked_list.tail.query, None)
self.linked_list.remove_from_tail()
else:
self.size += 1
# Add the new key and value
new_node = Node(results)
self.linked_list.append_to_front(new_node)
self.lookup[query] = new_node

View File

@@ -0,0 +1,159 @@
package oopdesign;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.HashSet;
/**
* Created by jeremyhu on 9/17/22.
*/
// Actors
// Class Theater Kiosk
// Handles the logic for buying a ticket and updating class theater info
// Class Theater:
// Unique ID
// Class Seats:
// Two types: handicap, regular
// Class Movie
// Name
// date time
// Theater ID
// Cost: [regular, child, veteran, elderly]
// Workflow:
// Buying a ticket and updating theater data
// I will assume all the monetary transaction work is done elsewhere
public class MovieTickets {
HashMap<Integer, Theater> theaters;
private Integer theaterSizeDefault = 70;
enum TicketTypes {
REGULAR, CHILD, VETERAN, ELDER;
}
enum SeatTypes {
HANDICAP, REGULAR;
}
class Movie {
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
private String title;
public LocalDate getDate() {
return date;
}
public void setDate(LocalDate date) {
this.date = date;
}
private LocalDate date;
public BigDecimal getRegularPrice() {
return regularPrice;
}
public void setRegularPrice(BigDecimal regularPrice) {
this.regularPrice = regularPrice;
}
private BigDecimal regularPrice;
public Theater getTheater() {
return theater;
}
public void setTheater(Theater theater) {
this.theater = theater;
}
private Theater theater;
public Movie(String title, LocalDate date, BigDecimal price, Theater theater) {
this.title = title;
this.date = date;
this.regularPrice = price;
this.theater = theater;
}
}
class Kiosk {
public int buyTicket(Movie m, Seat s, TicketTypes tType) {
// I assume all the money transaction logic is taken care of somewhere else
processTransaction(m, s, tType);
Theater theater = m.getTheater();
if(theater.remainingSeats() <= 0) {
return -1;
}
theater.takeSeat(s.getId());
return s.getId();
}
public void processTransaction(Movie m, Seat s, TicketTypes tType) {
return;
}
}
class Seat {
private int id;
private SeatTypes type;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Seat(int id, SeatTypes type) {
this.id = id;
this.type = type;
}
}
class Theater {
private HashMap<Integer, Seat> seats;
private int id;
public Theater(int id) {
this.id = id;
this.seats = new HashMap<Integer, Seat>();
}
public int getID() {
return this.id;
}
public void setID(int id) {
this.id = id;
}
public int remainingSeats() {
return seats.size();
}
public int takeSeat(int seatID) {
seats.remove(seatID);
return seats.size();
}
}
}

View File

@@ -0,0 +1,2 @@
Movie tickets kiosk system OOP example in Java
- Jeremy Hu 09/2022

View File

@@ -0,0 +1,174 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/ido777/system-design-primer-update)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Design an online chat"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and assumptions\n",
"\n",
"* Assume we'll focus on the following workflows:\n",
" * Text conversations only\n",
" * Users\n",
" * Add a user\n",
" * Remove a user\n",
" * Update a user\n",
" * Add to a user's friends list\n",
" * Add friend request\n",
" * Approve friend request\n",
" * Reject friend request\n",
" * Remove from a user's friends list\n",
" * Create a group chat\n",
" * Invite friends to a group chat\n",
" * Post a message to a group chat\n",
" * Private 1-1 chat\n",
" * Invite a friend to a private chat\n",
" * Post a message to a private chat\n",
"* No need to worry about scaling initially"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Solution"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting online_chat.py\n"
]
}
],
"source": [
"%%writefile online_chat.py\n",
"from abc import ABCMeta\n",
"\n",
"\n",
"class UserService(object):\n",
"\n",
" def __init__(self):\n",
" self.users_by_id = {} # key: user id, value: User\n",
"\n",
" def add_user(self, user_id, name, pass_hash): # ...\n",
" def remove_user(self, user_id): # ...\n",
" def add_friend_request(self, from_user_id, to_user_id): # ...\n",
" def approve_friend_request(self, from_user_id, to_user_id): # ...\n",
" def reject_friend_request(self, from_user_id, to_user_id): # ...\n",
"\n",
"\n",
"class User(object):\n",
"\n",
" def __init__(self, user_id, name, pass_hash):\n",
" self.user_id = user_id\n",
" self.name = name\n",
" self.pass_hash = pass_hash\n",
" self.friends_by_id = {} # key: friend id, value: User\n",
" self.friend_ids_to_private_chats = {} # key: friend id, value: private chats\n",
" self.group_chats_by_id = {} # key: chat id, value: GroupChat\n",
" self.received_friend_requests_by_friend_id = {} # key: friend id, value: AddRequest\n",
" self.sent_friend_requests_by_friend_id = {} # key: friend id, value: AddRequest\n",
"\n",
" def message_user(self, friend_id, message): # ...\n",
" def message_group(self, group_id, message): # ...\n",
" def send_friend_request(self, friend_id): # ...\n",
" def receive_friend_request(self, friend_id): # ...\n",
" def approve_friend_request(self, friend_id): # ...\n",
" def reject_friend_request(self, friend_id): # ...\n",
"\n",
"\n",
"class Chat(metaclass=ABCMeta):\n",
"\n",
" def __init__(self, chat_id):\n",
" self.chat_id = chat_id\n",
" self.users = []\n",
" self.messages = []\n",
"\n",
"\n",
"class PrivateChat(Chat):\n",
"\n",
" def __init__(self, first_user, second_user):\n",
" super(PrivateChat, self).__init__()\n",
" self.users.append(first_user)\n",
" self.users.append(second_user)\n",
"\n",
"\n",
"class GroupChat(Chat):\n",
"\n",
" def add_user(self, user): # ...\n",
" def remove_user(self, user): # ... \n",
"\n",
"\n",
"class Message(object):\n",
"\n",
" def __init__(self, message_id, message, timestamp):\n",
" self.message_id = message_id\n",
" self.message = message\n",
" self.timestamp = timestamp\n",
"\n",
"\n",
"class AddRequest(object):\n",
"\n",
" def __init__(self, from_user_id, to_user_id, request_status, timestamp):\n",
" self.from_user_id = from_user_id\n",
" self.to_user_id = to_user_id\n",
" self.request_status = request_status\n",
" self.timestamp = timestamp\n",
"\n",
"\n",
"class RequestStatus(Enum):\n",
"\n",
" UNREAD = 0\n",
" READ = 1\n",
" ACCEPTED = 2\n",
" REJECTED = 3"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.6"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -0,0 +1,110 @@
from abc import ABCMeta
from enum import Enum
class UserService(object):
def __init__(self):
self.users_by_id = {} # key: user id, value: User
def add_user(self, user_id, name, pass_hash):
if user_id not in self.users_by_id:
new_user = User(user_id, name, pass_hash)
self.users_by_id[user_id] = new_user
return new_user
else:
# User with this ID already exists
return None
def remove_user(self, user_id):
pass
def add_friend_request(self, from_user_id, to_user_id):
pass
def approve_friend_request(self, from_user_id, to_user_id):
pass
def reject_friend_request(self, from_user_id, to_user_id):
pass
class User(object):
def __init__(self, user_id, name, pass_hash):
self.user_id = user_id
self.name = name
self.pass_hash = pass_hash
self.friends_by_id = {} # key: friend id, value: User
self.friend_ids_to_private_chats = {} # key: friend id, value: private chats
self.group_chats_by_id = {} # key: chat id, value: GroupChat
self.received_friend_requests_by_friend_id = {} # key: friend id, value: AddRequest
self.sent_friend_requests_by_friend_id = {} # key: friend id, value: AddRequest
def message_user(self, friend_id, message):
pass
def message_group(self, group_id, message):
pass
def send_friend_request(self, friend_id):
pass
def receive_friend_request(self, friend_id):
pass
def approve_friend_request(self, friend_id):
pass
def reject_friend_request(self, friend_id):
pass
class Chat(metaclass=ABCMeta):
def __init__(self, chat_id):
self.chat_id = chat_id
self.users = []
self.messages = []
class PrivateChat(Chat):
def __init__(self, first_user, second_user):
super(PrivateChat, self).__init__()
self.users.append(first_user)
self.users.append(second_user)
class GroupChat(Chat):
def add_user(self, user):
pass
def remove_user(self, user):
pass
class Message(object):
def __init__(self, message_id, message, timestamp):
self.message_id = message_id
self.message = message
self.timestamp = timestamp
class AddRequest(object):
def __init__(self, from_user_id, to_user_id, request_status, timestamp):
self.from_user_id = from_user_id
self.to_user_id = to_user_id
self.request_status = request_status
self.timestamp = timestamp
class RequestStatus(Enum):
UNREAD = 0
READ = 1
ACCEPTED = 2
REJECTED = 3

View File

@@ -0,0 +1,204 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/ido777/system-design-primer-update)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Design a parking lot"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and assumptions\n",
"\n",
"* What types of vehicles should we support?\n",
" * Motorcycle, Car, Bus\n",
"* Does each vehicle type take up a different amount of parking spots?\n",
" * Yes\n",
" * Motorcycle spot -> Motorcycle\n",
" * Compact spot -> Motorcycle, Car\n",
" * Large spot -> Motorcycle, Car\n",
" * Bus can park if we have 5 consecutive \"large\" spots\n",
"* Does the parking lot have multiple levels?\n",
" * Yes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Solution"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting parking_lot.py\n"
]
}
],
"source": [
"%%writefile parking_lot.py\n",
"from abc import ABCMeta, abstractmethod\n",
"\n",
"\n",
"class VehicleSize(Enum):\n",
"\n",
" MOTORCYCLE = 0\n",
" COMPACT = 1\n",
" LARGE = 2\n",
"\n",
"\n",
"class Vehicle(metaclass=ABCMeta):\n",
"\n",
" def __init__(self, vehicle_size, license_plate, spot_size):\n",
" self.vehicle_size = vehicle_size\n",
" self.license_plate = license_plate\n",
" self.spot_size = spot_size\n",
" self.spots_taken = []\n",
"\n",
" def clear_spots(self):\n",
" for spot in self.spots_taken:\n",
" spot.remove_vehicle(self)\n",
" self.spots_taken = []\n",
"\n",
" def take_spot(self, spot):\n",
" self.spots_taken.append(spot)\n",
"\n",
" @abstractmethod\n",
" def can_fit_in_spot(self, spot):\n",
" pass\n",
"\n",
"\n",
"class Motorcycle(Vehicle):\n",
"\n",
" def __init__(self, license_plate):\n",
" super(Motorcycle, self).__init__(VehicleSize.MOTORCYCLE, license_plate, spot_size=1)\n",
"\n",
" def can_fit_in_spot(self, spot):\n",
" return True\n",
"\n",
"\n",
"class Car(Vehicle):\n",
"\n",
" def __init__(self, license_plate):\n",
" super(Car, self).__init__(VehicleSize.COMPACT, license_plate, spot_size=1)\n",
"\n",
" def can_fit_in_spot(self, spot):\n",
" return True if (spot.size == LARGE or spot.size == COMPACT) else False\n",
"\n",
"\n",
"class Bus(Vehicle):\n",
"\n",
" def __init__(self, license_plate):\n",
" super(Bus, self).__init__(VehicleSize.LARGE, license_plate, spot_size=5)\n",
"\n",
" def can_fit_in_spot(self, spot):\n",
" return True if spot.size == LARGE else False\n",
"\n",
"\n",
"class ParkingLot(object):\n",
"\n",
" def __init__(self, num_levels):\n",
" self.num_levels = num_levels\n",
" self.levels = []\n",
"\n",
" def park_vehicle(self, vehicle):\n",
" for level in levels:\n",
" if level.park_vehicle(vehicle):\n",
" return True\n",
" return False\n",
"\n",
"\n",
"class Level(object):\n",
"\n",
" SPOTS_PER_ROW = 10\n",
"\n",
" def __init__(self, floor, total_spots):\n",
" self.floor = floor\n",
" self.num_spots = total_spots\n",
" self.available_spots = 0\n",
" self.parking_spots = []\n",
"\n",
" def spot_freed(self):\n",
" self.available_spots += 1\n",
"\n",
" def park_vehicle(self, vehicle):\n",
" spot = self._find_available_spot(vehicle)\n",
" if spot is None:\n",
" return None\n",
" else:\n",
" spot.park_vehicle(vehicle)\n",
" return spot\n",
"\n",
" def _find_available_spot(self, vehicle):\n",
" \"\"\"Find an available spot where vehicle can fit, or return None\"\"\"\n",
" # ...\n",
"\n",
" def _park_starting_at_spot(self, spot, vehicle):\n",
" \"\"\"Occupy starting at spot.spot_number to vehicle.spot_size.\"\"\"\n",
" # ...\n",
"\n",
"\n",
"class ParkingSpot(object):\n",
"\n",
" def __init__(self, level, row, spot_number, spot_size, vehicle_size):\n",
" self.level = level\n",
" self.row = row\n",
" self.spot_number = spot_number\n",
" self.spot_size = spot_size\n",
" self.vehicle_size = vehicle_size\n",
" self.vehicle = None\n",
"\n",
" def is_available(self):\n",
" return True if self.vehicle is None else False\n",
"\n",
" def can_fit_vehicle(self, vehicle):\n",
" if self.vehicle is not None:\n",
" return False\n",
" return vehicle.can_fit_in_spot(self)\n",
"\n",
" def park_vehicle(self, vehicle): # ...\n",
" def remove_vehicle(self): # ..."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

View File

@@ -0,0 +1,125 @@
from abc import ABCMeta, abstractmethod
from enum import Enum
class VehicleSize(Enum):
MOTORCYCLE = 0
COMPACT = 1
LARGE = 2
class Vehicle(metaclass=ABCMeta):
def __init__(self, vehicle_size, license_plate, spot_size):
self.vehicle_size = vehicle_size
self.license_plate = license_plate
self.spot_size = spot_size
self.spots_taken = []
def clear_spots(self):
for spot in self.spots_taken:
spot.remove_vehicle(self)
self.spots_taken = []
def take_spot(self, spot):
self.spots_taken.append(spot)
@abstractmethod
def can_fit_in_spot(self, spot):
pass
class Motorcycle(Vehicle):
def __init__(self, license_plate):
super().__init__(VehicleSize.MOTORCYCLE, license_plate, spot_size=1)
def can_fit_in_spot(self, spot):
return True
class Car(Vehicle):
def __init__(self, license_plate):
super().__init__(VehicleSize.COMPACT, license_plate, spot_size=1)
def can_fit_in_spot(self, spot):
return spot.size in (VehicleSize.LARGE, VehicleSize.COMPACT)
class Bus(Vehicle):
def __init__(self, license_plate):
super().__init__(VehicleSize.LARGE, license_plate, spot_size=5)
def can_fit_in_spot(self, spot):
return spot.size == VehicleSize.LARGE
class ParkingLot(object):
def __init__(self, num_levels):
self.num_levels = num_levels
self.levels = [] # List of Levels
def park_vehicle(self, vehicle):
for level in self.levels:
if level.park_vehicle(vehicle):
return True
return False
class Level(object):
SPOTS_PER_ROW = 10
def __init__(self, floor, total_spots):
self.floor = floor
self.num_spots = total_spots
self.available_spots = 0
self.spots = [] # List of ParkingSpots
def spot_freed(self):
self.available_spots += 1
def park_vehicle(self, vehicle):
spot = self._find_available_spot(vehicle)
if spot is None:
return None
else:
spot.park_vehicle(vehicle)
return spot
def _find_available_spot(self, vehicle):
"""Find an available spot where vehicle can fit, or return None"""
pass
def _park_starting_at_spot(self, spot, vehicle):
"""Occupy starting at spot.spot_number to vehicle.spot_size."""
pass
class ParkingSpot(object):
def __init__(self, level, row, spot_number, spot_size, vehicle_size):
self.level = level
self.row = row
self.spot_number = spot_number
self.spot_size = spot_size
self.vehicle_size = vehicle_size
self.vehicle = None
def is_available(self):
return self.vehicle == None
def can_fit_vehicle(self, vehicle):
if self.vehicle is not None:
return False
return vehicle.can_fit_in_spot(self)
def park_vehicle(self, vehicle):
pass
def remove_vehicle(self):
pass