move(direction, steps)
Moves Noodle. The direction is "up", "down", "left" or "right", and the number of steps is optional (it's 1 if you leave it out). If something's in the way, Noodle bumps into it and stays put.
move("right")
move("down", 3)
Noodle's world
look(direction)
Gives back what's next to Noodle in that direction, as a string: "wall", "grass", "snack", "home", "water", "ice", "door", "open door", "bridge", "gap", "stone" or "sign".
if look("right") == "wall":
move("down")
Noodle's world
say(...) and print(...)
Both make Noodle say something in a speech bubble. Give them as many values as you like, separated by commas.
say("hi!")
print("I have", 3, "snacks")
Noodle's world
read()
Reads the sign Noodle is standing on. Signs can hold numbers, text, lists or tuples. Walk onto the sign first!
move("right")
route = read()
Noodle's world
where()
Noodle's position as a tuple (x, y). x counts to the right and y counts down, starting from the top-left corner.
x, y = where()
Noodle's world
at_home() and snacks_left()
at_home() is True when Noodle is home. snacks_left() gives back how many snacks haven't been eaten yet.
while not at_home():
move("right")
say(snacks_left())
Noodle's world
The world's rules
Some variables shape the world itself. door_open (True/False) opens doors, pond ("water"/"ice") freezes ponds, bridge_length (a whole number) builds bridge planks, and stones (a list) decides which stepping stones exist. They're checked live while your code runs.
door_open = True
pond = "ice"
bridge_length = 5
stones = ["stone", "stone"]
Noodle's world
print()
Shows values. Separate several values with commas, and print puts spaces between them.
print("Hello, world!")
print("2 + 2 is", 2 + 2)
Introduction
Comments
Everything after a # is a comment: a note for humans that Python skips. Handy for explaining code, or for switching a line off.
# this whole line is a comment
move("right") # so is this part
Introduction
Running code: top to bottom
Python runs your program one line at a time, from top to bottom, unless a loop, if or function call tells it to jump somewhere else.
print("first")
print("second")
Introduction
Calling a function
A function is a named piece of code. You call it by writing its name and brackets. Values inside the brackets are arguments.
move("right", 3) # function: move, arguments: "right" and 3
len("hello") # gives back 5
Introduction
Reading error messages
When Python gets stuck it raises an error. The message tells you *what* went wrong and the line number tells you *where*. Read the last line first. Common ones: SyntaxError (Python can't read the line), NameError (unknown name, often a typo), TypeError (wrong kind of value), IndentationError.
mvoe("right")
# NameError: name 'mvoe' is not defined
Introduction
Variables
name = value stores a value under a name. Use the name later and Python fills in the value. Assigning again replaces the old value.
steps = 3
steps = steps + 1
print(steps) # 4
Variables and types
Naming variables
Names can contain letters, digits and _, but can't start with a digit. Python cares about capitals (Age and age are different). The Python style is snake_case: lowercase words joined with underscores.
snack_count = 3
bridge_length = 5
Variables and types
int and float
An int is a whole number (3, -12). A float has a decimal point (3.0, 0.5). Dividing with / always gives a float.
a = 7 # int
b = 7.5 # float
print(10 / 2) # 5.0
Variables and types
str (strings)
A str is text, in single or double quotes. Text and numbers are different things: "5" is not 5.
name = "Noodle"
food = 'peach'
Variables and types
bool, True and False
A bool is either True or False, always with a capital letter. Comparisons give back booleans.
door_open = True
print(3 > 5) # False
Variables and types
None
None means "no value". Functions without a return give back None.
result = None
if result is None:
print("nothing yet")
Variables and types
type()
Tells you the type of a value.
print(type(5)) # <class 'int'>
print(type("5")) # <class 'str'>
Variables and types
Converting types: int(), float(), str()
Turn values from one type into another. int() also chops off decimals (it doesn't round).
int("5") + 5 # 10
str(5) + "5" # "55"
int(3.9) # 3
float("2.5") # 2.5
Variables and types
+, -, *, /, //, %, **
Maths operators. + add, - subtract, * multiply, / divide (always gives a float), // divide and round down (a whole number), % the remainder, ** to the power of. Brackets work like in maths.
7 + 2 # 9
7 - 2 # 5
7 * 2 # 14
7 / 2 # 3.5
7 // 2 # 3
7 % 2 # 1
7 ** 2 # 49
(1 + 2) * 3 # 9
Variables and types
+= and friends
x += 1 is short for x = x + 1. There's also -=, *= and /=.
steps = 0
steps += 1
steps += 1
print(steps) # 2
Variables and types
input()
In normal Python, input("question") asks the user to type something and gives it back as a string. (Noodle's world has no keyboard, so it doesn't work in the game.)
name = input("What's your name? ")
print("Hi", name)
Variables and types
==, !=, >=, <=, >, <
Comparisons. == is equal to, != is not equal to, >= is greater than or equal to, <= is less than or equal to, > is greater than, < is less than. They give back True or False. Don't mix up == (compare) and = (store)!
5 == 5 # True
5 != 3 # True
4 >= 4 # True
3 <= 2 # False
3 > 2 # True
3 < 2 # False
"a" == "A" # False
Conditionals
if
Runs the indented block only if the condition is True. The line ends with a colon, and the block is indented 4 spaces.
if snacks_left() == 0:
say("all done!")
Conditionals
else
Runs when the if condition was False.
if look("right") == "wall":
move("down")
else:
move("right")
Conditionals
elif
"else, if": checks another condition. Python runs the first block whose condition is True, and skips the rest.
if way == "left":
move("left")
elif way == "down":
move("down")
else:
move("right")
Conditionals
and, or, not
and is True when both sides are. or is True when at least one side is. not flips True and False.
if hungry and snacks_left() > 0:
say("snack time")
if not at_home():
move("right")
Conditionals
Truthy and falsy
In an if, empty things (0, "", [], None) count as False, and everything else counts as True.
snacks = []
if snacks:
print("snacks!")
else:
print("no snacks")
Conditionals
for loops
Repeats the indented block once for each item in a sequence (a range, list, string, file…). The loop variable holds the current item.
for step in range(3):
move("right")
for letter in "abc":
print(letter)
Loops
range()
range(5) counts 0, 1, 2, 3, 4. range(1, 5) counts 1 to 4 (it stops *before* the end). range(0, 10, 2) counts in steps of 2.
list(range(5)) # [0, 1, 2, 3, 4]
list(range(1, 5)) # [1, 2, 3, 4]
list(range(0, 10, 3)) # [0, 3, 6, 9]
Loops
while loops
Repeats as long as the condition stays True. Make sure something inside the loop eventually makes it False, or it runs forever.
while not at_home():
move("right")
Loops
break and continue
break leaves the loop straight away. continue skips the rest of this round and starts the next one.
for i in range(10):
if i == 3:
continue # skip 3
if i == 6:
break # stop at 6
print(i)
Loops
Indentation
Python uses indentation (4 spaces) to know which lines belong inside a loop, if or function. The same line means something different inside a block than after it.
for i in range(3):
print("inside, 3 times")
print("after, once")
Loops
Counting and adding up
Start at 0 before the loop, and add to it inside. This pattern is called an accumulator.
total = 0
for price in [3, 5, 2]:
total += price
print(total) # 10
Loops
Nested loops
A loop inside a loop. The inner loop runs completely for every round of the outer one.
for row in range(3):
for col in range(3):
print(row, col)
Loops
def
Defines a new function. The indented lines are its body, which runs every time the function is called.
def scoop():
move("down", 2)
move("up", 2)
scoop()
scoop()
Functions
pass
pass means "do nothing". Python needs at least one line inside a function, loop, if or class, so pass is a placeholder until you write the real code.
def later():
pass # fill this in later
class Tile:
pass
Functions
Parameters and arguments
Parameters are variables in the def line. Arguments are the values you pass in when calling. Each call can use different values.
def zig(n):
move("right", n)
move("down", n)
zig(1)
zig(2)
Functions
return
Hands a value back to whoever called the function, and ends the function there.
def double(x):
return x * 2
print(double(4)) # 8
Functions
Default values
Parameters can have a default value, used when that argument is left out.
def hop(direction, steps=1):
move(direction, steps)
hop("right") # 1 step
hop("right", 3) # 3 steps
Functions
Scope: local and global
Variables created inside a function are local: they only exist while it runs. To change an outside variable from inside a function, declare it global (or better: return the new value).
count = 0
def bump():
global count
count += 1
Functions
Docstrings
A string right at the start of a function describes what it does. help(function) shows it.
def scoop():
"""Go down into a pocket and come back up."""
move("down")
move("up")
Functions
Lists
A list holds several values in order, in square brackets with commas. Lists can hold any mix of types and can change.
stones = ["stone", "water", "stone"]
numbers = [3, 1, 4, 1, 5]
empty = []
Lists
Indexing: list[0]
Get one item by its position, counting from 0. Negative indexes count from the end: [-1] is the last item.
colors = ["red", "green", "blue"]
colors[0] # "red"
colors[-1] # "blue"
colors[1] = "pink"
Lists
Slicing: list[a:b]
list[a:b] gives a new list from index a up to (not including) b. Leave one side out to go to the start or end.
n = [10, 20, 30, 40, 50]
n[1:3] # [20, 30]
n[:2] # [10, 20]
n[3:] # [40, 50]
Lists
.append() and .insert()
.append(x) adds x to the end. .insert(i, x) puts x at position i.
stones = ["stone"]
stones.append("stone")
stones.insert(0, "moss")
Lists
.remove() and .pop()
.remove(x) removes the first x. .pop() removes and gives back the last item (or .pop(i) for position i).
snacks = ["peach", "plum", "fig"]
snacks.remove("plum")
last = snacks.pop() # "fig"
Lists
len(), in, min(), max(), sum()
len() counts items. x in list checks if something's in it. min, max and sum do what they say.
n = [3, 1, 4]
len(n) # 3
4 in n # True
max(n) # 4
sum(n) # 8
Lists
Looping over a list
for item in list: goes through every item. enumerate() gives you the index too.
for d in ["up", "right"]:
move(d)
for i, d in enumerate(["up", "right"]):
print(i, d)
Lists
Sorting
sorted(list) gives back a new sorted list. list.sort() sorts the list itself. reversed(list) goes through it backwards.
sorted([3, 1, 2]) # [1, 2, 3]
for d in reversed(trip):
print(d)
Lists
List comprehensions
A short way to build a list from a loop: [expression for item in sequence], optionally with if.
squares = [n * n for n in range(5)] # [0, 1, 4, 9, 16]
evens = [n for n in range(10) if n % 2 == 0]
Lists
Strings are sequences
A string is a sequence of characters. You can index it, slice it, loop through it and measure it with len(). Strings can't be changed in place: methods give back new strings.
word = "noodle"
word[0] # "n"
word[1:4] # "ood"
len(word) # 6
Strings
f-strings
Put an f before the quotes, and anything in {curly braces} gets filled in with its value.
eaten = 3
print(f"I ate {eaten} snacks!")
print(f"{eaten * 2} if I'm greedy")
Strings
.lower(), .upper(), .title()
Give back a copy in lowercase, UPPERCASE or Title Case.
"PLEASE".lower() # "please"
"noodle".upper() # "NOODLE"
Strings
.strip()
Removes spaces (and newlines) from both ends. .lstrip() and .rstrip() do one side.
" hi ".strip() # "hi"
"right\n".strip() # "right"
Strings
.split() and .join()
.split() breaks a string into a list of words (or split on something else: .split(",")). " ".join(list) glues a list of strings together.
"up up down".split() # ["up", "up", "down"]
"-".join(["a", "b", "c"]) # "a-b-c"
Strings
.count(), .replace(), .find()
.count(x) counts how often x appears. .replace(old, new) swaps text. .find(x) gives the position of x (or -1). .startswith() and .endswith() check the ends, and x in s checks if x appears anywhere.
"banana".count("a") # 3
"sss".replace("s", "z") # "zzz"
"noodle".find("d") # 3
"oo" in "noodle" # True
Strings
Escape characters
A backslash starts a special character: \n new line, \t tab, \" a quote inside a string, \\ a backslash.
print("line one\nline two")
print("she said \"hi\"")
Strings
Dictionaries
A dictionary pairs keys with values, in curly braces. Look up a value by its key.
words = {"sss": "right", "hiss": "down"}
words["sss"] # "right"
Dictionaries and tuples
Adding and changing keys
Assign to a key to add it or change it. del d[key] removes it.
words["ssh"] = "up"
words["sss"] = "RIGHT"
del words["hiss"]
Dictionaries and tuples
.get() and in
Looking up a missing key gives a KeyError. .get(key, default) gives the default instead, and key in d checks first.
words.get("zzz", "?") # "?"
if "sss" in words:
print("found it")
Dictionaries and tuples
Looping over a dictionary
for key in d: goes through the keys. .values() gives values, and .items() gives (key, value) pairs.
for key, value in words.items():
print(key, "means", value)
Dictionaries and tuples
Tuples
A tuple is like a list that can't change, written with round brackets. Good for things that belong together, like (x, y).
home = (5, 3)
home[0] # 5
Dictionaries and tuples
Unpacking
Split a tuple (or list) into separate variables in one go. Functions can return several values this way.
x, y = (5, 3)
def corners():
return 0, 9
low, high = corners()
Dictionaries and tuples
open() and with
open(name) opens a file. Using it with with closes the file automatically when the block ends.
with open("route.txt") as f:
text = f.read()
Files
Reading a file
.read() gives the whole file as one string. for line in f: goes line by line (each line still ends in \n, so .strip() it). .readlines() gives a list of lines.
with open("route.txt") as f:
for line in f:
move(line.strip())
Files
Writing a file
Open with "w" to write. This replaces whatever was in the file. .write() doesn't add newlines for you.
with open("diary.txt", "w") as f:
f.write("dear diary\n")
f.write("today was nice\n")
Files
Appending to a file
Open with "a" to add to the end of a file instead of replacing it.
with open("diary.txt", "a") as f:
f.write("one more line\n")
Files
File not found
Opening a file that doesn't exist (for reading) raises FileNotFoundError. Check the name and the folder you're in.
import os
print(os.listdir(".")) # what files are here?
Files
class
A class is a blueprint for making objects. By convention, class names use CapitalWords.
class Door:
def __init__(self):
self.is_open = False
Classes I
Objects (instances)
Call the class like a function to build an object. Each object has its own data.
front = Door()
back = Door()
front.is_open = True # back is still closed
Classes I
__init__ and self
__init__ runs whenever a new object is made. self is the new object; the other parameters are the values you passed in.
class Pond:
def __init__(self, surface):
self.surface = surface
pond = Pond("ice")
Classes I
Attributes
Data stored on an object, reached with a dot. Set them in __init__ with self.name = value.
pond.surface # "ice"
pond.surface = "water"
Classes I
Methods
A function defined inside a class. Its first parameter is always self, the object it's called on.
class Bridge:
def __init__(self):
self.length = 0
def build(self, planks):
self.length += planks
b = Bridge()
b.build(3)
Classes I
Inheritance
class Child(Parent): makes a class that gets all of the parent's methods and attributes for free.
class Pond:
def describe(self):
return "a pond"
class FrozenPond(Pond):
pass
FrozenPond().describe() # "a pond"
Classes II
Overriding methods
A child class can define a method with the same name as the parent's to replace it.
class Tile:
def is_solid(self):
return False
class Stone(Tile):
def is_solid(self):
return True
Classes II
super()
Calls the parent's version of a method, so you can add to it instead of replacing it.
class FrozenPond(Pond):
def __init__(self):
super().__init__()
self.surface = "ice"
Classes II
__str__
A special method that decides what print(obj) and str(obj) show. (__repr__ is similar, for the programmer's view.)
class Snack:
def __init__(self, fruit):
self.fruit = fruit
def __str__(self):
return "a tasty " + self.fruit
Classes II
__len__ and other special methods
Special methods plug your objects into Python's built-in features: __len__ makes len(obj) work, __eq__ makes == work, __add__ makes + work, and __str__ decides what print shows.
class Basket:
def __init__(self):
self.items = []
def __len__(self):
return len(self.items)
len(Basket()) # 0
Classes II
isinstance()
Checks whether an object is made from a class (or one of its children).
isinstance(Stone(), Tile) # True
isinstance(5, str) # False
Classes II
Polymorphism
Different classes can answer the same method call in their own way, so code can treat them all alike.
for tile in [Stone(), Tile(), Lilypad()]:
print(tile.is_solid())
Classes II
import numpy as np
Numpy is the standard toolbox for numbers in Python. It's imported with the nickname np by tradition. (In the game, it downloads the first time you use it.)
import numpy as np
Numpy and Matplotlib
np.array
An array is like a list made for maths. Maths on an array happens to every item at once.
a = np.array([1, 2, 3])
a * 2 # array([2, 4, 6])
a + a # array([2, 4, 6])
Numpy and Matplotlib
sum, mean, max, argmax
Whole-array calculations. argmax gives the index of the biggest value rather than the value itself.
a = np.array([3, 1, 7, 2])
a.sum() # 13
a.mean() # 3.25
a.max() # 7
np.argmax(a) # 2
Numpy and Matplotlib
np.arange and np.linspace
np.arange(0, 10, 2) works like range, but gives an array. np.linspace(0, 1, 5) gives 5 evenly spaced numbers from 0 to 1.
np.arange(0, 10, 2) # [0 2 4 6 8]
np.linspace(0, 1, 5) # [0. 0.25 0.5 0.75 1.]
Numpy and Matplotlib
Filtering with conditions
Comparing an array gives an array of True/False, which you can use to pick items.
a = np.array([3, 1, 7, 2])
a[a > 2] # array([3, 7])
Numpy and Matplotlib
Line charts with plt.plot
import matplotlib.pyplot as plt, then plt.plot(ys) (or plt.plot(xs, ys)) draws a line. In the game, charts appear in Noodle's notes. In normal Python, call plt.show() to open them.
import matplotlib.pyplot as plt
plt.plot([2, 5, 3])
plt.title("Noodle's walk")
plt.xlabel("sign")
plt.ylabel("height")
Numpy and Matplotlib
Bar and scatter charts
plt.bar(labels, values) draws bars, plt.scatter(xs, ys) draws dots, and plt.hist(values) draws a histogram.
plt.bar(["peach", "plum"], [3, 5])
plt.scatter([1, 2, 3], [3, 1, 2])
Numpy and Matplotlib
import
A module is a file of ready-made code. import name loads it, and you use its contents with a dot.
import math
math.sqrt(16) # 4.0
Modules
from ... import ...
Takes just the names you need out of a module, so you can use them without the module's name in front.
from math import sqrt, pi
sqrt(9) # 3.0
Modules
import ... as ...
Gives a module a shorter nickname.
import numpy as np
import matplotlib.pyplot as plt
Modules
math
Maths tools: sqrt (square root), ceil (round up), floor (round down), pi, and more. The built-in round() rounds to the nearest whole number.
import math
math.ceil(4.2) # 5
math.floor(4.8) # 4
round(4.5) # 4 (rounds to even!)
Modules
random
Random numbers and choices: randint(a, b) (a whole number from a to b), choice(list), shuffle(list).
import random
random.randint(1, 6) # a dice roll
random.choice(["up", "down"])
Modules
datetime
Dates and times. date(year, month, day) makes a date, and .strftime() formats it: %A is the weekday name, %d the day, %B the month name, %Y the year.
from datetime import date
d = date(2024, 2, 29)
d.strftime("%A") # "Thursday"
d.strftime("%d %B %Y") # "29 February 2024"
date.today()
Modules
Your own modules
Any .py file is a module: put functions in tools.py and import tools from another file in the same folder. Code under if __name__ == "__main__": only runs when the file is run directly, not when imported.
# tools.py
def double(x):
return x * 2
if __name__ == "__main__":
print(double(21))
Modules