Skip to content

Bughouse

Congratulations. You're the Head of Technology at Crooked Orbit, a small, slightly chaotic space logistics company run by a crew of questionable astronauts with even more questionable code.

The pay is decent. The codebase is not...

Your job is to work through the issues: bugs, broken functions, things that technically run but really shouldn't.

#1: Name formatter

Pip Hey, so I wrote this name formatter and it's just... not doing anything? 😭 Like it runs fine, no errors, but the names are still all lowercase. I'm calling .capitalize() on each one, I can see it right there in the loop. I don't get it. Can you please have a look?
python
def format_name(full_name):
    parts = full_name.split()
    for part in parts:
        part = part.capitalize()
    return " ".join(parts)

print(format_name("alice van den berg"))

Expected: Alice Van Den Berg

Got: alice van den berg

What's going wrong, and how do you fix it?

Hint

Think about what actually happens when you reassign a variable inside a for loop. Does changing part affect the list it came from?

One way to fix it

When you write for part in parts, Python gives you each item from the list one at a time. part is just a temporary variable that holds the current item. It's not a direct connection to the list itself.

So when you write part = part.capitalize(), you're replacing what part points to. But parts still holds the original strings. You never actually changed the list.

Here's a way to see it clearly:

python
parts = ["alice", "van", "den", "berg"]

for part in parts:
    part = part.capitalize()

print(parts)
# ['alice', 'van', 'den', 'berg']  # nothing changed

To fix it, you need to build a new list with the capitalised values. A list comprehension does this cleanly:

python
def format_name(full_name):
    parts = full_name.split()
    return " ".join(part.capitalize() for part in parts)

print(format_name("alice van den berg"))
# Alice Van Den Berg

part.capitalize() for part in parts goes through each name, capitalises it, and collects the results into a new list. Then .join() puts them back together with spaces.

#2: Top score

Zee Okay so this is embarrassing but I've got this function that's supposed to find the highest score in a list and it just... prints None? Every time. I've gone through the loop like five times, the logic looks fine to me. The scores are definitely in there. I don't know what's going on 😅
python
def highest_score(scores):
    best = 0
    for score in scores:
        if score > best:
            best = score

results = [45, 92, 78, 88, 65]
print(f"Top score: {highest_score(results)}")

Expected: Top score: 92

Got: Top score: None

What's going wrong, and how do you fix it?

Hint

What does a function return if you never write a return statement?

One way to fix it

The loop is fine. The logic is fine. The problem is that highest_score never actually hands anything back. It finds the best score and then does nothing with it.

In Python, if a function doesn't have a return statement, it returns None automatically. That's what you're printing.

The fix is one line at the end:

python
def highest_score(scores):
    best = 0
    for score in scores:
        if score > best:
            best = score
    return best

results = [45, 92, 78, 88, 65]
print(f"Top score: {highest_score(results)}")
# Top score: 92

This comes up a lot. The function looks complete because the logic is all there, but without return, the result just disappears when the function finishes.

#3: Budget check

Orla Okay so I'm building this little budget tracker thing and it crashes every time I enter a number. Like it doesn't even do anything with it, it just immediately blows up with a TypeError. I've checked the logic and it looks fine to me? The threshold is a float, the input is a number... what is it complaining about... help please
python
def check_budget(threshold):
    spent = input("How much have you spent? ")
    if spent >= threshold:
        print("Over budget!")
    else:
        print("You're within budget.")

check_budget(500.0)

Error:

TypeError: '>=' not supported between instances of 'str' and 'float'

What's going wrong, and how do you fix it?

Hint

What type does input() always return, regardless of what the user types?

One way to fix it

input() always returns a string. It doesn't matter that the user typed a number. Python doesn't know that. It just hands back whatever was typed as text.

So when the code tries to compare spent >= threshold, it's comparing a string to a float, and Python refuses to do that. That's the TypeError.

The fix is to convert the input to a number before comparing it. Use float() to handle decimal values:

python
def check_budget(threshold):
    spent = float(input("How much have you spent? "))
    if spent >= threshold:
        print("Over budget!")
    else:
        print("You're within budget.")

check_budget(500.0)
# How much have you spent? 620
# Over budget!

Wrapping input() in float() converts the string to a number right away, so the comparison works as expected.

This is one of the most common sources of confusion in Python: input() looks like it's reading a number, but it always gives you a string. You have to convert it yourself.

#4: Cargo label

Dex I'm generating cargo labels for the manifest and it keeps crashing on this one line. The item name is a string, the weight is a number, seems fine to me? But Python is throwing a fit about concatenation 😩
python
def cargo_label(item, weight):
    label = "Item: " + item + " | Weight: " + weight + "kg"
    return label

print(cargo_label("Moon rocks", 42))

Error:

TypeError: can only concatenate str (not "int") to str

What's going wrong, and how do you fix it?

Hint

Python's + operator doesn't automatically convert numbers to strings. What do you need to do before you can join them?

One way to fix it

When you use + to join strings in Python, every value has to be a string. weight is an integer, so Python doesn't know how to attach it to the surrounding text. It refuses rather than guessing.

The fix is to convert weight to a string with str():

python
def cargo_label(item, weight):
    label = "Item: " + item + " | Weight: " + str(weight) + "kg"
    return label

print(cargo_label("Moon rocks", 42))
# Item: Moon rocks | Weight: 42kg

An f-string is often cleaner for this kind of thing, since it handles the conversion for you:

python
def cargo_label(item, weight):
    return f"Item: {item} | Weight: {weight}kg"

print(cargo_label("Moon rocks", 42))
# Item: Moon rocks | Weight: 42kg

Inside {}, Python automatically converts the value to its string representation, so you don't need str() at all.

#5: Passenger manifest

Cass Something really weird is happening. I've got this function that builds a passenger list for each flight, and the first flight looks fine. But by the third one the manifest has everyone from all the previous flights in it too? I'm not sharing any data between calls, each one is completely separate. I genuinely have no idea what's going on 😰
python
def build_manifest(name, passengers=[]):
    passengers.append(name)
    return passengers

flight_1 = build_manifest("Pip")
flight_2 = build_manifest("Zee")
flight_3 = build_manifest("Orla")

print(flight_1)
print(flight_2)
print(flight_3)

Expected:

['Pip']
['Zee']
['Orla']

Got:

['Pip']
['Pip', 'Zee']
['Pip', 'Zee', 'Orla']

What's going wrong, and how do you fix it?

Hint

Default argument values in Python are evaluated once, when the function is defined. Not each time the function is called. What does that mean for a mutable object like a list?

One way to fix it

This is one of Python's most well-known surprises. When you write passengers=[] as a default argument, Python creates that list once when the function is defined. Every call that uses the default is sharing the exact same list object. So appending to it in one call affects every future call.

The standard fix is to use None as the default and create a fresh list inside the function:

python
def build_manifest(name, passengers=None):
    if passengers is None:
        passengers = []
    passengers.append(name)
    return passengers

flight_1 = build_manifest("Pip")
flight_2 = build_manifest("Zee")
flight_3 = build_manifest("Orla")

print(flight_1)  # ['Pip']
print(flight_2)  # ['Zee']
print(flight_3)  # ['Orla']

Now each call that doesn't pass a list gets its own brand new one.

This applies to any mutable default: lists, dicts, sets. If you want a fresh one each time, don't put it in the signature. Use None and create it inside.