Achievement Unlocked - Zong!

Goto All Posts

Zong! It’s like Pong, but Zach made it! For the past few weeks Zach has been taking an interactive programming course. Today, on Christmas Eve 2021, Zach finished the first major project - writing Zong (aka Pong bit the ball has a big Z on it :) ) ! I’m so proud of Zach, not only did he write the game, but he’s been doing 2-4 hours a week of programming, and much of it with me. It’s a pleasure to be a part of this.

(I didn’t notice until I took this photo, but the whole time we’ve been writing Zong, the TV behind Zach has had a AIs playing a pong like game)

You can play Zong on a laptop by going here, and then clicking the run button, and then be sure to press inside the window that comes up for the keys to be registered.

For me, Zach programming brings incredible joy - seeing Zach flourish, knowing Zach can have a great career, and being able to have an activity with infinite depth with Zach. I’ve always romanticized how a blacksmith’s son becomes a blacksmith.

With this project I experience it deeply. There is such a craft, and so many potholes, and rabbit holes you can hit while programming. Because I’m with Zach while he’s programming, I can share my insights. Not just out errors that I see that are obvious (missing a ‘,’ or wrong braces, or mismatched XYZ), but when to stop and write out what you’re thinking.

If you’re for a beginner programmer course, I highly recommend Introduction to Interactive Programming in Python

Some of the fun challenges we worked through (and thinking about our Industry’s solutions):

  • Off by one errors
  • Typos as a result of static typing
  • Duplicate code
  • Confusion of using Tuples instead of classes
  • Separation of concerns
  • Different input models.

And for my programmer friends here’s the code:

# Implementation of classic arcade game Pong

import simplegui
import random

# initialize globals - pos and vel encode vertical info for paddles
WIDTH = 600
HEIGHT = 400
BALL_RADIUS = 15
PAD_WIDTH = 8
PAD_HEIGHT = 80
HALF_PAD_WIDTH = PAD_WIDTH / 2
HALF_PAD_HEIGHT = PAD_HEIGHT / 2
LEFT = False
RIGHT = True
Score1 = 0
Score2 = 0
paddle1_y = 0
paddle2_y = 0
paddle1_vel = 0
paddle2_vel = 0
# initialize ball_pos and ball_vel for new ball in middle of table
# if direction is RIGHT, the ball's velocity is upper right, else upper left
def spawn_ball(direction):
    #print("BALL SPAWNED NERDS!")
    global ball_pos, ball_vel # these are vectors stored as lists
    ball_pos = [300,200]
    if direction == LEFT:
        ball_vel = [200.0 / 60.0,  random.randint(-170.0,170) / 60.0]
    else:
        ball_vel = [-200.0 / 60.0, random.randint(-170.0,170) / 60.0]

# define event handlers
def new_game():
    global paddle1_pos, paddle2_pos, paddle1_vel, paddle2_vel, Score1, Score2, paddle1_y, paddle2_y # these are numbers
    global score1, score2  # these are ints
    spawn_ball(LEFT)
    Score1 = 0
    Score2 = 0
    paddle1_y = 0
    paddle2_y = 0

def makepad(x,y):
    poop = [[x,y],[x+PAD_WIDTH,y],[x+PAD_WIDTH,y+PAD_HEIGHT],[x,y+PAD_HEIGHT]]
    return poop
def draw(canvas):
    global Score1, Score2, paddle1_y, paddle2_y, ball_pos, ball_vel, PAD1_HEIGHT, PAD2_HEIGHT


    # draw mid line and gutters
    canvas.draw_line([WIDTH / 2, 0],[WIDTH / 2, HEIGHT], 1, "White")
    canvas.draw_line([PAD_WIDTH, 0],[PAD_WIDTH, HEIGHT], 1, "White")
    canvas.draw_line([WIDTH - PAD_WIDTH, 0],[WIDTH - PAD_WIDTH, HEIGHT], 1, "White")
    canvas.draw_text(f'{Score1}', (20, 50), 50, 'Red')
    canvas.draw_text(f'{Score2}', (550, 50),50, 'Green')

    # update ball
    ball_pos[0] += ball_vel[0]
    ball_pos[1] += ball_vel[1]

    # draw ball
    canvas.draw_circle(ball_pos, BALL_RADIUS, 2, "Yellow", "White")

    # update paddle's vertical position, keep paddle on the screen
    # draw paddles
    canvas.draw_polygon(makepad(0,paddle1_y),1,'Brown', 'Brown')
    canvas.draw_polygon(makepad(WIDTH - PAD_WIDTH,paddle2_y),1,'Green', 'Green')
    # determine whether paddle and ball collide

    #Collide and reflect off of right side of canvas
 #not random if velocity is negative
    if ball_pos[0] - BALL_RADIUS < 20:
        ball_vel[0] = - ball_vel[0]
        #print("left hand wall collision", ball_pos, ball_vel)
        if (ball_pos[1] + BALL_RADIUS > paddle1_y) and (ball_pos[1] + BALL_RADIUS < (paddle1_y + PAD_HEIGHT)):
           # print("Left Paddle Collision")
            if ball_vel[0] > 0:
                ball_vel[0] += random.random() * 2
            else:
                ball_vel[0] -= 2
        else:
            Score2 += 1
            spawn_ball(LEFT)
    if ball_pos[0] + BALL_RADIUS > (WIDTH - 20):
        #print("RIGHT hand wall collision", ball_pos, ball_vel)
        ball_vel[0] = - ball_vel[0]
        if (ball_pos[1] + BALL_RADIUS > paddle2_y) and (ball_pos[1] + BALL_RADIUS < (paddle2_y + PAD_HEIGHT)):
            #print("Right Paddle Collision")
            if ball_vel[0] > 0:
                ball_vel[0] +=  random.random() * 2
            else:
                ball_vel[0] -= 2
        else:
            Score1 += 1
            spawn_ball(RIGHT)





    if ball_pos[1] + BALL_RADIUS > HEIGHT:
        ball_vel[1] = - ball_vel[1]
    if ball_pos[1] <= BALL_RADIUS:
        ball_vel[1] = - ball_vel[1]



    if (paddle1_y+PAD_HEIGHT <= HEIGHT)and(paddle1_y >= 0):
        paddle1_y += paddle1_vel
        paddle1_y = min(paddle1_y,HEIGHT - PAD_HEIGHT)
        paddle1_y = max(paddle1_y,0)
    if (paddle2_y+PAD_HEIGHT <= HEIGHT)and(paddle2_y >= 0):
        paddle2_y += paddle2_vel
        paddle2_y = min(paddle2_y,HEIGHT - PAD_HEIGHT)
        paddle2_y = max(paddle2_y,0)
    canvas.draw_text("Zong",(WIDTH // 2 - 55, 50),50,"Orange")
    canvas.draw_text("Z",(ball_pos[0] - 6, ball_pos[1] + 7),20,'Blue')
    # draw scores

def keydown(key):
    #print(key)
    global paddle1_vel,paddle2_vel
    if key == simplegui.KEY_MAP['s']:
       # print("down")
        paddle1_vel = 5
    if key == simplegui.KEY_MAP['w']:
        paddle1_vel = -5
    if key == simplegui.KEY_MAP['up']:
        paddle2_vel = -5
    if key == simplegui.KEY_MAP['down']:
       # print("s")
        paddle2_vel = 5




def keyup(key):
    #print("SUSSS")
    global paddle1_vel, paddle2_vel
    if key == simplegui.KEY_MAP['s'] or key == simplegui.KEY_MAP['w']:
        paddle1_vel = 0
    elif key == simplegui.KEY_MAP['up'] or key == simplegui.KEY_MAP['down']:
        paddle2_vel = 0

# create frame
frame = simplegui.create_frame("Zong", WIDTH, HEIGHT)
frame.set_draw_handler(draw)
frame.set_keydown_handler(keydown)
frame.set_keyup_handler(keyup)
button1 = frame.add_button('New Game', new_game)
label1 = frame.add_label('Press W and S to move the right paddle!')
label2 = frame.add_label('Press the Up and Down arrows to move the left paddle!')


# start frame
new_game()
frame.start()

Have a great week, and remember Give your kid a program, and he’ll stream on Twitch. Teach your kid to program, and he’ll be rich – ZiaT!