diff --git a/ball.py b/ball.py new file mode 100644 index 0000000..b2ee669 --- /dev/null +++ b/ball.py @@ -0,0 +1,39 @@ +from turtle import Turtle +from random import randint +UP = 90 +DOWN = 270 +TURTLE_WIDTH = 20 +PADDLE_MOVE_SIZE = 20 +BALL_MOVE_SIZE = 10 + +class Ball(Turtle): + def __init__(self, window): + super().__init__() + self.shape("circle") + self.color("white") + self.width = TURTLE_WIDTH/2 + self.height = TURTLE_WIDTH/2 + self.shapesize(stretch_wid = self.height/TURTLE_WIDTH, stretch_len = self.width/TURTLE_WIDTH) + self.penup() + self.window = window + self.x_pos = 0 + self.y_pos = -0.6 * self.window.window_height()/2 + self.setpos(self.x_pos, self.y_pos) + self.speed(1) + self.setheading(randint(20, 160)) + + def move(self, fps): + # Sets heading as the top right corner + # heading = math.degrees(math.atan(600/800)) + self.forward(200*(fps**(-1))) + + def bounce(self): + if self.heading() % 180 == 0: + self.setheading(180) + else: + self.setheading(360 - self.heading()) + + def reset_position(self): + self.goto(self.x_pos, self.y_pos) + self.setheading(randint(20, 160)) + diff --git a/breakout-linux.tar.gz b/breakout-linux.tar.gz new file mode 100644 index 0000000..84e526e Binary files /dev/null and b/breakout-linux.tar.gz differ diff --git a/breakout-linux/breakout b/breakout-linux/breakout new file mode 100755 index 0000000..e74b083 Binary files /dev/null and b/breakout-linux/breakout differ diff --git a/breakout-linux/highscore.txt b/breakout-linux/highscore.txt new file mode 100644 index 0000000..c227083 --- /dev/null +++ b/breakout-linux/highscore.txt @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/breakout-windows-x64.zip b/breakout-windows-x64.zip new file mode 100644 index 0000000..83806f7 Binary files /dev/null and b/breakout-windows-x64.zip differ diff --git a/breakout-windows-x64/breakout.exe b/breakout-windows-x64/breakout.exe new file mode 100755 index 0000000..cd9834b Binary files /dev/null and b/breakout-windows-x64/breakout.exe differ diff --git a/breakout-windows-x64/highscore.txt b/breakout-windows-x64/highscore.txt new file mode 100644 index 0000000..c227083 --- /dev/null +++ b/breakout-windows-x64/highscore.txt @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/breakout.py b/breakout.py new file mode 100644 index 0000000..8e493bf --- /dev/null +++ b/breakout.py @@ -0,0 +1,105 @@ +from turtle import Turtle, Screen +from paddle import Paddle +from ball import Ball +from brick import BrickManager +import time +from scoreboard import Scoreboard + +screen = Screen() +screen.setup(height = 900, width = 700) +screen.bgcolor("black") +screen.title("Atari Breakout") +screen.tracer(0) + +def game(): + for turtle in screen.turtles(): + turtle.reset() + + paddle = Paddle(screen) + ball = Ball(screen) + brickmanager = BrickManager(screen) + with open("highscore.txt", 'r', encoding='utf-8') as file: + highscore = int(file.readlines()[0].strip()) + + scoreboard = Scoreboard(screen, brickmanager, highscore) + FPS = 20 + screen.update() + + screen.listen() + screen.onkey(paddle.move_left, "Left") + screen.onkey(paddle.move_right, "Right") + screen.onkey(paddle.move_left, "a") + screen.onkey(paddle.move_right, "d") + screen.onkey(None,'r') + screen.onkey(None, 'c') + + game_ongoing = True + + def collision(ball, brick): + return abs(ball.xcor() - brick.xcor()) < 35 and abs(ball.ycor() - brick.ycor()) < 15 + + + while game_ongoing: + time.sleep(1/FPS) + screen.update() + + # Left and Right Wall Collision + if ball.xcor() > (screen.window_width()/2 - ball.width * 2) \ + or \ + ball.xcor() < (-screen.window_width()/2 + ball.width * 2): + ball.setheading(180 - ball.heading()) + + if ball.ycor() > 0.8 * screen.window_height()/2: + ball.reset_position() + + if ball.ycor() < - 0.9 * screen.window_height()/2: + break + + if collision(ball, paddle): + ball.bounce() + + # Detect collision with brick + brickmanager.update_available_for_collision() + if brickmanager.available_for_collision == 0: + brickmanager.place_bricks() + + for brick in brickmanager.available_for_collision: + if collision(ball, brick): + brick.setpos(screen.window_width()*2, 0) + ball.bounce() + pos = brickmanager.check_brick_pos(brick) + del brickmanager.grid[pos[0]][pos[1]] + brickmanager.score += brick.points + scoreboard.update_scoreboard() + + ball.move(FPS) + + score = brickmanager.score + if score > highscore: + highscore = score + with open("highscore.txt", "w", encoding='utf-8') as file: + file.write(str(highscore)) + + for turtle in screen.turtles(): + turtle.reset() + + gameover = Turtle(shape='square') + gameover.shapesize(stretch_wid=5, stretch_len=3) + gameover.color('white') + screen.onkey(game,'r') + screen.onkey(screen.bye, 'c') + + gameover.setheading(270) + gameover.write('Game Over', align='center', font=('Consolas', '30')) + gameover.forward(40) + gameover.write(f"Score: {score}", align='center', font=('Consolas', '30')) + gameover.forward(40) + gameover.write(f"High Score: {highscore}", align='center', font=('Consolas', '30')) + gameover.forward(40) + gameover.write("Press 'R' to Retry", align='center', font=('Consolas', '30')) + gameover.forward(40) + gameover.write("Press 'C' to Exit", align='center', font=('Consolas', '30')) + + screen.mainloop() + +game() diff --git a/brick.py b/brick.py new file mode 100644 index 0000000..4d9a118 --- /dev/null +++ b/brick.py @@ -0,0 +1,78 @@ +from turtle import Turtle + +UP = 90 +DOWN = 270 +TURTLE_WIDTH = 20 +PADDLE_MOVE_SIZE = 40 +BALL_MOVE_SIZE = 10 + +class Brick(Turtle): + def __init__(self, color, points): + super().__init__() + self.shape("square") + self.brick_color = color + self.color(self.brick_color) + self.width = TURTLE_WIDTH * 3 + self.height = TURTLE_WIDTH + self.shapesize(stretch_wid = self.height/TURTLE_WIDTH, stretch_len = self.width/TURTLE_WIDTH) + self.penup() + self.points = points + +class BrickManager(): + def __init__(self, window) -> None: + self.width = TURTLE_WIDTH * 3 + self.height = TURTLE_WIDTH + self.window = window + self.colors = {'yellow': 1, 'green': 3, 'orange': 5, 'red': 7} + self.gap = 10 + self.grid = None + self.max_brick = self.max_bricks() + self.available_for_collision = None + self.place_bricks() + self.score = 0 + + def max_bricks(self): + brick_num = int(self.window.window_width()/(TURTLE_WIDTH * 3)) + gap_size = self.gap * (brick_num - 1) + total_size = brick_num * (TURTLE_WIDTH * 3) + gap_size + if total_size > self.window.window_width(): + return brick_num - 1 + return brick_num + + def place_bricks(self): + current_x = - self.window.window_width()/2 + self.width/2 + current_y = 0.2 * self.window.window_height()/2 + self.grid = [] + for color, points in list(self.colors.items()): + for _ in range(2): + row = [] + for _ in range(self.max_brick): + brick = Brick(color, points) + brick.setpos(current_x, current_y) + current_x += self.width + self.gap + row.append(brick) + # Reset x position and go up on y position + self.grid.append(row) + current_x = - self.window.window_width()/2 + self.width/2 + current_y += self.height + self.gap + + def flat_grid(self, grid): + return [brick for row in grid for brick in row] + + def update_available_for_collision(self): + self.available_for_collision = [self.grid[0]] + for index, row in enumerate(self.grid): + if len(row) < self.max_brick: + try: + self.available_for_collision.append(self.grid[index + 1]) + except IndexError: + pass + + self.available_for_collision = self.flat_grid(self.available_for_collision) + + def check_brick_pos(self, brick): + return [(i, row.index(brick)) for i, row in enumerate(self.grid) if brick in row][0] + + + + diff --git a/highscore.txt b/highscore.txt new file mode 100644 index 0000000..c227083 --- /dev/null +++ b/highscore.txt @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/paddle.py b/paddle.py new file mode 100644 index 0000000..521af20 --- /dev/null +++ b/paddle.py @@ -0,0 +1,33 @@ +from turtle import Turtle + +UP = 90 +DOWN = 270 +TURTLE_WIDTH = 20 +PADDLE_MOVE_SIZE = 40 +BALL_MOVE_SIZE = 10 + +class Paddle(Turtle): + def __init__(self, window): + super().__init__() + self.shape("square") + self.color("white") + self.window = window + self.width = TURTLE_WIDTH * 3 + self.height = TURTLE_WIDTH + self.shapesize(stretch_wid = self.height/TURTLE_WIDTH, stretch_len = self.width/TURTLE_WIDTH) + + self.x_pos = 0 + self.y_pos = -0.8 * self.window.window_height()/2 + self.penup() + self.setpos(self.x_pos, self.y_pos) + + def move_right(self): + if self.position()[0] + self.width > self.window.window_width()/2: + pass + else: + self.goto(self.position()[0] + PADDLE_MOVE_SIZE, self.position()[1]) + def move_left(self): + if self.position()[0] - self.width < -self.window.window_width()/2: + pass + else: + self.goto(self.position()[0] - PADDLE_MOVE_SIZE, self.position()[1]) diff --git a/scoreboard.py b/scoreboard.py new file mode 100644 index 0000000..f13cdd0 --- /dev/null +++ b/scoreboard.py @@ -0,0 +1,19 @@ +from turtle import Turtle + +class Scoreboard(Turtle): + def __init__(self, window, brick_manager, highscore): + super().__init__() + self.color("white") + self.penup() + self.hideturtle() + self.window = window + self.brick_manager = brick_manager + self.highscore = highscore + self.update_scoreboard() + + def update_scoreboard(self): + self.clear() + self.setpos(0, 0.85 * self.window.window_height()/2) + self.write(f"Score: {self.brick_manager.score} High Score: {self.highscore}", + align = "center", font = ("Courier", 30, "normal")) +