Recitation 2: Pong Lab

For your first lab, you’ll be making the old arcade pong game. Most of the actual assignment details and pseudocode are described well and in detail on the lab page, so I won’t be repeating that information here. Instead, we’ll be going over information you’ll need about the lab.

Function Breakdown

It’s important from a design point of view to break apart this large coding assignment into multiple smaller functions. So how do you break down a large code project into multiple functions?

  1. Think of common functions you’ll be repeating multiple times.

What ideas come to mind? Remember, this is just meant to be a laundry list.

Great. That’s a good list of things we’ll have to do for this lab. So what’s the next step?

  1. Generalize your many specific functions to fewer functions.

When we are looking for functions to generalize, start with functions that are very similar with just minor differences. It’s bad coding practice to have almost similar code repeated many times. A good design maximizes generality.

Let’s start with these two as an example:

# Checking whether a ball has hit the left paddle
hit_left_paddle()
# Checking whether a ball has hit the left paddle
hit_right_paddle()

to check whether the ball hit the left paddle and the right paddle, the solution is obvious: Why don’t we combine the two so that a single function is in charge of handling whether a ball hit a paddle.

But how do we know which paddle it hit?

With a little thought, you can see that we can pass a parameter. How about we pass a parameter like an int – if it’s 0, then we check for the left paddle, and if it’s 1 we check for the right paddle.

But we do even need to know which paddle the ball hit? Why not check both every time? Great! We’ve condensed these two functions down to just one function, hit_paddle()!

Let’s keep generalizing. What about the functions to check whether a ball hits something?

There are two types of surfaces a ball can hit: a vertical surface and a horizontal surface. Why don’t we generalize further? Because different actions result in these cases. If the ball hits a horizontal surface, the x direction of the ball doesn’t change, only the y; if a ball hits a vertical surface, the opposite happens.

So we’ve effectively combined the “check vertical wall collision” and “check paddle collision” functions into one. Now we just have one function for all horizontal surfaces, and one function for all vertical surfaces. Great! Can we do better?

What about checking whether each paddle is out of bounds of the screen? Can we use function to test both paddles? Why not? If we passed a parameter to the function where 0 was the left paddle and 1 was the right paddle, or something akin to that, we could easily do that.

In fact, we can do even better than passing this “flag” parameter telling us which paddle we’re talking about. Since both paddles have the same width and length, and we know the paddles cannot move horizontally, the only thing we need to know is the y coordinate of the bottom left corner!

With this piece of information alone, we can construct this function.

These are the design questions you need to think about before starting coding right away. Your lab should have at least this level of function breakdown; feel free to go further, but these are the obvious ones that you should implement.

  1. Further generalization beyond the basic specs of the project.

Okay, that last one seemed a little mysterious. What does it mean?

You’ll notice the labs we give you have many extra credit options.

Global Scope

Remember the LEGB rule? It’s the rule that doesn’t allow this to happen.

def bummer():
    a = 5

bummer()
print a

Bummer! But if we breakdown the functions like the way we described above, we need a way to access those variables outside those functions. Otherwise what’s the point? So what can we do?

One option would be to make every variable that more than one function needs into a global variable declared at the very top of the file. But soon, your file will start to look like

WINDOW_WIDTH = 300
WINDOW_HEIGHT = 300
PADDLE_WIDTH = 40
PADDLE_LENGTH = 40
BALL_RADIUS = 10
ball_x = 0
ball_y = 0
paddle_left_x = 0
paddle_left_y = 0
paddle_right_x = 0
paddle_right_y = 0

And many, many more. Ew. That is not only really bad style but also just painfully awful. This is really bad practice and an abuse of global variables.

This is why we use the keyword global. Checkout what it can do.

def fun():
    global a
    a = 5

def notFun():
    global a
    a = 7

fun()
print a
notFun()
print a

Wow! Turns out, this not only doesn’t produce an error, but it outputs what we want:

5
7

Even though a is in two separate functions, because we used the global keyword, Python knows we’re referring to the same a!

You’ll want to use this to minimize the number of variables at the top of your program.