Functions: Arguments, Scope & Small Programs

Pre-COSMOS Day 11 - Functions, part 2 of 2, an advanced session of 50 slides. It picks up where Day 10 left off and goes deep on how small functions cooperate to make a real program. It covers positional against keyword arguments and why defaults must come last, then scope: locals do not leak, parameters are local copies, and assigning to a global raises an UnboundLocalError - with the clean alternative of passing values in and returning them out. From there it covers the accumulator pattern, calling functions inside loops, decomposing a problem into helpers with a main loop, and the random module, using seed for reproducible demos. It culminates in a fully scaffolded your-turn build of Robo-Battle: two robots, a decide(hp) function, and a turn loop that runs until one robot's hp reaches 0, with a randomized-damage stretch. There are five checks, three traps, two real photos, and diagrams of scope and decomposition. Every snippet, including the seeded random calls and the full battle, was run in real Python with the output copied verbatim into the trace tables.

Subject: Python · 83 slides · code lesson

Open the interactive version of this deck · Homework for this lesson

What this lesson covers

The lesson, slide by slide

1. Arguments, Scope & Small Programs

Title

Day 11 · Functions, Part 2 of 2

This is the day it clicks: many small functions, cooperating - exactly how the camp robot's code is organized.

2. What you will be able to do

Objectives

Yesterday you wrote single helpers. Today you make them work together into a real program. By the end you can:

3. What survived from Dictionaries: Mapping Colors to Actions?

Warm-up

Discussion prompt

Before we open Functions: Arguments, Scope & Small Programs: without looking back, what was the main idea of Dictionaries: Mapping Colors to Actions, and what could you do by the end of it that you could not do before?

Hint: One sentence for the idea, one for the skill. If the second one is blank, that is the part to revisit.

Answer:

Dictionaries as the natural way to map a color to an action or a state to what comes next - exactly how the camp's finite-state robot logic works (the diagnostic rated this fragile). Key-to-value lookup, dict vs list, the KeyError trap (keys not number positions: actions["red"] not actions[0]), safe lookups with .get(key, default) and the in operator, adding/updating keys and len, looping with keys()/values()/items(), and two advanced patterns: the counter/tally and the dict-as-finite-state-machine.

4. Why this is the day it clicks

Concept

A humanoid robot lit from above in the dark
Real robot code = many small functions cooperating.

Real programs aren't one long script. They're several small functions working together - which is exactly how the camp's robot code is organized.

Day 10 gave you the parts: def, parameters, return. Today is assembly - making the parts cooperate without tripping over each other.

The thing that makes cooperation safe is understanding scope - so that's where most of today goes.

5. Break it if you can: Why this is the day it clicks

Counterexample

Discussion prompt

Real programs aren't one long script. They're several small functions working together - which is exactly how the camp's robot code is organized.

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

Answer:

Day 10 gave you the parts: def, parameters, return. Today is assembly - making the parts cooperate without tripping over each other.

6. Today's roadmap

Concept

Four ideas, then you build a whole game out of them:

Arguments
Positional vs keyword, and default order.
Scope
What stays inside a function, and what doesn't.
Functions + loops
Call helpers inside a loop to run a program.
Robo-Battle
Two robots, decide(hp), a turn loop.

7. Which is which: Today's roadmap

Matching

Match the pairs

From Today's roadmap — match each one to what it actually does. The descriptions have been shuffled.

  • c1. Arguments
  • c2. Scope
  • c3. Functions + loops
  • c4. Robo-Battle
  • b1. Positional vs keyword, and default order.
  • b2. What stays inside a function, and what doesn't.
  • b3. Call helpers inside a loop to run a program.
  • b4. Two robots, decide(hp), a turn loop.

Why: Arguments, Scope, Functions + loops, Robo-Battle are easy to tell apart while they are sitting next to their descriptions and much harder afterwards, which is what this checks.

8. Arguments, Up Close

Section

Section 1

9. Positional vs keyword arguments

Concept

Positional: values fill the parameters left-to-right by position. Keyword: you name the parameter, power=9, so position no longer matters.

keyword argument — An argument passed by name, like direction="left". It goes to that exact parameter, so you can skip earlier ones that have defaults.

10. One function, several call styles

Worked example

direction and power have defaults, so callers can supply as much or as little as they want.

def move(speed, direction="forward", power=5):
    return (speed, direction, power)

print(move(10))
print(move(10, "left"))
print(move(10, power=9))

move(10, power=9) skips direction (keeps its default) and sets power by name.

callspeed, direction, powerreturns
move(10)10, forward, 5(10, 'forward', 5)
move(10, "left")10, left, 5(10, 'left', 5)
move(10, power=9)10, forward, 9(10, 'forward', 9)

11. Fill in: speed, direction, power for One function, several call styles

Comparison

Comparison matrix

From One function, several call styles: refill the speed, direction, power column from what you know. The rest of the table is as it appeared.

callspeed, direction, powerreturns
move(10)10, forward, 5(10, 'forward', 5)
move(10, "left")10, left, 5(10, 'left', 5)
move(10, power=9)10, forward, 9(10, 'forward', 9)

12. Why keyword arguments help

Intuition

Compare set_motor(1, 0, 1) with set_motor(left=1, right=0, brake=1). Same call - but the second one reads like a sentence.

Keyword arguments make a call self-documenting and let you skip the middle settings you don't care about.

13. By analogy: Why keyword arguments help

Analogy

Discussion prompt

Explain Why keyword arguments help by analogy to something with no Python in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

Compare set_motor(1, 0, 1) with set_motor(left=1, right=0, brake=1). Same call - but the second one reads like a sentence.

14. Defaults come last

Concept

In a def, every parameter with a default must come after all the parameters without one. Otherwise Python can't tell which value belongs where.

default value — A fallback written as name=value in the def. Required (no-default) parameters must be listed before any defaulted ones.

15. Take the definitions apart: keyword argument vs default value

Definition probe

Sort into buckets

Every line below is part of the definition of keyword argument or of default value — one or the other, never both. Put each where it belongs.

keyword argument
An argument passed by name, like direction="left".; It goes to that exact parameter, so you can skip earlier ones that have defaults.
default value
A fallback written as name=value in the def.; Required (no-default) parameters must be listed before any defaulted ones.
b1
An argument passed by name, like direction="left". It goes to that exact parameter, so you can skip earlier ones that have defaults.
b2
A fallback written as name=value in the def. Required (no-default) parameters must be listed before any defaulted ones.

16. Mixing positional and keyword in one call

Worked example

You can start positional, then switch to keyword - but never go back.

def move(speed, direction="forward", power=5):
    return (speed, direction, power)

print(move(speed=10, direction="right"))
print(move(10, "left", 9))

All-keyword (line 4) and all-positional (line 5) both work.

callreturns
move(speed=10, direction="right")(10, 'right', 5)
move(10, "left", 9)(10, 'left', 9)

17. What each one costs: Mixing positional and keyword in one call

Trade off

Comparison matrix

From Mixing positional and keyword in one call: every row here is a choice with a cost. Fill the returns column, then say which row you would actually pick and what you give up for it.

callreturns
move(speed=10, direction="right")(10, 'right', 5)
move(10, "left", 9)(10, 'left', 9)

18. Something is wrong here: a positional argument after a keyword one

Anomaly

Predict first

A student writes this, and it looks reasonable:

Once you go keyword, you can't drop back to positional.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: After speed=10 (keyword), the bare "left" is positional again.

Keep positionals first, then keywords - or go all keyword.

Why: After speed=10 (keyword), the bare "left" is positional again.

19. Trap: a positional argument after a keyword one

Trap

The trap

Once you go keyword, you can't drop back to positional.

Call move(speed=10, "left")

Why: After speed=10 (keyword), the bare "left" is positional again.

Python refuses before it even runs

Why: SyntaxError: positional argument follows keyword argument.

The fix

Keep positionals first, then keywords - or go all keyword.

Call move(10, "left")

Why: Both positional, in order - fine.

or move(speed=10, direction="left")

Why: All keyword - also fine, and clearer.

20. Rebuild the recipe: Calling a function cleanly

Ranking

Put in order

These are the steps of Calling a function cleanly, scrambled. Put them back in order before the next slide shows you.

  1. Positionals first, in the same order as the def.
  2. Then keywords (name=value) - never a positional after a keyword.
  3. Use keywords to skip middle defaults or to make the call readable.
  4. In the def itself, put required parameters before defaulted ones.

Why: This is the order the recipe itself gives. Recalling the sequence without the slide in front of you is the difference between recognising the method and being able to run it — most of what goes wrong in practice is a step done out of turn.

21. Calling a function cleanly

Pattern

A reliable order for every call you write:

  1. Positionals first, in the same order as the def.
  2. Then keywords (name=value) - never a positional after a keyword.
  3. Use keywords to skip middle defaults or to make the call readable.
  4. In the def itself, put required parameters before defaulted ones.

22. Where does it stop working: Calling a function cleanly

Edge cases

Discussion prompt

Calling a function cleanly works on the cases you have just seen. Push it to the edge: what is the most degenerate input it still handles — empty, zero, one item, everything equal — and what is the first case where it stops being true? Name the case, not just "it breaks".

Hint: Try the smallest legal input, then the largest, then the one where two things collide. Methods are specified at their edges; the middle takes care of itself.

Answer:

  1. Positionals first, in the same order as the def.
  2. Then keywords (name=value) - never a positional after a keyword.
  3. Use keywords to skip middle defaults or to make the call readable.
  4. In the def itself, put required parameters before defaulted ones.

23. Check: keyword arguments

Check

Recall def move(speed, direction="forward", power=5).

Check your understanding

What does move(10, power=9) return?

  • A. (10, 'forward', 9) (correct)
  • B. (10, 9, 5)
  • C. (10, 'forward', 5)
  • D. An error

Answer: A

Why: speed=10 fills positionally, direction keeps its default 'forward', and power=9 sets power by name. Result: (10, 'forward', 9).

Why B tempts people
power=9 sets the parameter named power, not the next positional slot (direction). direction keeps its default, it doesn't become 9.
Why C tempts people
The keyword power=9 does override the default 5 - it isn't ignored, so power is 9, not 5.
Why D tempts people
Skipping a middle default by using a keyword is allowed, so this call is valid - no error.

24. Scope

Section

Section 2

25. Local vs global

Concept

A variable created inside a function is local: it's born when the function starts and gone when it ends. Code outside can't see it.

scope — Where a name is visible. Names made inside a function live in that function's local scope only; names at the top level are global.

26. Teach it back: Local vs global

Explain it

Discussion prompt

Explain Local vs global to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

A variable created inside a function is local: it's born when the function starts and gone when it ends. Code outside can't see it.

27. Picture it first: Scope is a room with no windows

Picture it

Figure (svg): An outer box labeled your program contains an inner box labeled function with a local variable x that cannot be seen outside

x lives only inside f().

Discussion prompt

Read the picture before the words. What is this showing, and what is the one thing it is built to make obvious? Commit to an answer, then read on.

Hint: Name the parts, then say what changes between them — and if nothing changes, say what is being held still.

Answer:

A function is a room. Variables you make inside stay in the room; when the function ends, the room is torn down and they're gone. The only thing that leaves is what you return (hand out the door).

28. Scope is a room with no windows

Intuition

A function is a room. Variables you make inside stay in the room; when the function ends, the room is torn down and they're gone. The only thing that leaves is what you return (hand out the door).

Figure (svg): An outer box labeled your program contains an inner box labeled function with a local variable x that cannot be seen outside

x lives only inside f().

29. A local doesn't leak out

Worked example

x is made inside f. Reading it outside is an error.

def f():
    x = 5
    return x

print(f())
print(x)

Line 5 works (the return value); line 6 fails - x doesn't exist out here.

lineresult
print(f())5 (the returned value)
print(x)NameError: name 'x' is not defined

30. Parameters are local copies

Concept

When you pass a number in, the parameter is a fresh local copy. Changing it inside does not touch the caller's variable.

So a function can't secretly reach out and rewrite your variables. If you want a new value, the function must return it.

31. Changing a parameter doesn't change the caller

Worked example

drain lowers its own copy of b. The outside battery never moves.

def drain(b):
    b = b - 5
    return b

battery = 20
drain(battery)
print(battery)

We called drain but threw the result away, and battery is untouched.

linebattery isnote
battery = 2020the caller's variable
drain(battery)20b is a separate copy; result discarded
print(battery)20unchanged

32. Something is wrong here: expecting a function to change your variable

Anomaly

Predict first

A student writes this, and it looks reasonable:

Calling a function and expecting your variable to update on its own.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: Inside, b = b - 5 changes only the local copy b.

Catch the return value back into your variable.

Why: Inside, b = b - 5 changes only the local copy b.

33. Trap: expecting a function to change your variable

Trap

The trap

Calling a function and expecting your variable to update on its own.

battery = 20, then drain(battery)

Why: Inside, b = b - 5 changes only the local copy b.

print(battery) → 20

Why: The caller's battery never moved - the new value was thrown away.

The fix

Catch the return value back into your variable.

battery = drain(battery)

Why: The function hands the new value out; you store it back in battery.

print(battery) → 15

Why: State flows out through return, not by reaching into the function.

34. Break it on purpose: expecting a function to change your variable

Break the constraint

Discussion prompt

The rule this trap just fixed:

The function hands the new value out; you store it back in battery.

Now break it on purpose. Build a case that violates it and follow the consequences until something visibly fails. Where does the failure first show up — and would you have noticed it if you had not been looking?

Hint: The dangerous rules are the ones whose violation still produces an answer. If yours fails loudly, try to find one that fails quietly.

Answer:

Inside, b = b - 5 changes only the local copy b.

35. The clean rule: pass in, return out

Concept

Don't fight scope - use it. The safe way to share state between functions:

36. Updating state with return

Worked example

Each call returns the new hp; we store it back each time.

def take_damage(hp, dmg):
    return hp - dmg

hp = 20
hp = take_damage(hp, 5)
hp = take_damage(hp, 8)
print(hp)

hp is reassigned each time - that's how the value persists.

linereturnshp now
hp = 20-20
hp = take_damage(hp, 5)1515
hp = take_damage(hp, 8)77

37. Something is wrong here: ignoring the value you returned

Anomaly

Predict first

A student writes this, and it looks reasonable:

Even a correct return is wasted if you don't catch it.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: It computes 15 and hands it back - but nothing catches it, so it vanishes.

Assign the result back into your variable.

Why: It computes 15 and hands it back - but nothing catches it, so it vanishes.

38. Trap: ignoring the value you returned

Trap

The trap

Even a correct return is wasted if you don't catch it.

take_damage(hp, 5) on its own line

Why: It computes 15 and hands it back - but nothing catches it, so it vanishes.

hp is still 20

Why: Calling a function does not change your variable; only assignment does.

The fix

Assign the result back into your variable.

hp = take_damage(hp, 5)

Why: The returned 15 is stored in hp.

hp is now 15

Why: The = is what makes the new value stick.

39. Which of these survive contact with Functions: Arguments, Scope & Small Programs?

Two truths and a lie

Sort into buckets

Some of these hold up and some are the exact mistakes this lesson is built to prevent. Sort them.

Holds up
Real programs aren't one long script. They're several small functions working together - which is exactly how the camp's robot code is organized.; Four ideas, then you build a whole game out of them:; Compare set_motor(1, 0, 1) with set_motor(left=1, right=0, brake=1). Same call - but the second one reads like a sentence.
Breaks
Once you go keyword, you can't drop back to positional.; Calling a function and expecting your variable to update on its own.
sound
These are stated as this lesson states them — each one survives the edge cases Functions: Arguments, Scope & Small Programs puts it through.
flawed
Each of these is lifted from a trap in this deck: reasonable-sounding, and wrong in a way that only shows up once you rely on it.

40. Rule out three: Check: scope

Elimination

Eliminate the wrong options

What happens? def f(): x = 5 f() print(x)

3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.

  • A. Error: x is not defined
  • B. 5
  • C. 0
  • D. None

Survives elimination: A

Why: x is created inside f(), so it is local - it stops existing when f() ends. print(x) outside raises NameError: name 'x' is not defined.

41. Check: scope

Check

Think about where x lives.

Check your understanding

What happens?

def f():
x = 5

f()
print(x)

  • A. Error: x is not defined (correct)
  • B. 5
  • C. 0
  • D. None

Answer: A

Why: x is created inside f(), so it is local - it stops existing when f() ends. print(x) outside raises NameError: name 'x' is not defined.

Why B tempts people
Assumes a variable made inside a function is visible outside. Locals stay local; x is gone once f() returns.
Why C tempts people
There is no x outside the function at all - it isn't 0, it simply doesn't exist.
Why D tempts people
An undefined name raises a NameError; it doesn't quietly become None.

42. Functions Inside Loops

Section

Section 3

43. Call a helper on every pass

Concept

A wheeled robot car with an ultrasonic sensor facing a cardboard maze wall
Each loop pass: read, decide, act.

A program comes alive when a loop calls a function over and over - one decision per pass.

The loop is the when; the function is the what. Together they're the robot's heartbeat.

44. decide() inside a for loop

Worked example

One helper, called once per color in the list.

def decide(color):
    if color == "red":
        return "turn"
    return "forward"

for c in ["red", "green", "red"]:
    print(decide(c))

return ends the function each pass - the loop keeps going.

passcdecide(c) prints
1redturn
2greenforward
3redturn

45. Watch it run: decide() inside a for loop

Pattern

Step through it

Step through decide() inside a for loop one row at a time. What is driving the change, and what would the row after the last one be?

  1. Step 1: pass is 1
  2. Step 2: pass is 2
  3. Step 3: pass is 3

46. The accumulator pattern

Concept

Keep a running value in a variable, and on each loop pass reassign it from a function's return. The value carries forward, pass to pass.

accumulator — A variable that holds a running result across a loop, updated each pass - here, battery = drain(battery, cost).

47. Where does each piece belong: Functions: Arguments, Scope & Small Programs

Sorting

Sort into buckets

These are the pieces of Functions: Arguments, Scope & Small Programs, out of order. Put each one back under the part of the lesson it belongs to.

Arguments, Up Close
Positional vs keyword arguments; One function, several call styles; Why keyword arguments help
Scope
Local vs global; Scope is a room with no windows; A local doesn't leak out
Functions Inside Loops
Call a helper on every pass; decide() inside a for loop; The accumulator pattern
s1
Arguments, Up Close is where Functions: Arguments, Scope & Small Programs puts Positional vs keyword arguments, One function, several call styles, Why keyword arguments help. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.
s2
Scope is where Functions: Arguments, Scope & Small Programs puts Local vs global, Scope is a room with no windows, A local doesn't leak out. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.
s3
Functions Inside Loops is where Functions: Arguments, Scope & Small Programs puts Call a helper on every pass, decide() inside a for loop, The accumulator pattern. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.

48. Draining the battery over a loop

Worked example

battery is the accumulator; each pass costs a different amount.

def drain(b, cost):
    return b - cost

battery = 20
for cost in [3, 5, 2]:
    battery = drain(battery, cost)

print(battery)

Each pass feeds the previous battery back in - that's the accumulation.

passcostbattery after
start-20
1317
2512
3210

49. Watch it run: Draining the battery over a loop

Pattern

Step through it

Step through Draining the battery over a loop one row at a time. What is driving the change, and what would the row after the last one be?

  1. Step 1: pass is start
  2. Step 2: pass is 1
  3. Step 3: pass is 2
  4. Step 4: pass is 3

50. Loop = heartbeat, function = decision

Intuition

Every game and every robot controller is this shape: a loop that beats, and on each beat it calls functions to decide and update.

Robo-Battle, coming up, is exactly that: a loop of turns, each turn calling decide and applying the result.

51. Answer it before you see the options: Check: a function in a loop

Prediction

Predict first

def decide(color): if color == "red": return "turn" return "forward" for c in ["red", "red", "green"]: print(decide(c)) What prints?

Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.

Correct: turn, turn, forward

Why: decide is called each pass: 'red' -> turn, 'red' -> turn, 'green' -> forward. return ends the function call, not the loop, so all three lines print.

52. Check: a function in a loop

Check

Trace it pass by pass.

Check your understanding

def decide(color):
if color == "red":
return "turn"
return "forward"

for c in ["red", "red", "green"]:
print(decide(c))

What prints?

  • A. turn, turn, forward (correct)
  • B. turn, forward, forward
  • C. just turn
  • D. red, red, green

Answer: A

Why: decide is called each pass: 'red' -> turn, 'red' -> turn, 'green' -> forward. return ends the function call, not the loop, so all three lines print.

Why B tempts people
Miscounts the list - there are two 'red's before 'green', so the first two are both turn.
Why C tempts people
return ends the function call, not the for loop. The loop runs three times and prints three lines.
Why D tempts people
print(decide(c)) prints the returned action, not the color c itself.

53. Designing a Small Program

Section

Section 4

54. Picture it first: Break the problem into functions

Picture it

Figure (svg): A box labeled run_game branches down into three smaller boxes decide, power, and take_turn

Top job, split into small helpers.

Discussion prompt

Read the picture before the words. What is this showing, and what is the one thing it is built to make obvious? Commit to an answer, then read on.

Hint: Name the parts, then say what changes between them — and if nothing changes, say what is being held still.

Answer:

Don't write one giant block. Ask: what are the small jobs? Give each its own function with a clear name and a clear return.

55. Break the problem into functions

Concept

Don't write one giant block. Ask: what are the small jobs? Give each its own function with a clear name and a clear return.

Figure (svg): A box labeled run_game branches down into three smaller boxes decide, power, and take_turn

Top job, split into small helpers.

56. One main loop runs the show

Concept

The helpers do the small jobs; a main loop ties them together and keeps the state moving. That's the whole architecture of a small program.

Read it top-down: the loop says what order things happen, the helpers say how each step works.

57. A two-function mini program

Worked example

status calls is_low - a small helper feeding a bigger one.

def is_low(b):
    return b < 10

def status(b):
    if is_low(b):
        return "charge"
    return "go"

print(status(8), status(20))

status(8): is_low(8) is True -> "charge". status(20): False -> "go".

callis_low(b)returns
status(8)Truecharge
status(20)Falsego

58. Fill in: returns for A two-function mini program

Comparison

Comparison matrix

From A two-function mini program: refill the returns column from what you know. The rest of the table is as it appeared.

callis_low(b)returns
status(8)Truecharge
status(20)Falsego

59. A Pinch of Randomness

Section

Section 5

60. The random module

Concept

import random once at the top, then random.randint(a, b) gives a whole number from a to b - including both ends.

random.randint(a, b) — Returns a random integer N with a <= N <= b. Unlike range(), the top value b CAN come up.

61. Term to definition: Functions: Arguments, Scope & Small Programs

Matching

Match the pairs

Match each term to the definition this lesson gave it — not the one you would guess from the word.

  • t1. keyword argument
  • t2. default value
  • t3. scope
  • t4. accumulator
  • t5. random.randint(a, b)
  • d1. An argument passed by name, like direction="left". It goes to that exact parameter, so you can skip earlier ones that have defaults.
  • d2. A fallback written as name=value in the def. Required (no-default) parameters must be listed before any defaulted ones.
  • d3. Where a name is visible. Names made inside a function live in that function's local scope only; names at the top level are global.
  • d4. A variable that holds a running result across a loop, updated each pass - here, battery = drain(battery, cost).
  • d5. Returns a random integer N with a <= N <= b. Unlike range(), the top value b CAN come up.

Why: These are the working definitions of keyword argument, default value, scope, accumulator, random.randint(a, b) as Functions: Arguments, Scope & Small Programs uses them. Pairing them correctly is the test of whether you could state each one with the slide switched off.

62. Repeatable randomness with seed

Worked example

random.seed(7) makes the 'random' numbers come out the same every run - perfect for testing and for matching this slide.

import random
random.seed(7)

for _ in range(5):
    print(random.randint(1, 6), end=" ")

With seed 7, these five rolls are fixed.

rollresult (seed 7)
13
22
34
46
51

63. Watch it run: Repeatable randomness with seed

Pattern

Step through it

Step through Repeatable randomness with seed one row at a time. What is driving the change, and what would the row after the last one be?

  1. Step 1: roll is 1
  2. Step 2: roll is 2
  3. Step 3: roll is 3
  4. Step 4: roll is 4
  5. Step 5: roll is 5

64. Why seed first

Concept

Without a seed, every run differs - great for a real game, but awful for debugging. Seeding pins the sequence so you can reproduce a bug, or so your output matches a teammate's.

Set the seed once, at the top, before any randint calls.

65. Teach it back: Why seed first

Explain it

Discussion prompt

Explain Why seed first to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

Without a seed, every run differs - great for a real game, but awful for debugging. Seeding pins the sequence so you can reproduce a bug, or so your output matches a teammate's.

66. Answer it before you see the options: Check: randint range

Prediction

Predict first

random.randint(1, 6) can return which values?

Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.

Correct: 1, 2, 3, 4, 5, or 6

Why: random.randint(a, b) includes BOTH ends, so 1 through 6 - exactly like rolling a die.

67. Check: randint range

Check

Careful - this is where range() habits bite.

Check your understanding

random.randint(1, 6) can return which values?

  • A. 1, 2, 3, 4, 5, or 6 (correct)
  • B. 1, 2, 3, 4, or 5
  • C. 0, 1, 2, 3, 4, 5, or 6
  • D. 0, 1, 2, 3, 4, or 5

Answer: A

Why: random.randint(a, b) includes BOTH ends, so 1 through 6 - exactly like rolling a die.

Why B tempts people
That's range()/slicing thinking, where the end is excluded. randint includes the top value 6.
Why C tempts people
The low end is 1 (the first argument), not 0 - randint doesn't start below a.
Why D tempts people
Both ends are wrong: randint(1, 6) starts at 1 and includes 6.

68. Build It: Robo-Battle

Section

Section 6 · Your turn

69. The build: two robots, taking turns

Concept

You'll write two helpers, then a loop that runs the rounds until one robot's hp hits 0. Type every line, run after each helper, and read errors - don't erase them.

#piecejob
1decide(hp)return "defend" if hp < 10, else "attack"
2power(move)return 6 for attack, 2 for defend
3the turn loopalternate A/B until one hp reaches 0

70. By analogy: The build: two robots, taking turns

Analogy

Discussion prompt

Explain The build: two robots, taking turns by analogy to something with no Python in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

You'll write two helpers, then a loop that runs the rounds until one robot's hp hits 0. Type every line, run after each helper, and read errors - don't erase them.

71. Helper 1 — decide(hp)

Worked example

Your turn: write decide(hp) that returns "defend" when hp < 10, otherwise "attack". Predict decide(20).

Hint: if hp < 10: return "defend" then a plain return "attack" below it.

def decide(hp):
    if hp < 10:
        return "defend"
    return "attack"
callreturns
decide(20)attack
decide(5)defend

72. Helper 2 — power(move), then one hit

Worked example

Your turn: write power(move) (6 for attack, 2 for defend), then use it to lower the enemy's hp by that much.

Hint: if move == "attack": return 6 then return 2. Apply it: enemy = enemy - power(move).

def power(move):
    if move == "attack":
        return 6
    return 2

enemy = 20
enemy = enemy - power("attack")
print(enemy)
callreturnsenemy after
power("attack")620 - 6 = 14
power("defend")2(would be 18)

73. Helper 3 — the turn loop

Worked example

Your turn: alternate turns in a while loop until one hp reaches 0. Flip a turn flag each pass.

Hint: while hp_a > 0 and hp_b > 0: then if turn == "A": ... else: ..., and switch turn at the end of each branch.

while hp_a > 0 and hp_b > 0:
    if turn == "A":
        move = decide(hp_a)
        hp_b = hp_b - power(move)
        print("A", move, "B-hp", hp_b)
        turn = "B"
    else:
        move = decide(hp_b)
        hp_a = hp_a - power(move)
        print("B", move, "A-hp", hp_a)
        turn = "A"
whose turndecide usesresult
Ahp_ahit B for power(move)
Bhp_bhit A for power(move)

74. Put it together: Robo-Battle

Worked example

Both start at 20, A goes first. Watch a robot start defending once its hp drops below 10.

hp_a = 20
hp_b = 20
turn = "A"

while hp_a > 0 and hp_b > 0:
    if turn == "A":
        move = decide(hp_a)
        hp_b = hp_b - power(move)
        print("A", move, "B-hp", hp_b)
        turn = "B"
    else:
        move = decide(hp_b)
        hp_a = hp_a - power(move)
        print("B", move, "A-hp", hp_a)
        turn = "A"

print("Winner", "A" if hp_a > 0 else "B")

When B's hp is 8 and 2 (both < 10) it defends, doing only 2 to A.

turnmoveresult
AattackB-hp 14
BattackA-hp 14
AattackB-hp 8
BdefendA-hp 12
AattackB-hp 2
BdefendA-hp 10
AattackB-hp -4 → Winner A

If yours prints those rows and Winner A - you built a whole game from cooperating functions.

75. What each one costs: Put it together: Robo-Battle

Trade off

Comparison matrix

From Put it together: Robo-Battle: every row here is a choice with a cost. Fill the move column, then say which row you would actually pick and what you give up for it.

turnmoveresult
AattackB-hp 14
BattackA-hp 14
AattackB-hp 8
BdefendA-hp 12
AattackB-hp 2
BdefendA-hp 10
AattackB-hp -4 → Winner A

76. Rule out three: Check: battle behavior

Elimination

Eliminate the wrong options

Both robots start at hp 20 and A goes first. On turn 1, what does A do, and what is B's hp right after?

3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.

  • A. A attacks; B drops to 14
  • B. A defends; B drops to 18
  • C. A attacks; B drops to 16
  • D. Nothing yet - the loop hasn't started

Survives elimination: A

Why: hp_a is 20, which is not < 10, so decide returns 'attack'. An attack does 6, so B goes 20 - 6 = 14.

77. Check: battle behavior

Check

Use power(attack)=6, power(defend)=2, and decide: defend if hp < 10.

Check your understanding

Both robots start at hp 20 and A goes first. On turn 1, what does A do, and what is B's hp right after?

  • A. A attacks; B drops to 14 (correct)
  • B. A defends; B drops to 18
  • C. A attacks; B drops to 16
  • D. Nothing yet - the loop hasn't started

Answer: A

Why: hp_a is 20, which is not < 10, so decide returns 'attack'. An attack does 6, so B goes 20 - 6 = 14.

Why B tempts people
The threshold is hp < 10. At 20 the robot attacks - it only defends once its own hp is below 10.
Why C tempts people
An attack does 6 damage (defend does 2). 20 - 6 = 14, not 16.
Why D tempts people
The while loop runs while both are above 0; both start at 20, so round one runs immediately.

78. Stretch — random damage with a seed

Worked example

Stretch: make attacks do a random 4-8 and defends 1-3. Seed first so your run matches.

Hint: import random, random.seed(11), then return random.randint(4, 8) for attack.

import random
random.seed(11)

def power(move):
    if move == "attack":
        return random.randint(4, 8)
    return random.randint(1, 3)

# ...same battle loop as before, printing the damage...
turnmovedmgresult
Aattack7B-hp 13
Battack8A-hp 12
Aattack7B-hp 6
Bdefend2A-hp 10
Aattack8B-hp -2 → Winner A

79. Fill in: result for Stretch — random damage with a seed

Comparison

Comparison matrix

From Stretch — random damage with a seed: refill the result column from what you know. The rest of the table is as it appeared.

turnmovedmgresult
Aattack7B-hp 13
Battack8A-hp 12
Aattack7B-hp 6
Bdefend2A-hp 10
Aattack8B-hp -2 → Winner A

80. Show it off

Concept

Explain your program out loud, in function terms:

If you can answer all four, you've got functions, scope, and small-program design cold.

81. Break it if you can: Show it off

Counterexample

Discussion prompt

Explain your program out loud, in function terms:

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

Answer:

If you can answer all four, you've got functions, scope, and small-program design cold.

82. Connect it up: Functions: Arguments, Scope & Small Programs

Connect it up

Draw it

One page, no notation unless you need it: draw how these connect — Arguments, Up Close · Scope · Functions Inside Loops · Designing a Small Program · A Pinch of Randomness · Build It: Robo-Battle. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.

83. What you can do now

Recap

ideathe move
name an argumentpower=9
a value made inside stays insidelocal scope
update a variablehp = take_damage(hp, 5)
run roundswhile ...: call helpers
dice roll 1-6random.randint(1, 6)

That's both function days done - you can now read and write the kind of multi-function code the robot actually runs.

Sources

  1. Python 3 Tutorial - More on Defining Functions (positional/keyword args, default values)
  2. Python 3 Reference - Naming and binding; UnboundLocalError and the global statement
  3. Python 3 - random.randint and random.seed
  4. All snippets executed in Python 3; output (keyword-arg tuples, UnboundLocalError, seeded randint = [3,2,4,6,1], and the full Robo-Battle play-by-play) copied verbatim into trace tables. — Author verification run, 2026-06-19 (Pre-COSMOS Prep Plan, Day 11).

Want this taught 1-on-1? Alexander tutors Python — $55/session, free consultation.

Book on Wyzant · Text (657) 465-8108