~ Game Design


Challenge 3

So, you've managed to add a timer function to the game - this is always handy - and allowed a mere 20 seconds for game play! Your game also has the win./lose functionality built in! 

CHALLENGE : 

1. Copy and paste the code below (this is the solution to Challenge 2 and has a working timer, timed element and "win" and "lose" built in to the game)

2. Extend the program to do the following: 

>>Add the high scores save and view feature to the game. The first three high scores are stored in a file in the following format (Name: Score). If the user 'wins', they can add their high score to the file (Press S on the Menu). Pressing 'V' would enable the high scores from the file to be viewed (displayed on screen). This is going to involve some file handling. Yep, it's getting harder - but keep going. Incidentally, the game, as it is, with a score of 200 to reach, is very difficult! You may want to change the winning score to something like 10 for testing purposes or you'll go a little crazy!

CODE : 

import pygame
import random
import math

#FILE 2: Timer
#Added the variables timeToEnd and timeEllapsed to make a timer
#For ending the game. Modified the winning conditions to reflect this

SCREEN_WIDTH = 400
SCREEN_HEIGHT = 300


class Ball:
        def __init__(self, x, y, radius, color, screen):
                self.x = x
                self.y = y
                self.radius = radius
                self.screen = screen
                self.color = color

        def draw(self):
                pygame.draw.circle(screen, self.color, [self.x, self.y], self.radius)

class PlayerBall(Ball):
        def __init__(self, x, y, radius, color, screen):
                Ball.__init__(self, x, y, radius, color, screen)
                self.green_cooldown = 0
                self.red_cooldown = 0

        def move(self, mv_type):
                if mv_type == "UP":
                        self.y -= 5
                elif mv_type == "DOWN":
                        self.y += 5
                elif mv_type == "LEFT":
                        self.x -= 5
                elif mv_type == "RIGHT":
                        self.x += 5

                if self.x - self.radius < 0:
                        self.x = self.radius
                elif self.x + self.radius > SCREEN_WIDTH:
                        self.x = SCREEN_WIDTH - self.radius
                if self.y - self.radius < 0:
                        self.y = self.radius
                elif self.y + self.radius > SCREEN_HEIGHT:
                        self.y = SCREEN_HEIGHT - self.radius

        def check_contact(self, greenBall, redBall):
                to_return = 0
                if math.sqrt((self.y - greenBall.y) ** 2 + (self.x - greenBall.x) ** 2) < self.radius + greenBall.radius:
                        if self.green_cooldown == 0:
                                self.green_cooldown = 10
                                to_return += 10
                if math.sqrt((self.y - redBall.y) ** 2 + (self.x - redBall.x) ** 2) < self.radius + redBall.radius:
                        if self.red_cooldown == 0:
                                self.red_cooldown = 10
                                to_return -= 10
                return to_return

class GreenBall(Ball):
        def __init__(self, x, y, radius, color, screen):
                Ball.__init__(self, x, y, radius, color, screen)
                self.vy = random.randint(0, 4) - 2
                self.vx = random.randint(0, 4) - 2
                while self.vy == 0 or self.vx == 0:
                        self.vy = random.randint(0, 4) - 2
                        self.vx = random.randint(0, 4) - 2

        def move(self):
                self.x += self.vx
                self.y += self.vy

                if self.x - self.radius < 0:
                        self.x = self.radius
                        self.vx *= -1
                elif self.x + self.radius > SCREEN_WIDTH:
                        self.x = SCREEN_WIDTH - self.radius
                        self.vx *= -1
                if self.y - self.radius < 0:
                        self.y = self.radius
                        self.vy *= -1
                elif self.y + self.radius > SCREEN_HEIGHT:
                        self.y = SCREEN_HEIGHT - self.radius
                        self.vy *= -1

class RedBall(Ball):
        def __init__(self, x, y, radius, color, screen):
                Ball.__init__(self, x, y, radius, color, screen)
                self.vy = random.randint(0, 6) - 3
                self.vx = random.randint(0, 6) - 3
                while self.vy == 0 or self.vx == 0:
                        self.vy = random.randint(0, 6) - 3
                        self.vx = random.randint(0, 6) - 3

        def move(self):
                self.x += self.vx
                self.y += self.vy

                if self.x - self.radius < 0:
                        self.x = self.radius
                        self.vx *= -1
                elif self.x + self.radius > SCREEN_WIDTH:
                        self.x = SCREEN_WIDTH - self.radius
                        self.vx *= -1
                if self.y - self.radius < 0:
                        self.y = self.radius
                        self.vy *= -1
                elif self.y + self.radius > SCREEN_HEIGHT:
                        self.y = SCREEN_HEIGHT - self.radius
                        self.vy *= -1


def renderMenu():
    myfont = pygame.font.SysFont("monospace", 30)
    title = myfont.render("Main Menu", 1, (0,0,0))
    screen.blit(title, (SCREEN_WIDTH/4,0))
    play = myfont.render("P - Play", 1, (0,0,0))
    screen.blit(play, (0,50))
    quit = myfont.render("Q - Quit", 1, (0,0,0))
    screen.blit(quit, (0,100))
    save = myfont.render("S - Save Scores", 1, (0,0,0))
    screen.blit(save, (0,150))
    view = myfont.render("V - View Scores", 1, (0,0,0))
    screen.blit(view, (0,200))

    res = 0
        
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_p]:
        res = 1
    if pressed[pygame.K_q]:
        res = 2
    if pressed[pygame.K_s]:
        res = 3
    if pressed[pygame.K_v]:
        res = 4
        
    pygame.display.flip()
    clock.tick(60)
    return res

pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
done = False
score = 0

myfont = pygame.font.SysFont("monospace", 15)

clock = pygame.time.Clock()

ball1 = PlayerBall(100, 100, 20, (0, 0, 0), screen)
ball2 = GreenBall(200, 200, 5, (0, 255, 0), screen)
ball3 = RedBall(250, 300, 90, (255, 0, 0), screen)

menuChoice = 0

#Variables to determine time to end the game (in milliseconds)
timeToEnd = 20000
timeEllapsed = 0

while not done:
        for event in pygame.event.get():
                if event.type == pygame.QUIT:
                        done = True
        
        screen.fill((255, 255, 255))

        if menuChoice != 1:
                menuChoice = renderMenu()
                if menuChoice == 2:
                    done = True
                continue

        #Add a finishing condition for when a certain amount of time passed
        if score > -100 and score < 100 and timeEllapsed < timeToEnd:
                pressed = pygame.key.get_pressed()
                if pressed[pygame.K_UP]:
                        ball1.move("UP")
                if pressed[pygame.K_DOWN]:
                        ball1.move("DOWN")
                if pressed[pygame.K_LEFT]:
                        ball1.move("LEFT")
                if pressed[pygame.K_RIGHT]:
                        ball1.move("RIGHT")     

                label = myfont.render("SCORE: " + str(score), 1, (0,0,0))
                screen.blit(label, (10, SCREEN_HEIGHT - 20))

                #Set up the label for the timer's value and put it on screen
                timeLabel = myfont.render("TIME: " + str((timeToEnd - timeEllapsed)/1000 + 1), 1,(0,0,0))
                screen.blit(timeLabel,(300, SCREEN_HEIGHT - 20))

                ball2.move()
                ball3.move()
                score += ball1.check_contact(ball2, ball3)

                ball2.draw()
                ball3.draw()
                ball1.draw()

                if ball1.green_cooldown > 0:
                        ball1.green_cooldown -= 1
                if ball1.red_cooldown > 0:
                        ball1.red_cooldown -= 1

                #Update the timer
                timeEllapsed += clock.get_time()
        else:
                myfont = pygame.font.SysFont("monospace", 50)
                if score < 100:
                        label = myfont.render("YOU LOSE!", 1, (0,0,0))
                        screen.blit(label, (0,0))
                else:
                        label = myfont.render("YOU WIN!", 1, (0,0,0))
                        screen.blit(label, (0,0))

        pygame.display.flip()
        clock.tick(60)

pygame.quit()