Python Quest

Track 16 · Lesson 1 of 12

A process for solving problems

Turn an unfamiliar task into working code by restating it, collecting examples, planning in pseudocode, implementing, testing and reflecting.

About 35 minBeginner3 exercisesFree

You will be able to

  • Restate a problem in your own words and name its inputs, outputs and rules
  • Write concrete examples, including edge cases, before writing code
  • Break a task into steps and express the plan as pseudocode
  • Test a function against your examples and use failures to improve it

An algorithm is a precise, step-by-step method for solving a problem. You already write algorithms every time you write a function: "loop over the prices, add each one to a running total, return the total" is an algorithm.

The hard part of a new task is rarely the Python syntax. It is deciding what the steps should be. Imagine your manager at Sahra Logistics asks: "Which hour of the day had the most deliveries yesterday?" If you start typing straight away you will probably write something that works for the first example you think of and breaks on the second. This lesson gives you a process that experienced programmers follow, often without noticing:

  1. Understand — restate the problem. What goes in? What comes out? What are the rules?
  2. Examples — work out a few inputs and their correct outputs by hand, including awkward ones.
  3. Decompose — split the task into smaller steps you already know how to do.
  4. Plan — write the steps as pseudocode: plain-language instructions shaped like code.
  5. Implement — translate the plan into Python.
  6. Test — run your examples. Every mismatch teaches you something.
  7. Reflect — could it be clearer? Faster? What would break it?

Step 1 and 2: understand, then make examples

Restating the delivery question precisely forces decisions. Input: a list of delivery times as "HH:MM" strings. Output: the hour (an integer 0–23) with the most deliveries. Rules you have to choose: what if two hours tie? What if the list is empty?

Examples settle those choices before any code exists:

InputOutputWhy
["09:15", "09:40", "14:05"]9two deliveries at 9, one at 14
["14:05", "09:15", "14:30", "09:40"]9a tie: we choose the earlier hour
["23:59"]23a single delivery
[]Nonenothing to report

The last three rows are edge cases: inputs at the boundary of what the function must handle. Empty input, a single item, ties and duplicates are the usual suspects.

Step 3 and 4: decompose and plan

The task splits into two smaller ones you know from earlier tracks: count deliveries per hour (a dictionary of counts) and find the hour with the largest count (a loop that remembers the best so far). Written as pseudocode:

if there are no times: answer None
counts = empty mapping from hour to count
for each time:
    hour = the number before the colon, as an integer
    add 1 to counts[hour]
best = None
for each hour from smallest to largest:
    if best is None or counts[hour] > counts[best]:
        best = hour
answer best

Pseudocode has no strict syntax. Its job is to let you check the logic — "does looping from the smallest hour give the earlier hour on a tie?" — before Python details get in the way. (It does, because > only replaces the best on a strictly larger count.)

Steps 5 and 6: implement the plan, then test it against the examples
def busiest_hour(times):
    if not times:
        return None
    counts = {}
    for t in times:
        hour = int(t.split(":")[0])
        counts[hour] = counts.get(hour, 0) + 1
    best = None
    for hour in sorted(counts):
        if best is None or counts[hour] > counts[best]:
            best = hour
    return best


examples = [
    (["09:15", "09:40", "14:05"], 9),
    (["14:05", "09:15", "14:30", "09:40"], 9),
    (["23:59"], 23),
    ([], None),
]
for times, expected in examples:
    actual = busiest_hour(times)
    status = "ok" if actual == expected else "MISMATCH"
    print(status, times, "->", actual)

Try it: Add the example (["07:00", "07:30", "08:00", "08:10", "08:20"], 8) to the list and run it. Then change > to >= in the function and see which example breaks.

The table of examples became a tiny test loop. Each row is checked automatically and anything unexpected says MISMATCH. In the Testing track you would write these as assert statements or pytest tests; the idea is the same.

Reflect. The function makes one pass over the times and one pass over at most 24 hours, so it stays fast even for a million deliveries. Could it be clearer? Perhaps collections.Counter would shorten the counting step. Reflection is where you notice such options — after you have something correct.

Predict the output

def busiest_hour(times):
    counts = {}
    for t in times:
        hour = int(t.split(":")[0])
        counts[hour] = counts.get(hour, 0) + 1
    best = None
    for hour in counts:
        if best is None or counts[hour] > counts[best]:
            best = hour
    return best

print(busiest_hour(["14:05", "09:15", "14:30", "09:40"]))
print(busiest_hour(["09:15", "14:05", "14:30", "09:40"]))

Guided practice · ●●difficulty 2 of 5 · about 8 min

Split a bill into instalments

A furniture shop lets customers pay in equal monthly instalments of whole riyals. When the total does not divide evenly, the first months pay one riyal more.

Write instalments(total, months) that returns a list with one amount per month. Follow this plan:

base = total divided by months, rounded down
extra = the remainder of total divided by months
result = empty list
repeat months times, counting i from 0:
    if i is less than extra: add base + 1
    otherwise: add base
return result

Examples:

  • instalments(100, 3) → [34, 33, 33]
  • instalments(90, 3) → [30, 30, 30]
  • instalments(2, 4) → [1, 1, 0, 0]

Python code editor. Tab indents. Press Escape then Tab to leave the editor. Control or Command plus Enter runs the code; Shift plus Enter checks your answer.

Ctrl+Enter run · Shift+Enter check · your code is saved in this browser as you type

Output

Run your code to see its output, or Check to test it against the requirements.

The solution becomes available after you pass, or after all three hints and two checks.

Challenge · ●●●difficulty 3 of 5 · about 12 min

Find the runner-up score

A quiz app shows the top score and the runner-up score. Write runner_up(scores) that returns the second-highest distinct score in a list of integers, or None if there are fewer than two different scores.

Before you code, write your own examples. The rules to respect:

  • runner_up([70, 85, 90, 60]) → 85
  • runner_up([90, 90, 85]) → 85 (two people sharing the top score does not make 90 the runner-up)
  • runner_up([50, 50]) → None
  • runner_up([]) → None

Do not change the list you are given.

Python code editor. Tab indents. Press Escape then Tab to leave the editor. Control or Command plus Enter runs the code; Shift plus Enter checks your answer.

Ctrl+Enter run · Shift+Enter check · your code is saved in this browser as you type

Output

Run your code to see its output, or Check to test it against the requirements.

The solution becomes available after you pass, or after all three hints and two checks.

Debugging · ●●difficulty 2 of 5 · about 8 min

Fix the coldest-reading report

A cold-storage warehouse logs freezer temperatures every hour. coldest_hour(readings) should return the index of the first lowest reading, or None for an empty list.

Users report two problems:

  • For the chiller room, which always stays above zero, readings such as [5, 7, 6] give None instead of 0.
  • When the lowest temperature appears twice, as in [3, 1, 4, 1], it reports the later one (3) instead of the first (1).

Find and fix the bugs. Test your fix with the examples from the report and with an empty list.

Python code editor. Tab indents. Press Escape then Tab to leave the editor. Control or Command plus Enter runs the code; Shift plus Enter checks your answer.

Ctrl+Enter run · Shift+Enter check · your code is saved in this browser as you type

Output

Run your code to see its output, or Check to test it against the requirements.

The solution becomes available after you pass, or after all three hints and two checks.

Summary

  • Restate the problem: name the inputs, the output and every rule, including what happens on ties and empty input.
  • Write examples by hand before coding; edge cases such as empty, single, duplicates and negatives are part of the specification.
  • Decompose the task into steps you know and write them as pseudocode to check the logic early.
  • Turn your examples into automatic checks, then reflect on clarity, speed and inputs that could still break the code.