Python Cheat Sheet - Week 3

Things are moving at pace, and you've made a lot of progress. But be sure to understand what it is you're doing, and why; these lessons have to gloss over some of the detail so that they fit into an hour slot. Luckily we're here to fill in the gaps... Code is in blue, outputs are green.

Jump to the full code...

! The Editor and Indentation !

You might notice that the editor for this project is slightly different. It doesn't have the instructions on the left, for a start. But another thing to note is that in some functions, the indentation has been started at 2 spaces, rather than 4.

The truth is, it doesn't matter how far indented each line is in a function, condition or loop, as long as within that block they are all exactly the same. You might want to stick with 4, because that's the editor's default when you press the [enter] key. Just be careful to make sure everything lines up!

Scope for improvement

First thing here is to set a global variable to define the screen size. What does that mean? Well, variables have something called scope, which is the place where they can be accessed. If you define a variable outside of your functions, it is global, therefore available everywhere. But if you define it inside a function, then that function is the only place that can access it. That's a local variable

Scope is helpful, because it reduces the likelihood of clashing variables. It can also be tricky to get it right, but you'll gain a better understanding as you progress.

Adding images

Last week we drew a picture. This week we're adding existing ones. We set the background to black (see the notes on computer colours to learn why (0,0,0) is black), then we set a variable for the chosen image: planet = load_image('purple_planet.png')

Why do this? Because it loads the image before we need to use it, ensuring that it's ready when we do. It creates a p5.Image object, which we can use whenever we need it by referring to the variable that stores it.

Note that we used 2 variables, width and height, to centre it in the middle of the x-axis and at the bottom of the page. But we didn't define these variables. These are global variables set by p5 to store the size of the window.

Launch code

To move the rocket, we need a variable to store it's height. This is set to screen_size, which is equal to 400. You could use the height variable, but that's not been set yet - it gets set by the size() function in def setup().

You need to restate that the rocket variable is global in the draw_rocket() function, because... well, you just do. And here's a new 'operator':

rocket_y -= 1

This is a shorthand common to many languages, called subtraction assignment, and it simply means y = y - 1. You can also use +=, *= and /=

Loop the loop

Adding fancy exhaust drawing effects is fun, and you can explore these on your own. But the new concept here is the loop. Loops let you loop through a piece of code until a certain condition is met. Here we define the loop with this line:

for i in range(25):

Everything in the indented block of text after this line will be looped through for as many times as we set. 'i' increases with each 'iteration' of the loop (i is a common variable name in loops, because it stands for 'iterator'), but when it gets to 25, the loop exits and the next line is executed.

The other new thing here is the smoke, which uses transparency. You can learn more about that in the computer colours article

Taking on fuel

We did input in week 1. Here it is again, but this time it looks slightly different:

fuel = int(input('How many kilograms of fuel do you want to use?'))

The entire input() function is wrapped in an int() function. What this does is turn the input into an integer, so that we can do maths on it. It's not a foolproof method, because if you type in something other than an integer, it breaks. But if you don't use it, it breaks anyway, because it assumes the result of an input is a string, and you can't do maths on strings. I'll write more about data types in the Concepts section later.

We add the new variables we're using, fuel and burn, to the global statement in the rocket draw function, to make them available. Then (and this is where you need to be able to find your way around the code!) we wrap a condition around the actual drawing bit:

if fuel >= burn:

"If the amount of fuel is greater than or equal to the amount you burn in each frame, move the rocket." And then we have to take away the amont burnt from the amount of fuel remaining, otherwise we'd never use it up. Be very careful indenting the code that goes inside this new condition - one missplaced space, and it won't work at all!

The next logical step

Once our orbit height is drawn in space, we want to check that we've reached it, and we can do that by modifying the original condition (you could nest a new condition inside the existing one, but that means more indenting, ain't nobody got time for that). So we use the logical operator and. This is nice, right? It works just like it reads - if something, AND something, then... Using the and operator means that both conditions have to be true. Fuel should be greater than burn, and rocket position should be lower than orbit if we're going to move it. You can learn more about logical operators here

Tinted love

The tint() function is an easy way to signal success. To not tint the planet, you turn tint back off. Why does this work? Simply because of the order that the code runs in - when the main loop runs, the background is drawn first, then draw_rocket() turns the tint on, draws the rocket and turns it off again. Using functions to structure your code like this makes it much easier to follow when things are not working as you think they should.

The Code

Here's all the code from the project, in one place. It might help you if you get lost in the indentation - I know I did!

#!/bin/python3 # Import library code from p5 import * from random import randint # Setup global variables screen_size = 400 rocket_y = screen_size burn = 100 # How much fuel is burned in each frame orbit_radius = 250 orbit_y = screen_size - orbit_radius # The draw_rocket function goes here def draw_rocket(): global rocket_y, fuel, burn # Use the global rocket_y variable if fuel >= burn and rocket_y > orbit_y: # Still got fuel rocket_y -= 1 # Move the rocket fuel -= burn print('Fuel left: ', fuel) no_stroke() # Turn off the stroke for i in range(25): # Draw 25 burning exhaust ellipses fill(255, 255 - i * 10, 0) # Reduce the amount of green ellipse(width/2, rocket_y + i, 8, 3) # i increases each time the loop repeats fill(200, 200, 200, 100) # Transparent grey for i in range(20): # Draw 20 random smoke ellipses ellipse(width/2 + randint(-5, 5), rocket_y + randint(20, 50), randint(5, 10), randint(5, 10)) if fuel < burn and rocket_y > orbit_y: # No more fuel and not in orbit tint(255, 0, 0) # Failure elif fuel < 1000 and rocket_y <= orbit_y: tint(0, 255, 0) # Success elif fuel >= 1000 and rocket_y <= orbit_y: tint(255, 200, 0) # Too much fuel image(rocket, width/2, rocket_y, 64, 64) no_tint() # The draw_background function goes here def draw_background(): background(0) # Short for background(0, 0, 0) — black image(planet, width/2, height, 300, 300) # Draw the image no_fill() # Turn off any fill stroke(255) # Set a white stroke stroke_weight(2) ellipse(width/2, height, orbit_radius * 2, orbit_radius * 2) def setup(): # Setup your animation here size(screen_size, screen_size) image_mode(CENTER) # Positions the image in the center global planet, rocket planet = load_image('purple_planet.png') # Your chosen planet rocket = load_image('rocket.png') def draw(): # Things to do in every frame draw_background() draw_rocket() fuel = int(input('How many kilograms of fuel do you want to use?')) run()