Pong

VickiLanger

Asks "why?" too much
Gold Member
Local time
13:56
Joined
Oct 7, 2019
Messages
189
Pronouns
she/her

What better way is there to learn than by doing? So, I built pong. I've had a hard time understanding how to break down programs into smaller chunks. So, this really helped me understand a bit more about how to do that. Specifically, this script is broken down to screen, score, paddle_a, paddle_b, and ball.

paddle_a, paddle_b, and ball are all Turtle objects from the turtle module in Python.

This was highly influenced by this video by @TokyoEdTech.

Source Code:

Python:
"""
pong.py: build classic pong game with turtle module
12 November 2019
@Vicki_Langer
"""
import turtle

win = turtle.Screen()
win.title("Pong by @Vicki_Langer")
win.bgcolor("#0b3c49")
win.setup(width=800, height=600)
win.tracer(0)

# score
score_a = 0
score_b = 0

# Paddle A
paddle_a = turtle.Turtle()
paddle_a.speed(0)
paddle_a.shape("square")
paddle_a.color("#f2a130")
paddle_a.shapesize(stretch_wid=5, stretch_len=1)
paddle_a.penup()
paddle_a.goto(-350, 0)

# Paddle B
paddle_b = turtle.Turtle()
paddle_b.speed(0)
paddle_b.shape("square")
paddle_b.color("#f2a130")
paddle_b.shapesize(stretch_wid=5, stretch_len=1)
paddle_b.penup()
paddle_b.goto(350, 0)

# Ball
ball = turtle.Turtle()
ball.speed(0)
ball.shape("square")
ball.color("#f2a130")
ball.penup()
ball.goto(0, 0)
ball.dx = 2  # ball moves right by 2 pixels
ball.dy = 2  # ball moves up by 2 pixels

# scoreboard
pen = turtle.Turtle()
pen.speed(0)
pen.color("#f2a130")
pen.penup()
pen.hideturtle()
pen.goto(0, 260)
pen.write("Player A: 0  Player B: 0", align="center",
          font=("Courier", 24, "normal"))

# function
def paddle_a_up():
    y = paddle_a.ycor()
    y += 20
    paddle_a.sety(y)


def paddle_a_down():
    y = paddle_a.ycor()
    y += -20
    paddle_a.sety(y)


def paddle_b_up():
    y = paddle_b.ycor()
    y += 20
    paddle_b.sety(y)


def paddle_b_down():
    y = paddle_b.ycor()
    y += -20
    paddle_b.sety(y)


# keyboard binding
win.listen()
win.onkeypress(paddle_a_up, "w")
win.onkeypress(paddle_a_down, "s")
win.onkeypress(paddle_b_up, "Up")
win.onkeypress(paddle_b_down, "Down")

# main game loop
while True:
    win.update()

    # move the ball
    ball.setx(ball.xcor() + ball.dx)
    ball.sety(ball.ycor() + ball.dy)

    # border checking
    if ball.ycor() > 290:
        ball.sety(290)
        ball.dy *= -1

    if ball.ycor() < -285:
        ball.sety(-285)
        ball.dy *= -1

    if ball.xcor() > 390:
        ball.goto(0, 0)
        ball.dx *= -1
        score_a += 1
        pen.clear()
        pen.write("Player A: {}  Player B: {}".format(score_a, score_b), align="center",
                  font=("Courier", 24, "normal"))

    if ball.xcor() < -390:
        ball.goto(0, 0)
        ball.dx *= -1
        score_b += 1
        pen.clear()
        pen.write("Player A: {}  Player B: {}".format(score_a, score_b), align="center",
                  font=("Courier", 24, "normal"))

    # paddle and ball collision
    if (ball.xcor() > 340 and ball.xcor() < 350) and (ball.ycor() < paddle_b.ycor() + 40 and ball.ycor() > paddle_b.ycor() - 40):
        ball.setx(340)
        ball.dx *= -1

    if (ball.xcor() < -340 and ball.xcor() > -350) and (ball.ycor() < paddle_a.ycor() + 40 and ball.ycor() > paddle_a.ycor() - 40):
        ball.setx(-340)
        ball.dx *= -1

Edit: Added how and why

 
Last edited:
Upvote 3

Gummibeer

Astroneer
Moderator
Local time
19:56
Joined
Oct 5, 2019
Messages
1,177
Pronouns
he/him

Would you mind sharing a playable version and/or source code? Some insights on how/why you've created it and so on? 😊

 

VickiLanger

Asks "why?" too much
Gold Member
Local time
13:56
Joined
Oct 7, 2019
Messages
189
Pronouns
she/her

Would you mind sharing a playable version and/or source code? Some insights on how/why you've created it and so on? 😊

I would share a playable version, but I have no clue how to do that. I'll add the how and why to the original post

 

Gummibeer

Astroneer
Moderator
Local time
19:56
Joined
Oct 5, 2019
Messages
1,177
Pronouns
he/him

I would share a playable version, but I have no clue how to do that. I'll add the how and why to the original post
Sharing the source code should be enough in python. Or you use any of the build bundlers like py2exe (no idea if there's similar for Mac/Linux.

 

VickiLanger

Asks "why?" too much
Gold Member
Local time
13:56
Joined
Oct 7, 2019
Messages
189
Pronouns
she/her

Sharing the source code should be enough in python. Or you use any of the build bundlers like py2exe (no idea if there's similar for Mac/Linux.
Hmm, I'll have to look into that. For now, the souce code is in the original post.

 

Gummibeer

Astroneer
Moderator
Local time
19:56
Joined
Oct 5, 2019
Messages
1,177
Pronouns
he/him

I've had a hard time understanding how to break down programs into smaller chunks.
Some thoughts how you could extend the current state and follow more the OOP way.

* create a "paddle" class that accepts (window, start position, axis, name and keys as argument)
** WHY: you remove duplicated code and can easily add a third/fourth paddle operating on the top/bottom
* create a "gamemanager" class that handles the game logic - initialize all objects, keep references and manage the "while true" loop in a run() method
** WHY: this will remove the game logic from the objects
* create a "user/player" class which holds the score and a reference to the paddle(s)
** WHY: with 4 paddles you could have 4 players or 2 players with 2 paddles it also splits the score and management from the manager and paddle

PS: @Adam could you add Markdown support? :)

 
Last edited:

VickiLanger

Asks "why?" too much
Gold Member
Local time
13:56
Joined
Oct 7, 2019
Messages
189
Pronouns
she/her

Some thoughts how you could extend the current state and follow more the OOP way.

* create a "paddle" class that accepts (window, start position, axis, name and keys as argument)
** WHY: you remove duplicated code and can easily add a third/fourth paddle operating on the top/bottom
* create a "gamemanager" class that handles the game logic - initialize all objects, keep references and manage the "while true" loop in a run() method
** WHY: this will remove the game logic from the objects
* create a "user/player" class which holds the score and a reference to the paddle(s)
** WHY: with 4 paddles you could have 4 players or 2 players with 2 paddles it also splits the score and management from the manager and paddle

PS: @Adam could you add Markdown support? :)


The tutorial mentioned it was not using OOP. My goal was to get it working and go back to make it object-oriented. So, I think that's the plan for this morning.

 

VickiLanger

Asks "why?" too much
Gold Member
Local time
13:56
Joined
Oct 7, 2019
Messages
189
Pronouns
she/her

Some thoughts how you could extend the current state and follow more the OOP way.

* create a "paddle" class that accepts (window, start position, axis, name and keys as argument)
** WHY: you remove duplicated code and can easily add a third/fourth paddle operating on the top/bottom
* create a "gamemanager" class that handles the game logic - initialize all objects, keep references and manage the "while true" loop in a run() method
** WHY: this will remove the game logic from the objects
* create a "user/player" class which holds the score and a reference to the paddle(s)
** WHY: with 4 paddles you could have 4 players or 2 players with 2 paddles it also splits the score and management from the manager and paddle

PS: @Adam could you add Markdown support? :)


Not super sure I'm on the right path, but maybe this? Okay, well, I'm pretty sure this is not exactly what you were suggesting. goto is a turtle class, I wasn't sure if I should be using that is my argument. What would I use the window argument for?

Python:
# Paddle A
paddle_a = turtle.Turtle()
paddle_a.speed(0)
paddle_a.shape("square")
paddle_a.color("#f2a130")
paddle_a.shapesize(stretch_wid=5, stretch_len=1)
paddle_a.penup()
paddle_a.goto(-350, 0)

# Paddle B
paddle_b = turtle.Turtle()
paddle_b.speed(0)
paddle_b.shape("square")
paddle_b.color("#f2a130")
paddle_b.shapesize(stretch_wid=5, stretch_len=1)
paddle_b.penup()
paddle_b.goto(350, 0)

Python:
class paddle(self, goto):

    # attributes
    paddle = turtle.Turtle()
    paddle.speed(0)
    paddle.shape("square")
    paddle.color("#f2a130")
    paddle.shapesize(stretch_wid=5, stretch_len=1)
    paddle.penup()

    def __init__(self, goto):
        self.goto = goto


# Instantiate the paddles
paddle_a = paddle("a", (-350, 0))
paddle_b = paddle("b", (350, 0))

 
Last edited:

VickiLanger

Asks "why?" too much
Gold Member
Local time
13:56
Joined
Oct 7, 2019
Messages
189
Pronouns
she/her

So, I guess I already had it broken down into objects, I just didn't have it set up in classes. From here, I could still add game manager and player classes.

Python:
"""
pong.py: build classic pong game with turtle module
12 November 2019
@Vicki_Langer
"""
import turtle


class Window:
    def __init__(self, width, height, bgcolor, title):

        self.width = width
        self.height = height
        self.bgcolor = bgcolor
        self.title = title

        self.window = turtle.Screen()
        self.window.bgcolor(self.bgcolor)
        self.window.title(self.title)
        self.window.setup(width=self.width, height=self.height)


class Paddle:
    def __init__(self, color, posx, posy, shape="square", width=5, len=1):

        self.color = color
        self.posx = posx
        self.posy = posy
        self.shape = shape
        self.width = width
        self.len = len

        self.paddle = turtle.Turtle()
        self.paddle.speed(0)
        self.paddle.shape(self.shape)
        self.paddle.color(self.color)
        self.paddle.shapesize(stretch_wid=self.width, stretch_len=self.len)
        self.paddle.penup()
        self.paddle.goto(self.posx, self.posy)

    def paddle_up(self):
        y = self.paddle.ycor()
        if y < 245:
            y += 30
            self.paddle.sety(y)

    def paddle_down(self):
        y = self.paddle.ycor()
        if y > -245:
            y -= 30
            self.paddle.sety(y)


class Ball:

    def __init__(self, color, vx=4, vy=4, shape="square", posx=0, posy=0):

        self.shape = shape
        self.color = color
        self.vx = vx
        self.vy = vy
        self.posx = posx
        self.posy = posy

        self.ball = turtle.Turtle()
        self.ball.speed(0)
        self.ball.shape(self.shape)
        self.ball.color(self.color)
        self.ball.penup()
        self.ball.goto(self.posx, self.posy)


class Score:

    score_1 = 0
    score_2 = 0

    def __init__(self, color, posx, posy, shape="square"):
        self.color = color
        self.posx = posx
        self.posy = posy
        self.shape = shape

        self.score = turtle.Turtle()
        self.score.speed(0)
        self.score.shape(self.shape)
        self.score.color(self.color)
        self.score.penup()
        self.score.goto(self.posx, self.posy)
        self.score.hideturtle()
        self.score.write("Player A: 0  Player B: 0", align="center", font=("Courier", 24, "normal"))


win = Window(800, 600, "#0b3c49", "Pong by @Vicki_Langer")
paddle_1 = Paddle("#f2a130", -350, 0)
paddle_2 = Paddle("#f2a130", 350, 0)
b = Ball("#f2a130", vx=2, vy=2)
s = Score("#f2a130", 0, 260)


win.window.listen()
win.window.onkeypress(paddle_1.paddle_up, "w")
win.window.onkeypress(paddle_1.paddle_down, "s")
win.window.onkeypress(paddle_2.paddle_up, "Up")
win.window.onkeypress(paddle_2.paddle_down, "Down")

while True:

    win.window.update()

    b.ball.setx(b.ball.xcor() + b.vx)
    b.ball.sety(b.ball.ycor() + b.vy)

    if b.ball.ycor() > 280:
        b.vy *= -1

    if b.ball.ycor() < -280:
        b.vy *= -1

    if b.ball.xcor() > 400:
        s.score_1 += 1
        s.score.clear()
        s.score.write("Player A: {}  Player B: {}".format(s.score_1, s.score_2), align="center", font=("Courier", 24, "normal"))
        b.ball.goto(0, 0)
        b.vx *= -1

    elif b.ball.xcor() < -400:
        s.score_2 += 1
        s.score.clear()
        s.score.write("Player A: {}  Player B: {}".format(s.score_1, s.score_2), align="center", font=("Courier", 24, "normal"))
        b.ball.goto(0, 0)
        b.vx *= -1

    if b.ball.xcor() < -330 and b.ball.ycor() < paddle_1.paddle.ycor() + 50 and b.ball.ycor() > paddle_1.paddle.ycor() - 50:
        b.vx *= -1

    elif b.ball.xcor() > 330 and b.ball.ycor() < paddle_2.paddle.ycor() + 50 and b.ball.ycor() > paddle_2.paddle.ycor() - 50:
        b.vx *= -1

 
Last edited:

Gummibeer

Astroneer
Moderator
Local time
19:56
Joined
Oct 5, 2019
Messages
1,177
Pronouns
he/him

So, I guess I already had it broken down into objects, I just didn't have it set up in classes. From here, I could still add game manager and player classes.

Python:
"""
pong.py: build classic pong game with turtle module
12 November 2019
@Vicki_Langer
"""
import turtle


class Window:
    def __init__(self, width, height, bgcolor, title):

        self.width = width
        self.height = height
        self.bgcolor = bgcolor
        self.title = title

        self.window = turtle.Screen()
        self.window.bgcolor(self.bgcolor)
        self.window.title(self.title)
        self.window.setup(width=self.width, height=self.height)


class Paddle:
    def __init__(self, color, posx, posy, shape="square", width=5, len=1):

        self.color = color
        self.posx = posx
        self.posy = posy
        self.shape = shape
        self.width = width
        self.len = len

        self.paddle = turtle.Turtle()
        self.paddle.speed(0)
        self.paddle.shape(self.shape)
        self.paddle.color(self.color)
        self.paddle.shapesize(stretch_wid=self.width, stretch_len=self.len)
        self.paddle.penup()
        self.paddle.goto(self.posx, self.posy)

    def paddle_up(self):
        y = self.paddle.ycor()
        if y < 245:
            y += 30
            self.paddle.sety(y)

    def paddle_down(self):
        y = self.paddle.ycor()
        if y > -245:
            y -= 30
            self.paddle.sety(y)


class Ball:

    def __init__(self, color, vx=4, vy=4, shape="square", posx=0, posy=0):

        self.shape = shape
        self.color = color
        self.vx = vx
        self.vy = vy
        self.posx = posx
        self.posy = posy

        self.ball = turtle.Turtle()
        self.ball.speed(0)
        self.ball.shape(self.shape)
        self.ball.color(self.color)
        self.ball.penup()
        self.ball.goto(self.posx, self.posy)


class Score:

    score_1 = 0
    score_2 = 0

    def __init__(self, color, posx, posy, shape="square"):
        self.color = color
        self.posx = posx
        self.posy = posy
        self.shape = shape

        self.score = turtle.Turtle()
        self.score.speed(0)
        self.score.shape(self.shape)
        self.score.color(self.color)
        self.score.penup()
        self.score.goto(self.posx, self.posy)
        self.score.hideturtle()
        self.score.write("Player A: 0  Player B: 0", align="center", font=("Courier", 24, "normal"))


win = Window(800, 600, "#0b3c49", "Pong by @Vicki_Langer")
paddle_1 = Paddle("#f2a130", -350, 0)
paddle_2 = Paddle("#f2a130", 350, 0)
b = Ball("#f2a130", vx=2, vy=2)
s = Score("#f2a130", 0, 260)


win.window.listen()
win.window.onkeypress(paddle_1.paddle_up, "w")
win.window.onkeypress(paddle_1.paddle_down, "s")
win.window.onkeypress(paddle_2.paddle_up, "Up")
win.window.onkeypress(paddle_2.paddle_down, "Down")

while True:

    win.window.update()

    b.ball.setx(b.ball.xcor() + b.vx)
    b.ball.sety(b.ball.ycor() + b.vy)

    if b.ball.ycor() > 280:
        b.vy *= -1

    if b.ball.ycor() < -280:
        b.vy *= -1

    if b.ball.xcor() > 400:
        s.score_1 += 1
        s.score.clear()
        s.score.write("Player A: {}  Player B: {}".format(s.score_1, s.score_2), align="center", font=("Courier", 24, "normal"))
        b.ball.goto(0, 0)
        b.vx *= -1

    elif b.ball.xcor() < -400:
        s.score_2 += 1
        s.score.clear()
        s.score.write("Player A: {}  Player B: {}".format(s.score_1, s.score_2), align="center", font=("Courier", 24, "normal"))
        b.ball.goto(0, 0)
        b.vx *= -1

    if b.ball.xcor() < -330 and b.ball.ycor() < paddle_1.paddle.ycor() + 50 and b.ball.ycor() > paddle_1.paddle.ycor() - 50:
        b.vx *= -1

    elif b.ball.xcor() > 330 and b.ball.ycor() < paddle_2.paddle.ycor() + 50 and b.ball.ycor() > paddle_2.paddle.ycor() - 50:
        b.vx *= -1
Good progress so far! A prove for yourself that you've really got the whole thing and can also adjust it would be to add two more paddles on the empty screen borders. This will need some adjustments in several places but would be a cool extension to the game.
After this you can add all the game meta stuff: a menu to choose game mode (2 users & 2 paddles, 2/4 or 4/4 or even much more fun 4/2 - playing the same entity with two persons is so much fun! 😅). Persistent Highscore and so on. And if you want to beat the **** out of it you could add network multiplayer stuff via socket.io, p2p or any service.

An idea for the window: you could check if extending the turtle screen would work. And you should move every class declaration in it's own class. At least that's common in the languages I know.^^

I hope you have fun doing it and wish you much more fun and time to learn! 😊

But I can't really help with python principles and best practices.

 

VickiLanger

Asks "why?" too much
Gold Member
Local time
13:56
Joined
Oct 7, 2019
Messages
189
Pronouns
she/her

Good progress so far! A prove for yourself that you've really got the whole thing and can also adjust it would be to add two more paddles on the empty screen borders. This will need some adjustments in several places but would be a cool extension to the game.
After this you can add all the game meta stuff: a menu to choose game mode (2 users & 2 paddles, 2/4 or 4/4 or even much more fun 4/2 - playing the same entity with two persons is so much fun! 😅). Persistent Highscore and so on. And if you want to beat the **** out of it you could add network multiplayer stuff via socket.io, p2p or any service.

An idea for the window: you could check if extending the turtle screen would work. And you should move every class declaration in it's own class. At least that's common in the languages I know.^^

I hope you have fun doing it and wish you much more fun and time to learn! 😊

But I can't really help with python principles and best practices.


I like the way you're thinking. I think I'm going to leave it alone at this point. I'll work on making sure my next project is better.

 
Top