Brokk forged the hammer that holds the sky up, and he has not spoken a civil word since. He takes orders only as terse commands chalked on a slate, and he keeps his stock in a ledger file that he trusts more than his own brother. Build him the order-taker he will not smash.
The fight
Four stages, each building on the last. Every stage opens with your finished code from the stage before, so you pick up where you left off. This fight draws on match, keyword-only parameters, closures, your own exceptions, files and dates.
Stage 1: The slate. Read Brokk's chalked orders.
Stage 2: The anvil. Forge, sell and count the stock.
Stage 3: The ledger. Keep the stock in a file Brokk can trust.
Stage 4: The order book. Tell customers when their order will be ready.
The fight
0 of 4 stages won
Stage 1 of 4
+60 XP
Brokk's orders come chalked on a slate: forge <item> <qty>, sell <item> <qty> or stock. Define an exception class UnknownOrder with a docstring, then write parse(line), which splits the line into words and uses match to return a tuple: 'forge axe 3' gives ('forge', 'axe', 3), 'sell nails 12' gives ('sell', 'nails', 12) and 'stock' gives ('stock',). Anything else, including a quantity that is not all digits, raises UnknownOrder with the whole line in its message. The caught helper at the top is for the tests.
def caught(action):
"""Brokk's tongs: run action and hand back the exception it raised, or None.
The tests use this; leave it as it is."""
try:
action()
except Exception as error:
return error
return None
class UnknownOrder(Exception):
# Give the class a docstring
pass
def parse(line):
# match on the words of the line and return a tuple, or raise UnknownOrder
pass
Ctrl + Enter to run
Run your code to check it against the tests.
Stage 2 of 4
+60 XP
Keep stage 1 and write make_forge(*, strict=True), a factory that returns a function run(line). Each forge keeps its own stock dictionary in the enclosing function. run parses the line and returns a string: forging adds to the stock and returns f'Forged {qty} {item}'; selling takes from it and returns f'Sold {qty} {item}'; stock returns the stock as item=qty pairs sorted by item and joined with ', ', or 'empty' when there is none. An item whose stock reaches 0 is removed. Selling more than there is raises ValueError when strict is true; when it is false, Brokk sells only what he has. strict can only be given by keyword.
def caught(action):
"""Brokk's tongs: run action and hand back the exception it raised, or None.
The tests use this; leave it as it is."""
try:
action()
except Exception as error:
return error
return None
class UnknownOrder(Exception):
"""Brokk has never heard of this order."""
def parse(line):
match line.split():
case ['forge', item, qty] if qty.isdigit():
return ('forge', item, int(qty))
case ['sell', item, qty] if qty.isdigit():
return ('sell', item, int(qty))
case ['stock']:
return ('stock',)
case _:
raise UnknownOrder(f'Brokk does not understand: {line}')
def make_forge(*, strict=True):
# Keep a stock dictionary here, define run(line) inside, and return run
pass
Ctrl + Enter to run
Run your code to check it against the tests.
Stage 3 of 4
+60 XP
Brokk keeps his stock in a ledger file, one item,qty line per item. Keep everything, define an exception class CorruptLedger, and write save(stock, path), which writes the stock dictionary to the file with its lines sorted by item, and load(path), which reads it back into a dictionary with whole-number quantities. A missing file loads as {}. A line that cannot be read, because it does not have exactly one comma or its quantity is not a whole number, raises CorruptLedger with the message f'line {number}: {line}' (lines count from 1), chained from the original error with raise ... from. The chalk helper is for the tests.
def caught(action):
"""Brokk's tongs: run action and hand back the exception it raised, or None.
The tests use this; leave it as it is."""
try:
action()
except Exception as error:
return error
return None
class UnknownOrder(Exception):
"""Brokk has never heard of this order."""
def parse(line):
match line.split():
case ['forge', item, qty] if qty.isdigit():
return ('forge', item, int(qty))
case ['sell', item, qty] if qty.isdigit():
return ('sell', item, int(qty))
case ['stock']:
return ('stock',)
case _:
raise UnknownOrder(f'Brokk does not understand: {line}')
def make_forge(*, strict=True):
stock = {}
def run(line):
match parse(line):
case ('forge', item, qty):
stock[item] = stock.get(item, 0) + qty
return f'Forged {qty} {item}'
case ('sell', item, qty):
have = stock.get(item, 0)
Ctrl + Enter to run
Run your code to check it against the tests.
Stage 4 of 4
+60 XP
Orders take working days, and Brokk never works on a Sunday. Keep everything and write ready_on(start, days), which takes an ISO date string such as '2026-09-19' and a number of working days, and returns the ISO date string when the order is ready. Count forward one day at a time with timedelta, and do not count Sundays (weekday() is 6 on a Sunday). date.fromisoformat() turns the string into a date, and a date's .isoformat() turns it back. For example, ready_on('2026-09-19', 1) is '2026-09-21', because the 20th is a Sunday.
def caught(action):
"""Brokk's tongs: run action and hand back the exception it raised, or None.
The tests use this; leave it as it is."""
try:
action()
except Exception as error:
return error
return None
class UnknownOrder(Exception):
"""Brokk has never heard of this order."""
def parse(line):
match line.split():
case ['forge', item, qty] if qty.isdigit():
return ('forge', item, int(qty))
case ['sell', item, qty] if qty.isdigit():
return ('sell', item, int(qty))
case ['stock']:
return ('stock',)
case _:
raise UnknownOrder(f'Brokk does not understand: {line}')
def make_forge(*, strict=True):
stock = {}
def run(line):
match parse(line):
case ('forge', item, qty):
stock[item] = stock.get(item, 0) + qty
return f'Forged {qty} {item}'
case ('sell', item, qty):
have = stock.get(item, 0)