I was asked to do a technical test with the following specification, but my solution was rejected and I was not selected for the available position (Junior to "normal" level, with 4 days of time to finish the test). Could you point out which areas need improvement in my solution?
In software company X, engineers work best when consuming one cup of espresso an hour. The office espresso machine has a first-come-first-serve queue that applies to everyone, except for certain "super busy" engineers who are prioritized before non-super-busy ones. Among competing super-busies the first-come-first-serve principle applies again.
Please implement a simulator for this espresso machine. Input parameters are number of engineers, the chance that an engineer becomes super-busy in some unit of time, and for how long they stay super-busy.
And my solution in Python 3.4: (GitHub version)
machine.py
import msvcrt # Windows-only non-blocking input. Universal would need a lot more things
import random
from classes.espresso import Espresso
from classes.queue import Queue
from classes.coffeeenum import CoffeeEnum
from classes.order import Order
# Launch the machine
num_engineers = 1705
chance_superbusy = 25 # percent
max_time_superbusy = 120 # minutes
espresso_machine = Espresso(num_engineers, chance_superbusy, max_time_superbusy)
espresso_machine.start()
# Setup a few things
queue = Queue()
queue_id = 0
coffee_name = CoffeeEnum()
superbusy = False
# Run the show!
espresso_machine.display_coffee_choices()
while True:
if queue.has_orders() and espresso_machine.is_ready():
espresso_machine.brew(queue.get_next(), queue)
# Read input to get new orders and enter superbusy mode
if msvcrt.kbhit():
# There's a chance the engineer is superbusy!
if random.randrange(0, 100) < chance_superbusy:
print("By surprise, you are now superbusy!")
superbusy = True
input = msvcrt.getch().decode("ascii").upper()
# Engineer can also choose to be superbusy to skip the line (hmm...)
if input == "Y":
print("You are superbusy! Your order now has priority.")
superbusy = True
if input == "S":
queue.status()
if input.isdigit():
if int(input) in range(1, 6):
print("You ordered:", coffee_name.get_name(input))
queue_id += 1
print("Your queue ID is", queue_id)
new_order = [queue_id, input, superbusy]
queue.append(new_order)
superbusy = False
print("Ready for a new order.")
classes/queue.py:
from classes.coffeeenum import CoffeeEnum
from classes.names import Names
class Queue:
normal_queue = []
super_queue = []
# Gets names of coffee types
coffee_enum = CoffeeEnum()
# Connect queue ID with a person name
names = Names()
def __init__(self):
print("Queue is ready")
print("----------")
def get_next(self):
if self.super_queue:
return self.super_queue[0]
elif self.normal_queue:
return self.normal_queue[0]
def has_orders(self):
if self.super_queue or self.normal_queue:
return True
else:
return False
def status(self):
if self.has_orders():
print("Queue contains:")
print("=====")
if self.super_queue:
print("> Superbusy orders:")
for order in self.super_queue:
print(self.names.get_name(order[0]),
"ordered:", self.coffee_enum.get_name(order[1]))
if self.normal_queue:
print("> Normal orders:")
for order in self.normal_queue:
print(self.names.get_name(order[0]),
"ordered:", self.coffee_enum.get_name(order[1]))
print("=====")
else:
print("Queue is empty! Ready for orders...")
def append(self, list_order):
print("> Received order:",
self.coffee_enum.get_name(list_order[1]),
"for", self.names.get_name(list_order[0]),
"(Superbusy:", list_order[2], ")")
if list_order[2]: # Superbusy
self.super_queue.append(list_order)
else:
self.normal_queue.append(list_order)
def remove_order(self, list_order):
if list_order:
if list_order[2]: # Superbusy order
self.super_queue.remove(list_order)
else:
self.normal_queue.remove(list_order)
classes/espresso.py:
from time import time
from classes.queue import Queue
class Espresso:
num_engineers = 0
chance_superbusy = 0 # percent
max_time_superbusy = 0 # minutes
is_running = False
is_brewing = False
brew_started_time = 0
brew_ready_time = time() + 2 # +2 to prevent unneeded message on launch
queue = 0
current_brew = 0
def __init__(self, num_engineers, chance_superbusy, max_time_superbusy):
print("Espresso is booting...")
print("> Engineer count:", num_engineers)
print("> Chance superbusy:", chance_superbusy, "%")
print("> Max time being superbusy:", max_time_superbusy, "minutes")
def start(self):
is_running = True
print("Espresso machine started OK")
print("> Press any key except A-Z or 0-9 to exit")
def display_coffee_choices(self):
print("Pick your poison, Mr. Engineer! \n"
"If you are superbusy, press (Y) for priority lane! \n"
"(S) for Queue Status")
print("1 - Espresso")
print("2 - Cappuccino")
print("3 - Latte")
print("4 - Macchiato")
print("5 - Irish Coffee")
return "" # Otherwise will display "None"
def is_ready(self):
if time() >= self.brew_ready_time and self.is_brewing:
print("Brew ready!")
self.queue.remove_order(self.current_brew)
if not self.queue.has_orders():
print("Queue empty, ready for new orders!")
self.is_brewing = False
if self.is_brewing:
return False
else:
return True
def brew(self, coffee, queue):
if queue.has_orders():
self.queue = queue
self.current_brew = coffee
print("Brewing now... ready in 15 seconds!")
self.brew_started_time = time()
self.brew_ready_time = time() + 15
self.is_brewing = True
classes/order.py:
from classes.coffeeenum import CoffeeEnum
class Order:
id = 0
product = 0
is_superbusy = False
def __init__(self, list_order):
# This is used to get the coffee names from the id
coff = CoffeeEnum()
# Order comes in as a list: [id, product, boolean superbusy]
self.id = list_order[0]
self.product = coff.get_name(list_order[1])
self.is_superbusy = list_order[2]
classes/names.py:
import random
class Names:
def get_name(self, id):
id = int(id)
# Keep value in 0-9 range for demo purposes
if id >= 10:
id = random.randint(0, 9)
return {
1: "Albert",
2: "Benjamin",
3: "Charles",
4: "Dmitri",
5: "Euclid",
6: "Francis",
7: "Gustav",
8: "Hermann",
9: "Isaac",
0: "John",
}.get(id, "Bjarne")
classes/names.py:
class CoffeeEnum:
def get_name(self, id):
id = int(id)
return {
1: "Espresso",
2: "Cappuccino",
3: "Latte",
4: "Macchiato",
5: "Irish Coffee",
}.get(id, "Coffee")