Source code for pong

import numpy as np
from random import randint, choice
from time import time, sleep
from threading import Thread

LEFT_WIN = -1
RIGHT_WIN = +1
NO_WIN = 0

MOVE_UP = +1
MOVE_DOWN = -1
DONT_MOVE = 0


[docs]class BallOrPuck: """Class that represents either a ball or a puck. Args: game (GameOfPong): Pong game. x_pos (float): Initial x position in unit length. y_pos (float): Initial y position in unit length. velocity (float): Change in position per iteration. direction (list, float): Heading. """ def __init__(self, game, x_pos=0.5, y_pos=0.5, velocity=1 / 5., direction=0): self.x_pos = x_pos self.y_pos = y_pos self.velocity = velocity self.direction = direction self.game = game self.update_cell()
[docs] def get_cell(self): return self.cell
[docs] def update_cell(self): """Update cell based on position. """ x_cell = int(np.floor( (self.x_pos / self.game.x_length) * self.game.x_grid)) y_cell = int(np.floor( (self.y_pos / self.game.y_length) * self.game.y_grid)) self.cell = [x_cell, y_cell]
[docs]class Ball(BallOrPuck): """Class representing the ball. Args: radius (float): Radius of ball in unit length. For other args, see :class:`BallOrPuck`. """ def __init__(self, game, x_pos=0.5, y_pos=0.5, velocity=0.025, direction=[-1 / 2., 1 / 2.], radius=0.02): BallOrPuck.__init__(self, game, x_pos, y_pos, velocity, direction) self.ball_radius = radius # unit length self.update_cell()
[docs]class Puck(BallOrPuck): """Class representing the puck. Args: direction (float, int): +1 for up, -1 for down, 0 for no movement. For other args, see :class:`BallOrPuck`. """ def __init__(self, game, length=0.07, y_pos=0.5, velocity=0.05, direction=0.): BallOrPuck.__init__(self, game, game.x_length, y_pos, velocity, direction) self.paddle_length = length # unit length self.update_cell()
[docs]class GameOfPong(object): """Class representing a game of Pong. Playing field: 1 by 1 discretized into cells. Args: x_grid (int): Number of cells to discretize x-axis into. y_grid (int): Number of cells to discretize y-axis into. debug (bool): Print debugging messages. norm (string): "L1" or "L2", defines norm to be used for velocity vector. """ def __init__(self, x_grid=32, y_grid=32, debug=False, norm="L1"): self.x_length = 1 # length in x-direction in unit length self.y_length = 1 # length in y-direction in unit length self.x_grid = x_grid self.y_grid = y_grid self.right_paddle = Puck(self ) # AI paddle, center position along y-axis if norm == "L2": initial_angle = choice([0., 180.]) + randint( -45, 45) # random initial direction toward either end initial_vx = np.cos(initial_angle / 360. * 2 * np.pi) initial_vy = np.sin(initial_angle / 360. * 2 * np.pi) elif norm == "L1": initial_vx = 0.5 + 0.5 * np.random.random() initial_vy = 1. - initial_vx initial_vx *= choice([-1., 1.]) initial_vy *= choice([-1., 1.]) self.ball = Ball(self, direction=[initial_vx, initial_vy]) self.result = False self.debug = debug
[docs] def update_ball_direction(self): """In case of a collision, update the direction of ball velocity. Also determine if the ball is in either player's net. Returns: Either NO_WIN or RIGHT_WIN. """ if self.ball.y_pos + self.ball.ball_radius >= self.y_length: # upper edge self.ball.direction[1] = -self.ball.direction[1] return NO_WIN if self.ball.y_pos - self.ball.ball_radius <= 0: # lower edge self.ball.direction[1] = -self.ball.direction[1] return NO_WIN # left paddle/wall if self.ball.x_pos - self.ball.ball_radius <= 0: self.ball.direction[0] = -self.ball.direction[0] return NO_WIN # right paddle if self.ball.x_pos + self.ball.ball_radius >= self.x_length: # check if win for right player if self.right_paddle.y_pos - self.right_paddle.paddle_length / 2 <= self.ball.y_pos <= self.right_paddle.y_pos + self.right_paddle.paddle_length / 2: self.ball.direction[0] = -self.ball.direction[0] return NO_WIN else: return RIGHT_WIN return NO_WIN
[docs] def propagate_ball_and_paddles(self): """Update ball and paddle coordinates based on direction and velocity. Also update their cells. """ self.right_paddle.y_pos += self.right_paddle.direction * self.right_paddle.velocity if self.right_paddle.y_pos < 0: self.right_paddle.y_pos = 0 if self.right_paddle.y_pos > self.y_length: self.right_paddle.y_pos = self.y_length self.ball.y_pos += self.ball.velocity * self.ball.direction[1] self.ball.x_pos += self.ball.velocity * self.ball.direction[0] self.ball.update_cell() self.right_paddle.update_cell()
[docs] def move_right_paddle_up(self): self.right_paddle.direction = MOVE_UP
[docs] def move_right_paddle_down(self): self.right_paddle.direction = MOVE_DOWN
[docs] def dont_move_right_paddle(self): self.right_paddle.direction = DONT_MOVE
[docs] def get_right_paddle_position(self): return self.right_paddle.y_pos
[docs] def get_right_paddle_length(self): return self.right_paddle.paddle_length
[docs] def get_right_paddle_cell(self): return self.right_paddle.get_cell()
[docs] def get_ball_cell(self): return self.ball.get_cell()
[docs] def step(self): """Perform one game step. """ ball_status = self.update_ball_direction() self.propagate_ball_and_paddles() if ball_status != NO_WIN: self.result = ball_status return ball_status