## ~ Game Design

### Challenge 4

So, you've managed to set up a high scores feature to the game - this is rather difficult to scope if you're just starting out, so well done! You have also activated the other buttons on the menu screen so you can view and save high scores accordingly.

### CHALLENGE :

1. Copy and paste the code below (this is the solution to Challenge 3 and includes the high scores functionality that is now built in)

2. Extend the program to do the following:

>>Add an angel sprite to the game (you can use any image of an angel that you please!) The angel sprite drops like a bullet from the top of the screen at three random intervals and from three random positions each time. If the player ball collides with the angel sprite, the score jumps to 100 each time. Please note that you need to add the image to the same folder that your .py game is in.

### CODE:

```import pygame
import random
import math
import sys
import os

#FILE 3: High Scores
#saveScores: Saves the highscores to an external file
#scoreSavedScreen: Shows a confirmation screen for when scores are saved
#viewScoresScreen: Shows the highscores

#The menuChoice if has been expanded to include the aforementioned functions
#updateScores is called when the player wins
#Now the program asks for the player's name in the console before starting

SCREEN_WIDTH = 400
SCREEN_HEIGHT = 300

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

def draw(self):

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:
elif self.x + self.radius > SCREEN_WIDTH:
if self.y - self.radius < 0:
elif self.y + self.radius > SCREEN_HEIGHT:

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

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

f = pygame.font.SysFont("monospace", 30)
title = f.render("Main Menu", 1, (0,0,0))
screen.blit(title, (SCREEN_WIDTH/4,0))
play = f.render("P - Play", 1, (0,0,0))
screen.blit(play, (0,50))
quit = f.render("Q - Quit", 1, (0,0,0))
screen.blit(quit, (0,100))
save = f.render("S - Save Scores", 1, (0,0,0))
screen.blit(save, (0,150))
view = f.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

#Function that updates the highscores in memory
for i in range(len(highScores)):
if(score > highScores[i]):
highScores = highScores[0:i] + [score] + highScores[i:-1]
break
return highScores

#Function that saves the scores to a file named score.txt
def saveScores(highScores):
f = open("score.txt","w")
for score in highScores:
f.write(score + " " + str(score) + "\n")
f.close()

#Function that reads the scores from score.txt
if os.path.isfile("score.txt"):
f = open("score.txt","r")
highScores = []
for line in f:
l = line.split(" ")
highScores.append((l,int(l)))
f.close()
return highScores
else:
return [("Player",0),("Player",0), ("Player",0)]

#Displays a score saved screen for a small amount of time
def scoreSavedScreen(won):
screen.fill((255, 255, 255))
text = "Scores Saved!"
text2 = ""
if not won:
text = "You must have won"
text2 = "to save scores!"

f = pygame.font.SysFont("monospace", 30)
title = f.render(text, 1, (0,0,0))
screen.blit(title, (0,50))
secondLine = f.render(text2,1,(0,0,0))
screen.blit(secondLine, (0,100))
pygame.display.flip()
pygame.time.delay(2000)

#Displays the highscores
def viewScoresScreen():
screen.fill((255, 255, 255))

s1 = hScores + ": " + str(hScores)
s2 = hScores + ": " + str(hScores)
s3 = hScores + ": " + str(hScores)

f = pygame.font.SysFont("monospace", 30)
s1Label = f.render(s1, 1, (0,0,0))
screen.blit(s1Label, (0,50))
s2Label = f.render(s2, 1, (0,0,0))
screen.blit(s2Label, (0,100))
s3Label = f.render(s3, 1, (0,0,0))
screen.blit(s3Label, (0,150))

pygame.display.flip()
pygame.time.delay(2000)

#Before the game initiates, we ask the player to enter their name in the console
name = input()

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)

timeToEnd = 20000
timeEllapsed = 0

#Variable to determine if the player won
won = False

#List to store the three best scores

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

screen.fill((255, 255, 255))

#Added the choices for saving and viewing scores
score = 0
won = False
timeEllapsed = 0
done = True
if won:
saveScores(highScores)
scoreSavedScreen(won)
viewScoresScreen()
continue

if score > -100 and score < 200 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))
timeLabel = myfont.render("TIME: " + str((timeToEnd - timeEllapsed) / 1000 + 1), 1,(0,0,0))
screen.blit(label, (10, SCREEN_HEIGHT - 20))
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

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

#Return to the menu after the game has ended so the player can choose to play again, save or view scores