LEARN PYTHON | 02 DECISIONS AND LOOPS

Build it. Test it. Explain it.

Put comparisons, decisions, loops and debugging to work in small useful programs. Work independently before opening a hint, then compare your tested solution with one possible approach.

YOUR CHALLENGE ROUTE

Complete these tasks in your Python editor. The page is a challenge guide, not a general Python runner. Use fictional details where helpful. Work in order or choose a task matching an area you need to practise.

The solutions use the comparisons, decisions, loops and targeted input handling taught in Section 2. Earlier tasks state their numeric-text assumptions; later tasks practise controlled rejection. Record each program’s limits when testing.

TRACK YOUR TESTED SOLUTIONS

Tick a task only after you have run it, checked at least two relevant cases and explained one line. These are self-recorded checks, not an automatic assessment of your source code.

Progress lasts while this page is open. Save your actual Python files in your project folder.

01 | FOUNDATION

TEST A VALID SCORE

Create a score checker that reports whether an INTEGER is between zero and one hundred inclusive. First print the condition results, rather than immediately hiding them in a decision.

YOUR REQUIREMENTS

  • Convert suitable input using int().
  • Store the lower comparison and upper comparison.
  • Combine them with and.
  • Print a labelled validity result.
  • Test both endpoints and the immediately outside values.
Hint: where to begin

Inside a range requires both limits. Compare score >= 0 and score <= 100; or between these inside comparisons would accept outside values.

TEST YOUR PROGRAM

Check predictions against actual results
Test input or actionExpected result
-1Validity False
0Validity True
100Validity True
101Validity False
Reveal one possible solution and explanation
score = int(input("Whole-number score: "))
lower_ok = score >= 0
upper_ok = score <= 100
valid = lower_ok and upper_ok
print("Lower limit:", lower_ok)
print("Upper limit:", upper_ok)
print("Valid score:", valid)

The two comparisons establish separate limits. Both must hold for a valid score. This first version assumes integer text; a range condition does not catch a conversion failure occurring before it.

A different implementation can also be correct if it meets the requirements and tests. Compare meaning and behaviour, rather than only copying the source.

Extension: Print an outside-range Boolean using or and verify that it is the opposite result for these ordinary integers.

Revisit the related lesson

02 | REPAIR LOGIC

REPAIR THE EITHER-RESPONSE RULE

A response checker accepts no when it should accept only yes or y. Repair the condition and explain why the original was wrong.

YOUR REQUIREMENTS

  • Ask for text and strip surrounding whitespace.
  • Compare the response explicitly with both accepted strings.
  • Report whether the response is accepted.
  • Test yes, y and no.

SOURCE TO REPAIR

response = input("Type yes or y: ")
accepted = response == "yes" or "y"
print("Accepted:", accepted)
Hint: where to begin

The non-empty string "y" is truthy by itself. It does not compare the response with y.

TEST YOUR PROGRAM

Check predictions against actual results
Test input or actionExpected result
yesTrue
yTrue
noFalse
Blank responseFalse
Reveal one possible solution and explanation
response = input("Type yes or y: ").strip()
accepted = (response == "yes") or (response == "y")
print("Accepted:", accepted)

Each operand is now a comparison. The original response == "yes" or "y" evaluates a truthy second operand when the first comparison is false, so it does not implement the intended two-response test.

A different implementation can also be correct if it meets the requirements and tests. Compare meaning and behaviour, rather than only copying the source.

Extension: Add lower() to make the check case-insensitive. Explain that it returns lowercase text rather than modifying the original string automatically.

Revisit the related lesson

03 | FOUNDATION

CLASSIFY A NUMBER

Write a program that reports positive, negative or zero, then independently reports whether the number is even.

YOUR REQUIREMENTS

  • Receive an INTEGER.
  • Use one if/elif/else chain for the three exclusive categories.
  • Use a separate if for evenness.
  • Label the messages clearly.
Hint: where to begin

Modulo two has remainder zero for even integers, including zero and negative even values. The independent evenness notice can apply alongside a sign category.

TEST YOUR PROGRAM

Check predictions against actual results
Test input or actionExpected result
-2Negative and Even
0Zero and Even
3Positive only
Reveal one possible solution and explanation
number = int(input("Whole number: "))
if number > 0:
    print("Positive")
elif number < 0:
    print("Negative")
else:
    print("Zero")
if number % 2 == 0:
    print("Even")

Sign categories are exclusive, so one chain is appropriate. Evenness is a separate property. An independent if allows a positive or negative number to receive an evenness notice without replacing its sign result.

A different implementation can also be correct if it meets the requirements and tests. Compare meaning and behaviour, rather than only copying the source.

Extension: Add a separate odd notice and test a negative odd integer.

Revisit the related lesson

04 | REPAIR SELECTION

REPAIR THE SCORE BANDS

The broken chain below never awards Distinction to valid scores of eighty or more. Correct it and also reject scores outside zero to one hundred.

YOUR REQUIREMENTS

  • Validate the range before classifying.
  • Test the highest band first.
  • Return exactly one category message.
  • Use boundary tests for both category changes.

SOURCE TO REPAIR

score = 85
if score >= 50:
    print("Pass")
elif score >= 80:
    print("Distinction")
else:
    print("Not a pass")
Hint: where to begin

In a chain, the first matching branch wins. A later mathematically true condition may be not tested.

TEST YOUR PROGRAM

Check predictions against actual results
Test input or actionExpected result
49; 50Not a pass; Pass
79; 80Pass; Distinction
-1; 101Invalid score for both
Reveal one possible solution and explanation
score = int(input("Score from 0 to 100: "))
if score < 0 or score > 100:
    print("Invalid score")
elif score >= 80:
    print("Distinction")
elif score >= 50:
    print("Pass")
else:
    print("Not a pass")

The range rejection prevents invalid values receiving ordinary bands. Testing >= 80 before >= 50 leaves the lower threshold for values not already in the distinction route. Only one chain branch runs.

A different implementation can also be correct if it meets the requirements and tests. Compare meaning and behaviour, rather than only copying the source.

Extension: Use the debugger or diagnostic prints to record which conditions are not tested for score 85.

Revisit the related lesson

05 | COUNTED REPETITION

MAKE A COUNTDOWN AND TABLE

Use range to create a countdown, then a multiplication table. Explain the supplied values rather than only copying a header.

YOUR REQUIREMENTS

  • Count from five down to one with a negative step.
  • Print Launch after the countdown body.
  • Print a chosen number’s table from multiplier one through ten.
  • Read the chosen base once before its table loop.
Hint: where to begin

Both range stops are excluded. The countdown stops before zero, and the table stops before eleven.

TEST YOUR PROGRAM

Check predictions against actual results
Test input or actionExpected result
Countdown5, 4, 3, 2, 1, then Launch!
Base 3First product 3; final product 30; ten rows
Base 0Ten products equal zero
Reveal one possible solution and explanation
for number in range(5, 0, -1):
    print(number)
print("Launch!")
base = int(input("Table number: "))
for multiplier in range(1, 11):
    print(f"{base} x {multiplier} = {base * multiplier}")

for assigns each supplied range value to its loop variable. The indented body repeats; the launch statement is outside the body and runs once after it. Excluding the stop gives the intended endpoints.

A different implementation can also be correct if it meets the requirements and tests. Compare meaning and behaviour, rather than only copying the source.

Extension: Ask for the countdown start. State what happens for zero and a negative start under your chosen requirement.

Revisit the related lesson

06 | ACCUMULATION

TOTAL THE EVEN VALUES

Calculate the sum and count of even integers from one through an inclusive positive limit. Report a mean only when at least one qualifies.

YOUR REQUIREMENTS

  • Initialise total and count once before the loop.
  • Use a range that includes the requested limit.
  • Add a value only when its remainder modulo two is zero.
  • Count qualifying values separately.
  • Guard the mean against count zero.
Hint: where to begin

A total adds the amount; a count adds one. A limit of one contains no positive even values.

TEST YOUR PROGRAM

Check predictions against actual results
Test input or actionExpected result
Limit 6Total 12; count 3; mean 4.0
Limit 5Total 6; count 2; mean 3.0
Limit 1Total 0; count 0; no mean calculation
Reveal one possible solution and explanation
limit = int(input("Positive whole-number limit: "))
total = 0
count = 0
for number in range(1, limit + 1):
    if number % 2 == 0:
        total += number
        count += 1
print("Even total:", total)
print("Even count:", count)
if count > 0:
    print("Even mean:", total / count)
else:
    print("No qualifying values")

limit + 1 compensates for the excluded stop. The condition filters additions without changing the range visits. The guard prevents division by zero when no positive even value qualifies.

A different implementation can also be correct if it meets the requirements and tests. Compare meaning and behaviour, rather than only copying the source.

Extension: Add temporary partial-total prints, predict them for limit six, then remove the diagnostic output.

Revisit the related lesson

07 | CONDITION-CONTROLLED LOOP

RETRY A SCORE UNTIL VALID

Ask for an INTEGER score and keep retrying until it lies from zero to one hundred inclusive. This version assumes every response is integer text.

YOUR REQUIREMENTS

  • Receive a value before the first condition test.
  • Repeat while either outside-range condition is true.
  • Read a new response during every retry.
  • Print the accepted score after the loop.
Hint: where to begin

Inside uses and; outside uses or. A valid first response should need no retry-body execution.

TEST YOUR PROGRAM

Check predictions against actual results
Test input or actionExpected result
50Immediate acceptance with no retry message
-1, then 100One retry, then accepted 100
101, then 0One retry, then accepted 0
Reveal one possible solution and explanation
score = int(input("Score from 0 to 100: "))
while score < 0 or score > 100:
    print("Outside the allowed range")
    score = int(input("Try again: "))
print("Accepted score:", score)

while tests before its body. The initial valid response skips the body; an invalid response enters it. The new assignment ensures the next test examines the new value instead of repeatedly rejecting the same one.

A different implementation can also be correct if it meets the requirements and tests. Compare meaning and behaviour, rather than only copying the source.

Extension: After mastering the core, add targeted ValueError handling so unsuitable text can also be retried.

Revisit the related lesson

08 | SENTINEL AND VALIDATION

TOTAL UNTIL THE FINISH MARKER

Total non-negative INTEGER counts until -1 is entered. Ignore other negative values with feedback rather than adding them. Assume integer text input.

YOUR REQUIREMENTS

  • Use -1 as the sentinel.
  • Exit with break before adding the sentinel.
  • Use continue to skip other negative values.
  • Read a new value at the beginning of every iteration.
  • Print the final total once.
Hint: where to begin

A while True loop needs a reachable exit. Reading at the top means continue still leads to another input rather than freezing the state.

TEST YOUR PROGRAM

Check predictions against actual results
Test input or actionExpected result
4, 7, -1Total 11
-2, 5, -1Reject -2; total 5
-1Total 0 with no ordinary values
Reveal one possible solution and explanation
total = 0
while True:
    value = int(input("Non-negative count, or -1 to finish: "))
    if value == -1:
        break
    if value < 0:
        print("That negative value is not accepted")
        continue
    total += value
print("Total:", total)

The sentinel is a control signal, not data. Its check precedes the negative-value rejection. continue skips the addition for an unacceptable negative count; the next iteration reads a fresh value.

A different implementation can also be correct if it meets the requirements and tests. Compare meaning and behaviour, rather than only copying the source.

Extension: Count accepted values separately and output their mean only when count is positive.

Revisit the related lesson

09 | CONTROL-FLOW INVESTIGATION

TRACE BREAK AND CONTINUE

Trace the sample below before running it. Explain why three and five do not print, and why the loop finishes without printing six.

YOUR REQUIREMENTS

  • Record count after each update.
  • Record which if tests are reached.
  • List printed values in order.
  • Distinguish leaving the iteration from leaving the loop.
Hint: where to begin

The update occurs before continue. break at five stops the loop before its normal condition reaches six.

TEST YOUR PROGRAM

Check predictions against actual results
Test input or actionExpected result
Original programOutputs 1, 2, 4, then Finished
Change skipped value to 2Outputs 1, 3, 4, then Finished
Explain count at exitcount is 5; break stops before print
Reveal one possible solution and explanation
count = 0
while count < 6:
    count += 1
    if count == 3:
        continue
    if count == 5:
        break
    print(count)
print("Finished")

continue at three skips the second test and print for that iteration. break at five exits the loop entirely. The initial update keeps progress even when the remaining body is skipped.

A different implementation can also be correct if it meets the requirements and tests. Compare meaning and behaviour, rather than only copying the source.

Extension: Trace what would happen if the update were moved below continue. Do not run an unbounded broken version to prove the problem.

Revisit the related lesson

10 | DEBUG AND TEST

RUN A SMALL DEBUGGING CLINIC

Repair the repeated-total reset and then add controlled integer input handling to a separate short program. Explain the fault and the handler’s responsibility.

YOUR REQUIREMENTS

  • Initialise total before its for loop.
  • Predict total for one through three.
  • Use except ValueError around an integer conversion.
  • Test successful and failed conversion separately.
  • Do not claim conversion handling proves range validity.

SOURCE TO REPAIR

for number in range(1, 4):
    total = 0
    total += number
print(total)
Hint: where to begin

A repeated reset erases earlier contributions. Put only the potentially failing conversion inside try.

TEST YOUR PROGRAM

Check predictions against actual results
Test input or actionExpected result
Repaired totalFinal total 6
Input 4Converted count: 4
Input fourControlled conversion message, not a successful count
Input -2Conversion succeeds; a range rule would be separate
Reveal one possible solution and explanation
total = 0
for number in range(1, 4):
    total += number
print("Total:", total)
text = input("Whole-number count: ")
try:
    count = int(text)
except ValueError:
    print("That response is not a whole number")
else:
    print("Converted count:", count)

total = 0 belongs before repetition to preserve earlier additions. The ValueError handler responds to unsuitable integer text. A successfully converted negative number may still be unsuitable for the application, so validation remains separate.

A different implementation can also be correct if it meets the requirements and tests. Compare meaning and behaviour, rather than only copying the source.

Extension: Use a labelled diagnostic print after each addition, compare with a dry run and remove it after verification.

Revisit the related lesson

11 | CAPSTONE

BUILD THE GUESSING GAME

Build the complete Section 2 game with secret twelve for testing before using a random target.

YOUR REQUIREMENTS

  • Allow five accepted in-range INTEGER guesses from 1 to 20.
  • Give low/high feedback and record success.
  • Retry unsuitable text and out-of-range numbers without charging an attempt.
  • Accept quit as a voluntary exit.
  • Distinguish quit, win and exhausted guesses after the loop.
  • Test a correct fifth guess before switching to randint.
Hint: where to begin

Choose the secret and initialise flags before the loop. Update attempts only after successful conversion and range checks.

TEST YOUR PROGRAM

Check predictions against actual results
Test input or actionExpected result
7, 16, 12Low, high, win with 3 accepted guesses
hello, 25, 12Two rejection messages; win with 1 accepted guess
1, 2, 3, 4, 12Win on guess 5, not a loss
1, 2, 3, 4, 5Loss reveals secret 12
quitVoluntary exit after 0 accepted guesses
Reveal one possible solution and explanation
# Number guessing game: fixed secret for reproducible testing
MIN_NUMBER = 1
MAX_NUMBER = 20
MAX_ATTEMPTS = 5
secret = 12
attempts = 0
won = False
quit_game = False

print("Guess the secret number")
print(f"Range: {MIN_NUMBER} to {MAX_NUMBER}; accepted guesses: {MAX_ATTEMPTS}")

while attempts < MAX_ATTEMPTS and not won:
    text = input(f"Guess {MIN_NUMBER} to {MAX_NUMBER}, or quit: ").strip()
    if text == "quit":
        quit_game = True
        break

    try:
        guess = int(text)
    except ValueError:
        print("Enter a whole number or the command quit")
        continue

    if guess < MIN_NUMBER or guess > MAX_NUMBER:
        print("That number is outside the allowed range")
        continue

    attempts += 1
    if guess < secret:
        print("Too low")
    elif guess > secret:
        print("Too high")
    else:
        won = True

if quit_game:
    print(f"Game ended after {attempts} accepted guesses.")
elif won:
    print(f"Correct! You used {attempts} accepted guesses.")
else:
    print(f"No guesses left. The secret was {secret}.")

The state records what happened, rather than treating every loop exit as a loss. continue prevents rejected responses reaching the counter; break handles quit; the while condition stops after success or the accepted-attempt limit.

A different implementation can also be correct if it meets the requirements and tests. Compare meaning and behaviour, rather than only copying the source.

Extension: Add a remaining-attempt message after each wrong accepted guess. Rerun all five route tests.

Revisit the related lesson

12 | REFLECT AND EXTEND

PERSONALISE, HAND OVER AND REGRESSION TEST

Improve the game without weakening its rules. Give a partner a short user guide and ask them to test it without coaching.

YOUR REQUIREMENTS

  • Choose one improvement: clearer remaining guesses or case-insensitive quit.
  • Make one change at a time.
  • Rerun win, last-attempt win, loss, invalid-response and quit tests.
  • Keep fixed-secret testing separate from random play.
  • Write a short explanation of one loop-control decision.
Hint: where to begin

To make the command case-insensitive, compare text.lower() with "quit". Keep integer conversion on the original stripped response.

TEST YOUR PROGRAM

Check predictions against actual results
Test input or actionExpected result
QUITVoluntary exit after 0 accepted guesses
quit with surrounding spacesVoluntary exit after 0 accepted guesses
12Win with 1 accepted guess
hello, 12One rejection; win with 1 accepted guess
Reveal one possible solution and explanation
# Number guessing game: fixed secret for reproducible testing
MIN_NUMBER = 1
MAX_NUMBER = 20
MAX_ATTEMPTS = 5
secret = 12
attempts = 0
won = False
quit_game = False

print("Guess the secret number")
print(f"Range: {MIN_NUMBER} to {MAX_NUMBER}; accepted guesses: {MAX_ATTEMPTS}")

while attempts < MAX_ATTEMPTS and not won:
    text = input(f"Guess {MIN_NUMBER} to {MAX_NUMBER}, or quit: ").strip()
    if text.lower() == "quit":
        quit_game = True
        break

    try:
        guess = int(text)
    except ValueError:
        print("Enter a whole number or the command quit")
        continue

    if guess < MIN_NUMBER or guess > MAX_NUMBER:
        print("That number is outside the allowed range")
        continue

    attempts += 1
    if guess < secret:
        print("Too low")
    elif guess > secret:
        print("Too high")
    else:
        won = True

if quit_game:
    print(f"Game ended after {attempts} accepted guesses.")
elif won:
    print(f"Correct! You used {attempts} accepted guesses.")
else:
    print(f"No guesses left. The secret was {secret}.")

lower returns lowercase text for the command comparison. The existing strip removes surrounding whitespace. The numeric path, validation order and accepted-attempt count remain unchanged. Rechecking earlier routes provides evidence that the extension did not damage them.

A different implementation can also be correct if it meets the requirements and tests. Compare meaning and behaviour, rather than only copying the source.

Extension: Plan replay as a later extension: identify which state must be reset each round before attempting to implement it.

Revisit the related lesson

KEEP A SMALL TEST RECORD

Record input, expected behaviour, actual behaviour and your conclusion. Choose cases that distinguish the fault rather than only ordinary values.

ProgramInputExpected
Score bands80Distinction only
Even totalLimit 1Total 0, count 0, no mean
Guessing gamehello, 25, 12 with secret 12Two rejections, win with one accepted guess

An unhandled exception is not the same as intended controlled rejection. For a known limitation, record the failure and the broken assumption. After a correction, rerun earlier passing cases.

READY FOR THE NEXT SECTION?

You are ready to move on when you can explain Boolean conditions, choose a selection route, trace a for or while loop, handle a known conversion failure and test the game’s different outcomes.

If one skill is uncertain, revisit its lesson and change a worked example before repeating the challenge. The Section 2 workbook is coming soon.

Next: strings, indexing and methods Python course navigation