Search code examples
pythonglobal-variablespong

Issue with a global variable setting


The program is supposed to update the value of the global variable int_choice every time a player scores (it's a pong game)

int_choice can only have a value of 1 or 0. If it's 1, the function left_or_right "tells" the ball to go right, if it's 0, the ball goes left.

int_choice is updated in few places: at the beginning it's initialized, then in the left_or_right() function, then in the draw() function.

Every time the user scores, the ball should be respawned from the centre of the table towards that user, but the ball always respawns twice in the same direction and then twice in the opposite direction and so on, regardless of who was the last one to score.

Here's the code:

import random

int_choice = random.randint(0,1)
direc = None

def left_or_right():
    global direc, int_choice
    if int_choice == 0:
        direc = "LEFT"
    elif int_choice == 1:
        direc = "RIGHT"
    return direc

def spawn_ball(direction):
    left_or_right()
    global ball_pos, ball_vel # these are vectors stored as lists
    ball_pos = [WIDTH / 2, HEIGHT / 2]
    if direction == "LEFT":
        ball_vel[0] = (random.randrange(12, 25)*(-0.1))
        print "Velocity[0]: ", ball_vel[0]
        ball_vel[1] =  (random.randrange(6, 19)*(-0.1))
    elif direction == "RIGHT":
        ball_vel[0] = (random.randrange(12, 25)*(0.1))
        print "Velocity[0]: ", ball_vel[0]
        ball_vel[1] =  (random.randrange(6, 19)*(-0.1))
        print "Velocity[1]: ", ball_vel[1]

def new_game():
    global paddle1_pos, paddle2_pos, paddle1_vel, paddle2_vel, direc
    global score1, score2, 
    spawn_ball(direc)
    score1 = 0
    score2 = 0

def draw(canvas):
    global remaining_names, score1, score2, paddle1_pos, paddle2_pos,          ball_pos, ball_vel, BALL_RADIUS, direc
    global int_choice


    # update ball
    ball_pos[0] += ball_vel[0]
    ball_pos[1] += ball_vel[1]
    if ball_pos[1] - BALL_RADIUS <= 0:
        ball_vel[1] = ball_vel[1] + (ball_vel[1] * (-2))     
    elif ball_pos[1] + BALL_RADIUS >= HEIGHT:
        ball_vel[1] = ball_vel[1] + (ball_vel[1] * (-2))
    elif ball_pos[0] - BALL_RADIUS <= (0 + PAD_WIDTH):
        if (ball_pos[1] > paddle1_pos) and (ball_pos[1] < (paddle1_pos + PAD_HEIGHT)):
            ball_vel[0] = ball_vel[0] + (ball_vel[0] * (-2.1))
        else:
            int_choice = 1
            spawn_ball(direc)
            score2 = score2 + 1

    elif (ball_pos[0] + BALL_RADIUS) >= (WIDTH - PAD_WIDTH):
        if (ball_pos[1] > paddle2_pos) and (ball_pos[1] < (paddle2_pos + PAD_HEIGHT)):
            ball_vel[0] = ball_vel[0] + (ball_vel[0] * (-2.1))
        else:
            int_choice = 0
            spawn_ball(direc)
            score1 = score1 + 1

Solution

  • You pass in the old value of direc, before left_or_right is called.

    Say, you set int_cohice to 1:

    int_choice = 1
    spawn_ball(direc)  # old value of `direc`, nothing changed this yet
    

    then in spawn_ball():

    def spawn_ball(direction):
        left_or_right()
    

    so direction is set the old value, but left_or_right() sets it to a new value, which is then entirely ignored in spawn_ball(). You use direction throughout the function.

    The quick fix is to use the return value of left_or_right(); or use the direc global. Since either operates on globals, there is no point in passing in direc here:

    int_choice = 1
    spawn_ball()  # don't pass anything in
    

    and

    def spawn_ball():
        direction = left_or_right()
    

    However, the better way is to always pass in a direction, and completely remove the (double) globals.

    Just pass in a number, you can give that number symbolic names:

    LEFT, RIGHT = 0, 1  # symbolic names for direction
    
    def spawn_ball(direction):
        ball_pos = [WIDTH / 2, HEIGHT / 2]
        if direction == LEFT:  # using the global symbolic name
            return ball_pos, [
                random.randrange(12, 25)*(-0.1),
                random.randrange(6, 19)*(-0.1)]
        else:   # naturally the other option is going to be RIGHT
            return ball_pos, [
                random.randrange(12, 25)*(0.1)
                random.randrange(6, 19)*(-0.1)]
    

    Note that the function returns new ball positions and velocity; store the result when you call the function:

    ball_pos, ball_vel = spawn_ball(direction)
    

    Perhaps the draw function still treats these as globals, but that's no longer a concern for the spawn_ball() function at the very least.

    Now all you need to do is set one local variable to either LEFT or RIGHT to spawn a ball and pass that variable into the function.