Exemplo n.º 1
0
class ShotTracker:
    def __init__(self, win, angle, velocity, height):
        """win is the GraphWin to display the shot. angle, velocity,
            and height are initial projectile parameters.
        """

        self.proj = Projectile(angle, velocity, height)
        self.marker = Circle(Point(0, height), 3)
        self.marker.setFill("red")
        self.marker.setOutline("red")
        self.marker.draw(win)

    def update(self, dt):
        """Move the shot dt seconds farther along its flight """

        # Update the projectile
        self.proj.update(dt)

        # Moves the circle to the new projectile location
        center = self.marker.getCenter()
        dx = self.proj.getX() - center.getX()
        dy = self.proj.getY() - center.getY()
        self.marker.move(dx, dy)

    def getX(self):
        """ return the current x coordinate of the shot's center """
        return self.proj.getX()

    def getY(self):
        """ return the current y coordinate of the  shot's center """
        return self.proj.getY()

    def undraw(self):
        """ undraw the shot """
        self.marker.undraw()
Exemplo n.º 2
0
class Wheel():

    def __init__(self, center, wheel_radius, tire_radius):
        self.tire_circle = Circle(center, tire_radius)
        self.wheel_circle = Circle(center, wheel_radius)

    def draw(self, win): 
        self.tire_circle.draw(win) 
        self.wheel_circle.draw(win) 

    def move(self, dx, dy): 
        self.tire_circle.move(dx, dy) 
        self.wheel_circle.move(dx, dy)

    def set_color(self, wheel_color, tire_color):
        self.tire_circle.setFill(tire_color) 
        self.wheel_circle.setFill(wheel_color)

    def undraw(self): 
        self.tire_circle .undraw() 
        self.wheel_circle .undraw() 

    def get_size(self):
        return self.tire_circle.getRadius()

    def get_center(self):
        return self.tire_circle.getCenter()

    def animate(self, win, dx, dy, n):
        if n > 0:
            self.move(dx, dy)
            win.after(100, self.animate, win, dx, dy, n - 1)
Exemplo n.º 3
0
class Face:
    def __init__(self, window, center, size):
        eyeSize = 0.15 * size
        eyeOff = size / 3.0
        mouthSize = 0.8 * size
        mouthOff = size / 2.0
        self.head = Circle(center, size)
        self.head.draw(window)
        self.leftEye = Circle(center, eyeSize)
        self.leftEye.move(-eyeOff, -eyeOff)
        self.rightEye = Circle(center, eyeSize)
        self.rightEye.move(eyeOff, -eyeOff)
        self.leftEye.draw(window)
        self.rightEye.draw(window)
        p1 = center.clone()
        p1.move(-mouthSize / 2, mouthOff)
        p2 = center.clone()
        p2.move(mouthSize / 2, mouthOff)
        self.mouth = Line(p1, p2)
        self.mouth.draw(window)

    def Move(self, dx, dy):
        for p in self.points:
            p.Move(dx, dy)

    def Flinch(self, center, size, window):
        eyesize = 0.15 * size
        eyeOff = size / 3.0
        self.leftEye = Line(Point(center.x + eyesize / 2, center.y),
                            Point(center.x - eyesize / 2, center.y))
        self.leftEye.move(-eyeOff, -eyeOff)
        self.rightEye = Circle(center, eyesize)
        self.rightEye.move(eyeOff, -eyeOff)
        self.leftEye.draw(window)
        self.rightEye.draw(window)
Exemplo n.º 4
0
class Asteroid:
    def __init__(self, pos, dest, win, vel, type):
        self.pos = pos
        self.dest = Point(dest.getX() - pos.getX(), dest.getY() - pos.getY())
        self.vel = vel
        self.type = type
        self.aliveFlag = True
        self.outOfBounds = False

        r = 25 * type
        self.object = Circle(self.pos, r)
        self.object.setFill(color_rgb(randrange(100, 255), 240, 90))

        self.draw(win)

    def draw(self, win):
        self.object.draw(win)

    def undraw(self):
        self.object.undraw()

    def update(self, dt, win):
        #normalize values
        dest = norm(self.dest.getX(), self.dest.getY())

        #calculate x and y movement
        x = dest.getX() * self.vel * dt
        y = dest.getY() * self.vel * dt

        self.object.move(x, y)
        self.pos = self.object.getCenter()

        if self.pos.getX() < -100:
            self.outOfBounds = True

    def getAliveFlag(self):
        return self.aliveFlag

    def setAliveFlag(self, statement=True):
        self.aliveFlag = statement

    def getOutOfBounds(self):
        return self.outOfBounds

    def getType(self):
        return self.type

    def getObject(self):
        return self.object
Exemplo n.º 5
0
class Bullet:
    def __init__(self, pos, win, r, dest, vel=1.0, c=color_rgb(255, 255, 255)):
        self.pos = pos  #position
        self.dest = Point(dest.getX() - pos.getX(),
                          dest.getY() - pos.getY())  #destination
        self.radius = r  #radius
        self.vel = vel  #velocity

        self.object = Circle(self.pos, r)  #Circle
        self.object.setFill(c)  #set colour
        self.object.setOutline('black')
        self.draw(win)  #draw

        self.aliveFlag = True

    def getAliveFlag(self):
        return self.aliveFlag

    def update(self, dt, win):
        #normalize values
        dest = norm(self.dest.getX(), self.dest.getY())

        #calculate x and y movement
        x = dest.getX() * self.vel * dt
        y = dest.getY() * self.vel * dt

        #move object
        self.object.move(x, y)

        #set position
        self.pos = self.object.getCenter()

        #Split into two if statements for readability
        if self.pos.getX() > win.getWidth() or self.pos.getY() > win.getHeight(
        ):
            self.aliveFlag = False
        elif self.pos.getX() < 0 or self.pos.getY() < 0:
            self.aliveFlag = False

    def getObject(self):
        return self.object

    def draw(self, win):
        self.object.draw(win)

    #remove object from screen
    def undraw(self):
        self.object.undraw()
Exemplo n.º 6
0
class Particle:
    def __init__(self, window, p=Point(0, 0)):
        self.particle = None
        self.drawn = False
        self.color = "RED"
        self.position = p
        self.x = p.getX()
        self.y = p.getY()
        self.size = 3
        self.dX = 0
        self.dY = 0
        self.win = window
        self.particleTurnCount = 0

    def setCoord(self, x, y):
        self.x = x
        self.y = y

    def setColor(self, color):
        self.color = color
        if self.particle:
            self.particle.setFill(color)

    def setSize(self, size):
        self.size = size
        if self.drawn:
            self.undraw()
            self.draw()

    def draw(self):
        self.particle = Circle(Point(self.x, self.y), self.size)
        self.particle.setFill(self.color)
        self.particle.draw(self.win)
        self.drawn = True

    def undraw(self):
        self.particle.undraw()
        self.drawn = False

    def setParticleMovement(self, dx, dy):
        self.dX = dx
        self.dY = dy

    def move(self):
        self.particle.move(self.dX, self.dY)
        self.position = Point(self.position.getX() + self.dX, self.position.getY() + self.dY)
        self.particle.undraw()
        self.particle.draw(self.win)
Exemplo n.º 7
0
class ShotTracker:
    def __init__(self, win, angle, velocity, height):
        self.proj = Projectile(angle, velocity, height)
        self.marker = Circle(Point(0, height), 3)  # 圆心, 半径
        self.marker.setFill('red')
        self.marker.setOutline('red')
        self.marker.draw(win)

    def update(self, dt):
        self.proj.update(dt)

        center = self.marker.getCenter()
        dx = self.proj.getX() - center.getX()
        dy = self.proj.getY() - center.getY()
        self.marker.move(dx, dy)

    def getX(self):
        return self.proj.getX()

    def getY(self):
        return self.proj.getY()

    def undraw(self):
        self.marker.undraw()
Exemplo n.º 8
0
Arquivo: ball.py Projeto: zj1730/SJTU
class circle:
    
    def __init__(self,window,P1,level):
        from graphics import GraphWin,Circle
        import random
        from time import time
        self.p1=P1
        self.window=window
        self.cir=Circle(self.p1,0.4)
        self.level=level
        self.cir.setWidth(0)
       
        i=random.randrange(6,9+self.level)
        self.color=i
        if self.color==6:                 #颜色对应数字
            self.cir.setFill("red")
        if self.color==7:
            self.cir.setFill("green")
        if self.color==8:
            self.cir.setFill("black")
        if self.color==9:
            self.cir.setFill("blue")
        if self.color==10:
            self.cir.setFill("orange")
        for s in range(100):
            cirs=Circle(self.p1,0.0+s*0.004)
            if self.color==6:
                cirs.setFill("red")
            if self.color==7:
                cirs.setFill("green")
            if self.color==8:
                cirs.setFill("black")
            if self.color==9:
                cirs.setFill("blue")
            if self.color==10:
                cirs.setFill("orange")
            cirs.draw(self.window)
            empty=0                     #空循环控制时间(time间隔太大)
            while empty<=30000:
                empty=empty+1
            cirs.undraw()    
        self.cir.draw(self.window)
        self.activate=True
            
    
    def click(self,pr):
        from graphics import Circle
        import time
        pd=False       
        if self.activate==True and (pr.getX()-self.p1.getX())**2+(pr.getY()-self.p1.getY())**2<=0.24:
            
            for i in range(3):                    #点击动画
                self.cir.move(0,-0.12/3.0)
                time.sleep(0.01)
            for i in range(3):
                self.cir.move(0,0.12/3.0)
                time.sleep(0.01)
            for i in range(3):
                self.cir.move(0,-0.12/3.0)
                time.sleep(0.01)
            for i in range(3):
                self.cir.move(0,0.12/3.0)
                time.sleep(0.01)
            pd=True
        return pd
  
            
           
    def undraw(self):
        self.cir.undraw()
    def close(self):
        self.cir.undraw()
        self.activate=False
    def move(self,pr):
        self.cir.move(pr.getX()-self.p1.getX(),pr.getY()-self.p1.getY())
        self.p1.move(pr.getX()-self.p1.getX(),pr.getY()-self.p1.getY())
Exemplo n.º 9
0
def update(win: GraphWin, sun: Circle):
    if sun.getP1().x - sun.getRadius() > win.width:
        sun.move(-win.width - 2 * sun.getRadius() - 20, 0)
    sun.move(1, 0)
Exemplo n.º 10
0
class Player:
    def __init__(self, pos, r, vel=1.0, c=color_rgb(255, 255, 255)):
        self.pos = pos  #position
        self.radius = r  #radius
        self.vel = vel  #velocity
        self.colour = c

        self.object = Circle(self.pos, r)
        self.object.setFill(c)
        self.object.setOutline('black')
        self.bullets = []  #bullet list

        self.score = 0

        print(self.object.getRadius())

    def modifyScore(self, value):
        self.score += value
        #print("Score:"+str(self.score))

    def getScore(self):
        return self.score

    def update(self, dt, win):
        x = 0.0
        y = 0.0
        if k.kUp():  #If key up is pressed
            y -= self.vel * dt
        if k.kDown():  #If key down is pressed
            y += self.vel * dt
        if k.kLeft():  #If key left is pressed
            x -= self.vel * dt
        if k.kRight():  #If key right is pressed
            x += self.vel * dt

        #Check if mouse has been clicked
        mouse = k.kMouseLeft(win)
        if mouse != None:
            #print(mouse)
            self.bullets.append(
                Bullet(self.pos, win, 10, Point(mouse.getX(), mouse.getY()),
                       1000, self.colour))

        #Update bullets and check if out of bounds
        for bullet in self.bullets:
            bullet.update(dt, win)
            if bullet.getAliveFlag() != True:
                bullet.undraw()
                self.bullets.remove(bullet)

        self.object.move(x, y)
        self.pos = self.object.getCenter()

    def getBullets(self):
        return self.bullets

    def getObject(self):
        return self.object

    def getRadius(self):
        return self.object.getRadius()

    def getPos(self):
        return self.pos

    def draw(self, win):
        self.object.draw(win)

    #undraw object
    def undraw(self):
        self.object.undraw()
Exemplo n.º 11
0
def endGame(field, game_panel, player_name, score):

    # Let the user know the game is finished
    end_game_text = Text(Point(200, 200), "Finished! Click to close")
    end_game_text.draw(field)

    # Draw graphic objects at different places that represent balloons with a
    #   string connected to it.
    balloon_1 = Circle(Point(145, 110), 18)
    balloon_1.setFill("red")
    balloon_1.setOutline("red")
    balloon_1.draw(field)
    triangle_1 = Polygon(Point(137, 135), Point(145, 128), Point(153, 135))
    triangle_1.setFill("red")
    triangle_1.setOutline('red')
    triangle_1.draw(field)
    string_1 = Line(Point(145, 135), Point(145, 180))
    string_1.draw(field)

    balloon_2 = Circle(Point(340, 300), 18)
    balloon_2.setFill("red")
    balloon_2.setOutline("red")
    balloon_2.draw(field)
    triangle_2 = Polygon(Point(332, 325), Point(340, 318), Point(348, 325))
    triangle_2.setFill("red")
    triangle_2.setOutline('red')
    triangle_2.draw(field)
    string_2 = Line(Point(340, 325), Point(340, 370))
    string_2.draw(field)

    balloon_3 = Circle(Point(75, 275), 18)
    balloon_3.setFill("red")
    balloon_3.setOutline("red")
    balloon_3.draw(field)
    triangle_3 = Polygon(Point(67, 300), Point(75, 293), Point(83, 300))
    triangle_3.setFill("red")
    triangle_3.setOutline('red')
    triangle_3.draw(field)
    string_3 = Line(Point(75, 300), Point(75, 345))
    string_3.draw(field)

    # Create a while loop that moves the objets every 0.05 seconds upwards to
    #   make it appear as if they are floating
    while True:
        sleep(0.05)
        balloon_1.move(0, -10)
        triangle_1.move(0, -10)
        string_1.move(0, -10)
        balloon_2.move(0, -10)
        triangle_2.move(0, -10)
        string_2.move(0, -10)
        balloon_3.move(0, -10)
        triangle_3.move(0, -10)
        string_3.move(0, -10)

        # If a click is detetced in the field, the window will close even if the
        #   balloons are still moving in the while loop
        click = field.checkMouse()
        if click != None:
            break

    # Add player score to top_scores.txt
    writeScores(player_name, score)

    # Set close condition to True
    close = True

    # Return close condition
    return close
Exemplo n.º 12
0
class Ball:
    def __init__(self, window: GraphWin, initial_position: Vector,
                 radius: float, speed: float, initial_direction: Vector,
                 score: Score, lose_life_function):
        self.window = window

        self.initial_direction = initial_direction
        self.initial_position = initial_position
        self.radius = radius
        self.initial_speed = speed
        self.current_speed = speed
        self.score = score

        self.is_alive = True
        self.still_ball = True

        self.position = self.initial_position
        self.velocity = self.initial_direction * speed

        self.sprite = Circle(self.position.to_point(), self.radius)

        self.damage = lose_life_function

    def draw(self, window: GraphWin):
        self.sprite.undraw()
        self.sprite = Circle(self.position.to_point(), self.radius)
        self.sprite.setFill("green")
        self.sprite.draw(window)

    def undraw(self):
        self.sprite.undraw()

    def reset(self):
        self.move_ball(self.initial_position)
        self.velocity = self.initial_direction * self.initial_speed
        self.is_alive = True
        self.still_ball = True
        self.current_speed = self.initial_speed

    def release(self):
        self.still_ball = False

    def update(self, elapsed_time: float, player: Player,
               squares: Sequence[PointSquare]):
        if self.still_ball:
            self.move_ball(player.position + Vector(0, -(self.radius + 10)))
            return

        self.world_collide()

        potential_position = self.position + (self.velocity * elapsed_time)

        new_potential_position, collided = self.collide_player(
            player, potential_position)

        if collided:
            potential_position = new_potential_position
        else:
            for square in squares:
                if not square.is_active:
                    continue

                new_potential_position, collided = self.collide_player(
                    square, potential_position)
                if collided:
                    square.hit()
                    potential_position = new_potential_position
                    break

        self.move_ball(potential_position)

    def move_ball(self, new_position: Vector):
        delta = new_position - self.position
        self.sprite.move(delta.x, delta.y)
        self.position = new_position

    def world_collide(self):
        if self.position.x - self.radius <= 0 or self.position.x + self.radius >= self.window.width:
            self.velocity = Vector(-self.velocity.x, self.velocity.y)

            if self.position.x > self.window.width / 2:
                self.move_ball(
                    Vector(self.window.width - (self.radius + 5),
                           self.position.y))
            else:
                self.move_ball(Vector(0 + (self.radius + 5), self.position.y))

        if self.position.y - self.radius <= 0 or self.position.y + self.radius >= self.window.height:
            self.velocity = Vector(self.velocity.x, -self.velocity.y)

            if self.position.y > self.window.height / 2:
                self.damage()
            else:
                self.move_ball(Vector(self.position.x, 0 + self.radius + 5))

    def collide_player(self, box_collider: Box, potential_position):

        # potential_position = self.position + self.velocity
        collided = False

        if type(box_collider) is Player:
            is_player = True
        else:
            is_player = False

        nearest_point = Vector(0, 0)
        nearest_point.x = max(
            box_collider.position.x - box_collider.half_size.x,
            min(potential_position.x,
                box_collider.position.x + box_collider.half_size.x))
        nearest_point.y = max(
            box_collider.position.y - box_collider.half_size.y,
            min(potential_position.y,
                box_collider.position.y + box_collider.half_size.y))

        ray_to_nearest = nearest_point - potential_position
        overlap = self.radius - ray_to_nearest.mag()

        if ray_to_nearest.mag() == 0:
            overlap = 0

        if overlap > 0:
            potential_position = potential_position - (
                ray_to_nearest.normalized() * overlap)

            right = (Vector(1, 0)).cos_angle(potential_position -
                                             box_collider.position)
            right_rec = (Vector(1, 0)).cos_angle(
                Vector(box_collider.half_size.x, -box_collider.half_size.y))
            left_rec = (Vector(1, 0)).cos_angle(
                Vector(-box_collider.half_size.x, -box_collider.half_size.y))

            collided_top = left_rec <= right <= right_rec

            top = (Vector(0, 1)).cos_angle(potential_position -
                                           box_collider.position)
            down_rec = (Vector(0, 1)).cos_angle(
                Vector(-box_collider.half_size.x, box_collider.half_size.y))
            top_rec = (Vector(0, 1)).cos_angle(
                Vector(-box_collider.half_size.x, -box_collider.half_size.y))

            collided_right = top_rec <= top <= down_rec

            if collided_top:
                if not is_player:
                    self.velocity = Vector(self.velocity.x, -self.velocity.y)
                else:
                    self.velocity = (potential_position - box_collider.position
                                     ).normalized() * self.current_speed

            if collided_right:
                self.velocity = Vector(-self.velocity.x, self.velocity.y)

            if not is_player:
                self.score.add_score()
                self.current_speed += 10

            self.velocity = self.velocity.normalized() * self.current_speed

            collided = True

        return potential_position, collided
Exemplo n.º 13
0
num_snowballs = 75
for i in range(num_snowballs):
    x = randint(0, 199)
    y = randint(0, 199)
    p = Point(x, y)
    snowball = Circle(p, randint(2, 3))
    snowball.setFill("white")
    snowball.draw(win)
    snowballs.append(snowball)

# Freeze between frames in seconds
animation_delay = 0.1

frames_to_render = 20
for i in range(frames_to_render):
    for snowball in snowballs:
        snowball.move(0, randint(1, 3))
        if snowball.getCenter().getY() > 199:
            snowball.undraw()
            snowball.move(0, -200)
            snowball.draw(win)
    # time.sleep(animation_delay)

label = Text(Point(99, 99), "Happy SNOW DAY!")
label.setFill("white")
label.setSize(18)
label.draw(win)

win.getMouse()
win.close()
Exemplo n.º 14
0
def moveCircle():
    #first move circle text
    moveCircle = Text(Point(25, 33), "Now lets create and move circles wherever you'd like")
    moveCircle.setFace("times roman")
    moveCircle.draw(win)
    sleep(1.5)
    moveCircle.undraw()

    #second move circle text
    moveCircle2 = Text(Point(25, 33), "When the red circle appears, move it by pressing 'w' 'a' 's' 'd'")
    moveCircle2.setFace("times roman")
    moveCircle2.draw(win)
    sleep(6)
    moveCircle2.undraw()

    #third color circle text
    moveCircle2 = Text(Point(25, 33), "You can create a new circle with a different color by pressing 'r', 'g', 'b'")
    moveCircle2.setFace("times roman")
    moveCircle2.draw(win)
    sleep(6)
    moveCircle2.undraw()

    #new circle def
    circle = Circle(Point(25, 25), 5)
    setfill = "red"
    circle.setFill(setfill)
    circle.draw(win)

    #move circle with for loops and an if function
    for i in range(1000):
        key = win.getKey()
        if key == "w":
            for w in range(1):
                circle.move(0,2)
        if key == "s":    
            for s in range(1):
                circle.move(0,-2)
        if key == "d":
            for d in range(1):
                circle.move(2,0)
        if key == "a":
            for a in range(1):
                circle.move(-2,0)
        
        if key == "r":
            circle = Circle(Point(25, 25), 5)
            setfill = "red"
            circle.setFill(setfill)
            circle.draw(win)
        if key == "g":
            circle = Circle(Point(25, 25), 5)
            setfill = "green"
            circle.setFill(setfill)
            circle.draw(win)
        if key == "b":
            circle = Circle(Point(25, 25), 5)
            setfill = "blue"
            circle.setFill(setfill)
            circle.draw(win)
        if key == "x":
            win.close()
            begin()
        #circle.undraw()
            
    win.close()
Exemplo n.º 15
0
class Fighter():
    def __init__(self, team, points, win):
        self.win = win
        self.radius = 0.1
        self.position = points[0]
        self.looking = points[1]
        self.fire_line = self.get_fire()
        self.color = "red" if team == 0 else "blue"
        self.view_left = self.get_left()
        self.view_left.setFill(self.color)
        self.view_right = self.get_right()
        self.view_right.setFill(self.color)
        self.firing = True
        self.view = False
        self.body = Circle(self.position, self.radius)
        self.body.setFill(self.color)

    def draw(self):
        self.body.draw(self.win)
        self.toggle_fire()
        self.toggle_view()

    def die(self):
        self.fire_line.undraw()
        self.view_left.undraw()
        self.view_right.undraw()
        self.body.setFill("orange")

    def undraw(self):
        self.body.undraw()

    def update(self, points):
        self.update_position(points[0])
        self.update_view(points[1])

    def update_position(self, position):
        self.body.move(position.x - self.position.x,
                       position.y - self.position.y)
        self.position = position

    def update_view(self, looking):
        self.fire_line.undraw()
        self.view_left.undraw()
        self.view_right.undraw()
        self.looking = looking
        self.fire_line = self.get_fire()
        self.view_left = self.get_left()
        self.view_right = self.get_right()
        if self.firing:
            self.fire_line.draw(self.win)
        if self.looking:
            self.view_left.draw(self.win)
            self.view_right.draw(self.win)

    def toggle_fire(self):
        self.firing = not self.firing
        if self.firing:
            self.fire_line.draw(self.win)
        else:
            self.fire_line.undraw()

    def toggle_view(self):
        self.view = not self.view
        if self.view:
            self.view_left.draw(self.win)
            self.view_right.draw(self.win)
        else:
            self.view_left.undraw()
            self.view_right.undraw()

    def get_left(self):
        x = self.position.x + (self.looking.x - self.position.x) * math.cos(
            math.pi / 3) - (self.looking.y - self.position.y) * math.sin(
                math.pi / 3)
        y = self.position.y + (self.looking.x - self.position.x) * math.sin(
            math.pi / 3) + (self.looking.y - self.position.y) * math.cos(
                math.pi / 3)
        l = Line(self.position, Point(x, y))
        l.setFill(self.color)
        return l

    def get_right(self):
        x = self.position.x + (self.looking.x - self.position.x) * math.cos(
            -math.pi / 3) - (self.looking.y - self.position.y) * math.sin(
                -math.pi / 3)
        y = self.position.y + (self.looking.x - self.position.x) * math.sin(
            -math.pi / 3) + (self.looking.y - self.position.y) * math.cos(
                -math.pi / 3)
        l = Line(self.position, Point(x, y))
        l.setFill(self.color)
        return l

    def get_fire(self):
        l = Line(self.position, self.looking)
        l.setFill("orange")
        return l

    def __repr__(self):
        return "Fighter({}, {})".format(str(self.position), str(self.radius))
Exemplo n.º 16
0
class BreakoutGame:
    def __init__(self, config):
        self.board = BulletinBoard(config.get_board_width(),
                                   config.get_board_height())
        self.wall = BrickWall(self.board.get_width(),
                              self.board.get_height() * 0.3,
                              config.get_color_matrix(),
                              config.get_color_map(), config.outline_bricks())
        paddle_width = self.board.get_width() // 8
        paddle_height = self.board.get_height() // 40
        self.paddle = Rectangle(paddle_width,
                                paddle_height,
                                "black",
                                filled=True)
        self.board.pin(self.wall, 0, 0.1 * self.board.get_height())
        self.board.pin(self.paddle,
                       self.board.get_width() / 2,
                       0.9 * self.board.get_height())
        self.reset_ball()
        self.message_box = TextBox("", "Helvetica Neue", 20, "#0000FF")
        self.dy = config.get_initial_y_velocity()
        self.dx = random.uniform(config.get_min_x_velocity(),
                                 config.get_max_x_velocity())
        self.dx = random.choice([-1, 1]) * self.dx
        self.lives_left = config.get_num_balls()
        self.time_step = config.get_time_step()
        self.game_in_progress = False
        self.game_over = False

    def start(self):
        self.board.listen_for("mousemove", self.mousemove_action)
        self.timer = self.board.call_every(self.step, self.time_step)
        self.board.listen_for("click", self.click_action)

    def mousemove_action(self, x, y):
        paddle_x = min(self.board.get_width() - self.paddle.get_width(), x)
        self.board.unpin(self.paddle)
        self.board.pin(self.paddle, paddle_x, 0.9 * self.board.get_height())

    def display_message(self, msg):
        self.board.unpin(self.message_box)
        self.message_box = TextBox(msg, "Helvetica Neue", 20, "#0000FF")
        self.board.pin(self.message_box, self.board.get_width() // 2, 10)

    def reset_ball(self):
        ball_size = self.board.get_width() // 25
        self.ball = Circle(ball_size, "black", filled=True)
        self.board.pin(self.ball,
                       self.board.get_width() / 2,
                       self.board.get_height() / 2)

    def click_action(self, x, y):
        if not self.game_over:
            self.game_in_progress = True

    def check_for_bounce(self):
        (x, y) = self.ball.get_center()
        radius = self.ball.get_radius()
        if x - radius <= 0 or x + radius >= self.board.get_width():
            self.dx = -self.dx
        if y - radius <= 0:
            self.dy = -self.dy

    def element_at(self, x, y):
        result = self.wall.shatter(x, y - (0.1 * self.board.get_height()))
        if not result:
            return self.board.element_at(x, y)
        else:
            return self.wall

    def check_for_collision(self):
        def get_colliding_objects():
            (x, y) = self.ball.get_center()
            radius = self.ball.get_radius()
            corners = [(x - radius, y - radius), (x + radius, y - radius),
                       (x - radius, y + radius), (x + radius, y + radius)]
            colliders = set()
            for (corner_x, corner_y) in corners:
                element = self.element_at(corner_x, corner_y)
                if element != None:
                    colliders.add(element)
            return colliders

        for collider in get_colliding_objects():
            if collider == self.paddle:
                self.dy = -abs(self.dy)
            elif collider == self.wall:
                self.dy = -self.dy

    def check_for_game_end(self):
        if self.wall.all_shattered():
            self.game_in_progress = False
            self.game_over = True
            self.display_message("YOU WIN!")
        else:
            (x, y) = self.ball.get_center()
            radius = self.ball.get_radius()
            if y - radius >= self.board.get_height():
                self.board.unpin(self.ball)
                self.lives_left -= 1
                self.game_in_progress = False
                if self.lives_left <= 0:
                    self.game_over = True
                    self.display_message("GAME OVER")
                else:
                    self.reset_ball()

    def step(self):
        if self.game_in_progress and not self.game_over:
            self.ball.move(self.dx, self.dy)
            self.check_for_bounce()
            self.check_for_collision()
            self.check_for_game_end()