Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import os
- import io
- import shutil
- import mathimport os
- import io
- import shutilimport os
- import io
- import shutil
- import math
- import time
- import random
- import pygame
- from pygame.locals import *
- ###################################
- # P Y B A L L
- # PyBall - A Pong-Based Adaptive AI Demonstration / Game
- # Created by TAGC
- # Last update: 20/03/2012
- # Description: This program is developed as part of an
- # assessed project at Imperial College, London
- # to demonstrate the use of AI in gaming and
- # contrast the use of static and dynamic
- # AI techniques to allow further analysis into
- # how different kinds of AI behaviour can affect
- # replayability and game immersion
- # Notes: This script will only run if it comes as part
- # of the "PyBallStuff" package. This package
- # contains the audio, image and font files used
- # by this program
- ###################################
- # I N S T A L L A T I O N
- # This script is written in Python 3.2 and requires a Python 3
- # interpreter to run. This can be obtained from
- # http://www.python.org/download/ (it is strongly recommended
- # that the user obtains the 32-bit version even if they have
- # a 64-bit operating system)
- # This script also requires an installation of the python
- # module "PyGame" available at
- # http://www.pygame.org/download.shtml (again, it is strongly
- # recommended that the user obtains the 32-bit version)
- # In addition to these, the user will need to install the
- # following fonts:
- # > "Birth of a Hero"
- # > "Angelic War"
- # > "Hawaii Lover"
- # > "Sergeant SixPack"
- # These are all available at http://www.1001freefonts.com/
- # if the user does not possess the "Fonts" folder included
- # inside the folder "PyBallStuff"
- # The "PyBallStuff" folder must be placed in the
- # "C:/Python32" directory
- ###################################
- # L A U N C H I N G
- # To execute the program, click on "Run" on the top bar
- # of the interpreter and select "Run Module" or press F5
- ###################################
- # C O N T R O L S
- # Special Controls
- ###################
- # "Enter" - begins the game after the program launches
- # "Escape" - if the user has the Settings Menu open, this
- # exits that screen, otherwise it quits the
- # programs
- # Setting Controls
- ###################
- # "1" - Toggles through the sound level settings
- # "2" - Pauses / unpauses the program
- # "3" - Toggles Fast Forward mode on and off; when
- # this mode is active, the logic of the game
- # runs slightly faster
- # "4" - Toggles Graphical Information mode on and off;
- # when on, extra objects are rendered to the
- # screen to display more information on the
- # states of the pads' memories, etc
- # "5" - Toggles Textual Information mode on and off;
- # when on, information on the game is printed
- # out to the console
- # "6" - Opens or closes the Settings Menu
- # "Left" - when the Settings Menu is open, this displays
- # the previous page of options (if not already
- # there)
- # "Right" - when the Settings Menu is open, this displays
- # the next page of options (if not already
- # there)
- # Pad Controls
- ###############
- # "A" - if the green pad is under human control, moves
- # that pad left
- # "D" - if the green pad is under human control, moves
- # that pad right
- # "I" - if the blue pad is under human control, moves
- # that pad up
- # "K" - if the blue pad is under human control, moves
- # that pad down
- # "Left" - if the red pad is under human control, moves
- # that pad left
- # "Right" - if the red pad is under human control, moves
- # that pad right
- # "Num 8" - if the orange pad is under human control, moves
- # that pad up
- # "Num 2" - if the orange pad is under human control, moves
- # that pad down
- window_width = 700
- window_height = 700
- pad_width = 25
- pad_height = 100
- ball_radius = 2
- corner_buffer_coef = 0.20
- population_size = 20
- games_per_test = 50
- xmin = window_width * corner_buffer_coef - pad_height / 2
- xmax = window_width * (1-corner_buffer_coef) - pad_height / 2
- ymin = window_height * corner_buffer_coef - pad_height / 2
- ymax = window_height * (1-corner_buffer_coef) - pad_height / 2
- plane_reflect_angles = {0:180, 90:360, 180:180, 270:360}
- pad_speed = 0.5
- ball_accel = 0.00001
- max_x_difference = window_width
- max_y_difference = window_height
- max_angular_difference = 180
- max_gravity_difference = 100
- pyball_stuff_path = r"C:\Python32\PyBallStuff\\"
- pad_image_path = pyball_stuff_path + r"Pictures\Pads"
- sound_level_path = pyball_stuff_path + r"Pictures\SoundLevels"
- letter_path = pyball_stuff_path + r"Pictures\Letters"
- picture_path = pyball_stuff_path + r"Pictures"
- music_path = pyball_stuff_path + r"Audio"
- pad_memory_path = pyball_stuff_path + r"Memories"
- data_path = pyball_stuff_path + r"Data"
- font_size_11 = 11
- font_size_12 = 12
- font_size_14 = 14
- font_size_16 = 16
- font_size_18 = 18
- font_size_20 = 20
- font_size_22 = 22
- font_size_24 = 24
- font_size_26 = 26
- font_size_30 = 30
- font_size_40 = 40
- frames_per_score_message = 1000
- score_message_thresholds = {"Okay":5, "Good":10, "Great":15, "Excellent!":20,
- "Superb!":25, "Amazing!":30, "Outstanding!":50,
- "Unbelievable!":100}
- class Colour:
- def __init__(self, r, g, b):
- self.r = r
- self.g = g
- self.b = b
- return
- def toList(self):
- listrep = [self.r, self.g, self.b]
- return listrep
- class Pad:
- def __init__(self, x, y, surface):
- self.x = x
- self.y = y
- self.x_target = x
- self.y_target = y
- self.surface = surface
- self.moving = (0, 0)
- self.AI = 1
- self.score = 0
- self.memory = []
- return
- def move(self, x_translate, y_translate):
- self.moving = (x_translate, y_translate)
- self.xnew = self.x + x_translate
- self.ynew = self.y + y_translate
- if xmin <= self.xnew and self.xnew <= xmax:
- self.x = self.xnew
- else:
- x_translate = 0
- if ymin <= self.ynew and self.ynew <= ymax:
- self.y = self.ynew
- else:
- y_translate = 0
- self.moving = (x_translate, y_translate)
- return
- def losePoint(self, x_miss, y_miss):
- self.score -= 1
- if GameState.toggle_learning and Balls.ball.last_pad != self and self.AI == 1:
- memory_tuple = MemoryTuple(x_miss, y_miss)
- printData("SAVING TO MEMORY...")
- printData(memory_tuple.getData())
- printData("")
- self.memory.append(memory_tuple)
- return
- def toString(self):
- stringrep = ("(%s, %s)") % (self.x, self.y)
- return stringrep
- class Ball:
- def __init__(self, x, y, velocity, angle):
- self.x = x
- self.y = y
- self.velocity = velocity
- self.angle = angle
- self.last_pad = -1
- return
- def move(self):
- cut_off_modifier = 0.9
- collisionHandling(self)
- (pad_0_x, pad_0_y) = getPadMidpoint(0)
- (pad_1_x, pad_1_y) = getPadMidpoint(1)
- (pad_2_x, pad_2_y) = getPadMidpoint(2)
- (pad_3_x, pad_3_y) = getPadMidpoint(3)
- if self.x < pad_0_x * cut_off_modifier:
- Pads.padlist[0].losePoint(self.x, self.y)
- Launchers.launcher.launchBall()
- elif self.x > window_width - (window_width - pad_2_x) * cut_off_modifier:
- Pads.padlist[2].losePoint(self.x, self.y)
- Launchers.launcher.launchBall()
- elif self.y < pad_3_y * cut_off_modifier:
- Pads.padlist[3].losePoint(self.x, self.y)
- Launchers.launcher.launchBall()
- elif self.y > window_height - (window_height - pad_1_y) * cut_off_modifier:
- Pads.padlist[1].losePoint(self.x, self.y)
- Launchers.launcher.launchBall()
- else:
- (self.x_translate, self.y_translate) = self.getVelocityComponents()
- if gravity_ring.active:
- target = gravity_ring
- grav_pull_angle = degToRad(angleBetweenPoints((self.x, self.y),
- (target.pos_x, target.pos_y)))
- distance = getPointDistance(self.x, self.y, target.pos_x, target.pos_y)
- grav_x = gravity_ring.grav_coef / 2000 * math.cos(grav_pull_angle) * math.pow(0.96, distance)
- grav_y = gravity_ring.grav_coef / 2000 * math.sin(grav_pull_angle) * math.pow(0.96, distance)
- if abs(self.x_translate + grav_x) < abs(self.x_translate):
- grav_x *= 0.3
- if abs(self.y_translate - grav_y) < abs(self.y_translate):
- grav_y *= 0.3
- self.velocity = velocityFromComponents(self.x_translate + grav_x,
- self.y_translate - grav_y)
- self.angle = angleFromComponents(self.x_translate + grav_x,
- self.y_translate - grav_y)
- self.velocity += GameState.ball_accel
- self.x += self.x_translate
- self.y += self.y_translate
- return
- def getVelocityComponents(self):
- x_translate = math.cos(degToRad(self.angle)) * self.velocity
- y_translate = -math.sin(degToRad(self.angle)) * self.velocity
- components = (x_translate, y_translate)
- return components
- def toString(self):
- stringrep = ("Position: (%s, %s), moving at %s units/frame at angle %s"
- ) % (self.x, self.y, self.velocity, self.angle)
- return stringrep
- class GravFocal:
- def __init__(self, x, y, velocity, angle, grav_ring):
- self.pos_x = x
- self.pos_y = y
- self.velocity = velocity
- self.angle = angle
- self.grav_ring = grav_ring
- self.chasing = ""
- i = 0
- while self.chasing == "" or self.chasing == self:
- if i < len(grav_ring.grav_focals):
- self.chasing = random.choice(grav_ring.grav_focals)
- i += 1
- else:
- self.chasing = grav_ring
- break
- return
- def move(self):
- (self.x_translate, self.y_translate) = self.getVelocityComponents()
- grav_pull_angle = degToRad(angleBetweenPoints((self.pos_x, self.pos_y),
- (self.chasing.pos_x, self.chasing.pos_y)))
- grav_x = gravity_ring.grav_coef / 5000 * math.cos(grav_pull_angle)
- grav_y = gravity_ring.grav_coef / 5000 * math.sin(grav_pull_angle)
- self.velocity = velocityFromComponents(self.x_translate + grav_x, self.y_translate - grav_y)
- self.angle = angleFromComponents(self.x_translate + grav_x, self.y_translate - grav_y)
- self.pos_x += self.x_translate
- self.pos_y += self.y_translate
- return
- def getVelocityComponents(self):
- x_translate = math.cos(degToRad(self.angle)) * self.velocity
- y_translate = -math.sin(degToRad(self.angle)) * self.velocity
- components = (x_translate, y_translate)
- return components
- class Launcher:
- def __init__(self, radius, cool_down_rate, hot_colour, cool_colour):
- self.x = window_width/2
- self.y = window_height/2
- self.radius = radius
- self.heat = 0
- self.cool_down_rate = cool_down_rate
- self.hot_colour = hot_colour
- self.cool_colour = cool_colour
- def launchBall(self):
- GameState.data.append((GameState.game_count, GameState.collision_count))
- if GameState.auto_saving:
- if GameState.game_count > 0 and not GameState.game_count % 10:
- saveGameData()
- if GameState.game_count > 0 and not GameState.game_count % 100:
- backUpGameData()
- GameState.game_count += 1
- GameState.collision_count = 0
- genNewBall()
- self.heat = 1
- GameState.num_rounds += 1
- if GameState.auto_saving and GameState.num_rounds > 0:
- if GameState.num_rounds % GameState.auto_saving == 0:
- savePadMemories()
- def coolDown(self):
- if self.heat > 0:
- self.heat -= self.cool_down_rate
- def getColour(self):
- self.hr = self.heat * self.hot_colour[0]
- self.cr = (1-self.heat) * self.cool_colour[0]
- self.hg = self.heat * self.hot_colour[1]
- self.cg = (1-self.heat) * self.cool_colour[1]
- self.hb = self.heat * self.hot_colour[2]
- self.cb = (1-self.heat) * self.cool_colour[2]
- colour = [self.hr + self.cr, self.hg + self.cg, self.hb + self.cb]
- return colour
- def getRadius(self):
- actual_radius = int(self.radius * math.pow(1.01, self.heat * 100))
- return actual_radius
- class SoundLevel:
- def __init__(self):
- self.levels = []
- self.current_level = 3
- self.volume = 1
- self.pos_x = 0
- self.pos_y = 0
- self.width = 0
- self.height = 0
- self.hovering = False
- self.surface = ""
- self.cover = ""
- self.channel = ""
- return
- def addSurface(self, surface):
- self.surface = surface
- self.width = surface.get_width()
- self.height = surface.get_height()
- self.cover = pygame.Surface((self.width, self.height), 0, 32)
- self.cover.fill(black.toList())
- return
- def addChannel(self, channel):
- self.channel = channel
- self.channel.set_volume(0.333 * self.current_level)
- return
- def changeSoundLevel(self):
- self.current_level += 1
- self.current_level %= 4
- self.addSurface(self.levels[self.current_level])
- return
- def adjustVolume(self):
- target = 0.333 * self.current_level
- difference = target - self.volume
- self.volume += difference * 0.002
- self.channel.set_volume(self.volume)
- return
- def setCoverTransparency(self):
- if self.hovering:
- alpha = 255 * 0.15
- else:
- alpha = 255 * 0.5
- self.cover.set_alpha(alpha)
- return
- class PauseButton:
- def __init__(self, x, y):
- self.active = False
- self.hovering = False
- self.pos_x = x
- self.pos_y = y
- self.width = 18
- self.height = 18
- return
- def switch(self):
- self.active = not self.active
- return
- class GenericButton:
- def __init__(self, x, y):
- self.active = False
- self.hovering = False
- self.pos_x = x
- self.pos_y = y
- self.width = 0
- self.height = 0
- return
- def addSurface(self, surface):
- self.surface = surface
- self.width = surface.get_width()
- self.height = surface.get_height()
- self.cover = pygame.Surface((self.width, self.height), 0, 32)
- self.cover.fill(black.toList())
- return
- def switch(self):
- self.active = not self.active
- return
- class MemoryTuple:
- def __init__(self, x_miss, y_miss):
- self.x_miss = x_miss
- self.y_miss = y_miss
- self.x_collision = Collision.xpos
- self.y_collision = Collision.ypos
- self.collision_i_angle = Collision.initial_angle
- self.collision_i_speed = Collision.initial_speed
- self.collision_f_angle = Collision.final_angle
- self.collision_f_speed = Collision.final_speed
- self.collision_grav_coef = Collision.grav_coef
- return
- def getData(self):
- memory_tuple = (self.x_miss, self.y_miss, self.x_collision,
- self.y_collision, self.collision_i_angle,
- self.collision_i_speed, self.collision_f_angle,
- self.collision_f_speed, self.collision_grav_coef)
- return memory_tuple
- class SettingOption:
- def __init__(self, y, name, info, modes):
- self.pos_x = 20
- self.pos_y = y
- self.width = 260
- self.height = 50
- self.min_height = 50
- self.max_height = 100
- self.name = name
- self.info = info
- self.modes = modes
- self.colour = green.toList()
- self.active = True
- self.hovering = False
- self.surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA, 32)
- return
- def construct(self):
- offset = 5
- max_distance = getPointDistance(0, 0, offset, offset)
- self.surface.lock()
- for x in range(self.width):
- for y in range(self.height):
- alpha_coef = 1 - math.pow(0.992, (self.width - x) * 2)
- alpha_subcoef = (abs(self.height / 2 - y) + (1 - x/self.width) * 20) * 5
- alpha_coef *= 1 - math.pow(0.992, alpha_subcoef)
- if x < offset and y < offset:
- distance = getPointDistance(x, y, offset, offset)
- alpha_coef *= max(0, 1 - distance / offset)
- elif x < offset and y > self.height - offset:
- distance = getPointDistance(x, y, offset, self.height - offset)
- alpha_coef *= max(0, 1 - distance / offset)
- elif x < offset:
- alpha_coef *= x / offset
- elif y < offset:
- alpha_coef *= y / offset
- elif y > self.height - offset:
- alpha_coef *= (self.height - y) / offset
- col_subcoef = min(x, self.width - x) + min(y, self.height - y)
- col_coef = math.pow(0.992, col_subcoef)
- if self.hovering:
- alpha_coef = min(1, alpha_coef * 1.2)
- col_coef = min(1, col_coef * 1.2)
- bg_colour = [self.colour[0]*col_coef, self.colour[1]*col_coef, self.colour[2]*col_coef, 255 * alpha_coef]
- self.surface.set_at((x, y), bg_colour)
- self.surface.unlock()
- x_text = 0.1 * self.width
- y_text = self.min_height / 2 - 5
- renderText(self.surface, x_text, y_text, "Free Sans Bold", font_size_16, white.toList(), self.name, False)
- x_text = 0.1 * self.width
- y_text = self.min_height - 5
- textlen = len(self.info)
- textsplit = self.info.split()
- while textlen and y_text + 10 < self.surface.get_height():
- text = []
- if textsplit:
- next_word_length = len(textsplit[0])
- else:
- next_word_length = 0
- while len(" ".join(text)) + next_word_length < 43:
- if textsplit:
- word = textsplit.pop(0)
- else:
- textlen = 0
- break
- text.append(word)
- textlen -= len(word)
- text = " ".join(text)
- renderText(self.surface, x_text, y_text, "Free Sans Bold", font_size_14, white.toList(), text, False)
- y_text += 10
- if self.hovering:
- x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
- y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
- radius = 2
- x_circ = round(x_offset + self.pos_x - 8)
- y_circ = round(y_offset + self.pos_y + self.height / 2 + radius / 2)
- pygame.draw.circle(Screen.screen, white.toList(), (x_circ, y_circ), radius)
- return
- def adjustColour(self):
- if self.modes[0] == 1 and self.active < 0:
- self.colour = crossColours(white, green, -self.active/10).toList()
- self.active += 1
- elif self.modes[0] == 1 and self.active >= 0:
- self.colour = green.toList()
- elif self.modes[0] == 2:
- self.colour = {False:red.toList(), True:green.toList()}[self.active]
- elif self.modes[0] > 2:
- self.colour = crossColours(green, red, self.active / (self.modes[0]-1)).toList()
- return
- def adjustSize(self):
- lower_index = (SettingsMenu.current_page - 1) * 5
- upper_index = SettingsMenu.current_page * 5
- self_index = SettingOptions.options.index(self)
- if self_index < lower_index or self_index > upper_index:
- return
- rate = 10
- delta_height = 0
- if self.hovering and self.height < self.max_height:
- delta_height = rate
- elif not self.hovering and self.height > self.min_height:
- delta_height = -rate
- if delta_height:
- if self.height + delta_height > self.max_height:
- actual_change = self.max_height - self.height
- elif self.height + delta_height < self.min_height:
- actual_change = self.height - self.min_height
- else:
- actual_change = delta_height
- self.height += actual_change
- self.surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA, 32)
- found_self = False
- for i in range(lower_index, upper_index):
- if i >= len(SettingOptions.options):
- break
- option = SettingOptions.options[i]
- if not found_self and self == option:
- found_self = True
- elif found_self:
- option.pos_y += actual_change
- return
- def switch(self):
- if self.modes[0] == 1:
- self.active = -10
- elif self.modes[0] == 2:
- self.active = not self.active
- elif self.modes[0] > 2:
- self.active = (self.active + 1) % self.modes[0]
- if "TOGGLE_LEARNING" in self.modes:
- GameState.toggle_learning = self.active
- if "TOGGLE_GENETIC" in self.modes:
- GameState.toggle_genetic = self.active
- if "TOGGLE_GRAVITY" in self.modes:
- gravity_ring.active = self.active
- if "SET_GRAV_COEF" in self.modes:
- val = input("Please enter new gravity coefficient: ")
- try:
- val = float(val)
- if 0 <= val and val <= 100:
- gravity_ring.grav_coef = val
- else:
- print("Invalid value")
- except:
- print("Error parsing value")
- if "SET_WEIGHT" in self.modes:
- val = input("Please enter new weight coefficient: ")
- try:
- val = float(val)
- if 0 <= val and val <= 1:
- WeightData.current_weight = val
- else:
- print("Invalid value")
- except:
- print("Error parsing value")
- if "SAVE_MEMORY" in self.modes:
- savePadMemories()
- if "BACK_UP_MEMORY" in self.modes:
- path = ""
- i = 0
- while path == "" or os.path.exists(path):
- i += 1
- path = os.path.join(pad_memory_path, r"BackUpMemories\Back-up %s" % i)
- backUpPadMemories(path)
- if "LOAD_MEMORY" in self.modes:
- loadPadMemories()
- if "RESET_MEMORY" in self.modes:
- for pad in Pads.padlist:
- pad.memory = []
- if "RESET_SCORES" in self.modes:
- for pad in Pads.padlist:
- pad.score = 0
- if "TOGGLE_AI_PAD1" in self.modes:
- Pads.padlist[0].AI = self.active / 2
- if "TOGGLE_AI_PAD2" in self.modes:
- Pads.padlist[1].AI = self.active / 2
- if "TOGGLE_AI_PAD3" in self.modes:
- Pads.padlist[2].AI = self.active / 2
- if "TOGGLE_AI_PAD4" in self.modes:
- Pads.padlist[3].AI = self.active / 2
- if "TOGGLE_ALL_AI" in self.modes:
- option_list = []
- for i in range(4):
- option_list.append("TOGGLE_AI_PAD%s" % (i+1))
- enable_all = False
- active_set = -1
- for option in SettingOptions.options:
- for related_option in option_list:
- if related_option in option.modes and option.active != active_set:
- if active_set == -1:
- active_set = option.active
- continue
- else:
- enable_all = True
- break
- if enable_all:
- active_set = 2
- else:
- active_set = (active_set + 1) % 3
- for option in SettingOptions.options:
- for related_option in option_list:
- if related_option in option.modes:
- option.active = active_set
- for pad in Pads.padlist:
- pad.AI = active_set / 2
- if "ACCEL_BALL" in self.modes:
- for pad in Pads.padlist:
- if self.active == 0:
- GameState.ball_accel = 0
- elif self.active == 1:
- GameState.ball_accel = ball_accel
- elif self.active == 2:
- GameState.ball_accel = ball_accel * 10
- else:
- GameState.ball_accel = ball_accel * 50
- if "FAST_PADS" in self.modes:
- GameState.pad_speed_mult = {False:1, True:2}[self.active]
- if "AUTO_SAVE" in self.modes:
- rounds = GameState.num_rounds
- if self.active == 0:
- GameState.auto_saving = 0
- elif self.active == 1:
- GameState.auto_saving = 100
- elif self.active == 2:
- GameState.auto_saving = 50
- else:
- GameState.auto_saving = 10
- return
- class GravityRing:
- def __init__(self):
- self.pos_x = int(window_width / 2)
- self.pos_y = int(window_height / 2)
- self.grav_coef = 0
- self.current_angle = 0
- self.current_radius = 0
- self.end_radius = 50
- self.rings = 8
- self.pump_index = 0
- self.pump_timer = 100
- self.active = False
- self.pumpvals = []
- self.grav_focals = []
- for i in range(10):
- self.grav_focals.append("")
- self.genNewGravFocal(i)
- for i in range(self.rings):
- self.pumpvals.append(0)
- return
- def genNewGravFocal(self, index):
- velocity = random.random()*0.2+0.2
- angle = 0
- while angle % 90 < 20:
- angle = random.randrange(360)
- grav_focal = GravFocal(window_width/2, window_height/2, velocity, angle, self)
- self.grav_focals[index] = grav_focal
- return
- def construct(self):
- if not self.current_radius > 0:
- return
- delta_angle = 360 / self.rings
- current_angle = 0
- for i in range(self.rings):
- circ_x = self.pos_x + round(self.current_radius * math.cos(degToRad(current_angle)))
- circ_y = self.pos_y + round(self.current_radius * math.sin(degToRad(current_angle)))
- circ_rad = round(2 * (1 + self.pumpvals[i] / 100))
- colour = crossColours(purple, grey, self.grav_coef / 180)
- pygame.draw.circle(Screen.screen, colour.toList(), (circ_x, circ_y), circ_rad, 1)
- current_angle += delta_angle
- if self.pumpvals[i]:
- self.pumpvals[i] -= 1
- return
- def handleGravFocals(self):
- if not self.current_radius > 0:
- return
- for i in range(len(self.grav_focals)):
- focal = self.grav_focals[i]
- if getPointDistance(self.pos_x, self.pos_y, focal.pos_x, focal.pos_y) > self.current_radius:
- self.genNewGravFocal(i)
- return
- focal.move()
- colour = crossColours(purple, grey, self.grav_coef / 180)
- focal_radius = 2
- focal_x = round(focal.pos_x - focal_radius / 2)
- focal_y = round(focal.pos_y - focal_radius / 2)
- pygame.draw.circle(Screen.screen, colour.toList(), (focal_x, focal_y), focal_radius, 1)
- def modifyRadius(self):
- if self.active and 0.95 * self.end_radius <= self.current_radius and self.current_radius < self.end_radius:
- self.current_radius = self.end_radius
- elif self.active and self.current_radius < 0.95 * self.end_radius:
- self.current_radius += self.end_radius / 250
- elif not self.active and 0.05 * self.end_radius >= self.current_radius and self.current_radius >= 0:
- self.current_radius = 0
- else:
- self.current_radius -= self.end_radius / 250
- return
- def pumpCircs(self):
- if not self.pump_timer:
- self.pumpvals[self.pump_index] = 100
- self.pump_index = (self.pump_index + 1) % self.rings
- if self.pump_timer > 100 - 50 * (self.grav_coef - 50) / 50:
- self.pump_timer = 0
- else:
- self.pump_timer += 1
- return
- class SettingOptions:
- options = []
- class WeightData:
- weight_scores = {}
- current_generation = []
- current_weight = 0
- current_weight_index = 0
- current_collisions = 0
- current_game = 0
- generation = 0
- class PadMemory:
- memory_blue = []
- memory_green = []
- memory_red = []
- memory_orange = []
- class Pads:
- padlist = []
- class Balls:
- ball = ""
- class Screen:
- screen = ""
- class FastForwardCover:
- surface = ""
- class SettingsMenu:
- modified_surface = ""
- default_surface = ""
- total_pages = 4
- current_page = 1
- class GameState:
- done = False
- fast_forward_cover_loaded = False
- toggle_learning = True
- toggle_genetic = True
- collision_count = 0
- max_count = 0
- num_rounds = 0
- score_messages_list = []
- score_messages = {"Okay":0, "Good":0, "Great":0, "Excellent!":0,
- "Superb!":0, "Amazing!":0, "Outstanding!":0,
- "Unbelievable!":0}
- ball_accel = 0
- pad_speed_mult = 1
- auto_saving = 0
- data = []
- game_count = 0
- class Collision:
- xpos = 0
- ypos = 0
- initial_angle = 0
- initial_speed = 0
- final_angle = 0
- final_speed = 0
- grav_coef = 0
- class Launchers:
- launcher = ""
- class KeyBools:
- aPressed = False
- dPressed = False
- iPressed = False
- kPressed = False
- leftPressed = False
- rightPressed = False
- kp8Pressed = False
- kp2Pressed = False
- class FontTypes:
- Angelic_War_40_font = ""
- Angelic_War_14_font = ""
- Birth_Of_A_Hero_30_font = ""
- Times_New_Roman_18_font = ""
- Free_Sans_Bold_10_font = ""
- Free_Sans_Bold_12_font = ""
- Free_Sans_Bold_16_font = ""
- Free_Sans_Bold_18_font = ""
- Free_Sans_Bold_30_font = ""
- Sergeant_SixPack_12_font = ""
- Sergeant_SixPack_14_font = ""
- Sergeant_SixPack_16_font = ""
- Sergeant_SixPack_18_font = ""
- Sergeant_SixPack_20_font = ""
- Sergeant_SixPack_22_font = ""
- Sergeant_SixPack_24_font = ""
- Sergeant_SixPack_26_font = ""
- black = Colour(0, 0, 0)
- grey = Colour(100, 100, 100)
- white = Colour(255, 255, 255)
- orange = Colour(255, 165, 0)
- red = Colour(255, 0, 0)
- green = Colour(0, 255, 0)
- blue = Colour(0, 0, 255)
- yellow = Colour(255, 255, 0)
- cool_blue = Colour(174, 238, 238)
- purple = Colour(223, 0, 255)
- dark_red = Colour(100, 0, 0)
- dark_green = Colour(0, 100, 0)
- dark_blue = Colour(0, 0, 100)
- sound_level = SoundLevel()
- pause_button = PauseButton(50, 14)
- fast_forward = GenericButton(79, 11)
- info_button = GenericButton(105, 10)
- print_button = GenericButton(135, 14)
- gear_button = GenericButton(160, 13)
- arrow_left = GenericButton(0, 460)
- arrow_right = GenericButton(0, 460)
- pause_surface = pygame.Surface((pause_button.width, pause_button.height), 0, 32)
- gravity_ring = GravityRing()
- ############################################################
- #
- # U T I L I T Y F U N C T I O N S
- #
- def degToRad(degrees):
- rads = degrees * math.pi / 180
- return rads
- def radToDeg(radians):
- degrees = radians * 180 / math.pi
- return degrees
- def crossColours(c1, c2, coef):
- anti_coef = 1 - coef
- c1 = c1.toList()
- c2 = c2.toList()
- result = Colour(c1[0]*coef + c2[0]*anti_coef,
- c1[1]*coef + c2[1]*anti_coef,
- c1[2]*coef + c2[2]*anti_coef)
- return result
- def objectWithinBounds(obj_x, obj_y, x_low, x_high, y_low, y_high):
- ball = Balls.ball
- within_bounds = x_low <= obj_x and obj_x <= x_high and y_low <= obj_y and obj_y <= y_high
- return within_bounds
- def angleBetweenPoints(p1, p2):
- (p1_x, p1_y) = p1
- (p2_x, p2_y) = p2
- x_dif = p2_x - p1_x
- y_dif = p2_y - p1_y
- angle = angleFromComponents(x_dif, y_dif)
- return angle
- def angleFromComponents(x_translate, y_translate):
- #quadrant 1, 2, 3, 4
- y_translate *= -1
- if x_translate == 0:
- x_translate = 0.0001
- if x_translate > 0 and y_translate > 0:
- theta = radToDeg(math.atan(y_translate / x_translate))
- elif x_translate < 0 and y_translate > 0:
- theta = radToDeg(math.atan(y_translate / x_translate)) + 180
- elif x_translate < 0 and y_translate < 0:
- theta = radToDeg(math.atan(y_translate / x_translate)) + 180
- else:
- theta = radToDeg(math.atan(y_translate / x_translate)) + 360
- theta %= 360
- return theta
- def velocityFromComponents(x_translate, y_translate):
- velocity = math.sqrt(x_translate * x_translate + y_translate * y_translate)
- return velocity
- def insidePad():
- ball = Balls.ball
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- padxmin = pad.x
- padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
- padymin = pad.y
- padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
- if padxmin <= ball.x and ball.x <= padxmax and padymin <= ball.y and ball.y <= padymax:
- return True
- return False
- def averageAngles(first_angle, second_angle):
- average_angle = (first_angle + second_angle) / 2
- return average_angle
- def getPointDistance(x1, y1, x2, y2):
- distance = math.sqrt(math.pow((x2 - x1), 2) + math.pow((y2 - y1), 2))
- return distance
- def getPadMidpoint(padindex):
- i = padindex
- pad = Pads.padlist[i]
- padxmin = pad.x
- padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
- padymin = pad.y
- padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
- padxmid = (padxmin + padxmax) / 2
- padymid = (padymin + padymax) / 2
- midpoint = (padxmid, padymid)
- return midpoint
- def getAngleDifference(angle1, angle2):
- angle_dif = 360 - max(angle1, angle2) + min(angle1, angle2)
- if angle_dif > 180:
- angle_dif = 360 - angle_dif
- return angle_dif
- ############################################################
- #
- # I N P U T / O U T P U T
- #
- def saveGameData():
- with open(os.path.join(data_path, "score_data.txt"), 'w') as file:
- for item in GameState.data:
- file.write(str(item) + "\n")
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- with open(os.path.join(data_path, "pad_%s_memory.txt" % i), 'w') as file:
- for memory in pad.memory:
- file.write(str(memory.getData()) + "\n")
- with open(os.path.join(data_path, "weight_coefficient.txt"), 'w') as file:
- file.write(str(WeightData.current_weight))
- with open(os.path.join(data_path, "grav_coefficient.txt"), 'w') as file:
- file.write(str(gravity_ring.grav_coef))
- return
- def backUpGameData():
- path = ""
- i = 0
- while path == "" or os.path.exists(path):
- i += 1
- path = os.path.join(data_path, r"BackUpData\Back-up %s" % i)
- try:
- os.mkdir(path)
- except Exception as e:
- print("Error occured whilst making memory back up: %s" % e)
- return
- for filename in ["score_data.txt", "grav_coefficient.txt", "weight_coefficient.txt",
- "pad_0_memory.txt", "pad_1_memory.txt", "pad_2_memory.txt",
- "pad_3_memory.txt"]:
- file = os.path.join(data_path, filename)
- shutil.copy(file, path)
- print("Game data back-up created")
- return
- def savePadMemories():
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- with open(os.path.join(pad_memory_path, "pad_%s_memory.txt" % i), 'w') as file:
- for memory in pad.memory:
- file.write(str(memory.getData()) + "\n")
- with open(os.path.join(pad_memory_path, "weight_coefficient.txt"), 'w') as file:
- file.write(str(WeightData.current_weight))
- return
- def backUpPadMemories(path):
- if not os.path.exists(path):
- try:
- os.mkdir(path)
- except Exception as e:
- print("Error occurred whilst making memory back up: %s" % e)
- return
- padlist = Pads.padlist
- for i in range(len(padlist)):
- file = os.path.join(pad_memory_path, "pad_%s_memory.txt" % i)
- shutil.copy(file, path)
- file = os.path.join(pad_memory_path, "weight_coefficient.txt")
- shutil.copy(file, path)
- print("New directory created")
- return
- def loadPadMemories():
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- with open(os.path.join(pad_memory_path, "pad_%s_memory.txt" % i), 'r') as file:
- pad.memory = parseMemoryData(file.read())
- with open(os.path.join(pad_memory_path, "weight_coefficient.txt"), 'r') as file:
- WeightData.current_weight = float(file.read())
- return
- def parseMemoryData(string_data):
- list_data = []
- for line in string_data.split("\n"):
- if not line:
- continue
- item_data = line.replace("(", "").replace(")", "").split(", ")
- x_miss = float(item_data[0])
- y_miss = float(item_data[1])
- memory_tuple = MemoryTuple(x_miss, y_miss)
- memory_tuple.x_collision = float(item_data[2])
- memory_tuple.y_collision = float(item_data[3])
- memory_tuple.collision_i_angle = float(item_data[4])
- memory_tuple.collision_i_speed = float(item_data[5])
- memory_tuple.collision_f_angle = float(item_data[6])
- memory_tuple.collision_f_speed = float(item_data[7])
- memory_tuple.collision_grav_coef = float(item_data[8])
- list_data.append(memory_tuple)
- return list_data
- ############################################################
- #
- # A D A P T I V E A I
- #
- def updatePadTargets():
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- if pad.AI != 1:
- continue
- (x_target, y_target) = findBestApproximation(i)
- printData("%s: (%s, %s)" % (i, x_target, y_target))
- printMemory(i)
- pad.x_target = x_target
- pad.y_target = y_target
- printData("")
- return
- def handleAI():
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- if pad.AI == 0.5:
- pad.x_target = Balls.ball.x
- pad.y_target = Balls.ball.y
- elif pad.AI == 0:
- continue
- (padxmid, padymid) = getPadMidpoint(i)
- if not i % 2:
- if padymid < pad.y_target:
- pad.move(0, pad_speed * GameState.pad_speed_mult)
- elif padymid > pad.y_target:
- pad.move(0, -pad_speed * GameState.pad_speed_mult)
- else:
- if padxmid < pad.x_target:
- pad.move(pad_speed * GameState.pad_speed_mult, 0)
- elif padxmid > pad.x_target:
- pad.move(-pad_speed * GameState.pad_speed_mult, 0)
- return
- def findBestApproximation(padindex):
- printData("FINDING APPROXIMATION FOR PAD %s...\n" % padindex)
- pad = Pads.padlist[padindex]
- memory = pad.memory
- ball = Balls.ball
- if not memory:
- approximation = getPadMidpoint(padindex)
- return approximation
- collision_data = getCollisionData()
- (last_collision_x, last_collision_y, last_collision_i_angle,
- last_collision_i_speed, last_collision_f_angle,
- last_collision_f_speed, last_collision_grav_coef) = collision_data
- best_approx = 0
- strictness_coef = 1.03
- for memory_tuple in memory:
- (x_miss, y_miss, x_collision, y_collision, _, _, f_angle, _,
- collision_grav_coef) = memory_tuple.getData()
- (divergence, x_divergence, y_divergence, f_angular_divergence,
- grav_divergence) = calculateDivergence(memory_tuple, collision_data)
- approximation = (divergence, x_miss, y_miss)
- printData("\n\nPAD: %s" % padindex)
- printData("\nLAST COLLISION (X) = %s, CONSIDERED CASE (X) = %s" % (last_collision_x, x_collision))
- printData("pos_x DIVERGENCE: %s" % x_divergence)
- printData("\nLAST COLLISION (Y) = %s, CONSIDERED CASE (Y) = %s" % (last_collision_y, y_collision))
- printData("pos_y DIVERGENCE: %s" % y_divergence)
- printData("\nLAST COLLISION (fAngle) = %s, CONSIDERED CASE (fAngle) = %s" % (last_collision_f_angle, f_angle))
- printData("FINAL ANGLE DIVERGENCE: %s" % f_angular_divergence)
- printData("\nLAST COLLISION (grav) = %s, CONSIDERED CASE (grav) = %s" % (last_collision_grav_coef,
- collision_grav_coef))
- printData("\nTOTAL DIVERGENCE: %s\n\n" % divergence)
- if not best_approx:
- best_approx = approximation
- else:
- (least_divergence, _, _) = best_approx
- if divergence < least_divergence:
- best_approx = approximation
- (_, pos_xition, pos_yition) = best_approx
- approximation = (pos_xition, pos_yition)
- return approximation
- def calculateDivergence(memory_tuple, collision_data):
- (last_collision_x, last_collision_y, last_collision_i_angle,
- last_collision_i_speed, last_collision_f_angle,
- last_collision_f_speed, last_collision_grav_coef) = collision_data
- (x_miss, y_miss, x_collision, y_collision,
- i_angle, i_speed, f_angle, f_speed,
- collision_grav_coef) = memory_tuple.getData()
- pos_x_dif = abs(x_collision - last_collision_x)
- pos_y_dif = abs(y_collision - last_collision_y)
- i_angle_dif = getAngleDifference(i_angle, last_collision_i_angle)
- i_speed_dif = abs(i_speed - last_collision_i_speed)
- f_angle_dif = getAngleDifference(f_angle, last_collision_f_angle)
- f_speed_dif = abs(f_speed - last_collision_f_speed)
- grav_dif = abs(collision_grav_coef - last_collision_grav_coef)
- x_divergence = 100 * pos_x_dif / max_x_difference
- y_divergence = 100 * pos_y_dif / max_y_difference
- f_angular_divergence = 100 * f_angle_dif / max_angular_difference
- grav_divergence = 100 * grav_dif / max_gravity_difference
- #Apply weights.
- x_divergence *= WeightData.current_weight
- y_divergence *= WeightData.current_weight
- f_angular_divergence *= (1 - WeightData.current_weight)
- grav_divergence *= 0.5
- total_divergence = x_divergence + y_divergence + f_angular_divergence + grav_divergence
- divergence_data = (total_divergence, x_divergence, y_divergence, f_angular_divergence, grav_divergence)
- return divergence_data
- ############################################################
- #
- # G E N E T I C A L G O R I T H M
- #
- def generateWeights():
- WeightData.generation += 1
- current_generation = produceChildren()
- while len(current_generation) < population_size:
- current_generation.append(random.random())
- WeightData.current_generation = current_generation
- print("NEW GENERATION: %s" % current_generation)
- return
- def selectBestWeights():
- best_weights = []
- current_generation = WeightData.current_generation
- #Get the best three weights.
- for i in range(int(0.5 * population_size)):
- best_score = -1
- best_weight = -1
- for weight, score in WeightData.weight_scores.items():
- if score > best_score and weight in current_generation and weight not in best_weights:
- best_weight = weight
- best_score = score
- if best_weight != -1:
- best_weights.append(best_weight)
- return best_weights
- def testNextWeight():
- WeightData.current_weight_index += 1
- index = WeightData.current_weight_index
- WeightData.current_weight = WeightData.current_generation[index]
- return
- def produceChildren():
- best_weights = selectBestWeights()
- children = []
- for i in range(len(best_weights)):
- for j in range(i + 1, len(best_weights)):
- if len(children) == population_size:
- break
- child = averageWeights(best_weights[i], best_weights[j])
- children.append(child)
- return children
- def averageWeights(weight_1, weight_2):
- average_weight = (weight_1 + weight_2) / 2
- return average_weight
- def scoreWeightValue():
- current_weight = WeightData.current_weight
- current_collisions = WeightData.current_collisions
- WeightData.weight_scores[current_weight] = current_collisions
- printWeightScores()
- return
- def printWeightScores():
- weight_scores = WeightData.weight_scores
- for weight, score in weight_scores.items():
- print("Weight %.4f: %s" % (weight, score))
- return
- def beginNewTest():
- print("NEW TEST!")
- scoreWeightValue()
- WeightData.current_collisions = 0
- WeightData.current_game = 0
- if WeightData.current_weight_index < population_size - 1:
- WeightData.current_weight_index += 1
- index = WeightData.current_weight_index
- WeightData.current_weight = WeightData.current_generation[index]
- else:
- beginNewGeneration()
- padlist = Pads.padlist
- for i in range(len(padlist)):
- padlist[i].memory = []
- return
- def beginNewGeneration():
- print("NEW GENERATION!")
- generateWeights()
- WeightData.current_weight_index = 0
- WeightData.current_weight = WeightData.current_generation[0]
- return
- ############################################################
- #
- # S U R F A C E S F R O M F I L E S
- #
- def getPadFromFile(padname):
- pad = pygame.image.load(os.path.join(pad_image_path, padname + ".png")).convert_alpha()
- return pad
- def getSpeakerIcon(sound_level):
- scale = 0.12
- speaker = pygame.image.load(os.path.join(sound_level_path, "sound%s.png" % sound_level)).convert_alpha()
- speaker = scaleImage(speaker, scale)
- return speaker
- def getLetterIcon(letter):
- scale = 0.09
- info_button = pygame.image.load(os.path.join(letter_path, "letter_" + letter + ".png")).convert_alpha()
- info_button = scaleImage(info_button, scale)
- return info_button
- def getFastForwardIcon():
- scale = 0.2
- fast_forward = pygame.image.load(os.path.join(picture_path, "fast_forward.png")).convert_alpha()
- fast_forward = scaleImage(fast_forward, scale)
- return fast_forward
- def getFastForwardCover():
- fast_forward_cover = pygame.image.load(os.path.join(picture_path, "pyballcover.png")).convert_alpha()
- return fast_forward_cover
- def getGearIcon():
- scale = 0.1
- gear_button = pygame.image.load(os.path.join(picture_path, "gear.png")).convert_alpha()
- gear_button = scaleImage(gear_button, scale)
- return gear_button
- def getArrowIcon(direction):
- scale = 0.1
- arrow_button = pygame.image.load(os.path.join(picture_path, "arrow_%s.png" % direction)).convert_alpha()
- arrow_button = scaleImage(arrow_button, scale)
- return arrow_button
- def getSettingsMenu():
- settings_menu = pygame.image.load(os.path.join(picture_path, "settings_menu.png")).convert_alpha()
- return settings_menu
- def scaleImage(image, scale_factor):
- scaled_image = pygame.transform.smoothscale(image, (int(image.get_width() * scale_factor), int(image.get_height() * scale_factor)))
- return scaled_image
- ############################################################
- #
- # S E S S I O N I N I T I A L I S A T I O N
- #
- def initialiseMusic(sound_level):
- pygame.mixer.init()
- channel = pygame.mixer.Channel(0)
- song = "relaxing_space.wav"
- music = pygame.mixer.Sound(os.path.join(music_path, song))
- channel.play(music, -1)
- sound_level.addChannel(channel)
- return
- def initialiseFonts():
- FontTypes.Angelic_War_14_font = pygame.font.Font(pygame.font.match_font("Angelic War"), font_size_14)
- FontTypes.Angelic_War_40_font = pygame.font.Font(pygame.font.match_font("Angelic War"), font_size_40)
- FontTypes.Birth_Of_A_Hero_30_font = pygame.font.Font(pygame.font.match_font("Birth of a Hero"), font_size_30)
- FontTypes.Times_New_Roman_18_font = pygame.font.Font(pygame.font.match_font("Times New Roman"), font_size_18)
- FontTypes.Free_Sans_Bold_10_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_11)
- FontTypes.Free_Sans_Bold_12_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_12)
- FontTypes.Free_Sans_Bold_14_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_14)
- FontTypes.Free_Sans_Bold_16_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_16)
- FontTypes.Free_Sans_Bold_18_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_18)
- FontTypes.Free_Sans_Bold_30_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_30)
- FontTypes.Sergeant_SixPack_12_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_12)
- FontTypes.Sergeant_SixPack_14_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_14)
- FontTypes.Sergeant_SixPack_16_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_16)
- FontTypes.Sergeant_SixPack_18_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_18)
- FontTypes.Sergeant_SixPack_20_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_20)
- FontTypes.Sergeant_SixPack_22_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_22)
- FontTypes.Sergeant_SixPack_24_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_24)
- FontTypes.Sergeant_SixPack_26_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_26)
- return
- def initialiseSession():
- offset = 75
- padblue = Pad(offset - pad_width/2,
- window_height/2 - pad_height/2,
- getPadFromFile("padblue"))
- padgreen = Pad(window_width/2 - pad_height/2,
- window_height - offset - pad_width/2,
- pygame.transform.rotate(getPadFromFile("padgreen"), 90))
- padred = Pad(window_width - offset - pad_width/2,
- window_height/2 - pad_height/2,
- pygame.transform.rotate(getPadFromFile("padred"), 180))
- padorange = Pad(window_width/2 - pad_height/2,
- offset - pad_width/2,
- pygame.transform.rotate(getPadFromFile("padorange"), 270))
- Pads.padlist = [padblue, padgreen, padred, padorange]
- info_button.addSurface(getLetterIcon("i"))
- print_button.addSurface(getLetterIcon("t"))
- fast_forward.addSurface(getFastForwardIcon())
- gear_button.addSurface(getGearIcon())
- arrow_left.addSurface(getArrowIcon("left"))
- arrow_right.addSurface(getArrowIcon("right"))
- sound_level.addSurface(getSpeakerIcon(sound_level.current_level))
- FastForwardCover.surface = getFastForwardCover()
- SettingsMenu.default_surface = getSettingsMenu()
- ResetSettingsMenu()
- Launchers.launcher = Launcher(3, 0.001, red.toList(), cool_blue.toList())
- Launchers.launcher.launchBall()
- for i in range(4):
- sound_level.levels.append(getSpeakerIcon(i))
- sound_level.pos_x = 14
- sound_level.pos_y = 10
- initialiseMusic(sound_level)
- initialiseSettings()
- for option in SettingOptions.options:
- if "TOGGLE_GENETIC" in option.modes and option.active:
- option.switch()
- elif "TOGGLE_GRAVITY" in option.modes and option.active:
- option.switch()
- elif "ACCEL_BALL" in option.modes:
- while option.active != 0:
- option.switch()
- elif "FAST_PADS" in option.modes:
- while option.active != 0:
- option.switch()
- elif "AUTO_SAVE" in option.modes:
- while option.active != 0:
- option.switch()
- elif "TOGGLE_AI_PAD1" in option.modes:
- while option.active != 2:
- option.switch()
- elif "TOGGLE_AI_PAD2" in option.modes:
- while option.active != 2:
- option.switch()
- elif "TOGGLE_AI_PAD3" in option.modes:
- while option.active != 2:
- option.switch()
- elif "TOGGLE_AI_PAD4" in option.modes:
- while option.active != 2:
- option.switch()
- arrow_left.pos_x = (SettingsMenu.default_surface.get_width() / 2) - 20 - arrow_left.width / 2
- arrow_right.pos_x = (SettingsMenu.default_surface.get_width() / 2) + 20 - arrow_right.width / 2
- GameState.score_messages_list = sorted(score_message_thresholds.items(), key=lambda item:item[1])
- return
- def initialiseSettings():
- options = SettingOptions.options
- base = 100
- offset = 60
- options.extend([SettingOption(base + offset * 0, "Toggle Learning",
- "When enabled, the pads learn from their mistakes and " +
- "improve their behaviour over time",
- [2, "TOGGLE_LEARNING"]),
- SettingOption(base + offset * 1, "Toggle Genetic Algorithm",
- "When enabled, the adaptive function will be put through " +
- "continuous testing to optimise pad learning efficiency",
- [2, "TOGGLE_GENETIC"]),
- SettingOption(base + offset * 2, "Toggle Gravity",
- "When enabled, the ball will be attracted towards the gravity " +
- "ring in the centre of the window with a force determined by " +
- "the ring's gravity coefficient",
- [2, "TOGGLE_GRAVITY"]),
- SettingOption(base + offset * 3, "Set Gravity Coefficient",
- "Sets the coefficient that determines how strongly the ball is " +
- "attracted towards the gravity ring when gravity is enabled. " +
- "Enter any number between 0 and 100 or an invalid value to quit",
- [0, "SET_GRAV_COEF"]),
- SettingOption(base + offset * 4, "Set Weight Coefficient",
- "Adjusts the behaviour of the pads. Enter any floating value " +
- "between 0 and 1 in the shell after clicking this button or " +
- "an invalid value (e.g. -1) to exit without modifying the weight",
- [0, "SET_WEIGHT"]),
- SettingOption(base + offset * 0, "Save Pad Memory",
- "Saves the data in each of the pad's memories to an " +
- "external file, allowing the data to be preserved " +
- "between different instances of the game",
- [1, "SAVE_MEMORY"]),
- SettingOption(base + offset * 1, "Back-up Pad Memory",
- "Creates a new directory specified by a name you enter and " +
- "creates copies of the current external memory states of the " +
- "pads into the directory",
- [1, "BACK_UP_MEMORY"]),
- SettingOption(base + offset * 2, "Load Pad Memory",
- "Loads data from an external file containing previously " +
- "stored data for each of the pads",
- [1, "LOAD_MEMORY"]),
- SettingOption(base + offset * 3, "Reset Memory",
- "Resets the pads' memories, but leaves any data stored in " +
- "external files untouched",
- [1, "RESET_MEMORY"]),
- SettingOption(base + offset * 4, "Reset Scores",
- "Resets the scores for each of the pads",
- [1, "RESET_SCORES"]),
- SettingOption(base + offset * 0, "Toggle AI - Pad 1",
- "When fully or semi-enabled, the left pad plays with adaptive or " +
- "static behavioural patterns respectively. When disabled, the pad " +
- "is controlled by a human player using the keys I and K",
- [3, "TOGGLE_AI_PAD1"]),
- SettingOption(base + offset * 1, "Toggle AI - Pad 2",
- "When fully or semi-enabled, the bottom pad plays with adaptive or " +
- "static behavioural patterns respectively. When disabled, the pad " +
- "is controlled by a human player using the keys A and D",
- [3, "TOGGLE_AI_PAD2"]),
- SettingOption(base + offset * 2, "Toggle AI - Pad 3",
- "When fully or semi-enabled, the right pad plays with adaptive or " +
- "static behavioural patterns respectively. When disabled, the pad " +
- "is controlled by a human player using the numpad keys 8 and 2",
- [3, "TOGGLE_AI_PAD3"]),
- SettingOption(base + offset * 3, "Toggle AI - Pad 4",
- "When fully or semi-enabled, the top pad plays with adaptive or " +
- "static behavioural patterns respectively. When disabled, the pad " +
- "is controlled by a human player using the arrow keys LEFT and RIGHT",
- [3, "TOGGLE_AI_PAD4"]),
- SettingOption(base + offset * 4, "Toggle All AIs",
- "Cycles all the AI's through different AI states (ON, SEMI-, OFF)",
- [1, "TOGGLE_ALL_AI"]),
- SettingOption(base + offset * 0, "Mode - Accelerating Ball",
- "Off - the ball does not automatically accelerate [1] - the ball " +
- "accelerates at a low rate [2] - the ball accelerates moderately " +
- "fast [3] - the ball accelerates at a high rate",
- [4, "ACCEL_BALL"]),
- SettingOption(base + offset * 1, "Mode - Fast Pads",
- "When disabled, pads move at their normal rate. When enabled, " +
- "pads are able to move twice as fast",
- [2, "FAST_PADS"]),
- SettingOption(base + offset * 2, "Auto-saving",
- "Off - auto-saving does not happen at all [1] - one save " +
- "is executed every 100 rounds [2] - one save is executed " +
- "every 50 rounds [3] - one save is executed every 10 rounds",
- [4, "AUTO_SAVE"])])
- SettingOptions.options = options
- return
- ############################################################
- #
- # T E X T O U T P U T
- #
- def printData(data):
- if print_button.active:
- print(data)
- return
- def printMemory(padindex):
- pad = Pads.padlist[padindex]
- printData("Pad %s" % padindex)
- if not pad.memory:
- printData("EMPTY MEMORY")
- for memory_tuple in pad.memory:
- printData(memory_tuple.getData())
- printData("")
- return
- ############################################################
- #
- # R E N D E R I N G
- #
- def drawText(surface, pos_x, pos_y, font_type, font_size, colour, message):
- if info_button.active:
- renderText(surface, pos_x, pos_y, font_type, font_size, colour, message)
- return
- def drawLine(surface, colour, start_pos, end_pos, width):
- if info_button.active:
- pygame.draw.aaline(surface, colour, start_pos, end_pos, width)
- return
- def drawCircle(surface, colour, position, radius, width):
- if info_button.active:
- pygame.draw.circle(surface, colour, position, radius, width)
- return
- def drawRect(surface, colour, area, width):
- if info_button.active:
- pygame.draw.rect(surface, colour, area, width)
- return
- def circsAtTargets(screen):
- padlist = Pads.padlist
- collision_data = getCollisionData()
- (last_collision_x, last_collision_y, last_collision_i_angle,
- last_collision_i_speed, last_collision_f_angle,
- last_collision_f_speed, _) = collision_data
- drawRect(screen, purple.toList(), [last_collision_x - 3,
- last_collision_y - 3,
- 6, 6], 1)
- colours = [cool_blue.toList(), green.toList(), red.toList(), orange.toList()]
- miss_circ_radius = 4
- more_tolerant_circ_radius = miss_circ_radius * 75
- less_tolerant_circ_radius = miss_circ_radius * 5
- displaying_text = False
- for i in range(len(padlist)):
- pad = padlist[i]
- if not pad.AI == 1:
- continue
- drawCircle(screen, colours[i], (int(pad.x_target), int(pad.y_target)), 5, 1)
- for memory_tuple in pad.memory:
- (x_miss, y_miss, x_collision, y_collision,
- i_angle, i_speed, f_angle, f_speed,
- collision_grav_coef) = memory_tuple.getData()
- colour = [cool_blue.toList(), green.toList(), red.toList(), orange.toList()][i]
- drawCircle(Screen.screen, colour, (int(x_miss), int(y_miss)), miss_circ_radius, 1)
- scale = 20
- x_move = f_speed * math.cos(degToRad(f_angle)) * scale
- y_move = f_speed * -math.sin(degToRad(f_angle)) * scale
- drawLine(screen, colour, (int(x_miss), int(y_miss)), (int(x_miss + x_move), int(y_miss + y_move)), 4)
- within_bounds = cursorWithinBounds(x_miss - more_tolerant_circ_radius / 2,
- x_miss + more_tolerant_circ_radius / 2,
- y_miss - more_tolerant_circ_radius / 2,
- y_miss + more_tolerant_circ_radius / 2)
- if within_bounds:
- drawLine(screen, colour, (int(x_collision), int(y_collision)), (int(x_miss), int(y_miss)), 4)
- within_bounds = cursorWithinBounds(x_miss - less_tolerant_circ_radius / 2,
- x_miss + less_tolerant_circ_radius / 2,
- y_miss - less_tolerant_circ_radius / 2,
- y_miss + less_tolerant_circ_radius / 2)
- if within_bounds and not displaying_text:
- displaying_text = True
- divergence_data = calculateDivergence(memory_tuple, collision_data)
- (total_divergence, x_divergence, y_divergence, f_angular_divergence,
- grav_divergence) = divergence_data
- total_divergence_string = "Total divergence: %.2f" % total_divergence
- x_divergence_string = "X divergence: %.2f" % x_divergence
- y_divergence_string = "Y divergence: %.2f" % y_divergence
- f_angular_divergence_string = "Angular divergence: %.2f" % f_angular_divergence
- grav_divergence_string = "Gravity divergence: %.2f" % grav_divergence
- if displaying_text:
- drawText(Screen.screen, window_width / 2, 200, "Free Sans Bold", font_size_16, white.toList(), total_divergence_string)
- drawText(Screen.screen, window_width / 2, 210, "Free Sans Bold", font_size_16, white.toList(), x_divergence_string)
- drawText(Screen.screen, window_width / 2, 220, "Free Sans Bold", font_size_16, white.toList(), y_divergence_string)
- drawText(Screen.screen, window_width / 2, 230, "Free Sans Bold", font_size_16, white.toList(), f_angular_divergence_string)
- drawText(Screen.screen, window_width / 2, 240, "Free Sans Bold", font_size_16, white.toList(), grav_divergence_string)
- return
- def renderText(surface, xpos, ypos, font_type, font_size, font_colour, message, fromCentre=True):
- if font_type.lower() == "angelic war" and font_size == font_size_14:
- font = FontTypes.Angelic_War_14_font
- elif font_type.lower() == "angelic war" and font_size == font_size_40:
- font = FontTypes.Angelic_War_40_font
- elif font_type.lower() == "times new roman" and font_size == font_size_18:
- font = FontTypes.Times_New_Roman_18_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_11:
- font = FontTypes.Free_Sans_Bold_10_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_12:
- font = FontTypes.Free_Sans_Bold_12_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_14:
- font = FontTypes.Free_Sans_Bold_14_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_16:
- font = FontTypes.Free_Sans_Bold_16_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_18:
- font = FontTypes.Free_Sans_Bold_18_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_30:
- font = FontTypes.Free_Sans_Bold_30_font
- elif font_type.lower() == "birth of a hero" and font_size == font_size_30:
- font = FontTypes.Birth_Of_A_Hero_30_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_12:
- font = FontTypes.Sergeant_SixPack_12_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_14:
- font = FontTypes.Sergeant_SixPack_14_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_16:
- font = FontTypes.Sergeant_SixPack_16_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_18:
- font = FontTypes.Sergeant_SixPack_18_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_20:
- font = FontTypes.Sergeant_SixPack_20_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_22:
- font = FontTypes.Sergeant_SixPack_22_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_24:
- font = FontTypes.Sergeant_SixPack_24_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_26:
- font = FontTypes.Sergeant_SixPack_26_font
- else:
- path = pygame.font.match_font(font_type)
- font = pygame.font.Font(path, font_size)
- print("Font not found")
- if fromCentre:
- (width, height) = font.size(message)
- xpos -= width / 2
- ypos -= height / 2
- textsurface = font.render(message, True, font_colour)
- surface.blit(textsurface, (xpos, ypos))
- return
- def drawObjects(screen):
- padblue = Pads.padlist[0]
- padgreen = Pads.padlist[1]
- padred = Pads.padlist[2]
- padorange = Pads.padlist[3]
- screen.blit(padblue.surface, (padblue.x, padblue.y))
- screen.blit(padorange.surface, (padorange.x, padorange.y))
- screen.blit(padred.surface, (padred.x, padred.y))
- screen.blit(padgreen.surface, (padgreen.x, padgreen.y))
- ball = Balls.ball
- ballcirc = pygame.draw.circle(screen, white.toList(), (int(ball.x - ball_radius/2), int(ball.y - ball_radius/2)), ball_radius, 1)
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- (padxmid, padymid) = getPadMidpoint(i)
- mid_offset = 5
- if i == 0:
- padxmid -= mid_offset
- elif i == 1:
- padymid += mid_offset
- elif i == 2:
- padxmid += mid_offset
- else:
- padymid -= mid_offset
- renderText(screen, padxmid, padymid, "Free Sans Bold", font_size_18, white.toList(), str(pad.score))
- controller = {1:"ADAPTIVE COMP", 0.5:"STATIC COMP", 0:"HUMAN"}[pad.AI]
- offset = 5
- padxmin = pad.x
- padxmax = pad.x + pad_width * ((i+1) % 2) + pad_height * (i % 2)
- padymin = pad.y
- padymax = pad.y + pad_height * ((i+1) % 2) + pad_width * (i % 2)
- if i == 0:
- x_text = padxmin - offset
- y_text = padymin - offset
- elif i == 1:
- x_text = padxmin - offset
- y_text = padymax + offset
- elif i == 2:
- x_text = padxmax + offset
- y_text = padymin - offset
- else:
- x_text = padxmin - offset
- y_text = padymin - offset
- renderText(screen, x_text, y_text, "Free Sans Bold", font_size_11, [35,35,35], controller)
- drawLauncher(screen)
- drawSpeaker(screen)
- drawPauseButton(screen)
- drawLetterButton("i", screen)
- drawLetterButton("t", screen)
- drawFastForwardButton(screen)
- drawGearButton(screen)
- drawWeightInfo(screen)
- drawScore(screen)
- drawScoreMessage(screen)
- gravity_ring.construct()
- return
- def drawWeightInfo(screen):
- current_weight = "Weight: %.4f" % WeightData.current_weight
- current_collisions = "Current Score: %s" % WeightData.current_collisions
- current_game = "Current Game: %s" % WeightData.current_game
- current_generation = "Generation: %s" % WeightData.generation
- renderText(screen, window_width - 110, 20, "Free Sans Bold", font_size_16, white.toList(), current_weight, False)
- if GameState.toggle_genetic:
- renderText(screen, window_width - 110, 30, "Free Sans Bold", font_size_16, white.toList(), current_collisions, False)
- renderText(screen, window_width - 110, 40, "Free Sans Bold", font_size_16, white.toList(), current_game, False)
- renderText(screen, window_width - 110, 50, "Free Sans Bold", font_size_16, white.toList(), current_generation, False)
- return
- def drawFastForwardCover(screen, ticks, real_ticks):
- if not GameState.fast_forward_cover_loaded:
- loadFastForwardCover()
- elif not real_ticks % 500:
- loadFastForwardCover()
- drawSpeaker(screen)
- drawPauseButton(screen)
- drawFastForwardButton(screen)
- ticks = "Ticks: %s" % ticks
- renderText(screen, window_width / 2, 300, "Free Sans Bold", font_size_18, white.toList(), ticks)
- current_weight = "Weight: %.4f" % WeightData.current_weight
- current_collisions = "Current Score: %s" % WeightData.current_collisions
- current_game = "Current Game: %s" % WeightData.current_game
- current_generation = "Generation: %s" % WeightData.generation
- renderText(screen, window_width / 2, 320, "Free Sans Bold", font_size_18, white.toList(), current_weight)
- if GameState.toggle_genetic:
- renderText(screen, window_width / 2, 335, "Free Sans Bold", font_size_18, white.toList(), current_collisions)
- renderText(screen, window_width / 2, 350, "Free Sans Bold", font_size_18, white.toList(), current_game)
- renderText(screen, window_width / 2, 365, "Free Sans Bold", font_size_18, white.toList(), current_generation)
- padlist = Pads.padlist
- pad_green_score = "Green Pad Score: %s" % padlist[0].score
- pad_blue_score = "Blue Pad Score: %s" % padlist[1].score
- pad_orange_score = "Orange Pad Score: %s" % padlist[2].score
- pad_red_score = "Red Pad Score: %s" % padlist[3].score
- renderText(screen, window_width / 2, 420, "Free Sans Bold", font_size_18, white.toList(), pad_green_score)
- renderText(screen, window_width / 2, 435, "Free Sans Bold", font_size_18, white.toList(), pad_blue_score)
- renderText(screen, window_width / 2, 450, "Free Sans Bold", font_size_18, white.toList(), pad_orange_score)
- renderText(screen, window_width / 2, 465, "Free Sans Bold", font_size_18, white.toList(), pad_red_score)
- offset = 6
- update_area = [200 + offset, 200 + offset, 300 - offset * 2, 500 - offset * 2]
- pygame.display.update(update_area)
- return
- def drawLauncher(screen):
- launcher = Launchers.launcher
- launcher_colour = launcher.getColour()
- launcher_radius = launcher.getRadius()
- xlauncher = int(launcher.x)
- ylauncher = int(launcher.y)
- if not pause_button.active:
- launcher.coolDown()
- pygame.draw.circle(screen, launcher_colour, (xlauncher, ylauncher), launcher_radius, 1)
- return
- def drawSpeaker(screen):
- speaker_icon = sound_level.surface
- sound_level.setCoverTransparency()
- xspeaker = sound_level.pos_x
- yspeaker = sound_level.pos_y
- screen.blit(speaker_icon, (xspeaker, yspeaker))
- screen.blit(sound_level.cover, (xspeaker, yspeaker))
- return
- def drawPauseButton(screen):
- pause_surface.fill(black.toList())
- if pause_button.active:
- pause_surface.set_alpha(255 * 0.85)
- fill_val = 0
- elif pause_button.hovering:
- pause_surface.set_alpha(255 * 0.85)
- fill_val = 1
- else:
- pause_surface.set_alpha(255 * 0.5)
- fill_val = 1
- pygame.draw.rect(pause_surface, white.toList(), [0,
- 0,
- pause_button.width * (1/3),
- pause_button.height], fill_val)
- pygame.draw.rect(pause_surface, white.toList(), [pause_button.width * (2/3),
- 0,
- pause_button.width * (1/3),
- pause_button.height], fill_val)
- screen.blit(pause_surface, (pause_button.pos_x, pause_button.pos_y))
- return
- def drawLetterButton(letter, screen):
- if letter.lower() == "i":
- button = info_button
- elif letter.lower() == "t":
- button = print_button
- else:
- assert False
- letter_surface = button.surface
- letter_cover = button.cover
- if button.active:
- letter_cover.set_alpha(0)
- elif button.hovering:
- letter_cover.set_alpha(255 * 0.25)
- else:
- letter_cover.set_alpha(255 * 0.5)
- x_surface = button.pos_x
- y_surface = button.pos_y
- screen.blit(letter_surface, (x_surface, y_surface))
- screen.blit(letter_cover, (x_surface, y_surface))
- return
- def drawFastForwardButton(screen):
- surface = fast_forward.surface
- cover = fast_forward.cover
- if fast_forward.active:
- cover.set_alpha(0)
- elif fast_forward.hovering:
- cover.set_alpha(255 * 0.25)
- else:
- cover.set_alpha(255 * 0.5)
- x_surface = fast_forward.pos_x
- y_surface = fast_forward.pos_y
- screen.blit(surface, (x_surface, y_surface))
- screen.blit(cover, (x_surface, y_surface))
- return
- def drawGearButton(screen):
- surface = gear_button.surface
- cover = gear_button.cover
- if gear_button.active:
- cover.set_alpha(0)
- elif gear_button.hovering:
- cover.set_alpha(255 * 0.25)
- else:
- cover.set_alpha(255 * 0.5)
- x_surface = gear_button.pos_x
- y_surface = gear_button.pos_y
- screen.blit(surface, (x_surface, y_surface))
- screen.blit(cover, (x_surface, y_surface))
- return
- def drawSettingsMenu(screen):
- surface = SettingsMenu.modified_surface
- for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
- if i >= len(SettingOptions.options):
- break
- option = SettingOptions.options[i]
- option.construct()
- surface.blit(option.surface, (option.pos_x, option.pos_y))
- for arrow in [arrow_left, arrow_right]:
- surface.blit(arrow.surface, (arrow.pos_x, arrow.pos_y))
- x_text = surface.get_width() / 2 + 1
- y_text = arrow_left.pos_y + 6
- renderText(surface, x_text, y_text, "Free Sans Bold", font_size_14, white.toList(), str(SettingsMenu.current_page))
- x_surface = (window_width - surface.get_width()) / 2
- y_surface = (window_height - surface.get_height()) / 2
- screen.blit(surface, (x_surface, y_surface))
- ResetSettingsMenu()
- return
- def drawScore(screen):
- score = GameState.collision_count
- max_score = GameState.max_count
- if max_score:
- ratio = 1 - score / max_score
- else:
- ratio = 1
- colour = crossColours(red, green, ratio)
- offset = 25
- renderText(screen, window_width / 2, window_height / 2 + offset, "Free Sans Bold", font_size_16, colour.toList(), str(score))
- renderText(screen, window_width / 2, window_height / 2 - offset, "Free Sans Bold", font_size_16, white.toList(), str(max_score))
- return
- def drawScoreMessage(screen):
- score_message = ""
- message_threshold = 0
- message_frames_left = 0
- alpha = 255
- for i in range(len(GameState.score_messages_list)):
- message = GameState.score_messages_list[i][0]
- frames_left = GameState.score_messages[message]
- if frames_left:
- alpha = int(255 * frames_left / frames_per_score_message)
- GameState.score_messages[message] = frames_left - 1
- score_message = message
- message_index = i
- message_frames_left = frames_left
- if score_message:
- colour = crossColours(crossColours(green, white, message_index / 7), black, message_frames_left / frames_per_score_message)
- font_size = 12 + i * 2
- renderText(screen, window_width / 2, window_height / 2 - 100, "Sergeant SixPack", font_size, colour.toList(), score_message)
- return
- def handleScoreMessages():
- score = GameState.collision_count
- for message, threshold in score_message_thresholds.items():
- if score == threshold:
- GameState.score_messages[message] = frames_per_score_message
- return
- ############################################################
- #
- # H A N D L E M E N U S A N D C O V E R S
- #
- def ResetSettingsMenu():
- SettingsMenu.modified_surface = SettingsMenu.default_surface.copy()
- return
- def setFastForwardCoverStatus(boolean):
- GameState.fast_forward_cover_loaded = boolean
- if boolean:
- sound_level.pos_x = 214
- sound_level.pos_y = 210
- pause_button.pos_x = 250
- pause_button.pos_y = 214
- fast_forward.pos_x = 278
- fast_forward.pos_y = 211
- else:
- sound_level.pos_x = 10
- sound_level.pos_y = 10
- pause_button.pos_x = 50
- pause_button.pos_y = 14
- fast_forward.pos_x = 79
- fast_forward.pos_y = 11
- return
- def loadFastForwardCover():
- cover = FastForwardCover.surface
- Screen.screen.blit(cover, (0, 0))
- pygame.display.flip()
- setFastForwardCoverStatus(True)
- return
- ############################################################
- #
- # K E Y B O A R D H A N D L I N G
- #
- def keyPressed(key):
- if key == K_ESCAPE:
- if gear_button.active:
- gear_button.switch()
- pause_button.switch()
- else:
- GameState.done = True
- return
- if gear_button.active:
- if key == K_RIGHT:
- SettingsMenu.current_page = min(SettingsMenu.total_pages,
- SettingsMenu.current_page + 1)
- elif key == K_LEFT:
- SettingsMenu.current_page = max(1, SettingsMenu.current_page - 1)
- if key == K_1:
- sound_level.changeSoundLevel()
- return
- elif key == K_2:
- pause_button.switch()
- return
- elif key == K_3:
- fast_forward.switch()
- if not fast_forward.active:
- setFastForwardCoverStatus(False)
- return
- elif key == K_4 and not fast_forward.active:
- info_button.switch()
- return
- elif key == K_5 and not fast_forward.active:
- print_button.switch()
- return
- elif key == K_6 and not fast_forward.active:
- gear_button.switch()
- pause_button.active = gear_button.active
- return
- if gear_button.active:
- return
- if key == K_a:
- KeyBools.aPressed = True
- elif key == K_d:
- KeyBools.dPressed = True
- elif key == K_i:
- KeyBools.iPressed = True
- elif key == K_k:
- KeyBools.kPressed = True
- elif key == K_LEFT:
- KeyBools.leftPressed = True
- elif key == K_RIGHT:
- KeyBools.rightPressed = True
- elif key == K_KP2:
- KeyBools.kp2Pressed = True
- elif key == K_KP8:
- KeyBools.kp8Pressed = True
- return
- def keyReleased(key):
- if key == K_a:
- KeyBools.aPressed = False
- elif key == K_d:
- KeyBools.dPressed = False
- elif key == K_i:
- KeyBools.iPressed = False
- elif key == K_k:
- KeyBools.kPressed = False
- elif key == K_LEFT:
- KeyBools.leftPressed = False
- elif key == K_RIGHT:
- KeyBools.rightPressed = False
- elif key == K_KP2:
- KeyBools.kp2Pressed = False
- elif key == K_KP8:
- KeyBools.kp8Pressed = False
- return
- ############################################################
- #
- # M O T I O N H A N D L I N G
- #
- def handleMotion():
- padblue = Pads.padlist[0]
- padgreen = Pads.padlist[1]
- padred = Pads.padlist[2]
- padorange = Pads.padlist[3]
- if not padgreen.AI:
- if KeyBools.aPressed:
- padgreen.move(-pad_speed * GameState.pad_speed_mult, 0)
- if KeyBools.dPressed:
- padgreen.move(pad_speed * GameState.pad_speed_mult, 0)
- if not KeyBools.aPressed and not KeyBools.dPressed:
- padgreen.moving = (0, 0)
- if not padblue.AI:
- if KeyBools.iPressed:
- padblue.move(0, -pad_speed * GameState.pad_speed_mult)
- if KeyBools.kPressed:
- padblue.move(0, pad_speed * GameState.pad_speed_mult)
- if not KeyBools.iPressed and not KeyBools.kPressed:
- padblue.moving = (0, 0)
- if not padorange.AI:
- if KeyBools.leftPressed:
- padorange.move(-pad_speed * GameState.pad_speed_mult, 0)
- if KeyBools.rightPressed:
- padorange.move(pad_speed * GameState.pad_speed_mult, 0)
- if not KeyBools.leftPressed and not KeyBools.rightPressed:
- padorange.moving = (0, 0)
- if not padred.AI:
- if KeyBools.kp8Pressed:
- padred.move(0, -pad_speed * GameState.pad_speed_mult)
- if KeyBools.kp2Pressed:
- padred.move(0, pad_speed * GameState.pad_speed_mult)
- if not KeyBools.kp8Pressed and not KeyBools.kp2Pressed:
- padred.moving = (0, 0)
- Balls.ball.move()
- gravity_ring.pumpCircs()
- gravity_ring.modifyRadius()
- gravity_ring.handleGravFocals()
- return
- ############################################################
- #
- # C O L L I S I O N H A N D L I N G
- #
- def collisionHandling(ball):
- collision = False
- (x_translate, y_translate) = ball.getVelocityComponents()
- xnew = ball.x + x_translate
- ynew = ball.y + y_translate
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- if ball.last_pad == pad:
- continue
- (padxmid, padymid) = getPadMidpoint(i)
- padxmin = pad.x
- padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
- padymin = pad.y
- padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
- inwards_reflect_normal = i * 90
- side_reflect_normal = (i+1) % 4 * 90
- tolerance = 0.01 * pad_height
- if objectWithinBounds(xnew, ynew, padxmin, padxmax, padymin, padymax):
- collision = pad
- if i == 0 or i == 2:
- #'side_reflect_normal' if ball collides with bottom / top side.
- #'inwards_reflect_normal' if ball collides with inwards-facing surface
- if (ynew > padymax - tolerance or ynew < padymin + tolerance):
- if i == 0 and xnew < padxmax and xnew > padxmax - tolerance:
- normal = inwards_reflect_normal
- elif i == 0:
- normal = side_reflect_normal
- elif i == 2 and xnew > padxmin and xnew < padxmin + tolerance:
- normal = inwards_reflect_normal
- else:
- normal = side_reflect_normal
- else:
- normal = inwards_reflect_normal
- reflected_angle = (plane_reflect_angles[normal] - ball.angle) % 360
- else:
- #'side_reflect_normal' if ball collides with left / right side.
- #'inwards_reflect_normal' if ball collides with inwards-facing surface
- if (xnew > padxmax - tolerance or xnew < padxmin + tolerance) and (i == 1 and ynew > padymin or i == 3 and ynew < padymax):
- if i == 1 and ynew > padymin and ynew < padymin + tolerance:
- normal = inwards_reflect_normal
- elif i == 1:
- normal = side_reflect_normal
- elif i == 3 and ynew < padymax and ynew > padymax - tolerance:
- normal = inwards_reflect_normal
- else:
- normal = side_reflect_normal
- else:
- normal = inwards_reflect_normal
- reflected_angle = (plane_reflect_angles[normal] - ball.angle) % 360
- break
- if collision:
- if WeightData.current_game < games_per_test:
- WeightData.current_collisions += 1
- GameState.collision_count += 1
- handleScoreMessages()
- if GameState.collision_count > GameState.max_count:
- GameState.max_count = GameState.collision_count
- modifier = 0.1
- (x_padmove, y_padmove) = collision.moving
- initial_angle = ball.angle
- initial_velocity = ball.velocity
- ball.angle = reflected_angle
- (x_ballmove, y_ballmove) = ball.getVelocityComponents()
- final_angle = angleFromComponents(x_ballmove + x_padmove * modifier, y_ballmove + y_padmove * modifier)
- final_velocity = velocityFromComponents(x_ballmove + x_padmove * modifier, y_ballmove + y_padmove * modifier)
- ball.angle = final_angle
- ball.velocity = final_velocity
- updateCollisionData(ball.x, ball.y, initial_angle, initial_velocity, final_angle, final_velocity)
- ball.last_pad = collision
- return
- def updateCollisionData(x, y, i_angle, i_speed, f_angle, f_speed):
- Collision.xpos = x
- Collision.ypos = y
- Collision.initial_angle = i_angle
- Collision.initial_speed = i_speed
- Collision.final_angle = f_angle
- Collision.final_speed = f_speed
- if gravity_ring.active:
- Collision.grav_coef = gravity_ring.grav_coef
- else:
- Collision.grav_coef = 0
- updatePadTargets()
- return
- def getCollisionData():
- collision_data = (Collision.xpos, Collision.ypos, Collision.initial_angle,
- Collision.initial_speed, Collision.final_angle,
- Collision.final_speed, Collision.grav_coef)
- return collision_data
- ############################################################
- #
- # B A L L G E N E R A T I O N
- #
- def genNewBall():
- if WeightData.current_game < games_per_test:
- WeightData.current_game += 1
- elif GameState.toggle_genetic:
- beginNewTest()
- velocity = random.random()*0.3+0.3
- angle = 0
- while angle % 90 < 20:
- angle = random.randrange(360)
- ball = Ball(window_width/2, window_height/2, velocity, angle)
- Balls.ball = ball
- printData("ANGLE BALL LAUNCHED AT: %s" % angle)
- updateCollisionData(ball.x, ball.y, angle, velocity, angle, velocity)
- return
- ############################################################
- #
- # M O U S E I N P U T
- #
- def cursorWithinBounds(low_x, high_x, low_y, high_y):
- (mouse_x, mouse_y) = pygame.mouse.get_pos()
- if low_x <= mouse_x and mouse_x <= high_x:
- if low_y <= mouse_y and mouse_y <= high_y:
- return True
- return False
- def hoverHandler(icon):
- hovering_over = cursorWithinBounds(icon.pos_x, icon.pos_x + icon.width,
- icon.pos_y, icon.pos_y + icon.height)
- return hovering_over
- def setHover(icon):
- if hoverHandler(icon):
- icon.hovering = True
- else:
- icon.hovering = False
- return
- def passiveMouseHandler():
- #(mouse_x, mouse_y) = pygame.mouse.get_pos()
- setHover(sound_level)
- setHover(pause_button)
- setHover(info_button)
- setHover(print_button)
- setHover(fast_forward)
- setHover(gear_button)
- setHover(arrow_left)
- setHover(arrow_right)
- if gear_button.active:
- x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
- y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
- for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
- if i >= len(SettingOptions.options):
- break
- option = SettingOptions.options[i]
- option.pos_x += x_offset
- option.pos_y += y_offset
- setHover(option)
- option.pos_x -= x_offset
- option.pos_y -= y_offset
- return
- def activeMouseHandler(position, button):
- if hoverHandler(sound_level):
- sound_level.changeSoundLevel()
- if hoverHandler(pause_button):
- pause_button.switch()
- if hoverHandler(info_button):
- info_button.switch()
- if hoverHandler(print_button):
- print_button.switch()
- if hoverHandler(fast_forward):
- fast_forward.switch()
- if not fast_forward.active:
- setFastForwardCoverStatus(False)
- if hoverHandler(gear_button):
- gear_button.switch()
- pause_button.active = gear_button.active
- if gear_button.active:
- x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
- y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
- for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
- if i >= len(SettingOptions.options):
- break
- option = SettingOptions.options[i]
- option.pos_x += x_offset
- option.pos_y += y_offset
- if hoverHandler(option):
- option.switch()
- option.pos_x -= x_offset
- option.pos_y -= y_offset
- for arrow in [arrow_left, arrow_right]:
- arrow.pos_x += x_offset
- arrow.pos_y += y_offset
- if hoverHandler(arrow):
- if arrow == arrow_left:
- SettingsMenu.current_page = max(1, SettingsMenu.current_page - 1)
- else:
- SettingsMenu.current_page = min(SettingsMenu.total_pages, SettingsMenu.current_page + 1)
- arrow.pos_x -= x_offset
- arrow.pos_y -= y_offset
- return
- ############################################################
- #
- # S E T T I N G S H A N D L I N G
- #
- def SettingOptionsHandler():
- for option in SettingOptions.options:
- option.adjustSize()
- option.adjustColour()
- return
- ############################################################
- #
- # M A I N G A M E L O O P
- #
- def main():
- pygame.init()
- screen = pygame.display.set_mode((window_width, window_height), 0, 32)
- Screen.screen = screen
- pygame.display.set_caption("PyBall")
- initialiseFonts()
- initialiseSession()
- beginNewGeneration()
- text_colour_val = 255
- demonstration_begun = False
- return_pressed = False
- ticks = 0
- real_ticks = 0
- while not GameState.done:
- real_ticks += 1
- screen.fill(black.toList())
- sound_level.adjustVolume()
- passiveMouseHandler()
- SettingOptionsHandler()
- events = pygame.event.get()
- for e in events:
- if e.type == QUIT:
- GameState.done = True
- break
- elif not demonstration_begun and e.type == KEYDOWN and e.key == K_RETURN:
- return_pressed = True
- elif e.type == KEYDOWN and demonstration_begun:
- keyPressed(e.key)
- elif e.type == KEYUP and demonstration_begun:
- keyReleased(e.key)
- elif e.type == MOUSEBUTTONDOWN:
- activeMouseHandler(e.pos, e.button)
- if text_colour_val:
- text_colour = [text_colour_val, text_colour_val, text_colour_val]
- renderText(screen, window_width / 2, 120, "Angelic War", font_size_40, text_colour, "PyBall")
- renderText(screen, window_width / 2, 160, "Angelic War", font_size_14, text_colour, "Created By Tag")
- if return_pressed:
- text_colour_val -= 1
- drawObjects(screen)
- pygame.display.flip()
- else:
- demonstration_begun = True
- if gear_button.active:
- pause_button.active = True
- fast_forward.active = False
- if info_button.active:
- circsAtTargets(screen)
- drawObjects(screen)
- drawSettingsMenu(screen)
- renderText(screen, window_width / 2, 160, "Birth Of A Hero", font_size_30, white.toList(), "S E T T I N G S")
- pygame.display.flip()
- elif pause_button.active and not fast_forward.active:
- renderText(screen, window_width / 2, 160, "Birth Of A Hero", font_size_30, white.toList(), "P A U S E D")
- if info_button.active:
- circsAtTargets(screen)
- drawObjects(screen)
- pygame.display.flip()
- elif fast_forward.active and not pause_button.active:
- ticks += 1
- drawFastForwardCover(screen, ticks, real_ticks)
- handleAI()
- handleMotion()
- elif fast_forward.active and pause_button.active:
- drawFastForwardCover(screen, ticks, real_ticks)
- else:
- if gravity_ring.active:
- renderText(screen, window_width / 2, 160, "Birth Of A Hero", font_size_30, white.toList(), "G R A V I T Y A C T I V E")
- ticks += 1
- handleAI()
- handleMotion()
- if info_button.active:
- circsAtTargets(screen)
- drawObjects(screen)
- pygame.display.flip()
- if GameState.done:
- pygame.quit()
- return
- if __name__ == "__main__":
- main()
- import math
- import time
- import random
- import pygame
- from pygame.locals import *
- ###################################
- # P Y B A L L
- # PyBall - A Pong-Based Adaptive AI Demonstration / Game
- # Created by TAGC
- # Last update: 20/03/2012
- # Description: This program is developed as part of an
- # assessed project at Imperial College, London
- # to demonstrate the use of AI in gaming and
- # contrast the use of static and dynamic
- # AI techniques to allow further analysis into
- # how different kinds of AI behaviour can affect
- # replayability and game immersion
- # Notes: This script will only run if it comes as part
- # of the "PyBallStuff" package. This package
- # contains the audio, image and font files used
- # by this program
- ###################################
- # I N S T A L L A T I O N
- # This script is written in Python 3.2 and requires a Python 3
- # interpreter to run. This can be obtained from
- # http://www.python.org/download/ (it is strongly recommended
- # that the user obtains the 32-bit version even if they have
- # a 64-bit operating system)
- # This script also requires an installation of the python
- # module "PyGame" available at
- # http://www.pygame.org/download.shtml (again, it is strongly
- # recommended that the user obtains the 32-bit version)
- # In addition to these, the user will need to install the
- # following fonts:
- # > "Birth of a Hero"
- # > "Angelic War"
- # > "Hawaii Lover"
- # > "Sergeant SixPack"
- # These are all available at http://www.1001freefonts.com/
- # if the user does not possess the "Fonts" folder included
- # inside the folder "PyBallStuff"
- # The "PyBallStuff" folder must be placed in the
- # "C:/Python32" directory
- ###################################
- # L A U N C H I N G
- # To execute the program, click on "Run" on the top bar
- # of the interpreter and select "Run Module" or press F5
- ###################################
- # C O N T R O L S
- # Special Controls
- ###################
- # "Enter" - begins the game after the program launches
- # "Escape" - if the user has the Settings Menu open, this
- # exits that screen, otherwise it quits the
- # programs
- # Setting Controls
- ###################
- # "1" - Toggles through the sound level settings
- # "2" - Pauses / unpauses the program
- # "3" - Toggles Fast Forward mode on and off; when
- # this mode is active, the logic of the game
- # runs slightly faster
- # "4" - Toggles Graphical Information mode on and off;
- # when on, extra objects are rendered to the
- # screen to display more information on the
- # states of the pads' memories, etc
- # "5" - Toggles Textual Information mode on and off;
- # when on, information on the game is printed
- # out to the console
- # "6" - Opens or closes the Settings Menu
- # "Left" - when the Settings Menu is open, this displays
- # the previous page of options (if not already
- # there)
- # "Right" - when the Settings Menu is open, this displays
- # the next page of options (if not already
- # there)
- # Pad Controls
- ###############
- # "A" - if the green pad is under human control, moves
- # that pad left
- # "D" - if the green pad is under human control, moves
- # that pad right
- # "I" - if the blue pad is under human control, moves
- # that pad up
- # "K" - if the blue pad is under human control, moves
- # that pad down
- # "Left" - if the red pad is under human control, moves
- # that pad left
- # "Right" - if the red pad is under human control, moves
- # that pad right
- # "Num 8" - if the orange pad is under human control, moves
- # that pad up
- # "Num 2" - if the orange pad is under human control, moves
- # that pad down
- window_width = 700
- window_height = 700
- pad_width = 25
- pad_height = 100
- ball_radius = 2
- corner_buffer_coef = 0.20
- population_size = 20
- games_per_test = 50
- xmin = window_width * corner_buffer_coef - pad_height / 2
- xmax = window_width * (1-corner_buffer_coef) - pad_height / 2
- ymin = window_height * corner_buffer_coef - pad_height / 2
- ymax = window_height * (1-corner_buffer_coef) - pad_height / 2
- plane_reflect_angles = {0:180, 90:360, 180:180, 270:360}
- pad_speed = 0.5
- ball_accel = 0.00001
- max_x_difference = window_width
- max_y_difference = window_height
- max_angular_difference = 180
- max_gravity_difference = 100
- pyball_stuff_path = r"C:\Python32\PyBallStuff\\"
- pad_image_path = pyball_stuff_path + r"Pictures\Pads"
- sound_level_path = pyball_stuff_path + r"Pictures\SoundLevels"
- letter_path = pyball_stuff_path + r"Pictures\Letters"
- picture_path = pyball_stuff_path + r"Pictures"
- music_path = pyball_stuff_path + r"Audio"
- pad_memory_path = pyball_stuff_path + r"Memories"
- data_path = pyball_stuff_path + r"Data"
- font_size_11 = 11
- font_size_12 = 12
- font_size_14 = 14
- font_size_16 = 16
- font_size_18 = 18
- font_size_20 = 20
- font_size_22 = 22
- font_size_24 = 24
- font_size_26 = 26
- font_size_30 = 30
- font_size_40 = 40
- frames_per_score_message = 1000
- score_message_thresholds = {"Okay":5, "Good":10, "Great":15, "Excellent!":20,
- "Superb!":25, "Amazing!":30, "Outstanding!":50,
- "Unbelievable!":100}
- class Colour:
- def __init__(self, r, g, b):
- self.r = r
- self.g = g
- self.b = b
- return
- def toList(self):
- listrep = [self.r, self.g, self.b]
- return listrep
- class Pad:
- def __init__(self, x, y, surface):
- self.x = x
- self.y = y
- self.x_target = x
- self.y_target = y
- self.surface = surface
- self.moving = (0, 0)
- self.AI = 1
- self.score = 0
- self.memory = []
- return
- def move(self, x_translate, y_translate):
- self.moving = (x_translate, y_translate)
- self.xnew = self.x + x_translate
- self.ynew = self.y + y_translate
- if xmin <= self.xnew and self.xnew <= xmax:
- self.x = self.xnew
- else:
- x_translate = 0
- if ymin <= self.ynew and self.ynew <= ymax:
- self.y = self.ynew
- else:
- y_translate = 0
- self.moving = (x_translate, y_translate)
- return
- def losePoint(self, x_miss, y_miss):
- self.score -= 1
- if GameState.toggle_learning and Balls.ball.last_pad != self and self.AI == 1:
- memory_tuple = MemoryTuple(x_miss, y_miss)
- printData("SAVING TO MEMORY...")
- printData(memory_tuple.getData())
- printData("")
- self.memory.append(memory_tuple)
- return
- def toString(self):
- stringrep = ("(%s, %s)") % (self.x, self.y)
- return stringrep
- class Ball:
- def __init__(self, x, y, velocity, angle):
- self.x = x
- self.y = y
- self.velocity = velocity
- self.angle = angle
- self.last_pad = -1
- return
- def move(self):
- cut_off_modifier = 0.9
- collisionHandling(self)
- (pad_0_x, pad_0_y) = getPadMidpoint(0)
- (pad_1_x, pad_1_y) = getPadMidpoint(1)
- (pad_2_x, pad_2_y) = getPadMidpoint(2)
- (pad_3_x, pad_3_y) = getPadMidpoint(3)
- if self.x < pad_0_x * cut_off_modifier:
- Pads.padlist[0].losePoint(self.x, self.y)
- Launchers.launcher.launchBall()
- elif self.x > window_width - (window_width - pad_2_x) * cut_off_modifier:
- Pads.padlist[2].losePoint(self.x, self.y)
- Launchers.launcher.launchBall()
- elif self.y < pad_3_y * cut_off_modifier:
- Pads.padlist[3].losePoint(self.x, self.y)
- Launchers.launcher.launchBall()
- elif self.y > window_height - (window_height - pad_1_y) * cut_off_modifier:
- Pads.padlist[1].losePoint(self.x, self.y)
- Launchers.launcher.launchBall()
- else:
- (self.x_translate, self.y_translate) = self.getVelocityComponents()
- if gravity_ring.active:
- target = gravity_ring
- grav_pull_angle = degToRad(angleBetweenPoints((self.x, self.y),
- (target.pos_x, target.pos_y)))
- distance = getPointDistance(self.x, self.y, target.pos_x, target.pos_y)
- grav_x = gravity_ring.grav_coef / 2000 * math.cos(grav_pull_angle) * math.pow(0.96, distance)
- grav_y = gravity_ring.grav_coef / 2000 * math.sin(grav_pull_angle) * math.pow(0.96, distance)
- if abs(self.x_translate + grav_x) < abs(self.x_translate):
- grav_x *= 0.3
- if abs(self.y_translate - grav_y) < abs(self.y_translate):
- grav_y *= 0.3
- self.velocity = velocityFromComponents(self.x_translate + grav_x,
- self.y_translate - grav_y)
- self.angle = angleFromComponents(self.x_translate + grav_x,
- self.y_translate - grav_y)
- self.velocity += GameState.ball_accel
- self.x += self.x_translate
- self.y += self.y_translate
- return
- def getVelocityComponents(self):
- x_translate = math.cos(degToRad(self.angle)) * self.velocity
- y_translate = -math.sin(degToRad(self.angle)) * self.velocity
- components = (x_translate, y_translate)
- return components
- def toString(self):
- stringrep = ("Position: (%s, %s), moving at %s units/frame at angle %s"
- ) % (self.x, self.y, self.velocity, self.angle)
- return stringrep
- class GravFocal:
- def __init__(self, x, y, velocity, angle, grav_ring):
- self.pos_x = x
- self.pos_y = y
- self.velocity = velocity
- self.angle = angle
- self.grav_ring = grav_ring
- self.chasing = ""
- i = 0
- while self.chasing == "" or self.chasing == self:
- if i < len(grav_ring.grav_focals):
- self.chasing = random.choice(grav_ring.grav_focals)
- i += 1
- else:
- self.chasing = grav_ring
- break
- return
- def move(self):
- (self.x_translate, self.y_translate) = self.getVelocityComponents()
- grav_pull_angle = degToRad(angleBetweenPoints((self.pos_x, self.pos_y),
- (self.chasing.pos_x, self.chasing.pos_y)))
- grav_x = gravity_ring.grav_coef / 5000 * math.cos(grav_pull_angle)
- grav_y = gravity_ring.grav_coef / 5000 * math.sin(grav_pull_angle)
- self.velocity = velocityFromComponents(self.x_translate + grav_x, self.y_translate - grav_y)
- self.angle = angleFromComponents(self.x_translate + grav_x, self.y_translate - grav_y)
- self.pos_x += self.x_translate
- self.pos_y += self.y_translate
- return
- def getVelocityComponents(self):
- x_translate = math.cos(degToRad(self.angle)) * self.velocity
- y_translate = -math.sin(degToRad(self.angle)) * self.velocity
- components = (x_translate, y_translate)
- return components
- class Launcher:
- def __init__(self, radius, cool_down_rate, hot_colour, cool_colour):
- self.x = window_width/2
- self.y = window_height/2
- self.radius = radius
- self.heat = 0
- self.cool_down_rate = cool_down_rate
- self.hot_colour = hot_colour
- self.cool_colour = cool_colour
- def launchBall(self):
- GameState.data.append((GameState.game_count, GameState.collision_count))
- if GameState.auto_saving:
- if GameState.game_count > 0 and not GameState.game_count % 10:
- saveGameData()
- if GameState.game_count > 0 and not GameState.game_count % 100:
- backUpGameData()
- GameState.game_count += 1
- GameState.collision_count = 0
- genNewBall()
- self.heat = 1
- GameState.num_rounds += 1
- if GameState.auto_saving and GameState.num_rounds > 0:
- if GameState.num_rounds % GameState.auto_saving == 0:
- savePadMemories()
- def coolDown(self):
- if self.heat > 0:
- self.heat -= self.cool_down_rate
- def getColour(self):
- self.hr = self.heat * self.hot_colour[0]
- self.cr = (1-self.heat) * self.cool_colour[0]
- self.hg = self.heat * self.hot_colour[1]
- self.cg = (1-self.heat) * self.cool_colour[1]
- self.hb = self.heat * self.hot_colour[2]
- self.cb = (1-self.heat) * self.cool_colour[2]
- colour = [self.hr + self.cr, self.hg + self.cg, self.hb + self.cb]
- return colour
- def getRadius(self):
- actual_radius = int(self.radius * math.pow(1.01, self.heat * 100))
- return actual_radius
- class SoundLevel:
- def __init__(self):
- self.levels = []
- self.current_level = 3
- self.volume = 1
- self.pos_x = 0
- self.pos_y = 0
- self.width = 0
- self.height = 0
- self.hovering = False
- self.surface = ""
- self.cover = ""
- self.channel = ""
- return
- def addSurface(self, surface):
- self.surface = surface
- self.width = surface.get_width()
- self.height = surface.get_height()
- self.cover = pygame.Surface((self.width, self.height), 0, 32)
- self.cover.fill(black.toList())
- return
- def addChannel(self, channel):
- self.channel = channel
- self.channel.set_volume(0.333 * self.current_level)
- return
- def changeSoundLevel(self):
- self.current_level += 1
- self.current_level %= 4
- self.addSurface(self.levels[self.current_level])
- return
- def adjustVolume(self):
- target = 0.333 * self.current_level
- difference = target - self.volume
- self.volume += difference * 0.002
- self.channel.set_volume(self.volume)
- return
- def setCoverTransparency(self):
- if self.hovering:
- alpha = 255 * 0.15
- else:
- alpha = 255 * 0.5
- self.cover.set_alpha(alpha)
- return
- class PauseButton:
- def __init__(self, x, y):
- self.active = False
- self.hovering = False
- self.pos_x = x
- self.pos_y = y
- self.width = 18
- self.height = 18
- return
- def switch(self):
- self.active = not self.active
- return
- class GenericButton:
- def __init__(self, x, y):
- self.active = False
- self.hovering = False
- self.pos_x = x
- self.pos_y = y
- self.width = 0
- self.height = 0
- return
- def addSurface(self, surface):
- self.surface = surface
- self.width = surface.get_width()
- self.height = surface.get_height()
- self.cover = pygame.Surface((self.width, self.height), 0, 32)
- self.cover.fill(black.toList())
- return
- def switch(self):
- self.active = not self.active
- return
- class MemoryTuple:
- def __init__(self, x_miss, y_miss):
- self.x_miss = x_miss
- self.y_miss = y_miss
- self.x_collision = Collision.xpos
- self.y_collision = Collision.ypos
- self.collision_i_angle = Collision.initial_angle
- self.collision_i_speed = Collision.initial_speed
- self.collision_f_angle = Collision.final_angle
- self.collision_f_speed = Collision.final_speed
- self.collision_grav_coef = Collision.grav_coef
- return
- def getData(self):
- memory_tuple = (self.x_miss, self.y_miss, self.x_collision,
- self.y_collision, self.collision_i_angle,
- self.collision_i_speed, self.collision_f_angle,
- self.collision_f_speed, self.collision_grav_coef)
- return memory_tuple
- class SettingOption:
- def __init__(self, y, name, info, modes):
- self.pos_x = 20
- self.pos_y = y
- self.width = 260
- self.height = 50
- self.min_height = 50
- self.max_height = 100
- self.name = name
- self.info = info
- self.modes = modes
- self.colour = green.toList()
- self.active = True
- self.hovering = False
- self.surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA, 32)
- return
- def construct(self):
- offset = 5
- max_distance = getPointDistance(0, 0, offset, offset)
- self.surface.lock()
- for x in range(self.width):
- for y in range(self.height):
- alpha_coef = 1 - math.pow(0.992, (self.width - x) * 2)
- alpha_subcoef = (abs(self.height / 2 - y) + (1 - x/self.width) * 20) * 5
- alpha_coef *= 1 - math.pow(0.992, alpha_subcoef)
- if x < offset and y < offset:
- distance = getPointDistance(x, y, offset, offset)
- alpha_coef *= max(0, 1 - distance / offset)
- elif x < offset and y > self.height - offset:
- distance = getPointDistance(x, y, offset, self.height - offset)
- alpha_coef *= max(0, 1 - distance / offset)
- elif x < offset:
- alpha_coef *= x / offset
- elif y < offset:
- alpha_coef *= y / offset
- elif y > self.height - offset:
- alpha_coef *= (self.height - y) / offset
- col_subcoef = min(x, self.width - x) + min(y, self.height - y)
- col_coef = math.pow(0.992, col_subcoef)
- if self.hovering:
- alpha_coef = min(1, alpha_coef * 1.2)
- col_coef = min(1, col_coef * 1.2)
- bg_colour = [self.colour[0]*col_coef, self.colour[1]*col_coef, self.colour[2]*col_coef, 255 * alpha_coef]
- self.surface.set_at((x, y), bg_colour)
- self.surface.unlock()
- x_text = 0.1 * self.width
- y_text = self.min_height / 2 - 5
- renderText(self.surface, x_text, y_text, "Free Sans Bold", font_size_16, white.toList(), self.name, False)
- x_text = 0.1 * self.width
- y_text = self.min_height - 5
- textlen = len(self.info)
- textsplit = self.info.split()
- while textlen and y_text + 10 < self.surface.get_height():
- text = []
- if textsplit:
- next_word_length = len(textsplit[0])
- else:
- next_word_length = 0
- while len(" ".join(text)) + next_word_length < 43:
- if textsplit:
- word = textsplit.pop(0)
- else:
- textlen = 0
- break
- text.append(word)
- textlen -= len(word)
- text = " ".join(text)
- renderText(self.surface, x_text, y_text, "Free Sans Bold", font_size_14, white.toList(), text, False)
- y_text += 10
- if self.hovering:
- x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
- y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
- radius = 2
- x_circ = round(x_offset + self.pos_x - 8)
- y_circ = round(y_offset + self.pos_y + self.height / 2 + radius / 2)
- pygame.draw.circle(Screen.screen, white.toList(), (x_circ, y_circ), radius)
- return
- def adjustColour(self):
- if self.modes[0] == 1 and self.active < 0:
- self.colour = crossColours(white, green, -self.active/10).toList()
- self.active += 1
- elif self.modes[0] == 1 and self.active >= 0:
- self.colour = green.toList()
- elif self.modes[0] == 2:
- self.colour = {False:red.toList(), True:green.toList()}[self.active]
- elif self.modes[0] > 2:
- self.colour = crossColours(green, red, self.active / (self.modes[0]-1)).toList()
- return
- def adjustSize(self):
- lower_index = (SettingsMenu.current_page - 1) * 5
- upper_index = SettingsMenu.current_page * 5
- self_index = SettingOptions.options.index(self)
- if self_index < lower_index or self_index > upper_index:
- return
- rate = 10
- delta_height = 0
- if self.hovering and self.height < self.max_height:
- delta_height = rate
- elif not self.hovering and self.height > self.min_height:
- delta_height = -rate
- if delta_height:
- if self.height + delta_height > self.max_height:
- actual_change = self.max_height - self.height
- elif self.height + delta_height < self.min_height:
- actual_change = self.height - self.min_height
- else:
- actual_change = delta_height
- self.height += actual_change
- self.surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA, 32)
- found_self = False
- for i in range(lower_index, upper_index):
- if i >= len(SettingOptions.options):
- break
- option = SettingOptions.options[i]
- if not found_self and self == option:
- found_self = True
- elif found_self:
- option.pos_y += actual_change
- return
- def switch(self):
- if self.modes[0] == 1:
- self.active = -10
- elif self.modes[0] == 2:
- self.active = not self.active
- elif self.modes[0] > 2:
- self.active = (self.active + 1) % self.modes[0]
- if "TOGGLE_LEARNING" in self.modes:
- GameState.toggle_learning = self.active
- if "TOGGLE_GENETIC" in self.modes:
- GameState.toggle_genetic = self.active
- if "TOGGLE_GRAVITY" in self.modes:
- gravity_ring.active = self.active
- if "SET_GRAV_COEF" in self.modes:
- val = input("Please enter new gravity coefficient: ")
- try:
- val = float(val)
- if 0 <= val and val <= 100:
- gravity_ring.grav_coef = val
- else:
- print("Invalid value")
- except:
- print("Error parsing value")
- if "SET_WEIGHT" in self.modes:
- val = input("Please enter new weight coefficient: ")
- try:
- val = float(val)
- if 0 <= val and val <= 1:
- WeightData.current_weight = val
- else:
- print("Invalid value")
- except:
- print("Error parsing value")
- if "SAVE_MEMORY" in self.modes:
- savePadMemories()
- if "BACK_UP_MEMORY" in self.modes:
- path = ""
- i = 0
- while path == "" or os.path.exists(path):
- i += 1
- path = os.path.join(pad_memory_path, r"BackUpMemories\Back-up %s" % i)
- backUpPadMemories(path)
- if "LOAD_MEMORY" in self.modes:
- loadPadMemories()
- if "RESET_MEMORY" in self.modes:
- for pad in Pads.padlist:
- pad.memory = []
- if "RESET_SCORES" in self.modes:
- for pad in Pads.padlist:
- pad.score = 0
- if "TOGGLE_AI_PAD1" in self.modes:
- Pads.padlist[0].AI = self.active / 2
- if "TOGGLE_AI_PAD2" in self.modes:
- Pads.padlist[1].AI = self.active / 2
- if "TOGGLE_AI_PAD3" in self.modes:
- Pads.padlist[2].AI = self.active / 2
- if "TOGGLE_AI_PAD4" in self.modes:
- Pads.padlist[3].AI = self.active / 2
- if "TOGGLE_ALL_AI" in self.modes:
- option_list = []
- for i in range(4):
- option_list.append("TOGGLE_AI_PAD%s" % (i+1))
- enable_all = False
- active_set = -1
- for option in SettingOptions.options:
- for related_option in option_list:
- if related_option in option.modes and option.active != active_set:
- if active_set == -1:
- active_set = option.active
- continue
- else:
- enable_all = True
- break
- if enable_all:
- active_set = 2
- else:
- active_set = (active_set + 1) % 3
- for option in SettingOptions.options:
- for related_option in option_list:
- if related_option in option.modes:
- option.active = active_set
- for pad in Pads.padlist:
- pad.AI = active_set / 2
- if "ACCEL_BALL" in self.modes:
- for pad in Pads.padlist:
- if self.active == 0:
- GameState.ball_accel = 0
- elif self.active == 1:
- GameState.ball_accel = ball_accel
- elif self.active == 2:
- GameState.ball_accel = ball_accel * 10
- else:
- GameState.ball_accel = ball_accel * 50
- if "FAST_PADS" in self.modes:
- GameState.pad_speed_mult = {False:1, True:2}[self.active]
- if "AUTO_SAVE" in self.modes:
- rounds = GameState.num_rounds
- if rounds == 0:
- GameState.auto_saving = 0
- elif self.active == 1:
- GameState.auto_saving = 100
- elif self.active == 2:
- GameState.auto_saving = 50
- else:
- GameState.auto_saving = 10
- return
- class GravityRing:
- def __init__(self):
- self.pos_x = int(window_width / 2)
- self.pos_y = int(window_height / 2)
- self.grav_coef = 0
- self.current_angle = 0
- self.current_radius = 0
- self.end_radius = 50
- self.rings = 8
- self.pump_index = 0
- self.pump_timer = 100
- self.active = False
- self.pumpvals = []
- self.grav_focals = []
- for i in range(10):
- self.grav_focals.append("")
- self.genNewGravFocal(i)
- for i in range(self.rings):
- self.pumpvals.append(0)
- return
- def genNewGravFocal(self, index):
- velocity = random.random()*0.2+0.2
- angle = 0
- while angle % 90 < 20:
- angle = random.randrange(360)
- grav_focal = GravFocal(window_width/2, window_height/2, velocity, angle, self)
- self.grav_focals[index] = grav_focal
- return
- def construct(self):
- if not self.current_radius > 0:
- return
- delta_angle = 360 / self.rings
- current_angle = 0
- for i in range(self.rings):
- circ_x = self.pos_x + round(self.current_radius * math.cos(degToRad(current_angle)))
- circ_y = self.pos_y + round(self.current_radius * math.sin(degToRad(current_angle)))
- circ_rad = round(2 * (1 + self.pumpvals[i] / 100))
- colour = crossColours(purple, grey, self.grav_coef / 180)
- pygame.draw.circle(Screen.screen, colour.toList(), (circ_x, circ_y), circ_rad, 1)
- current_angle += delta_angle
- if self.pumpvals[i]:
- self.pumpvals[i] -= 1
- return
- def handleGravFocals(self):
- if not self.current_radius > 0:
- return
- for i in range(len(self.grav_focals)):
- focal = self.grav_focals[i]
- if getPointDistance(self.pos_x, self.pos_y, focal.pos_x, focal.pos_y) > self.current_radius:
- self.genNewGravFocal(i)
- return
- focal.move()
- colour = crossColours(purple, grey, self.grav_coef / 180)
- focal_radius = 2
- focal_x = round(focal.pos_x - focal_radius / 2)
- focal_y = round(focal.pos_y - focal_radius / 2)
- pygame.draw.circle(Screen.screen, colour.toList(), (focal_x, focal_y), focal_radius, 1)
- def modifyRadius(self):
- if self.active and 0.95 * self.end_radius <= self.current_radius and self.current_radius < self.end_radius:
- self.current_radius = self.end_radius
- elif self.active and self.current_radius < 0.95 * self.end_radius:
- self.current_radius += self.end_radius / 250
- elif not self.active and 0.05 * self.end_radius >= self.current_radius and self.current_radius >= 0:
- self.current_radius = 0
- else:
- self.current_radius -= self.end_radius / 250
- return
- def pumpCircs(self):
- if not self.pump_timer:
- self.pumpvals[self.pump_index] = 100
- self.pump_index = (self.pump_index + 1) % self.rings
- if self.pump_timer > 100 - 50 * (self.grav_coef - 50) / 50:
- self.pump_timer = 0
- else:
- self.pump_timer += 1
- return
- class SettingOptions:
- options = []
- class WeightData:
- weight_scores = {}
- current_generation = []
- current_weight = 0
- current_weight_index = 0
- current_collisions = 0
- current_game = 0
- generation = 0
- class PadMemory:
- memory_blue = []
- memory_green = []
- memory_red = []
- memory_orange = []
- class Pads:
- padlist = []
- class Balls:
- ball = ""
- class Screen:
- screen = ""
- class FastForwardCover:
- surface = ""
- class SettingsMenu:
- modified_surface = ""
- default_surface = ""
- total_pages = 4
- current_page = 1
- class GameState:
- done = False
- fast_forward_cover_loaded = False
- toggle_learning = True
- toggle_genetic = True
- collision_count = 0
- max_count = 0
- num_rounds = 0
- score_messages_list = []
- score_messages = {"Okay":0, "Good":0, "Great":0, "Excellent!":0,
- "Superb!":0, "Amazing!":0, "Outstanding!":0,
- "Unbelievable!":0}
- ball_accel = 0
- pad_speed_mult = 1
- auto_saving = 0
- data = []
- game_count = 0
- class Collision:
- xpos = 0
- ypos = 0
- initial_angle = 0
- initial_speed = 0
- final_angle = 0
- final_speed = 0
- grav_coef = 0
- class Launchers:
- launcher = ""
- class KeyBools:
- aPressed = False
- dPressed = False
- iPressed = False
- kPressed = False
- leftPressed = False
- rightPressed = False
- kp8Pressed = False
- kp2Pressed = False
- class FontTypes:
- Angelic_War_40_font = ""
- Angelic_War_14_font = ""
- Birth_Of_A_Hero_30_font = ""
- Times_New_Roman_18_font = ""
- Free_Sans_Bold_10_font = ""
- Free_Sans_Bold_12_font = ""
- Free_Sans_Bold_16_font = ""
- Free_Sans_Bold_18_font = ""
- Free_Sans_Bold_30_font = ""
- Sergeant_SixPack_12_font = ""
- Sergeant_SixPack_14_font = ""
- Sergeant_SixPack_16_font = ""
- Sergeant_SixPack_18_font = ""
- Sergeant_SixPack_20_font = ""
- Sergeant_SixPack_22_font = ""
- Sergeant_SixPack_24_font = ""
- Sergeant_SixPack_26_font = ""
- black = Colour(0, 0, 0)
- grey = Colour(100, 100, 100)
- white = Colour(255, 255, 255)
- orange = Colour(255, 165, 0)
- red = Colour(255, 0, 0)
- green = Colour(0, 255, 0)
- blue = Colour(0, 0, 255)
- yellow = Colour(255, 255, 0)
- cool_blue = Colour(174, 238, 238)
- purple = Colour(223, 0, 255)
- dark_red = Colour(100, 0, 0)
- dark_green = Colour(0, 100, 0)
- dark_blue = Colour(0, 0, 100)
- sound_level = SoundLevel()
- pause_button = PauseButton(50, 14)
- fast_forward = GenericButton(79, 11)
- info_button = GenericButton(105, 10)
- print_button = GenericButton(135, 14)
- gear_button = GenericButton(160, 13)
- arrow_left = GenericButton(0, 460)
- arrow_right = GenericButton(0, 460)
- pause_surface = pygame.Surface((pause_button.width, pause_button.height), 0, 32)
- gravity_ring = GravityRing()
- ############################################################
- #
- # U T I L I T Y F U N C T I O N S
- #
- def degToRad(degrees):
- rads = degrees * math.pi / 180
- return rads
- def radToDeg(radians):
- degrees = radians * 180 / math.pi
- return degrees
- def crossColours(c1, c2, coef):
- anti_coef = 1 - coef
- c1 = c1.toList()
- c2 = c2.toList()
- result = Colour(c1[0]*coef + c2[0]*anti_coef,
- c1[1]*coef + c2[1]*anti_coef,
- c1[2]*coef + c2[2]*anti_coef)
- return result
- def objectWithinBounds(obj_x, obj_y, x_low, x_high, y_low, y_high):
- ball = Balls.ball
- within_bounds = x_low <= obj_x and obj_x <= x_high and y_low <= obj_y and obj_y <= y_high
- return within_bounds
- def angleBetweenPoints(p1, p2):
- (p1_x, p1_y) = p1
- (p2_x, p2_y) = p2
- x_dif = p2_x - p1_x
- y_dif = p2_y - p1_y
- angle = angleFromComponents(x_dif, y_dif)
- return angle
- def angleFromComponents(x_translate, y_translate):
- #quadrant 1, 2, 3, 4
- y_translate *= -1
- if x_translate == 0:
- x_translate = 0.0001
- if x_translate > 0 and y_translate > 0:
- theta = radToDeg(math.atan(y_translate / x_translate))
- elif x_translate < 0 and y_translate > 0:
- theta = radToDeg(math.atan(y_translate / x_translate)) + 180
- elif x_translate < 0 and y_translate < 0:
- theta = radToDeg(math.atan(y_translate / x_translate)) + 180
- else:
- theta = radToDeg(math.atan(y_translate / x_translate)) + 360
- theta %= 360
- return theta
- def velocityFromComponents(x_translate, y_translate):
- velocity = math.sqrt(x_translate * x_translate + y_translate * y_translate)
- return velocity
- def insidePad():
- ball = Balls.ball
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- padxmin = pad.x
- padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
- padymin = pad.y
- padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
- if padxmin <= ball.x and ball.x <= padxmax and padymin <= ball.y and ball.y <= padymax:
- return True
- return False
- def averageAngles(first_angle, second_angle):
- average_angle = (first_angle + second_angle) / 2
- return average_angle
- def getPointDistance(x1, y1, x2, y2):
- distance = math.sqrt(math.pow((x2 - x1), 2) + math.pow((y2 - y1), 2))
- return distance
- def getPadMidpoint(padindex):
- i = padindex
- pad = Pads.padlist[i]
- padxmin = pad.x
- padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
- padymin = pad.y
- padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
- padxmid = (padxmin + padxmax) / 2
- padymid = (padymin + padymax) / 2
- midpoint = (padxmid, padymid)
- return midpoint
- def getAngleDifference(angle1, angle2):
- angle_dif = 360 - max(angle1, angle2) + min(angle1, angle2)
- if angle_dif > 180:
- angle_dif = 360 - angle_dif
- return angle_dif
- ############################################################
- #
- # I N P U T / O U T P U T
- #
- def saveGameData():
- with open(os.path.join(data_path, "score_data.txt"), 'w') as file:
- for item in GameState.data:
- file.write(str(item) + "\n")
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- with open(os.path.join(data_path, "pad_%s_memory.txt" % i), 'w') as file:
- for memory in pad.memory:
- file.write(str(memory.getData()) + "\n")
- with open(os.path.join(data_path, "weight_coefficient.txt"), 'w') as file:
- file.write(str(WeightData.current_weight))
- with open(os.path.join(data_path, "grav_coefficient.txt"), 'w') as file:
- file.write(str(gravity_ring.grav_coef))
- return
- def backUpGameData():
- path = ""
- i = 0
- while path == "" or os.path.exists(path):
- i += 1
- path = os.path.join(data_path, r"BackUpData\Back-up %s" % i)
- try:
- os.mkdir(path)
- except Exception as e:
- print("Error occured whilst making memory back up: %s" % e)
- return
- for filename in ["score_data.txt", "grav_coefficient.txt", "weight_coefficient.txt",
- "pad_0_memory.txt", "pad_1_memory.txt", "pad_2_memory.txt",
- "pad_3_memory.txt"]:
- file = os.path.join(data_path, filename)
- shutil.copy(file, path)
- print("Game data back-up created")
- return
- def savePadMemories():
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- with open(os.path.join(pad_memory_path, "pad_%s_memory.txt" % i), 'w') as file:
- for memory in pad.memory:
- file.write(str(memory.getData()) + "\n")
- with open(os.path.join(pad_memory_path, "weight_coefficient.txt"), 'w') as file:
- file.write(str(WeightData.current_weight))
- return
- def backUpPadMemories(path):
- if not os.path.exists(path):
- try:
- os.mkdir(path)
- except Exception as e:
- print("Error occurred whilst making memory back up: %s" % e)
- return
- padlist = Pads.padlist
- for i in range(len(padlist)):
- file = os.path.join(pad_memory_path, "pad_%s_memory.txt" % i)
- shutil.copy(file, path)
- file = os.path.join(pad_memory_path, "weight_coefficient.txt")
- shutil.copy(file, path)
- print("New directory created")
- return
- def loadPadMemories():
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- with open(os.path.join(pad_memory_path, "pad_%s_memory.txt" % i), 'r') as file:
- pad.memory = parseMemoryData(file.read())
- with open(os.path.join(pad_memory_path, "weight_coefficient.txt"), 'r') as file:
- WeightData.current_weight = float(file.read())
- return
- def parseMemoryData(string_data):
- list_data = []
- for line in string_data.split("\n"):
- if not line:
- continue
- item_data = line.replace("(", "").replace(")", "").split(", ")
- x_miss = float(item_data[0])
- y_miss = float(item_data[1])
- memory_tuple = MemoryTuple(x_miss, y_miss)
- memory_tuple.x_collision = float(item_data[2])
- memory_tuple.y_collision = float(item_data[3])
- memory_tuple.collision_i_angle = float(item_data[4])
- memory_tuple.collision_i_speed = float(item_data[5])
- memory_tuple.collision_f_angle = float(item_data[6])
- memory_tuple.collision_f_speed = float(item_data[7])
- memory_tuple.collision_grav_coef = float(item_data[8])
- list_data.append(memory_tuple)
- return list_data
- ############################################################
- #
- # A D A P T I V E A I
- #
- def updatePadTargets():
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- if pad.AI != 1:
- continue
- (x_target, y_target) = findBestApproximation(i)
- printData("%s: (%s, %s)" % (i, x_target, y_target))
- printMemory(i)
- pad.x_target = x_target
- pad.y_target = y_target
- printData("")
- return
- def handleAI():
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- if pad.AI == 0.5:
- pad.x_target = Balls.ball.x
- pad.y_target = Balls.ball.y
- elif pad.AI == 0:
- continue
- (padxmid, padymid) = getPadMidpoint(i)
- if not i % 2:
- if padymid < pad.y_target:
- pad.move(0, pad_speed * GameState.pad_speed_mult)
- elif padymid > pad.y_target:
- pad.move(0, -pad_speed * GameState.pad_speed_mult)
- else:
- if padxmid < pad.x_target:
- pad.move(pad_speed * GameState.pad_speed_mult, 0)
- elif padxmid > pad.x_target:
- pad.move(-pad_speed * GameState.pad_speed_mult, 0)
- return
- def findBestApproximation(padindex):
- printData("FINDING APPROXIMATION FOR PAD %s...\n" % padindex)
- pad = Pads.padlist[padindex]
- memory = pad.memory
- ball = Balls.ball
- if not memory:
- approximation = getPadMidpoint(padindex)
- return approximation
- collision_data = getCollisionData()
- (last_collision_x, last_collision_y, last_collision_i_angle,
- last_collision_i_speed, last_collision_f_angle,
- last_collision_f_speed, last_collision_grav_coef) = collision_data
- best_approx = 0
- strictness_coef = 1.03
- for memory_tuple in memory:
- (x_miss, y_miss, x_collision, y_collision, _, _, f_angle, _,
- collision_grav_coef) = memory_tuple.getData()
- (divergence, x_divergence, y_divergence, f_angular_divergence,
- grav_divergence) = calculateDivergence(memory_tuple, collision_data)
- approximation = (divergence, x_miss, y_miss)
- printData("\n\nPAD: %s" % padindex)
- printData("\nLAST COLLISION (X) = %s, CONSIDERED CASE (X) = %s" % (last_collision_x, x_collision))
- printData("pos_x DIVERGENCE: %s" % x_divergence)
- printData("\nLAST COLLISION (Y) = %s, CONSIDERED CASE (Y) = %s" % (last_collision_y, y_collision))
- printData("pos_y DIVERGENCE: %s" % y_divergence)
- printData("\nLAST COLLISION (fAngle) = %s, CONSIDERED CASE (fAngle) = %s" % (last_collision_f_angle, f_angle))
- printData("FINAL ANGLE DIVERGENCE: %s" % f_angular_divergence)
- printData("\nLAST COLLISION (grav) = %s, CONSIDERED CASE (grav) = %s" % (last_collision_grav_coef,
- collision_grav_coef))
- printData("\nTOTAL DIVERGENCE: %s\n\n" % divergence)
- if not best_approx:
- best_approx = approximation
- else:
- (least_divergence, _, _) = best_approx
- if divergence < least_divergence:
- best_approx = approximation
- (_, pos_xition, pos_yition) = best_approx
- approximation = (pos_xition, pos_yition)
- return approximation
- def calculateDivergence(memory_tuple, collision_data):
- (last_collision_x, last_collision_y, last_collision_i_angle,
- last_collision_i_speed, last_collision_f_angle,
- last_collision_f_speed, last_collision_grav_coef) = collision_data
- (x_miss, y_miss, x_collision, y_collision,
- i_angle, i_speed, f_angle, f_speed,
- collision_grav_coef) = memory_tuple.getData()
- pos_x_dif = abs(x_collision - last_collision_x)
- pos_y_dif = abs(y_collision - last_collision_y)
- i_angle_dif = getAngleDifference(i_angle, last_collision_i_angle)
- i_speed_dif = abs(i_speed - last_collision_i_speed)
- f_angle_dif = getAngleDifference(f_angle, last_collision_f_angle)
- f_speed_dif = abs(f_speed - last_collision_f_speed)
- grav_dif = abs(collision_grav_coef - last_collision_grav_coef)
- x_divergence = 100 * pos_x_dif / max_x_difference
- y_divergence = 100 * pos_y_dif / max_y_difference
- f_angular_divergence = 100 * f_angle_dif / max_angular_difference
- grav_divergence = 100 * grav_dif / max_gravity_difference
- #Apply weights.
- x_divergence *= WeightData.current_weight
- y_divergence *= WeightData.current_weight
- f_angular_divergence *= (1 - WeightData.current_weight)
- grav_divergence *= 0.5
- total_divergence = x_divergence + y_divergence + f_angular_divergence + grav_divergence
- divergence_data = (total_divergence, x_divergence, y_divergence, f_angular_divergence, grav_divergence)
- return divergence_data
- ############################################################
- #
- # G E N E T I C A L G O R I T H M
- #
- def generateWeights():
- WeightData.generation += 1
- current_generation = produceChildren()
- while len(current_generation) < population_size:
- current_generation.append(random.random())
- WeightData.current_generation = current_generation
- print("NEW GENERATION: %s" % current_generation)
- return
- def selectBestWeights():
- best_weights = []
- current_generation = WeightData.current_generation
- #Get the best three weights.
- for i in range(int(0.5 * population_size)):
- best_score = -1
- best_weight = -1
- for weight, score in WeightData.weight_scores.items():
- if score > best_score and weight in current_generation and weight not in best_weights:
- best_weight = weight
- best_score = score
- if best_weight != -1:
- best_weights.append(best_weight)
- return best_weights
- def testNextWeight():
- WeightData.current_weight_index += 1
- index = WeightData.current_weight_index
- WeightData.current_weight = WeightData.current_generation[index]
- return
- def produceChildren():
- best_weights = selectBestWeights()
- children = []
- for i in range(len(best_weights)):
- for j in range(i + 1, len(best_weights)):
- if len(children) == population_size:
- break
- child = averageWeights(best_weights[i], best_weights[j])
- children.append(child)
- return children
- def averageWeights(weight_1, weight_2):
- average_weight = (weight_1 + weight_2) / 2
- return average_weight
- def scoreWeightValue():
- current_weight = WeightData.current_weight
- current_collisions = WeightData.current_collisions
- WeightData.weight_scores[current_weight] = current_collisions
- printWeightScores()
- return
- def printWeightScores():
- weight_scores = WeightData.weight_scores
- for weight, score in weight_scores.items():
- print("Weight %.4f: %s" % (weight, score))
- return
- def beginNewTest():
- print("NEW TEST!")
- scoreWeightValue()
- WeightData.current_collisions = 0
- WeightData.current_game = 0
- if WeightData.current_weight_index < population_size - 1:
- WeightData.current_weight_index += 1
- index = WeightData.current_weight_index
- WeightData.current_weight = WeightData.current_generation[index]
- else:
- beginNewGeneration()
- padlist = Pads.padlist
- for i in range(len(padlist)):
- padlist[i].memory = []
- return
- def beginNewGeneration():
- print("NEW GENERATION!")
- generateWeights()
- WeightData.current_weight_index = 0
- WeightData.current_weight = WeightData.current_generation[0]
- return
- ############################################################
- #
- # S U R F A C E S F R O M F I L E S
- #
- def getPadFromFile(padname):
- pad = pygame.image.load(os.path.join(pad_image_path, padname + ".png")).convert_alpha()
- return pad
- def getSpeakerIcon(sound_level):
- scale = 0.12
- speaker = pygame.image.load(os.path.join(sound_level_path, "sound%s.png" % sound_level)).convert_alpha()
- speaker = scaleImage(speaker, scale)
- return speaker
- def getLetterIcon(letter):
- scale = 0.09
- info_button = pygame.image.load(os.path.join(letter_path, "letter_" + letter + ".png")).convert_alpha()
- info_button = scaleImage(info_button, scale)
- return info_button
- def getFastForwardIcon():
- scale = 0.2
- fast_forward = pygame.image.load(os.path.join(picture_path, "fast_forward.png")).convert_alpha()
- fast_forward = scaleImage(fast_forward, scale)
- return fast_forward
- def getFastForwardCover():
- fast_forward_cover = pygame.image.load(os.path.join(picture_path, "pyballcover.png")).convert_alpha()
- return fast_forward_cover
- def getGearIcon():
- scale = 0.1
- gear_button = pygame.image.load(os.path.join(picture_path, "gear.png")).convert_alpha()
- gear_button = scaleImage(gear_button, scale)
- return gear_button
- def getArrowIcon(direction):
- scale = 0.1
- arrow_button = pygame.image.load(os.path.join(picture_path, "arrow_%s.png" % direction)).convert_alpha()
- arrow_button = scaleImage(arrow_button, scale)
- return arrow_button
- def getSettingsMenu():
- settings_menu = pygame.image.load(os.path.join(picture_path, "settings_menu.png")).convert_alpha()
- return settings_menu
- def scaleImage(image, scale_factor):
- scaled_image = pygame.transform.smoothscale(image, (int(image.get_width() * scale_factor), int(image.get_height() * scale_factor)))
- return scaled_image
- ############################################################
- #
- # S E S S I O N I N I T I A L I S A T I O N
- #
- def initialiseMusic(sound_level):
- pygame.mixer.init()
- channel = pygame.mixer.Channel(0)
- song = "relaxing_space.wav"
- music = pygame.mixer.Sound(os.path.join(music_path, song))
- channel.play(music, -1)
- sound_level.addChannel(channel)
- return
- def initialiseFonts():
- FontTypes.Angelic_War_14_font = pygame.font.Font(pygame.font.match_font("Angelic War"), font_size_14)
- FontTypes.Angelic_War_40_font = pygame.font.Font(pygame.font.match_font("Angelic War"), font_size_40)
- FontTypes.Birth_Of_A_Hero_30_font = pygame.font.Font(pygame.font.match_font("Birth of a Hero"), font_size_30)
- FontTypes.Times_New_Roman_18_font = pygame.font.Font(pygame.font.match_font("Times New Roman"), font_size_18)
- FontTypes.Free_Sans_Bold_10_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_11)
- FontTypes.Free_Sans_Bold_12_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_12)
- FontTypes.Free_Sans_Bold_14_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_14)
- FontTypes.Free_Sans_Bold_16_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_16)
- FontTypes.Free_Sans_Bold_18_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_18)
- FontTypes.Free_Sans_Bold_30_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_30)
- FontTypes.Sergeant_SixPack_12_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_12)
- FontTypes.Sergeant_SixPack_14_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_14)
- FontTypes.Sergeant_SixPack_16_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_16)
- FontTypes.Sergeant_SixPack_18_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_18)
- FontTypes.Sergeant_SixPack_20_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_20)
- FontTypes.Sergeant_SixPack_22_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_22)
- FontTypes.Sergeant_SixPack_24_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_24)
- FontTypes.Sergeant_SixPack_26_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_26)
- return
- def initialiseSession():
- offset = 75
- padblue = Pad(offset - pad_width/2,
- window_height/2 - pad_height/2,
- getPadFromFile("padblue"))
- padgreen = Pad(window_width/2 - pad_height/2,
- window_height - offset - pad_width/2,
- pygame.transform.rotate(getPadFromFile("padgreen"), 90))
- padred = Pad(window_width - offset - pad_width/2,
- window_height/2 - pad_height/2,
- pygame.transform.rotate(getPadFromFile("padred"), 180))
- padorange = Pad(window_width/2 - pad_height/2,
- offset - pad_width/2,
- pygame.transform.rotate(getPadFromFile("padorange"), 270))
- Pads.padlist = [padblue, padgreen, padred, padorange]
- info_button.addSurface(getLetterIcon("i"))
- print_button.addSurface(getLetterIcon("t"))
- fast_forward.addSurface(getFastForwardIcon())
- gear_button.addSurface(getGearIcon())
- arrow_left.addSurface(getArrowIcon("left"))
- arrow_right.addSurface(getArrowIcon("right"))
- sound_level.addSurface(getSpeakerIcon(sound_level.current_level))
- FastForwardCover.surface = getFastForwardCover()
- SettingsMenu.default_surface = getSettingsMenu()
- ResetSettingsMenu()
- Launchers.launcher = Launcher(3, 0.001, red.toList(), cool_blue.toList())
- Launchers.launcher.launchBall()
- for i in range(4):
- sound_level.levels.append(getSpeakerIcon(i))
- sound_level.pos_x = 14
- sound_level.pos_y = 10
- initialiseMusic(sound_level)
- initialiseSettings()
- for option in SettingOptions.options:
- if "TOGGLE_GENETIC" in option.modes and option.active:
- option.switch()
- elif "TOGGLE_GRAVITY" in option.modes and option.active:
- option.switch()
- elif "ACCEL_BALL" in option.modes:
- while option.active != 0:
- option.switch()
- elif "FAST_PADS" in option.modes:
- while option.active != 0:
- option.switch()
- elif "AUTO_SAVE" in option.modes:
- while option.active != 0:
- option.switch()
- elif "TOGGLE_AI_PAD1" in option.modes:
- while option.active != 2:
- option.switch()
- elif "TOGGLE_AI_PAD2" in option.modes:
- while option.active != 2:
- option.switch()
- elif "TOGGLE_AI_PAD3" in option.modes:
- while option.active != 2:
- option.switch()
- elif "TOGGLE_AI_PAD4" in option.modes:
- while option.active != 2:
- option.switch()
- arrow_left.pos_x = (SettingsMenu.default_surface.get_width() / 2) - 20 - arrow_left.width / 2
- arrow_right.pos_x = (SettingsMenu.default_surface.get_width() / 2) + 20 - arrow_right.width / 2
- GameState.score_messages_list = sorted(score_message_thresholds.items(), key=lambda item:item[1])
- return
- def initialiseSettings():
- options = SettingOptions.options
- base = 100
- offset = 60
- options.extend([SettingOption(base + offset * 0, "Toggle Learning",
- "When enabled, the pads learn from their mistakes and " +
- "improve their behaviour over time",
- [2, "TOGGLE_LEARNING"]),
- SettingOption(base + offset * 1, "Toggle Genetic Algorithm",
- "When enabled, the adaptive function will be put through " +
- "continuous testing to optimise pad learning efficiency",
- [2, "TOGGLE_GENETIC"]),
- SettingOption(base + offset * 2, "Toggle Gravity",
- "When enabled, the ball will be attracted towards the gravity " +
- "ring in the centre of the window with a force determined by " +
- "the ring's gravity coefficient",
- [2, "TOGGLE_GRAVITY"]),
- SettingOption(base + offset * 3, "Set Gravity Coefficient",
- "Sets the coefficient that determines how strongly the ball is " +
- "attracted towards the gravity ring when gravity is enabled. " +
- "Enter any number between 0 and 100 or an invalid value to quit",
- [0, "SET_GRAV_COEF"]),
- SettingOption(base + offset * 4, "Set Weight Coefficient",
- "Adjusts the behaviour of the pads. Enter any floating value " +
- "between 0 and 1 in the shell after clicking this button or " +
- "an invalid value (e.g. -1) to exit without modifying the weight",
- [0, "SET_WEIGHT"]),
- SettingOption(base + offset * 0, "Save Pad Memory",
- "Saves the data in each of the pad's memories to an " +
- "external file, allowing the data to be preserved " +
- "between different instances of the game",
- [1, "SAVE_MEMORY"]),
- SettingOption(base + offset * 1, "Back-up Pad Memory",
- "Creates a new directory specified by a name you enter and " +
- "creates copies of the current external memory states of the " +
- "pads into the directory",
- [1, "BACK_UP_MEMORY"]),
- SettingOption(base + offset * 2, "Load Pad Memory",
- "Loads data from an external file containing previously " +
- "stored data for each of the pads",
- [1, "LOAD_MEMORY"]),
- SettingOption(base + offset * 3, "Reset Memory",
- "Resets the pads' memories, but leaves any data stored in " +
- "external files untouched",
- [1, "RESET_MEMORY"]),
- SettingOption(base + offset * 4, "Reset Scores",
- "Resets the scores for each of the pads",
- [1, "RESET_SCORES"]),
- SettingOption(base + offset * 0, "Toggle AI - Pad 1",
- "When fully or semi-enabled, the left pad plays with adaptive or " +
- "static behavioural patterns respectively. When disabled, the pad " +
- "is controlled by a human player using the keys I and K",
- [3, "TOGGLE_AI_PAD1"]),
- SettingOption(base + offset * 1, "Toggle AI - Pad 2",
- "When fully or semi-enabled, the bottom pad plays with adaptive or " +
- "static behavioural patterns respectively. When disabled, the pad " +
- "is controlled by a human player using the keys A and D",
- [3, "TOGGLE_AI_PAD2"]),
- SettingOption(base + offset * 2, "Toggle AI - Pad 3",
- "When fully or semi-enabled, the right pad plays with adaptive or " +
- "static behavioural patterns respectively. When disabled, the pad " +
- "is controlled by a human player using the numpad keys 8 and 2",
- [3, "TOGGLE_AI_PAD3"]),
- SettingOption(base + offset * 3, "Toggle AI - Pad 4",
- "When fully or semi-enabled, the top pad plays with adaptive or " +
- "static behavioural patterns respectively. When disabled, the pad " +
- "is controlled by a human player using the arrow keys LEFT and RIGHT",
- [3, "TOGGLE_AI_PAD4"]),
- SettingOption(base + offset * 4, "Toggle All AIs",
- "Cycles all the AI's through different AI states (ON, SEMI-, OFF)",
- [1, "TOGGLE_ALL_AI"]),
- SettingOption(base + offset * 0, "Mode - Accelerating Ball",
- "Off - the ball does not automatically accelerate [1] - the ball " +
- "accelerates at a low rate [2] - the ball accelerates moderately " +
- "fast [3] - the ball accelerates at a high rate",
- [4, "ACCEL_BALL"]),
- SettingOption(base + offset * 1, "Mode - Fast Pads",
- "When disabled, pads move at their normal rate. When enabled, " +
- "pads are able to move twice as fast",
- [2, "FAST_PADS"]),
- SettingOption(base + offset * 2, "Auto-saving",
- "Off - auto-saving does not happen at all [1] - one save " +
- "is executed every 100 rounds [2] - one save is executed " +
- "every 50 rounds [3] - one save is executed every 10 rounds",
- [4, "AUTO_SAVE"])])
- SettingOptions.options = options
- return
- ############################################################
- #
- # T E X T O U T P U T
- #
- def printData(data):
- if print_button.active:
- print(data)
- return
- def printMemory(padindex):
- pad = Pads.padlist[padindex]
- printData("Pad %s" % padindex)
- if not pad.memory:
- printData("EMPTY MEMORY")
- for memory_tuple in pad.memory:
- printData(memory_tuple.getData())
- printData("")
- return
- ############################################################
- #
- # R E N D E R I N G
- #
- def drawText(surface, pos_x, pos_y, font_type, font_size, colour, message):
- if info_button.active:
- renderText(surface, pos_x, pos_y, font_type, font_size, colour, message)
- return
- def drawLine(surface, colour, start_pos, end_pos, width):
- if info_button.active:
- pygame.draw.aaline(surface, colour, start_pos, end_pos, width)
- return
- def drawCircle(surface, colour, position, radius, width):
- if info_button.active:
- pygame.draw.circle(surface, colour, position, radius, width)
- return
- def drawRect(surface, colour, area, width):
- if info_button.active:
- pygame.draw.rect(surface, colour, area, width)
- return
- def circsAtTargets(screen):
- padlist = Pads.padlist
- collision_data = getCollisionData()
- (last_collision_x, last_collision_y, last_collision_i_angle,
- last_collision_i_speed, last_collision_f_angle,
- last_collision_f_speed, _) = collision_data
- drawRect(screen, purple.toList(), [last_collision_x - 3,
- last_collision_y - 3,
- 6, 6], 1)
- colours = [cool_blue.toList(), green.toList(), red.toList(), orange.toList()]
- miss_circ_radius = 4
- more_tolerant_circ_radius = miss_circ_radius * 75
- less_tolerant_circ_radius = miss_circ_radius * 5
- displaying_text = False
- for i in range(len(padlist)):
- pad = padlist[i]
- if not pad.AI == 1:
- continue
- drawCircle(screen, colours[i], (int(pad.x_target), int(pad.y_target)), 5, 1)
- for memory_tuple in pad.memory:
- (x_miss, y_miss, x_collision, y_collision,
- i_angle, i_speed, f_angle, f_speed,
- collision_grav_coef) = memory_tuple.getData()
- colour = [cool_blue.toList(), green.toList(), red.toList(), orange.toList()][i]
- drawCircle(Screen.screen, colour, (int(x_miss), int(y_miss)), miss_circ_radius, 1)
- scale = 20
- x_move = f_speed * math.cos(degToRad(f_angle)) * scale
- y_move = f_speed * -math.sin(degToRad(f_angle)) * scale
- drawLine(screen, colour, (int(x_miss), int(y_miss)), (int(x_miss + x_move), int(y_miss + y_move)), 4)
- within_bounds = cursorWithinBounds(x_miss - more_tolerant_circ_radius / 2,
- x_miss + more_tolerant_circ_radius / 2,
- y_miss - more_tolerant_circ_radius / 2,
- y_miss + more_tolerant_circ_radius / 2)
- if within_bounds:
- drawLine(screen, colour, (int(x_collision), int(y_collision)), (int(x_miss), int(y_miss)), 4)
- within_bounds = cursorWithinBounds(x_miss - less_tolerant_circ_radius / 2,
- x_miss + less_tolerant_circ_radius / 2,
- y_miss - less_tolerant_circ_radius / 2,
- y_miss + less_tolerant_circ_radius / 2)
- if within_bounds and not displaying_text:
- displaying_text = True
- divergence_data = calculateDivergence(memory_tuple, collision_data)
- (total_divergence, x_divergence, y_divergence, f_angular_divergence,
- grav_divergence) = divergence_data
- total_divergence_string = "Total divergence: %.2f" % total_divergence
- x_divergence_string = "X divergence: %.2f" % x_divergence
- y_divergence_string = "Y divergence: %.2f" % y_divergence
- f_angular_divergence_string = "Angular divergence: %.2f" % f_angular_divergence
- grav_divergence_string = "Gravity divergence: %.2f" % grav_divergence
- if displaying_text:
- drawText(Screen.screen, window_width / 2, 200, "Free Sans Bold", font_size_16, white.toList(), total_divergence_string)
- drawText(Screen.screen, window_width / 2, 210, "Free Sans Bold", font_size_16, white.toList(), x_divergence_string)
- drawText(Screen.screen, window_width / 2, 220, "Free Sans Bold", font_size_16, white.toList(), y_divergence_string)
- drawText(Screen.screen, window_width / 2, 230, "Free Sans Bold", font_size_16, white.toList(), f_angular_divergence_string)
- drawText(Screen.screen, window_width / 2, 240, "Free Sans Bold", font_size_16, white.toList(), grav_divergence_string)
- return
- def renderText(surface, xpos, ypos, font_type, font_size, font_colour, message, fromCentre=True):
- if font_type.lower() == "angelic war" and font_size == font_size_14:
- font = FontTypes.Angelic_War_14_font
- elif font_type.lower() == "angelic war" and font_size == font_size_40:
- font = FontTypes.Angelic_War_40_font
- elif font_type.lower() == "times new roman" and font_size == font_size_18:
- font = FontTypes.Times_New_Roman_18_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_11:
- font = FontTypes.Free_Sans_Bold_10_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_12:
- font = FontTypes.Free_Sans_Bold_12_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_14:
- font = FontTypes.Free_Sans_Bold_14_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_16:
- font = FontTypes.Free_Sans_Bold_16_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_18:
- font = FontTypes.Free_Sans_Bold_18_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_30:
- font = FontTypes.Free_Sans_Bold_30_font
- elif font_type.lower() == "birth of a hero" and font_size == font_size_30:
- font = FontTypes.Birth_Of_A_Hero_30_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_12:
- font = FontTypes.Sergeant_SixPack_12_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_14:
- font = FontTypes.Sergeant_SixPack_14_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_16:
- font = FontTypes.Sergeant_SixPack_16_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_18:
- font = FontTypes.Sergeant_SixPack_18_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_20:
- font = FontTypes.Sergeant_SixPack_20_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_22:
- font = FontTypes.Sergeant_SixPack_22_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_24:
- font = FontTypes.Sergeant_SixPack_24_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_26:
- font = FontTypes.Sergeant_SixPack_26_font
- else:
- path = pygame.font.match_font(font_type)
- font = pygame.font.Font(path, font_size)
- print("Font not found")
- if fromCentre:
- (width, height) = font.size(message)
- xpos -= width / 2
- ypos -= height / 2
- textsurface = font.render(message, True, font_colour)
- surface.blit(textsurface, (xpos, ypos))
- return
- def drawObjects(screen):
- padblue = Pads.padlist[0]
- padgreen = Pads.padlist[1]
- padred = Pads.padlist[2]
- padorange = Pads.padlist[3]
- screen.blit(padblue.surface, (padblue.x, padblue.y))
- screen.blit(padorange.surface, (padorange.x, padorange.y))
- screen.blit(padred.surface, (padred.x, padred.y))
- screen.blit(padgreen.surface, (padgreen.x, padgreen.y))
- ball = Balls.ball
- ballcirc = pygame.draw.circle(screen, white.toList(), (int(ball.x - ball_radius/2), int(ball.y - ball_radius/2)), ball_radius, 1)
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- (padxmid, padymid) = getPadMidpoint(i)
- mid_offset = 5
- if i == 0:
- padxmid -= mid_offset
- elif i == 1:
- padymid += mid_offset
- elif i == 2:
- padxmid += mid_offset
- else:
- padymid -= mid_offset
- renderText(screen, padxmid, padymid, "Free Sans Bold", font_size_18, white.toList(), str(pad.score))
- controller = {1:"ADAPTIVE COMP", 0.5:"STATIC COMP", 0:"HUMAN"}[pad.AI]
- offset = 5
- padxmin = pad.x
- padxmax = pad.x + pad_width * ((i+1) % 2) + pad_height * (i % 2)
- padymin = pad.y
- padymax = pad.y + pad_height * ((i+1) % 2) + pad_width * (i % 2)
- if i == 0:
- x_text = padxmin - offset
- y_text = padymin - offset
- elif i == 1:
- x_text = padxmin - offset
- y_text = padymax + offset
- elif i == 2:
- x_text = padxmax + offset
- y_text = padymin - offset
- else:
- x_text = padxmin - offset
- y_text = padymin - offset
- renderText(screen, x_text, y_text, "Free Sans Bold", font_size_11, [35,35,35], controller)
- drawLauncher(screen)
- drawSpeaker(screen)
- drawPauseButton(screen)
- drawLetterButton("i", screen)
- drawLetterButton("t", screen)
- drawFastForwardButton(screen)
- drawGearButton(screen)
- drawWeightInfo(screen)
- drawScore(screen)
- drawScoreMessage(screen)
- gravity_ring.construct()
- return
- def drawWeightInfo(screen):
- current_weight = "Weight: %.4f" % WeightData.current_weight
- current_collisions = "Current Score: %s" % WeightData.current_collisions
- current_game = "Current Game: %s" % WeightData.current_game
- current_generation = "Generation: %s" % WeightData.generation
- renderText(screen, window_width - 110, 20, "Free Sans Bold", font_size_16, white.toList(), current_weight, False)
- if GameState.toggle_genetic:
- renderText(screen, window_width - 110, 30, "Free Sans Bold", font_size_16, white.toList(), current_collisions, False)
- renderText(screen, window_width - 110, 40, "Free Sans Bold", font_size_16, white.toList(), current_game, False)
- renderText(screen, window_width - 110, 50, "Free Sans Bold", font_size_16, white.toList(), current_generation, False)
- return
- def drawFastForwardCover(screen, ticks, real_ticks):
- if not GameState.fast_forward_cover_loaded:
- loadFastForwardCover()
- elif not real_ticks % 500:
- loadFastForwardCover()
- drawSpeaker(screen)
- drawPauseButton(screen)
- drawFastForwardButton(screen)
- ticks = "Ticks: %s" % ticks
- renderText(screen, window_width / 2, 300, "Free Sans Bold", font_size_18, white.toList(), ticks)
- current_weight = "Weight: %.4f" % WeightData.current_weight
- current_collisions = "Current Score: %s" % WeightData.current_collisions
- current_game = "Current Game: %s" % WeightData.current_game
- current_generation = "Generation: %s" % WeightData.generation
- renderText(screen, window_width / 2, 320, "Free Sans Bold", font_size_18, white.toList(), current_weight)
- if GameState.toggle_genetic:
- renderText(screen, window_width / 2, 335, "Free Sans Bold", font_size_18, white.toList(), current_collisions)
- renderText(screen, window_width / 2, 350, "Free Sans Bold", font_size_18, white.toList(), current_game)
- renderText(screen, window_width / 2, 365, "Free Sans Bold", font_size_18, white.toList(), current_generation)
- padlist = Pads.padlist
- pad_green_score = "Green Pad Score: %s" % padlist[0].score
- pad_blue_score = "Blue Pad Score: %s" % padlist[1].score
- pad_orange_score = "Orange Pad Score: %s" % padlist[2].score
- pad_red_score = "Red Pad Score: %s" % padlist[3].score
- renderText(screen, window_width / 2, 420, "Free Sans Bold", font_size_18, white.toList(), pad_green_score)
- renderText(screen, window_width / 2, 435, "Free Sans Bold", font_size_18, white.toList(), pad_blue_score)
- renderText(screen, window_width / 2, 450, "Free Sans Bold", font_size_18, white.toList(), pad_orange_score)
- renderText(screen, window_width / 2, 465, "Free Sans Bold", font_size_18, white.toList(), pad_red_score)
- offset = 6
- update_area = [200 + offset, 200 + offset, 300 - offset * 2, 500 - offset * 2]
- pygame.display.update(update_area)
- return
- def drawLauncher(screen):
- launcher = Launchers.launcher
- launcher_colour = launcher.getColour()
- launcher_radius = launcher.getRadius()
- xlauncher = int(launcher.x)
- ylauncher = int(launcher.y)
- if not pause_button.active:
- launcher.coolDown()
- pygame.draw.circle(screen, launcher_colour, (xlauncher, ylauncher), launcher_radius, 1)
- return
- def drawSpeaker(screen):
- speaker_icon = sound_level.surface
- sound_level.setCoverTransparency()
- xspeaker = sound_level.pos_x
- yspeaker = sound_level.pos_y
- screen.blit(speaker_icon, (xspeaker, yspeaker))
- screen.blit(sound_level.cover, (xspeaker, yspeaker))
- return
- def drawPauseButton(screen):
- pause_surface.fill(black.toList())
- if pause_button.active:
- pause_surface.set_alpha(255 * 0.85)
- fill_val = 0
- elif pause_button.hovering:
- pause_surface.set_alpha(255 * 0.85)
- fill_val = 1
- else:
- pause_surface.set_alpha(255 * 0.5)
- fill_val = 1
- pygame.draw.rect(pause_surface, white.toList(), [0,
- 0,
- pause_button.width * (1/3),
- pause_button.height], fill_val)
- pygame.draw.rect(pause_surface, white.toList(), [pause_button.width * (2/3),
- 0,
- pause_button.width * (1/3),
- pause_button.height], fill_val)
- screen.blit(pause_surface, (pause_button.pos_x, pause_button.pos_y))
- return
- def drawLetterButton(letter, screen):
- if letter.lower() == "i":
- button = info_button
- elif letter.lower() == "t":
- button = print_button
- else:
- assert False
- letter_surface = button.surface
- letter_cover = button.cover
- if button.active:
- letter_cover.set_alpha(0)
- elif button.hovering:
- letter_cover.set_alpha(255 * 0.25)
- else:
- letter_cover.set_alpha(255 * 0.5)
- x_surface = button.pos_x
- y_surface = button.pos_y
- screen.blit(letter_surface, (x_surface, y_surface))
- screen.blit(letter_cover, (x_surface, y_surface))
- return
- def drawFastForwardButton(screen):
- surface = fast_forward.surface
- cover = fast_forward.cover
- if fast_forward.active:
- cover.set_alpha(0)
- elif fast_forward.hovering:
- cover.set_alpha(255 * 0.25)
- else:
- cover.set_alpha(255 * 0.5)
- x_surface = fast_forward.pos_x
- y_surface = fast_forward.pos_y
- screen.blit(surface, (x_surface, y_surface))
- screen.blit(cover, (x_surface, y_surface))
- return
- def drawGearButton(screen):
- surface = gear_button.surface
- cover = gear_button.cover
- if gear_button.active:
- cover.set_alpha(0)
- elif gear_button.hovering:
- cover.set_alpha(255 * 0.25)
- else:
- cover.set_alpha(255 * 0.5)
- x_surface = gear_button.pos_x
- y_surface = gear_button.pos_y
- screen.blit(surface, (x_surface, y_surface))
- screen.blit(cover, (x_surface, y_surface))
- return
- def drawSettingsMenu(screen):
- surface = SettingsMenu.modified_surface
- for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
- if i >= len(SettingOptions.options):
- break
- option = SettingOptions.options[i]
- option.construct()
- surface.blit(option.surface, (option.pos_x, option.pos_y))
- for arrow in [arrow_left, arrow_right]:
- surface.blit(arrow.surface, (arrow.pos_x, arrow.pos_y))
- x_text = surface.get_width() / 2 + 1
- y_text = arrow_left.pos_y + 6
- renderText(surface, x_text, y_text, "Free Sans Bold", font_size_14, white.toList(), str(SettingsMenu.current_page))
- x_surface = (window_width - surface.get_width()) / 2
- y_surface = (window_height - surface.get_height()) / 2
- screen.blit(surface, (x_surface, y_surface))
- ResetSettingsMenu()
- return
- def drawScore(screen):
- score = GameState.collision_count
- max_score = GameState.max_count
- if max_score:
- ratio = 1 - score / max_score
- else:
- ratio = 1
- colour = crossColours(red, green, ratio)
- offset = 25
- renderText(screen, window_width / 2, window_height / 2 + offset, "Free Sans Bold", font_size_16, colour.toList(), str(score))
- renderText(screen, window_width / 2, window_height / 2 - offset, "Free Sans Bold", font_size_16, white.toList(), str(max_score))
- return
- def drawScoreMessage(screen):
- score_message = ""
- message_threshold = 0
- message_frames_left = 0
- alpha = 255
- for i in range(len(GameState.score_messages_list)):
- message = GameState.score_messages_list[i][0]
- frames_left = GameState.score_messages[message]
- if frames_left:
- alpha = int(255 * frames_left / frames_per_score_message)
- GameState.score_messages[message] = frames_left - 1
- score_message = message
- message_index = i
- message_frames_left = frames_left
- if score_message:
- colour = crossColours(crossColours(green, white, message_index / 7), black, message_frames_left / frames_per_score_message)
- font_size = 12 + i * 2
- renderText(screen, window_width / 2, window_height / 2 - 100, "Sergeant SixPack", font_size, colour.toList(), score_message)
- return
- def handleScoreMessages():
- score = GameState.collision_count
- for message, threshold in score_message_thresholds.items():
- if score == threshold:
- GameState.score_messages[message] = frames_per_score_message
- return
- ############################################################
- #
- # H A N D L E M E N U S A N D C O V E R S
- #
- def ResetSettingsMenu():
- SettingsMenu.modified_surface = SettingsMenu.default_surface.copy()
- return
- def setFastForwardCoverStatus(boolean):
- GameState.fast_forward_cover_loaded = boolean
- if boolean:
- sound_level.pos_x = 214
- sound_level.pos_y = 210
- pause_button.pos_x = 250
- pause_button.pos_y = 214
- fast_forward.pos_x = 278
- fast_forward.pos_y = 211
- else:
- sound_level.pos_x = 10
- sound_level.pos_y = 10
- pause_button.pos_x = 50
- pause_button.pos_y = 14
- fast_forward.pos_x = 79
- fast_forward.pos_y = 11
- return
- def loadFastForwardCover():
- cover = FastForwardCover.surface
- Screen.screen.blit(cover, (0, 0))
- pygame.display.flip()
- setFastForwardCoverStatus(True)
- return
- ############################################################
- #
- # K E Y B O A R D H A N D L I N G
- #
- def keyPressed(key):
- if key == K_ESCAPE:
- if gear_button.active:
- gear_button.switch()
- pause_button.switch()
- else:
- GameState.done = True
- return
- if gear_button.active:
- if key == K_RIGHT:
- SettingsMenu.current_page = min(SettingsMenu.total_pages,
- SettingsMenu.current_page + 1)
- elif key == K_LEFT:
- SettingsMenu.current_page = max(1, SettingsMenu.current_page - 1)
- if key == K_1:
- sound_level.changeSoundLevel()
- return
- elif key == K_2:
- pause_button.switch()
- return
- elif key == K_3:
- fast_forward.switch()
- if not fast_forward.active:
- setFastForwardCoverStatus(False)
- return
- elif key == K_4 and not fast_forward.active:
- info_button.switch()
- return
- elif key == K_5 and not fast_forward.active:
- print_button.switch()
- return
- elif key == K_6 and not fast_forward.active:
- gear_button.switch()
- pause_button.active = gear_button.active
- return
- if gear_button.active:
- return
- if key == K_a:
- KeyBools.aPressed = True
- elif key == K_d:
- KeyBools.dPressed = True
- elif key == K_i:
- KeyBools.iPressed = True
- elif key == K_k:
- KeyBools.kPressed = True
- elif key == K_LEFT:
- KeyBools.leftPressed = True
- elif key == K_RIGHT:
- KeyBools.rightPressed = True
- elif key == K_KP2:
- KeyBools.kp2Pressed = True
- elif key == K_KP8:
- KeyBools.kp8Pressed = True
- return
- def keyReleased(key):
- if key == K_a:
- KeyBools.aPressed = False
- elif key == K_d:
- KeyBools.dPressed = False
- elif key == K_i:
- KeyBools.iPressed = False
- elif key == K_k:
- KeyBools.kPressed = False
- elif key == K_LEFT:
- KeyBools.leftPressed = False
- elif key == K_RIGHT:
- KeyBools.rightPressed = False
- elif key == K_KP2:
- KeyBools.kp2Pressed = False
- elif key == K_KP8:
- KeyBools.kp8Pressed = False
- return
- ############################################################
- #
- # M O T I O N H A N D L I N G
- #
- def handleMotion():
- padblue = Pads.padlist[0]
- padgreen = Pads.padlist[1]
- padred = Pads.padlist[2]
- padorange = Pads.padlist[3]
- if not padgreen.AI:
- if KeyBools.aPressed:
- padgreen.move(-pad_speed * GameState.pad_speed_mult, 0)
- if KeyBools.dPressed:
- padgreen.move(pad_speed * GameState.pad_speed_mult, 0)
- if not KeyBools.aPressed and not KeyBools.dPressed:
- padgreen.moving = (0, 0)
- if not padblue.AI:
- if KeyBools.iPressed:
- padblue.move(0, -pad_speed * GameState.pad_speed_mult)
- if KeyBools.kPressed:
- padblue.move(0, pad_speed * GameState.pad_speed_mult)
- if not KeyBools.iPressed and not KeyBools.kPressed:
- padblue.moving = (0, 0)
- if not padorange.AI:
- if KeyBools.leftPressed:
- padorange.move(-pad_speed * GameState.pad_speed_mult, 0)
- if KeyBools.rightPressed:
- padorange.move(pad_speed * GameState.pad_speed_mult, 0)
- if not KeyBools.leftPressed and not KeyBools.rightPressed:
- padorange.moving = (0, 0)
- if not padred.AI:
- if KeyBools.kp8Pressed:
- padred.move(0, -pad_speed * GameState.pad_speed_mult)
- if KeyBools.kp2Pressed:
- padred.move(0, pad_speed * GameState.pad_speed_mult)
- if not KeyBools.kp8Pressed and not KeyBools.kp2Pressed:
- padred.moving = (0, 0)
- Balls.ball.move()
- gravity_ring.pumpCircs()
- gravity_ring.modifyRadius()
- gravity_ring.handleGravFocals()
- return
- ############################################################
- #
- # C O L L I S I O N H A N D L I N G
- #
- def collisionHandling(ball):
- collision = False
- (x_translate, y_translate) = ball.getVelocityComponents()
- xnew = ball.x + x_translate
- ynew = ball.y + y_translate
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- if ball.last_pad == pad:
- continue
- (padxmid, padymid) = getPadMidpoint(i)
- padxmin = pad.x
- padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
- padymin = pad.y
- padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
- inwards_reflect_normal = i * 90
- side_reflect_normal = (i+1) % 4 * 90
- tolerance = 0.01 * pad_height
- if objectWithinBounds(xnew, ynew, padxmin, padxmax, padymin, padymax):
- collision = pad
- if i == 0 or i == 2:
- #'side_reflect_normal' if ball collides with bottom / top side.
- #'inwards_reflect_normal' if ball collides with inwards-facing surface
- if (ynew > padymax - tolerance or ynew < padymin + tolerance):
- if i == 0 and xnew < padxmax and xnew > padxmax - tolerance:
- normal = inwards_reflect_normal
- elif i == 0:
- normal = side_reflect_normal
- elif i == 2 and xnew > padxmin and xnew < padxmin + tolerance:
- normal = inwards_reflect_normal
- else:
- normal = side_reflect_normal
- else:
- normal = inwards_reflect_normal
- reflected_angle = (plane_reflect_angles[normal] - ball.angle) % 360
- else:
- #'side_reflect_normal' if ball collides with left / right side.
- #'inwards_reflect_normal' if ball collides with inwards-facing surface
- if (xnew > padxmax - tolerance or xnew < padxmin + tolerance) and (i == 1 and ynew > padymin or i == 3 and ynew < padymax):
- if i == 1 and ynew > padymin and ynew < padymin + tolerance:
- normal = inwards_reflect_normal
- elif i == 1:
- normal = side_reflect_normal
- elif i == 3 and ynew < padymax and ynew > padymax - tolerance:
- normal = inwards_reflect_normal
- else:
- normal = side_reflect_normal
- else:
- normal = inwards_reflect_normal
- reflected_angle = (plane_reflect_angles[normal] - ball.angle) % 360
- break
- if collision:
- if WeightData.current_game < games_per_test:
- WeightData.current_collisions += 1
- GameState.collision_count += 1
- handleScoreMessages()
- if GameState.collision_count > GameState.max_count:
- GameState.max_count = GameState.collision_count
- modifier = 0.1
- (x_padmove, y_padmove) = collision.moving
- initial_angle = ball.angle
- initial_velocity = ball.velocity
- ball.angle = reflected_angle
- (x_ballmove, y_ballmove) = ball.getVelocityComponents()
- final_angle = angleFromComponents(x_ballmove + x_padmove * modifier, y_ballmove + y_padmove * modifier)
- final_velocity = velocityFromComponents(x_ballmove + x_padmove * modifier, y_ballmove + y_padmove * modifier)
- ball.angle = final_angle
- ball.velocity = final_velocity
- updateCollisionData(ball.x, ball.y, initial_angle, initial_velocity, final_angle, final_velocity)
- ball.last_pad = collision
- return
- def updateCollisionData(x, y, i_angle, i_speed, f_angle, f_speed):
- Collision.xpos = x
- Collision.ypos = y
- Collision.initial_angle = i_angle
- Collision.initial_speed = i_speed
- Collision.final_angle = f_angle
- Collision.final_speed = f_speed
- if gravity_ring.active:
- Collision.grav_coef = gravity_ring.grav_coef
- else:
- Collision.grav_coef = 0
- updatePadTargets()
- return
- def getCollisionData():
- collision_data = (Collision.xpos, Collision.ypos, Collision.initial_angle,
- Collision.initial_speed, Collision.final_angle,
- Collision.final_speed, Collision.grav_coef)
- return collision_data
- ############################################################
- #
- # B A L L G E N E R A T I O N
- #
- def genNewBall():
- if WeightData.current_game < games_per_test:
- WeightData.current_game += 1
- elif GameState.toggle_genetic:
- beginNewTest()
- velocity = random.random()*0.3+0.3
- angle = 0
- while angle % 90 < 20:
- angle = random.randrange(360)
- ball = Ball(window_width/2, window_height/2, velocity, angle)
- Balls.ball = ball
- printData("ANGLE BALL LAUNCHED AT: %s" % angle)
- updateCollisionData(ball.x, ball.y, angle, velocity, angle, velocity)
- return
- ############################################################
- #
- # M O U S E I N P U T
- #
- def cursorWithinBounds(low_x, high_x, low_y, high_y):
- (mouse_x, mouse_y) = pygame.mouse.get_pos()
- if low_x <= mouse_x and mouse_x <= high_x:
- if low_y <= mouse_y and mouse_y <= high_y:
- return True
- return False
- def hoverHandler(icon):
- hovering_over = cursorWithinBounds(icon.pos_x, icon.pos_x + icon.width,
- icon.pos_y, icon.pos_y + icon.height)
- return hovering_over
- def setHover(icon):
- if hoverHandler(icon):
- icon.hovering = True
- else:
- icon.hovering = False
- return
- def passiveMouseHandler():
- #(mouse_x, mouse_y) = pygame.mouse.get_pos()
- setHover(sound_level)
- setHover(pause_button)
- setHover(info_button)
- setHover(print_button)
- setHover(fast_forward)
- setHover(gear_button)
- setHover(arrow_left)
- setHover(arrow_right)
- if gear_button.active:
- x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
- y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
- for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
- if i >= len(SettingOptions.options):
- break
- option = SettingOptions.options[i]
- option.pos_x += x_offset
- option.pos_y += y_offset
- setHover(option)
- option.pos_x -= x_offset
- option.pos_y -= y_offset
- return
- def activeMouseHandler(position, button):
- if hoverHandler(sound_level):
- sound_level.changeSoundLevel()
- if hoverHandler(pause_button):
- pause_button.switch()
- if hoverHandler(info_button):
- info_button.switch()
- if hoverHandler(print_button):
- print_button.switch()
- if hoverHandler(fast_forward):
- fast_forward.switch()
- if not fast_forward.active:
- setFastForwardCoverStatus(False)
- if hoverHandler(gear_button):
- gear_button.switch()
- pause_button.active = gear_button.active
- if gear_button.active:
- x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
- y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
- for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
- if i >= len(SettingOptions.options):
- break
- option = SettingOptions.options[i]
- option.pos_x += x_offset
- option.pos_y += y_offset
- if hoverHandler(option):
- option.switch()
- option.pos_x -= x_offset
- option.pos_y -= y_offset
- for arrow in [arrow_left, arrow_right]:
- arrow.pos_x += x_offset
- arrow.pos_y += y_offset
- if hoverHandler(arrow):
- if arrow == arrow_left:
- SettingsMenu.current_page = max(1, SettingsMenu.current_page - 1)
- else:
- SettingsMenu.current_page = min(SettingsMenu.total_pages, SettingsMenu.current_page + 1)
- arrow.pos_x -= x_offset
- arrow.pos_y -= y_offset
- return
- ############################################################
- #
- # S E T T I N G S H A N D L I N G
- #
- def SettingOptionsHandler():
- for option in SettingOptions.options:
- option.adjustSize()
- option.adjustColour()
- return
- ############################################################
- #
- # M A I N G A M E L O O P
- #
- def main():
- pygame.init()
- screen = pygame.display.set_mode((window_width, window_height), 0, 32)
- Screen.screen = screen
- pygame.display.set_caption("PyBall")
- initialiseFonts()
- initialiseSession()
- beginNewGeneration()
- text_colour_val = 255
- demonstration_begun = False
- return_pressed = False
- ticks = 0
- real_ticks = 0
- while not GameState.done:
- real_ticks += 1
- screen.fill(black.toList())
- sound_level.adjustVolume()
- passiveMouseHandler()
- SettingOptionsHandler()
- events = pygame.event.get()
- for e in events:
- if e.type == QUIT:
- GameState.done = True
- break
- elif not demonstration_begun and e.type == KEYDOWN and e.key == K_RETURN:
- return_pressed = True
- elif e.type == KEYDOWN and demonstration_begun:
- keyPressed(e.key)
- elif e.type == KEYUP and demonstration_begun:
- keyReleased(e.key)
- elif e.type == MOUSEBUTTONDOWN:
- activeMouseHandler(e.pos, e.button)
- if text_colour_val:
- text_colour = [text_colour_val, text_colour_val, text_colour_val]
- renderText(screen, window_width / 2, 120, "Angelic War", font_size_40, text_colour, "PyBall")
- renderText(screen, window_width / 2, 160, "Angelic War", font_size_14, text_colour, "Created By Tag")
- if return_pressed:
- text_colour_val -= 1
- drawObjects(screen)
- pygame.display.flip()
- else:
- demonstration_begun = True
- if gear_button.active:
- pause_button.active = True
- fast_forward.active = False
- if info_button.active:
- circsAtTargets(screen)
- drawObjects(screen)
- drawSettingsMenu(screen)
- renderText(screen, window_width / 2, 160, "Birth Of A Hero", font_size_30, white.toList(), "S E T T I N G S")
- pygame.display.flip()
- elif pause_button.active and not fast_forward.active:
- renderText(screen, window_width / 2, 160, "Birth Of A Hero", font_size_30, white.toList(), "P A U S E D")
- if info_button.active:
- circsAtTargets(screen)
- drawObjects(screen)
- pygame.display.flip()
- elif fast_forward.active and not pause_button.active:
- ticks += 1
- drawFastForwardCover(screen, ticks, real_ticks)
- handleAI()
- handleMotion()
- elif fast_forward.active and pause_button.active:
- drawFastForwardCover(screen, ticks, real_ticks)
- else:
- if gravity_ring.active:
- renderText(screen, window_width / 2, 160, "Birth Of A Hero", font_size_30, white.toList(), "G R A V I T Y A C T I V E")
- ticks += 1
- handleAI()
- handleMotion()
- if info_button.active:
- circsAtTargets(screen)
- drawObjects(screen)
- pygame.display.flip()
- if GameState.done:
- pygame.quit()
- return
- if __name__ == "__main__":
- main()
- import time
- import random
- import pygame
- from pygame.locals import *
- ###################################
- # P Y B A L L
- # PyBall - A Pong-Based Adaptive AI Demonstration / Game
- # Created by TAGC
- # Last update: 16/03/2012
- # Description: This program is developed as part of an
- # assessed project at Imperial College, London
- # to demonstrate the use of AI in gaming and
- # contrast the use of static and dynamic
- # AI techniques to allow further analysis into
- # how different kinds of AI behaviour can affect
- # replayability and game immersion
- # Notes: This script will only run if it comes as part
- # of the "PyBallStuff" package. This package
- # contains the audio, image and font files used
- # by this program
- ###################################
- # I N S T A L L A T I O N
- # This script is written in Python 3.2 and requires a Python 3
- # interpreter to run. This can be obtained from
- # http://www.python.org/download/ (it is strongly recommended
- # that the user obtains the 32-bit version even if they have
- # a 64-bit operating system)
- # This script also requires an installation of the python
- # module "PyGame" available at
- # http://www.pygame.org/download.shtml (again, it is strongly
- # recommended that the user obtains the 32-bit version)
- # In addition to these, the user will need to install the
- # following fonts:
- # > "Birth of a Hero"
- # > "Angelic War"
- # > "Hawaii Lover"
- # > "Sergeant SixPack"
- # These are all available at http://www.1001freefonts.com/
- # if the user does not possess the "Fonts" folder included
- # inside the folder "PyBallStuff"
- # The "PyBallStuff" folder must be placed in the
- # "C:/Python32" directory
- ###################################
- # L A U N C H I N G
- # To execute the program, click on "Run" on the top bar
- # of the interpreter and select "Run Module" or press F5
- ###################################
- # C O N T R O L S
- # Special Controls
- ###################
- # "Enter" - begins the game after the program launches
- # "Escape" - if the user has the Settings Menu open, this
- # exits that screen, otherwise it quits the
- # programs
- # Setting Controls
- ###################
- # "1" - Toggles through the sound level settings
- # "2" - Pauses / unpauses the program
- # "3" - Toggles Fast Forward mode on and off; when
- # this mode is active, the logic of the game
- # runs slightly faster
- # "4" - Toggles Graphical Information mode on and off;
- # when on, extra objects are rendered to the
- # screen to display more information on the
- # states of the pads' memories, etc
- # "5" - Toggles Textual Information mode on and off;
- # when on, information on the game is printed
- # out to the console
- # "6" - Opens or closes the Settings Menu
- # "Left" - when the Settings Menu is open, this displays
- # the previous page of options (if not already
- # there)
- # "Right" - when the Settings Menu is open, this displays
- # the next page of options (if not already
- # there)
- # Pad Controls
- ###############
- # "A" - if the green pad is under human control, moves
- # that pad left
- # "D" - if the green pad is under human control, moves
- # that pad right
- # "I" - if the blue pad is under human control, moves
- # that pad up
- # "K" - if the blue pad is under human control, moves
- # that pad down
- # "Left" - if the red pad is under human control, moves
- # that pad left
- # "Right" - if the red pad is under human control, moves
- # that pad right
- # "Num 8" - if the orange pad is under human control, moves
- # that pad up
- # "Num 2" - if the orange pad is under human control, moves
- # that pad down
- window_width = 700
- window_height = 700
- pad_width = 25
- pad_height = 100
- ball_radius = 2
- corner_buffer_coef = 0.20
- population_size = 20
- games_per_test = 50
- xmin = window_width * corner_buffer_coef - pad_height / 2
- xmax = window_width * (1-corner_buffer_coef) - pad_height / 2
- ymin = window_height * corner_buffer_coef - pad_height / 2
- ymax = window_height * (1-corner_buffer_coef) - pad_height / 2
- plane_reflect_angles = {0:180, 90:360, 180:180, 270:360}
- pad_speed = 0.5
- ball_accel = 0.00001
- max_x_difference = window_width
- max_y_difference = window_height
- max_angular_difference = 180
- max_gravity_difference = 100
- pyball_stuff_path = r"C:\Python32\PyBallStuff\\"
- pad_image_path = pyball_stuff_path + r"Pictures\Pads"
- sound_level_path = pyball_stuff_path + r"Pictures\SoundLevels"
- letter_path = pyball_stuff_path + r"Pictures\Letters"
- picture_path = pyball_stuff_path + r"Pictures"
- music_path = pyball_stuff_path + r"Audio"
- pad_memory_path = pyball_stuff_path + r"Memories"
- data_path = pyball_stuff_path + r"Data"
- font_size_11 = 11
- font_size_12 = 12
- font_size_14 = 14
- font_size_16 = 16
- font_size_18 = 18
- font_size_20 = 20
- font_size_22 = 22
- font_size_24 = 24
- font_size_26 = 26
- font_size_30 = 30
- font_size_40 = 40
- frames_per_score_message = 1000
- score_message_thresholds = {"Okay":5, "Good":10, "Great":15, "Excellent!":20,
- "Superb!":25, "Amazing!":30, "Outstanding!":50,
- "Unbelievable!":100}
- class Colour:
- def __init__(self, r, g, b):
- self.r = r
- self.g = g
- self.b = b
- return
- def toList(self):
- listrep = [self.r, self.g, self.b]
- return listrep
- class Pad:
- def __init__(self, x, y, surface):
- self.x = x
- self.y = y
- self.x_target = x
- self.y_target = y
- self.surface = surface
- self.moving = (0, 0)
- self.AI = 1
- self.score = 0
- self.memory = []
- return
- def move(self, x_translate, y_translate):
- self.moving = (x_translate, y_translate)
- self.xnew = self.x + x_translate
- self.ynew = self.y + y_translate
- if xmin <= self.xnew and self.xnew <= xmax:
- self.x = self.xnew
- else:
- x_translate = 0
- if ymin <= self.ynew and self.ynew <= ymax:
- self.y = self.ynew
- else:
- y_translate = 0
- self.moving = (x_translate, y_translate)
- return
- def losePoint(self, x_miss, y_miss):
- self.score -= 1
- if GameState.toggle_learning and Balls.ball.last_pad != self and self.AI == 1:
- memory_tuple = MemoryTuple(x_miss, y_miss)
- printData("SAVING TO MEMORY...")
- printData(memory_tuple.getData())
- printData("")
- self.memory.append(memory_tuple)
- return
- def toString(self):
- stringrep = ("(%s, %s)") % (self.x, self.y)
- return stringrep
- class Ball:
- def __init__(self, x, y, velocity, angle):
- self.x = x
- self.y = y
- self.velocity = velocity
- self.angle = angle
- self.last_pad = -1
- return
- def move(self):
- cut_off_modifier = 0.9
- collisionHandling(self)
- (pad_0_x, pad_0_y) = getPadMidpoint(0)
- (pad_1_x, pad_1_y) = getPadMidpoint(1)
- (pad_2_x, pad_2_y) = getPadMidpoint(2)
- (pad_3_x, pad_3_y) = getPadMidpoint(3)
- if self.x < pad_0_x * cut_off_modifier:
- Pads.padlist[0].losePoint(self.x, self.y)
- Launchers.launcher.launchBall()
- elif self.x > window_width - (window_width - pad_2_x) * cut_off_modifier:
- Pads.padlist[2].losePoint(self.x, self.y)
- Launchers.launcher.launchBall()
- elif self.y < pad_3_y * cut_off_modifier:
- Pads.padlist[3].losePoint(self.x, self.y)
- Launchers.launcher.launchBall()
- elif self.y > window_height - (window_height - pad_1_y) * cut_off_modifier:
- Pads.padlist[1].losePoint(self.x, self.y)
- Launchers.launcher.launchBall()
- else:
- (self.x_translate, self.y_translate) = self.getVelocityComponents()
- if gravity_ring.active:
- target = gravity_ring
- grav_pull_angle = degToRad(angleBetweenPoints((self.x, self.y),
- (target.pos_x, target.pos_y)))
- distance = getPointDistance(self.x, self.y, target.pos_x, target.pos_y)
- grav_x = gravity_ring.grav_coef / 2000 * math.cos(grav_pull_angle) * math.pow(0.96, distance)
- grav_y = gravity_ring.grav_coef / 2000 * math.sin(grav_pull_angle) * math.pow(0.96, distance)
- if abs(self.x_translate + grav_x) < abs(self.x_translate):
- grav_x *= 0.3
- if abs(self.y_translate - grav_y) < abs(self.y_translate):
- grav_y *= 0.3
- self.velocity = velocityFromComponents(self.x_translate + grav_x,
- self.y_translate - grav_y)
- self.angle = angleFromComponents(self.x_translate + grav_x,
- self.y_translate - grav_y)
- self.velocity += GameState.ball_accel
- self.x += self.x_translate
- self.y += self.y_translate
- return
- def getVelocityComponents(self):
- x_translate = math.cos(degToRad(self.angle)) * self.velocity
- y_translate = -math.sin(degToRad(self.angle)) * self.velocity
- components = (x_translate, y_translate)
- return components
- def toString(self):
- stringrep = ("Position: (%s, %s), moving at %s units/frame at angle %s"
- ) % (self.x, self.y, self.velocity, self.angle)
- return stringrep
- class GravFocal:
- def __init__(self, x, y, velocity, angle, grav_ring):
- self.pos_x = x
- self.pos_y = y
- self.velocity = velocity
- self.angle = angle
- self.grav_ring = grav_ring
- self.chasing = ""
- i = 0
- while self.chasing == "" or self.chasing == self:
- if i < len(grav_ring.grav_focals):
- self.chasing = random.choice(grav_ring.grav_focals)
- i += 1
- else:
- self.chasing = grav_ring
- break
- return
- def move(self):
- (self.x_translate, self.y_translate) = self.getVelocityComponents()
- grav_pull_angle = degToRad(angleBetweenPoints((self.pos_x, self.pos_y),
- (self.chasing.pos_x, self.chasing.pos_y)))
- grav_x = gravity_ring.grav_coef / 5000 * math.cos(grav_pull_angle)
- grav_y = gravity_ring.grav_coef / 5000 * math.sin(grav_pull_angle)
- self.velocity = velocityFromComponents(self.x_translate + grav_x, self.y_translate - grav_y)
- self.angle = angleFromComponents(self.x_translate + grav_x, self.y_translate - grav_y)
- self.pos_x += self.x_translate
- self.pos_y += self.y_translate
- return
- def getVelocityComponents(self):
- x_translate = math.cos(degToRad(self.angle)) * self.velocity
- y_translate = -math.sin(degToRad(self.angle)) * self.velocity
- components = (x_translate, y_translate)
- return components
- class Launcher:
- def __init__(self, radius, cool_down_rate, hot_colour, cool_colour):
- self.x = window_width/2
- self.y = window_height/2
- self.radius = radius
- self.heat = 0
- self.cool_down_rate = cool_down_rate
- self.hot_colour = hot_colour
- self.cool_colour = cool_colour
- def launchBall(self):
- GameState.data.append((GameState.game_count, GameState.collision_count))
- if GameState.auto_saving:
- if GameState.game_count > 0 and not GameState.game_count % 10:
- saveGameData()
- if GameState.game_count > 0 and not GameState.game_count % 100:
- backUpGameData()
- GameState.game_count += 1
- GameState.collision_count = 0
- genNewBall()
- self.heat = 1
- GameState.num_rounds += 1
- if GameState.auto_saving and GameState.num_rounds > 0:
- if GameState.num_rounds % GameState.auto_saving == 0:
- savePadMemories()
- def coolDown(self):
- if self.heat > 0:
- self.heat -= self.cool_down_rate
- def getColour(self):
- self.hr = self.heat * self.hot_colour[0]
- self.cr = (1-self.heat) * self.cool_colour[0]
- self.hg = self.heat * self.hot_colour[1]
- self.cg = (1-self.heat) * self.cool_colour[1]
- self.hb = self.heat * self.hot_colour[2]
- self.cb = (1-self.heat) * self.cool_colour[2]
- colour = [self.hr + self.cr, self.hg + self.cg, self.hb + self.cb]
- return colour
- def getRadius(self):
- actual_radius = int(self.radius * math.pow(1.01, self.heat * 100))
- return actual_radius
- class SoundLevel:
- def __init__(self):
- self.levels = []
- self.current_level = 3
- self.volume = 1
- self.pos_x = 0
- self.pos_y = 0
- self.width = 0
- self.height = 0
- self.hovering = False
- self.surface = ""
- self.cover = ""
- self.channel = ""
- return
- def addSurface(self, surface):
- self.surface = surface
- self.width = surface.get_width()
- self.height = surface.get_height()
- self.cover = pygame.Surface((self.width, self.height), 0, 32)
- self.cover.fill(black.toList())
- return
- def addChannel(self, channel):
- self.channel = channel
- self.channel.set_volume(0.333 * self.current_level)
- return
- def changeSoundLevel(self):
- self.current_level += 1
- self.current_level %= 4
- self.addSurface(self.levels[self.current_level])
- return
- def adjustVolume(self):
- target = 0.333 * self.current_level
- difference = target - self.volume
- self.volume += difference * 0.002
- self.channel.set_volume(self.volume)
- return
- def setCoverTransparency(self):
- if self.hovering:
- alpha = 255 * 0.15
- else:
- alpha = 255 * 0.5
- self.cover.set_alpha(alpha)
- return
- class PauseButton:
- def __init__(self, x, y):
- self.active = False
- self.hovering = False
- self.pos_x = x
- self.pos_y = y
- self.width = 18
- self.height = 18
- return
- def switch(self):
- self.active = not self.active
- return
- class GenericButton:
- def __init__(self, x, y):
- self.active = False
- self.hovering = False
- self.pos_x = x
- self.pos_y = y
- self.width = 0
- self.height = 0
- return
- def addSurface(self, surface):
- self.surface = surface
- self.width = surface.get_width()
- self.height = surface.get_height()
- self.cover = pygame.Surface((self.width, self.height), 0, 32)
- self.cover.fill(black.toList())
- return
- def switch(self):
- self.active = not self.active
- return
- class MemoryTuple:
- def __init__(self, x_miss, y_miss):
- self.x_miss = x_miss
- self.y_miss = y_miss
- self.x_collision = Collision.xpos
- self.y_collision = Collision.ypos
- self.collision_i_angle = Collision.initial_angle
- self.collision_i_speed = Collision.initial_speed
- self.collision_f_angle = Collision.final_angle
- self.collision_f_speed = Collision.final_speed
- self.collision_grav_coef = Collision.grav_coef
- return
- def getData(self):
- memory_tuple = (self.x_miss, self.y_miss, self.x_collision,
- self.y_collision, self.collision_i_angle,
- self.collision_i_speed, self.collision_f_angle,
- self.collision_f_speed, self.collision_grav_coef)
- return memory_tuple
- class SettingOption:
- def __init__(self, y, name, info, modes):
- self.pos_x = 20
- self.pos_y = y
- self.width = 260
- self.height = 50
- self.min_height = 50
- self.max_height = 100
- self.name = name
- self.info = info
- self.modes = modes
- self.colour = green.toList()
- self.active = True
- self.hovering = False
- self.surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA, 32)
- return
- def construct(self):
- offset = 5
- max_distance = getPointDistance(0, 0, offset, offset)
- self.surface.lock()
- for x in range(self.width):
- for y in range(self.height):
- alpha_coef = 1 - math.pow(0.992, (self.width - x) * 2)
- alpha_subcoef = (abs(self.height / 2 - y) + (1 - x/self.width) * 20) * 5
- alpha_coef *= 1 - math.pow(0.992, alpha_subcoef)
- if x < offset and y < offset:
- distance = getPointDistance(x, y, offset, offset)
- alpha_coef *= max(0, 1 - distance / offset)
- elif x < offset and y > self.height - offset:
- distance = getPointDistance(x, y, offset, self.height - offset)
- alpha_coef *= max(0, 1 - distance / offset)
- elif x < offset:
- alpha_coef *= x / offset
- elif y < offset:
- alpha_coef *= y / offset
- elif y > self.height - offset:
- alpha_coef *= (self.height - y) / offset
- col_subcoef = min(x, self.width - x) + min(y, self.height - y)
- col_coef = math.pow(0.992, col_subcoef)
- if self.hovering:
- alpha_coef = min(1, alpha_coef * 1.2)
- col_coef = min(1, col_coef * 1.2)
- bg_colour = [self.colour[0]*col_coef, self.colour[1]*col_coef, self.colour[2]*col_coef, 255 * alpha_coef]
- self.surface.set_at((x, y), bg_colour)
- self.surface.unlock()
- x_text = 0.1 * self.width
- y_text = self.min_height / 2 - 5
- renderText(self.surface, x_text, y_text, "Free Sans Bold", font_size_16, white.toList(), self.name, False)
- x_text = 0.1 * self.width
- y_text = self.min_height - 5
- textlen = len(self.info)
- textsplit = self.info.split()
- while textlen and y_text + 10 < self.surface.get_height():
- text = []
- if textsplit:
- next_word_length = len(textsplit[0])
- else:
- next_word_length = 0
- while len(" ".join(text)) + next_word_length < 43:
- if textsplit:
- word = textsplit.pop(0)
- else:
- textlen = 0
- break
- text.append(word)
- textlen -= len(word)
- text = " ".join(text)
- renderText(self.surface, x_text, y_text, "Free Sans Bold", font_size_14, white.toList(), text, False)
- y_text += 10
- if self.hovering:
- x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
- y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
- radius = 2
- x_circ = round(x_offset + self.pos_x - 8)
- y_circ = round(y_offset + self.pos_y + self.height / 2 + radius / 2)
- pygame.draw.circle(Screen.screen, white.toList(), (x_circ, y_circ), radius)
- return
- def adjustColour(self):
- if self.modes[0] == 1 and self.active < 0:
- self.colour = crossColours(white, green, -self.active/10).toList()
- self.active += 1
- elif self.modes[0] == 1 and self.active >= 0:
- self.colour = green.toList()
- elif self.modes[0] == 2:
- self.colour = {False:red.toList(), True:green.toList()}[self.active]
- elif self.modes[0] > 2:
- self.colour = crossColours(green, red, self.active / (self.modes[0]-1)).toList()
- return
- def adjustSize(self):
- lower_index = (SettingsMenu.current_page - 1) * 5
- upper_index = SettingsMenu.current_page * 5
- self_index = SettingOptions.options.index(self)
- if self_index < lower_index or self_index > upper_index:
- return
- rate = 10
- delta_height = 0
- if self.hovering and self.height < self.max_height:
- delta_height = rate
- elif not self.hovering and self.height > self.min_height:
- delta_height = -rate
- if delta_height:
- if self.height + delta_height > self.max_height:
- actual_change = self.max_height - self.height
- elif self.height + delta_height < self.min_height:
- actual_change = self.height - self.min_height
- else:
- actual_change = delta_height
- self.height += actual_change
- self.surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA, 32)
- found_self = False
- for i in range(lower_index, upper_index):
- if i >= len(SettingOptions.options):
- break
- option = SettingOptions.options[i]
- if not found_self and self == option:
- found_self = True
- elif found_self:
- option.pos_y += actual_change
- return
- def switch(self):
- if self.modes[0] == 1:
- self.active = -10
- elif self.modes[0] == 2:
- self.active = not self.active
- elif self.modes[0] > 2:
- self.active = (self.active + 1) % self.modes[0]
- if "TOGGLE_LEARNING" in self.modes:
- GameState.toggle_learning = self.active
- if "TOGGLE_GENETIC" in self.modes:
- GameState.toggle_genetic = self.active
- if "TOGGLE_GRAVITY" in self.modes:
- gravity_ring.active = self.active
- if "SET_GRAV_COEF" in self.modes:
- val = input("Please enter new gravity coefficient: ")
- try:
- val = float(val)
- if 0 <= val and val <= 100:
- gravity_ring.grav_coef = val
- else:
- print("Invalid value")
- except:
- print("Error parsing value")
- if "SET_WEIGHT" in self.modes:
- val = input("Please enter new weight coefficient: ")
- try:
- val = float(val)
- if 0 <= val and val <= 1:
- WeightData.current_weight = val
- else:
- print("Invalid value")
- except:
- print("Error parsing value")
- if "SAVE_MEMORY" in self.modes:
- savePadMemories()
- if "BACK_UP_MEMORY" in self.modes:
- path = ""
- i = 0
- while path == "" or os.path.exists(path):
- i += 1
- path = os.path.join(pad_memory_path, r"BackUpMemories\Back-up %s" % i)
- backUpPadMemories(path)
- if "LOAD_MEMORY" in self.modes:
- loadPadMemories()
- if "RESET_MEMORY" in self.modes:
- for pad in Pads.padlist:
- pad.memory = []
- if "RESET_SCORES" in self.modes:
- for pad in Pads.padlist:
- pad.score = 0
- if "TOGGLE_AI_PAD1" in self.modes:
- Pads.padlist[0].AI = self.active / 2
- if "TOGGLE_AI_PAD2" in self.modes:
- Pads.padlist[1].AI = self.active / 2
- if "TOGGLE_AI_PAD3" in self.modes:
- Pads.padlist[2].AI = self.active / 2
- if "TOGGLE_AI_PAD4" in self.modes:
- Pads.padlist[3].AI = self.active / 2
- if "TOGGLE_ALL_AI" in self.modes:
- option_list = []
- for i in range(4):
- option_list.append("TOGGLE_AI_PAD%s" % (i+1))
- enable_all = False
- active_set = -1
- for option in SettingOptions.options:
- for related_option in option_list:
- if related_option in option.modes and option.active != active_set:
- if active_set == -1:
- active_set = option.active
- continue
- else:
- enable_all = True
- break
- if enable_all:
- active_set = 2
- else:
- active_set = (active_set + 1) % 3
- for option in SettingOptions.options:
- for related_option in option_list:
- if related_option in option.modes:
- option.active = active_set
- for pad in Pads.padlist:
- pad.AI = active_set / 2
- if "ACCEL_BALL" in self.modes:
- for pad in Pads.padlist:
- if self.active == 0:
- GameState.ball_accel = 0
- elif self.active == 1:
- GameState.ball_accel = ball_accel
- elif self.active == 2:
- GameState.ball_accel = ball_accel * 10
- else:
- GameState.ball_accel = ball_accel * 50
- if "FAST_PADS" in self.modes:
- GameState.pad_speed_mult = {False:1, True:2}[self.active]
- if "AUTO_SAVE" in self.modes:
- rounds = GameState.num_rounds
- if rounds == 0:
- GameState.auto_saving = 0
- elif self.active == 1:
- GameState.auto_saving = 100
- elif self.active == 2:
- GameState.auto_saving = 50
- else:
- GameState.auto_saving = 10
- return
- class GravityRing:
- def __init__(self):
- self.pos_x = int(window_width / 2)
- self.pos_y = int(window_height / 2)
- self.grav_coef = 0
- self.current_angle = 0
- self.current_radius = 0
- self.end_radius = 50
- self.rings = 8
- self.pump_index = 0
- self.pump_timer = 100
- self.active = False
- self.pumpvals = []
- self.grav_focals = []
- for i in range(10):
- self.grav_focals.append("")
- self.genNewGravFocal(i)
- for i in range(self.rings):
- self.pumpvals.append(0)
- return
- def genNewGravFocal(self, index):
- velocity = random.random()*0.2+0.2
- angle = 0
- while angle % 90 < 20:
- angle = random.randrange(360)
- grav_focal = GravFocal(window_width/2, window_height/2, velocity, angle, self)
- self.grav_focals[index] = grav_focal
- return
- def construct(self):
- if not self.current_radius > 0:
- return
- delta_angle = 360 / self.rings
- current_angle = 0
- for i in range(self.rings):
- circ_x = self.pos_x + round(self.current_radius * math.cos(degToRad(current_angle)))
- circ_y = self.pos_y + round(self.current_radius * math.sin(degToRad(current_angle)))
- circ_rad = round(2 * (1 + self.pumpvals[i] / 100))
- colour = crossColours(purple, grey, self.grav_coef / 180)
- pygame.draw.circle(Screen.screen, colour.toList(), (circ_x, circ_y), circ_rad, 1)
- current_angle += delta_angle
- if self.pumpvals[i]:
- self.pumpvals[i] -= 1
- return
- def handleGravFocals(self):
- if not self.current_radius > 0:
- return
- for i in range(len(self.grav_focals)):
- focal = self.grav_focals[i]
- if getPointDistance(self.pos_x, self.pos_y, focal.pos_x, focal.pos_y) > self.current_radius:
- self.genNewGravFocal(i)
- return
- focal.move()
- colour = crossColours(purple, grey, self.grav_coef / 180)
- focal_radius = 2
- focal_x = round(focal.pos_x - focal_radius / 2)
- focal_y = round(focal.pos_y - focal_radius / 2)
- pygame.draw.circle(Screen.screen, colour.toList(), (focal_x, focal_y), focal_radius, 1)
- def modifyRadius(self):
- if self.active and 0.95 * self.end_radius <= self.current_radius and self.current_radius < self.end_radius:
- self.current_radius = self.end_radius
- elif self.active and self.current_radius < 0.95 * self.end_radius:
- self.current_radius += self.end_radius / 250
- elif not self.active and 0.05 * self.end_radius >= self.current_radius and self.current_radius >= 0:
- self.current_radius = 0
- else:
- self.current_radius -= self.end_radius / 250
- return
- def pumpCircs(self):
- if not self.pump_timer:
- self.pumpvals[self.pump_index] = 100
- self.pump_index = (self.pump_index + 1) % self.rings
- if self.pump_timer > 100 - 50 * (self.grav_coef - 50) / 50:
- self.pump_timer = 0
- else:
- self.pump_timer += 1
- return
- class SettingOptions:
- options = []
- class WeightData:
- weight_scores = {}
- current_generation = []
- current_weight = 0
- current_weight_index = 0
- current_collisions = 0
- current_game = 0
- generation = 0
- class PadMemory:
- memory_blue = []
- memory_green = []
- memory_red = []
- memory_orange = []
- class Pads:
- padlist = []
- class Balls:
- ball = ""
- class Screen:
- screen = ""
- class FastForwardCover:
- surface = ""
- class SettingsMenu:
- modified_surface = ""
- default_surface = ""
- total_pages = 4
- current_page = 1
- class GameState:
- done = False
- fast_forward_cover_loaded = False
- toggle_learning = True
- toggle_genetic = True
- collision_count = 0
- max_count = 0
- num_rounds = 0
- score_messages_list = []
- score_messages = {"Okay":0, "Good":0, "Great":0, "Excellent!":0,
- "Superb!":0, "Amazing!":0, "Outstanding!":0,
- "Unbelievable!":0}
- ball_accel = 0
- pad_speed_mult = 1
- auto_saving = 0
- data = []
- game_count = 0
- class Collision:
- xpos = 0
- ypos = 0
- initial_angle = 0
- initial_speed = 0
- final_angle = 0
- final_speed = 0
- grav_coef = 0
- class Launchers:
- launcher = ""
- class KeyBools:
- aPressed = False
- dPressed = False
- iPressed = False
- kPressed = False
- leftPressed = False
- rightPressed = False
- kp8Pressed = False
- kp2Pressed = False
- class FontTypes:
- Angelic_War_40_font = ""
- Angelic_War_14_font = ""
- Birth_Of_A_Hero_30_font = ""
- Times_New_Roman_18_font = ""
- Free_Sans_Bold_10_font = ""
- Free_Sans_Bold_12_font = ""
- Free_Sans_Bold_16_font = ""
- Free_Sans_Bold_18_font = ""
- Free_Sans_Bold_30_font = ""
- Sergeant_SixPack_12_font = ""
- Sergeant_SixPack_14_font = ""
- Sergeant_SixPack_16_font = ""
- Sergeant_SixPack_18_font = ""
- Sergeant_SixPack_20_font = ""
- Sergeant_SixPack_22_font = ""
- Sergeant_SixPack_24_font = ""
- Sergeant_SixPack_26_font = ""
- black = Colour(0, 0, 0)
- grey = Colour(100, 100, 100)
- white = Colour(255, 255, 255)
- orange = Colour(255, 165, 0)
- red = Colour(255, 0, 0)
- green = Colour(0, 255, 0)
- blue = Colour(0, 0, 255)
- yellow = Colour(255, 255, 0)
- cool_blue = Colour(174, 238, 238)
- purple = Colour(223, 0, 255)
- dark_red = Colour(100, 0, 0)
- dark_green = Colour(0, 100, 0)
- dark_blue = Colour(0, 0, 100)
- sound_level = SoundLevel()
- pause_button = PauseButton(50, 14)
- fast_forward = GenericButton(79, 11)
- info_button = GenericButton(105, 10)
- print_button = GenericButton(135, 14)
- gear_button = GenericButton(160, 13)
- arrow_left = GenericButton(0, 460)
- arrow_right = GenericButton(0, 460)
- pause_surface = pygame.Surface((pause_button.width, pause_button.height), 0, 32)
- gravity_ring = GravityRing()
- ############################################################
- #
- # U T I L I T Y F U N C T I O N S
- #
- def degToRad(degrees):
- rads = degrees * math.pi / 180
- return rads
- def radToDeg(radians):
- degrees = radians * 180 / math.pi
- return degrees
- def crossColours(c1, c2, coef):
- anti_coef = 1 - coef
- c1 = c1.toList()
- c2 = c2.toList()
- result = Colour(c1[0]*coef + c2[0]*anti_coef,
- c1[1]*coef + c2[1]*anti_coef,
- c1[2]*coef + c2[2]*anti_coef)
- return result
- def objectWithinBounds(obj_x, obj_y, x_low, x_high, y_low, y_high):
- ball = Balls.ball
- within_bounds = x_low <= obj_x and obj_x <= x_high and y_low <= obj_y and obj_y <= y_high
- return within_bounds
- def angleBetweenPoints(p1, p2):
- (p1_x, p1_y) = p1
- (p2_x, p2_y) = p2
- x_dif = p2_x - p1_x
- y_dif = p2_y - p1_y
- angle = angleFromComponents(x_dif, y_dif)
- return angle
- def angleFromComponents(x_translate, y_translate):
- #quadrant 1, 2, 3, 4
- y_translate *= -1
- if x_translate == 0:
- x_translate = 0.0001
- if x_translate > 0 and y_translate > 0:
- theta = radToDeg(math.atan(y_translate / x_translate))
- elif x_translate < 0 and y_translate > 0:
- theta = radToDeg(math.atan(y_translate / x_translate)) + 180
- elif x_translate < 0 and y_translate < 0:
- theta = radToDeg(math.atan(y_translate / x_translate)) + 180
- else:
- theta = radToDeg(math.atan(y_translate / x_translate)) + 360
- theta %= 360
- return theta
- def velocityFromComponents(x_translate, y_translate):
- velocity = math.sqrt(x_translate * x_translate + y_translate * y_translate)
- return velocity
- def insidePad():
- ball = Balls.ball
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- padxmin = pad.x
- padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
- padymin = pad.y
- padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
- if padxmin <= ball.x and ball.x <= padxmax and padymin <= ball.y and ball.y <= padymax:
- return True
- return False
- def averageAngles(first_angle, second_angle):
- average_angle = (first_angle + second_angle) / 2
- return average_angle
- def getPointDistance(x1, y1, x2, y2):
- distance = math.sqrt(math.pow((x2 - x1), 2) + math.pow((y2 - y1), 2))
- return distance
- def getPadMidpoint(padindex):
- i = padindex
- pad = Pads.padlist[i]
- padxmin = pad.x
- padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
- padymin = pad.y
- padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
- padxmid = (padxmin + padxmax) / 2
- padymid = (padymin + padymax) / 2
- midpoint = (padxmid, padymid)
- return midpoint
- def getAngleDifference(angle1, angle2):
- angle_dif = 360 - max(angle1, angle2) + min(angle1, angle2)
- if angle_dif > 180:
- angle_dif = 360 - angle_dif
- return angle_dif
- ############################################################
- #
- # I N P U T / O U T P U T
- #
- def saveGameData():
- with open(os.path.join(data_path, "score_data.txt"), 'w') as file:
- for item in GameState.data:
- file.write(str(item) + "\n")
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- with open(os.path.join(data_path, "pad_%s_memory.txt" % i), 'w') as file:
- for memory in pad.memory:
- file.write(str(memory.getData()) + "\n")
- with open(os.path.join(data_path, "weight_coefficient.txt"), 'w') as file:
- file.write(str(WeightData.current_weight))
- with open(os.path.join(data_path, "grav_coefficient.txt"), 'w') as file:
- file.write(str(gravity_ring.grav_coef))
- return
- def backUpGameData():
- path = ""
- i = 0
- while path == "" or os.path.exists(path):
- i += 1
- path = os.path.join(data_path, r"BackUpData\Back-up %s" % i)
- try:
- os.mkdir(path)
- except Exception as e:
- print("Error occured whilst making memory back up: %s" % e)
- return
- for filename in ["score_data.txt", "grav_coefficient.txt", "weight_coefficient.txt",
- "pad_0_memory.txt", "pad_1_memory.txt", "pad_2_memory.txt",
- "pad_3_memory.txt"]:
- file = os.path.join(data_path, filename)
- shutil.copy(file, path)
- print("Game data back-up created")
- return
- def savePadMemories():
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- with open(os.path.join(pad_memory_path, "pad_%s_memory.txt" % i), 'w') as file:
- for memory in pad.memory:
- file.write(str(memory.getData()) + "\n")
- with open(os.path.join(pad_memory_path, "weight_coefficient.txt"), 'w') as file:
- file.write(str(WeightData.current_weight))
- return
- def backUpPadMemories(path):
- if not os.path.exists(path):
- try:
- os.mkdir(path)
- except Exception as e:
- print("Error occurred whilst making memory back up: %s" % e)
- return
- padlist = Pads.padlist
- for i in range(len(padlist)):
- file = os.path.join(pad_memory_path, "pad_%s_memory.txt" % i)
- shutil.copy(file, path)
- file = os.path.join(pad_memory_path, "weight_coefficient.txt")
- shutil.copy(file, path)
- print("New directory created")
- return
- def loadPadMemories():
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- with open(os.path.join(pad_memory_path, "pad_%s_memory.txt" % i), 'r') as file:
- pad.memory = parseMemoryData(file.read())
- with open(os.path.join(pad_memory_path, "weight_coefficient.txt"), 'r') as file:
- WeightData.current_weight = float(file.read())
- return
- def parseMemoryData(string_data):
- list_data = []
- for line in string_data.split("\n"):
- if not line:
- continue
- item_data = line.replace("(", "").replace(")", "").split(", ")
- x_miss = float(item_data[0])
- y_miss = float(item_data[1])
- memory_tuple = MemoryTuple(x_miss, y_miss)
- memory_tuple.x_collision = float(item_data[2])
- memory_tuple.y_collision = float(item_data[3])
- memory_tuple.collision_i_angle = float(item_data[4])
- memory_tuple.collision_i_speed = float(item_data[5])
- memory_tuple.collision_f_angle = float(item_data[6])
- memory_tuple.collision_f_speed = float(item_data[7])
- memory_tuple.collision_grav_coef = float(item_data[8])
- list_data.append(memory_tuple)
- return list_data
- ############################################################
- #
- # A D A P T I V E A I
- #
- def updatePadTargets():
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- if pad.AI != 1:
- continue
- (x_target, y_target) = findBestApproximation(i)
- printData("%s: (%s, %s)" % (i, x_target, y_target))
- printMemory(i)
- pad.x_target = x_target
- pad.y_target = y_target
- printData("")
- return
- def handleAI():
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- if pad.AI == 0.5:
- pad.x_target = Balls.ball.x
- pad.y_target = Balls.ball.y
- elif pad.AI == 0:
- continue
- (padxmid, padymid) = getPadMidpoint(i)
- if not i % 2:
- if padymid < pad.y_target:
- pad.move(0, pad_speed * GameState.pad_speed_mult)
- elif padymid > pad.y_target:
- pad.move(0, -pad_speed * GameState.pad_speed_mult)
- else:
- if padxmid < pad.x_target:
- pad.move(pad_speed * GameState.pad_speed_mult, 0)
- elif padxmid > pad.x_target:
- pad.move(-pad_speed * GameState.pad_speed_mult, 0)
- return
- def findBestApproximation(padindex):
- printData("FINDING APPROXIMATION FOR PAD %s...\n" % padindex)
- pad = Pads.padlist[padindex]
- memory = pad.memory
- ball = Balls.ball
- if not memory:
- approximation = getPadMidpoint(padindex)
- return approximation
- collision_data = getCollisionData()
- (last_collision_x, last_collision_y, last_collision_i_angle,
- last_collision_i_speed, last_collision_f_angle,
- last_collision_f_speed, last_collision_grav_coef) = collision_data
- best_approx = 0
- strictness_coef = 1.03
- for memory_tuple in memory:
- (x_miss, y_miss, x_collision, y_collision, _, _, f_angle, _,
- collision_grav_coef) = memory_tuple.getData()
- (divergence, x_divergence, y_divergence, f_angular_divergence,
- grav_divergence) = calculateDivergence(memory_tuple, collision_data)
- approximation = (divergence, x_miss, y_miss)
- printData("\n\nPAD: %s" % padindex)
- printData("\nLAST COLLISION (X) = %s, CONSIDERED CASE (X) = %s" % (last_collision_x, x_collision))
- printData("pos_x DIVERGENCE: %s" % x_divergence)
- printData("\nLAST COLLISION (Y) = %s, CONSIDERED CASE (Y) = %s" % (last_collision_y, y_collision))
- printData("pos_y DIVERGENCE: %s" % y_divergence)
- printData("\nLAST COLLISION (fAngle) = %s, CONSIDERED CASE (fAngle) = %s" % (last_collision_f_angle, f_angle))
- printData("FINAL ANGLE DIVERGENCE: %s" % f_angular_divergence)
- printData("\nLAST COLLISION (grav) = %s, CONSIDERED CASE (grav) = %s" % (last_collision_grav_coef,
- collision_grav_coef))
- printData("\nTOTAL DIVERGENCE: %s\n\n" % divergence)
- if not best_approx:
- best_approx = approximation
- else:
- (least_divergence, _, _) = best_approx
- if divergence < least_divergence:
- best_approx = approximation
- (_, pos_xition, pos_yition) = best_approx
- approximation = (pos_xition, pos_yition)
- return approximation
- def calculateDivergence(memory_tuple, collision_data):
- (last_collision_x, last_collision_y, last_collision_i_angle,
- last_collision_i_speed, last_collision_f_angle,
- last_collision_f_speed, last_collision_grav_coef) = collision_data
- (x_miss, y_miss, x_collision, y_collision,
- i_angle, i_speed, f_angle, f_speed,
- collision_grav_coef) = memory_tuple.getData()
- pos_x_dif = abs(x_collision - last_collision_x)
- pos_y_dif = abs(y_collision - last_collision_y)
- i_angle_dif = getAngleDifference(i_angle, last_collision_i_angle)
- i_speed_dif = abs(i_speed - last_collision_i_speed)
- f_angle_dif = getAngleDifference(f_angle, last_collision_f_angle)
- f_speed_dif = abs(f_speed - last_collision_f_speed)
- grav_dif = abs(collision_grav_coef - last_collision_grav_coef)
- x_divergence = 100 * pos_x_dif / max_x_difference
- y_divergence = 100 * pos_y_dif / max_y_difference
- f_angular_divergence = 100 * f_angle_dif / max_angular_difference
- grav_divergence = 100 * grav_dif / max_gravity_difference
- #Apply weights.
- x_divergence *= WeightData.current_weight
- y_divergence *= WeightData.current_weight
- f_angular_divergence *= (1 - WeightData.current_weight)
- grav_divergence *= 0.5
- total_divergence = x_divergence + y_divergence + f_angular_divergence + grav_divergence
- divergence_data = (total_divergence, x_divergence, y_divergence, f_angular_divergence, grav_divergence)
- return divergence_data
- ############################################################
- #
- # G E N E T I C A L G O R I T H M
- #
- def generateWeights():
- WeightData.generation += 1
- current_generation = produceChildren()
- while len(current_generation) < population_size:
- current_generation.append(random.random())
- WeightData.current_generation = current_generation
- print("NEW GENERATION: %s" % current_generation)
- return
- def selectBestWeights():
- best_weights = []
- current_generation = WeightData.current_generation
- #Get the best three weights.
- for i in range(int(0.5 * population_size)):
- best_score = -1
- best_weight = -1
- for weight, score in WeightData.weight_scores.items():
- if score > best_score and weight in current_generation and weight not in best_weights:
- best_weight = weight
- best_score = score
- if best_weight != -1:
- best_weights.append(best_weight)
- return best_weights
- def testNextWeight():
- WeightData.current_weight_index += 1
- index = WeightData.current_weight_index
- WeightData.current_weight = WeightData.current_generation[index]
- return
- def produceChildren():
- best_weights = selectBestWeights()
- children = []
- for i in range(len(best_weights)):
- for j in range(i + 1, len(best_weights)):
- if len(children) == population_size:
- break
- child = averageWeights(best_weights[i], best_weights[j])
- children.append(child)
- return children
- def averageWeights(weight_1, weight_2):
- average_weight = (weight_1 + weight_2) / 2
- return average_weight
- def scoreWeightValue():
- current_weight = WeightData.current_weight
- current_collisions = WeightData.current_collisions
- WeightData.weight_scores[current_weight] = current_collisions
- printWeightScores()
- return
- def printWeightScores():
- weight_scores = WeightData.weight_scores
- for weight, score in weight_scores.items():
- print("Weight %.4f: %s" % (weight, score))
- return
- def beginNewTest():
- print("NEW TEST!")
- scoreWeightValue()
- WeightData.current_collisions = 0
- WeightData.current_game = 0
- if WeightData.current_weight_index < population_size - 1:
- WeightData.current_weight_index += 1
- index = WeightData.current_weight_index
- WeightData.current_weight = WeightData.current_generation[index]
- else:
- beginNewGeneration()
- padlist = Pads.padlist
- for i in range(len(padlist)):
- padlist[i].memory = []
- return
- def beginNewGeneration():
- print("NEW GENERATION!")
- generateWeights()
- WeightData.current_weight_index = 0
- WeightData.current_weight = WeightData.current_generation[0]
- return
- ############################################################
- #
- # S U R F A C E S F R O M F I L E S
- #
- def getPadFromFile(padname):
- pad = pygame.image.load(os.path.join(pad_image_path, padname + ".png")).convert_alpha()
- return pad
- def getSpeakerIcon(sound_level):
- scale = 0.12
- speaker = pygame.image.load(os.path.join(sound_level_path, "sound%s.png" % sound_level)).convert_alpha()
- speaker = scaleImage(speaker, scale)
- return speaker
- def getLetterIcon(letter):
- scale = 0.09
- info_button = pygame.image.load(os.path.join(letter_path, "letter_" + letter + ".png")).convert_alpha()
- info_button = scaleImage(info_button, scale)
- return info_button
- def getFastForwardIcon():
- scale = 0.2
- fast_forward = pygame.image.load(os.path.join(picture_path, "fast_forward.png")).convert_alpha()
- fast_forward = scaleImage(fast_forward, scale)
- return fast_forward
- def getFastForwardCover():
- fast_forward_cover = pygame.image.load(os.path.join(picture_path, "pyballcover.png")).convert_alpha()
- return fast_forward_cover
- def getGearIcon():
- scale = 0.1
- gear_button = pygame.image.load(os.path.join(picture_path, "gear.png")).convert_alpha()
- gear_button = scaleImage(gear_button, scale)
- return gear_button
- def getArrowIcon(direction):
- scale = 0.1
- arrow_button = pygame.image.load(os.path.join(picture_path, "arrow_%s.png" % direction)).convert_alpha()
- arrow_button = scaleImage(arrow_button, scale)
- return arrow_button
- def getSettingsMenu():
- settings_menu = pygame.image.load(os.path.join(picture_path, "settings_menu.png")).convert_alpha()
- return settings_menu
- def scaleImage(image, scale_factor):
- scaled_image = pygame.transform.smoothscale(image, (int(image.get_width() * scale_factor), int(image.get_height() * scale_factor)))
- return scaled_image
- ############################################################
- #
- # S E S S I O N I N I T I A L I S A T I O N
- #
- def initialiseMusic(sound_level):
- pygame.mixer.init()
- channel = pygame.mixer.Channel(0)
- song = "relaxing_space.wav"
- music = pygame.mixer.Sound(os.path.join(music_path, song))
- channel.play(music, -1)
- sound_level.addChannel(channel)
- return
- def initialiseFonts():
- FontTypes.Angelic_War_14_font = pygame.font.Font(pygame.font.match_font("Angelic War"), font_size_14)
- FontTypes.Angelic_War_40_font = pygame.font.Font(pygame.font.match_font("Angelic War"), font_size_40)
- FontTypes.Birth_Of_A_Hero_30_font = pygame.font.Font(pygame.font.match_font("Birth of a Hero"), font_size_30)
- FontTypes.Times_New_Roman_18_font = pygame.font.Font(pygame.font.match_font("Times New Roman"), font_size_18)
- FontTypes.Free_Sans_Bold_10_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_11)
- FontTypes.Free_Sans_Bold_12_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_12)
- FontTypes.Free_Sans_Bold_14_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_14)
- FontTypes.Free_Sans_Bold_16_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_16)
- FontTypes.Free_Sans_Bold_18_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_18)
- FontTypes.Free_Sans_Bold_30_font = pygame.font.Font(pygame.font.match_font("Free Sans Bold"), font_size_30)
- FontTypes.Sergeant_SixPack_12_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_12)
- FontTypes.Sergeant_SixPack_14_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_14)
- FontTypes.Sergeant_SixPack_16_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_16)
- FontTypes.Sergeant_SixPack_18_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_18)
- FontTypes.Sergeant_SixPack_20_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_20)
- FontTypes.Sergeant_SixPack_22_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_22)
- FontTypes.Sergeant_SixPack_24_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_24)
- FontTypes.Sergeant_SixPack_26_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_26)
- return
- def initialiseSession():
- offset = 75
- padblue = Pad(offset - pad_width/2,
- window_height/2 - pad_height/2,
- getPadFromFile("padblue"))
- padgreen = Pad(window_width/2 - pad_height/2,
- window_height - offset - pad_width/2,
- pygame.transform.rotate(getPadFromFile("padgreen"), 90))
- padred = Pad(window_width - offset - pad_width/2,
- window_height/2 - pad_height/2,
- pygame.transform.rotate(getPadFromFile("padred"), 180))
- padorange = Pad(window_width/2 - pad_height/2,
- offset - pad_width/2,
- pygame.transform.rotate(getPadFromFile("padorange"), 270))
- Pads.padlist = [padblue, padgreen, padred, padorange]
- info_button.addSurface(getLetterIcon("i"))
- print_button.addSurface(getLetterIcon("t"))
- fast_forward.addSurface(getFastForwardIcon())
- gear_button.addSurface(getGearIcon())
- arrow_left.addSurface(getArrowIcon("left"))
- arrow_right.addSurface(getArrowIcon("right"))
- sound_level.addSurface(getSpeakerIcon(sound_level.current_level))
- FastForwardCover.surface = getFastForwardCover()
- SettingsMenu.default_surface = getSettingsMenu()
- ResetSettingsMenu()
- Launchers.launcher = Launcher(3, 0.001, red.toList(), cool_blue.toList())
- Launchers.launcher.launchBall()
- for i in range(4):
- sound_level.levels.append(getSpeakerIcon(i))
- sound_level.pos_x = 14
- sound_level.pos_y = 10
- initialiseMusic(sound_level)
- initialiseSettings()
- for option in SettingOptions.options:
- if "TOGGLE_GENETIC" in option.modes and option.active:
- option.switch()
- elif "TOGGLE_GRAVITY" in option.modes and option.active:
- option.switch()
- elif "ACCEL_BALL" in option.modes:
- while option.active != 0:
- option.switch()
- elif "FAST_PADS" in option.modes:
- while option.active != 0:
- option.switch()
- elif "AUTO_SAVE" in option.modes:
- while option.active != 0:
- option.switch()
- elif "TOGGLE_AI_PAD1" in option.modes:
- while option.active != 2:
- option.switch()
- elif "TOGGLE_AI_PAD2" in option.modes:
- while option.active != 2:
- option.switch()
- elif "TOGGLE_AI_PAD3" in option.modes:
- while option.active != 2:
- option.switch()
- elif "TOGGLE_AI_PAD4" in option.modes:
- while option.active != 2:
- option.switch()
- arrow_left.pos_x = (SettingsMenu.default_surface.get_width() / 2) - 20 - arrow_left.width / 2
- arrow_right.pos_x = (SettingsMenu.default_surface.get_width() / 2) + 20 - arrow_right.width / 2
- GameState.score_messages_list = sorted(score_message_thresholds.items(), key=lambda item:item[1])
- return
- def initialiseSettings():
- options = SettingOptions.options
- base = 100
- offset = 60
- options.extend([SettingOption(base + offset * 0, "Toggle Learning",
- "When enabled, the pads learn from their mistakes and " +
- "improve their behaviour over time",
- [2, "TOGGLE_LEARNING"]),
- SettingOption(base + offset * 1, "Toggle Genetic Algorithm",
- "When enabled, the adaptive function will be put through " +
- "continuous testing to optimise pad learning efficiency",
- [2, "TOGGLE_GENETIC"]),
- SettingOption(base + offset * 2, "Toggle Gravity",
- "When enabled, the ball will be attracted towards the gravity " +
- "ring in the centre of the window with a force determined by " +
- "the ring's gravity coefficient",
- [2, "TOGGLE_GRAVITY"]),
- SettingOption(base + offset * 3, "Set Gravity Coefficient",
- "Sets the coefficient that determines how strongly the ball is " +
- "attracted towards the gravity ring when gravity is enabled. " +
- "Enter any number between 0 and 100 or an invalid value to quit",
- [0, "SET_GRAV_COEF"]),
- SettingOption(base + offset * 4, "Set Weight Coefficient",
- "Adjusts the behaviour of the pads. Enter any floating value " +
- "between 0 and 1 in the shell after clicking this button or " +
- "an invalid value (e.g. -1) to exit without modifying the weight",
- [0, "SET_WEIGHT"]),
- SettingOption(base + offset * 0, "Save Pad Memory",
- "Saves the data in each of the pad's memories to an " +
- "external file, allowing the data to be preserved " +
- "between different instances of the game",
- [1, "SAVE_MEMORY"]),
- SettingOption(base + offset * 1, "Back-up Pad Memory",
- "Creates a new directory specified by a name you enter and " +
- "creates copies of the current external memory states of the " +
- "pads into the directory",
- [1, "BACK_UP_MEMORY"]),
- SettingOption(base + offset * 2, "Load Pad Memory",
- "Loads data from an external file containing previously " +
- "stored data for each of the pads",
- [1, "LOAD_MEMORY"]),
- SettingOption(base + offset * 3, "Reset Memory",
- "Resets the pads' memories, but leaves any data stored in " +
- "external files untouched",
- [1, "RESET_MEMORY"]),
- SettingOption(base + offset * 4, "Reset Scores",
- "Resets the scores for each of the pads",
- [1, "RESET_SCORES"]),
- SettingOption(base + offset * 0, "Toggle AI - Pad 1",
- "When fully or semi-enabled, the left pad plays with adaptive or " +
- "static behavioural patterns respectively. When disabled, the pad " +
- "is controlled by a human player using the keys I and K",
- [3, "TOGGLE_AI_PAD1"]),
- SettingOption(base + offset * 1, "Toggle AI - Pad 2",
- "When fully or semi-enabled, the bottom pad plays with adaptive or " +
- "static behavioural patterns respectively. When disabled, the pad " +
- "is controlled by a human player using the keys A and D",
- [3, "TOGGLE_AI_PAD2"]),
- SettingOption(base + offset * 2, "Toggle AI - Pad 3",
- "When fully or semi-enabled, the right pad plays with adaptive or " +
- "static behavioural patterns respectively. When disabled, the pad " +
- "is controlled by a human player using the numpad keys 8 and 2",
- [3, "TOGGLE_AI_PAD3"]),
- SettingOption(base + offset * 3, "Toggle AI - Pad 4",
- "When fully or semi-enabled, the top pad plays with adaptive or " +
- "static behavioural patterns respectively. When disabled, the pad " +
- "is controlled by a human player using the arrow keys LEFT and RIGHT",
- [3, "TOGGLE_AI_PAD4"]),
- SettingOption(base + offset * 4, "Toggle All AIs",
- "Cycles all the AI's through different AI states (ON, SEMI-, OFF)",
- [1, "TOGGLE_ALL_AI"]),
- SettingOption(base + offset * 0, "Mode - Accelerating Ball",
- "Off - the ball does not automatically accelerate [1] - the ball " +
- "accelerates at a low rate [2] - the ball accelerates moderately " +
- "fast [3] - the ball accelerates at a high rate",
- [4, "ACCEL_BALL"]),
- SettingOption(base + offset * 1, "Mode - Fast Pads",
- "When disabled, pads move at their normal rate. When enabled, " +
- "pads are able to move twice as fast",
- [2, "FAST_PADS"]),
- SettingOption(base + offset * 2, "Auto-saving",
- "Off - auto-saving does not happen at all [1] - one save " +
- "is executed every 100 rounds [2] - one save is executed " +
- "every 50 rounds [3] - one save is executed every 10 rounds",
- [4, "AUTO_SAVE"])])
- SettingOptions.options = options
- return
- ############################################################
- #
- # T E X T O U T P U T
- #
- def printData(data):
- if print_button.active:
- print(data)
- return
- def printMemory(padindex):
- pad = Pads.padlist[padindex]
- printData("Pad %s" % padindex)
- if not pad.memory:
- printData("EMPTY MEMORY")
- for memory_tuple in pad.memory:
- printData(memory_tuple.getData())
- printData("")
- return
- ############################################################
- #
- # R E N D E R I N G
- #
- def drawText(surface, pos_x, pos_y, font_type, font_size, colour, message):
- if info_button.active:
- renderText(surface, pos_x, pos_y, font_type, font_size, colour, message)
- return
- def drawLine(surface, colour, start_pos, end_pos, width):
- if info_button.active:
- pygame.draw.aaline(surface, colour, start_pos, end_pos, width)
- return
- def drawCircle(surface, colour, position, radius, width):
- if info_button.active:
- pygame.draw.circle(surface, colour, position, radius, width)
- return
- def drawRect(surface, colour, area, width):
- if info_button.active:
- pygame.draw.rect(surface, colour, area, width)
- return
- def circsAtTargets(screen):
- padlist = Pads.padlist
- collision_data = getCollisionData()
- (last_collision_x, last_collision_y, last_collision_i_angle,
- last_collision_i_speed, last_collision_f_angle,
- last_collision_f_speed, _) = collision_data
- drawRect(screen, purple.toList(), [last_collision_x - 3,
- last_collision_y - 3,
- 6, 6], 1)
- colours = [cool_blue.toList(), green.toList(), red.toList(), orange.toList()]
- miss_circ_radius = 4
- more_tolerant_circ_radius = miss_circ_radius * 75
- less_tolerant_circ_radius = miss_circ_radius * 5
- displaying_text = False
- for i in range(len(padlist)):
- pad = padlist[i]
- if not pad.AI == 1:
- continue
- drawCircle(screen, colours[i], (int(pad.x_target), int(pad.y_target)), 5, 1)
- for memory_tuple in pad.memory:
- (x_miss, y_miss, x_collision, y_collision,
- i_angle, i_speed, f_angle, f_speed,
- collision_grav_coef) = memory_tuple.getData()
- colour = [cool_blue.toList(), green.toList(), red.toList(), orange.toList()][i]
- drawCircle(Screen.screen, colour, (int(x_miss), int(y_miss)), miss_circ_radius, 1)
- scale = 20
- x_move = f_speed * math.cos(degToRad(f_angle)) * scale
- y_move = f_speed * -math.sin(degToRad(f_angle)) * scale
- drawLine(screen, colour, (int(x_miss), int(y_miss)), (int(x_miss + x_move), int(y_miss + y_move)), 4)
- within_bounds = cursorWithinBounds(x_miss - more_tolerant_circ_radius / 2,
- x_miss + more_tolerant_circ_radius / 2,
- y_miss - more_tolerant_circ_radius / 2,
- y_miss + more_tolerant_circ_radius / 2)
- if within_bounds:
- drawLine(screen, colour, (int(x_collision), int(y_collision)), (int(x_miss), int(y_miss)), 4)
- within_bounds = cursorWithinBounds(x_miss - less_tolerant_circ_radius / 2,
- x_miss + less_tolerant_circ_radius / 2,
- y_miss - less_tolerant_circ_radius / 2,
- y_miss + less_tolerant_circ_radius / 2)
- if within_bounds and not displaying_text:
- displaying_text = True
- divergence_data = calculateDivergence(memory_tuple, collision_data)
- (total_divergence, x_divergence, y_divergence, f_angular_divergence,
- grav_divergence) = divergence_data
- total_divergence_string = "Total divergence: %.2f" % total_divergence
- x_divergence_string = "X divergence: %.2f" % x_divergence
- y_divergence_string = "Y divergence: %.2f" % y_divergence
- f_angular_divergence_string = "Angular divergence: %.2f" % f_angular_divergence
- grav_divergence_string = "Gravity divergence: %.2f" % grav_divergence
- if displaying_text:
- drawText(Screen.screen, window_width / 2, 200, "Free Sans Bold", font_size_16, white.toList(), total_divergence_string)
- drawText(Screen.screen, window_width / 2, 210, "Free Sans Bold", font_size_16, white.toList(), x_divergence_string)
- drawText(Screen.screen, window_width / 2, 220, "Free Sans Bold", font_size_16, white.toList(), y_divergence_string)
- drawText(Screen.screen, window_width / 2, 230, "Free Sans Bold", font_size_16, white.toList(), f_angular_divergence_string)
- drawText(Screen.screen, window_width / 2, 240, "Free Sans Bold", font_size_16, white.toList(), grav_divergence_string)
- return
- def renderText(surface, xpos, ypos, font_type, font_size, font_colour, message, fromCentre=True):
- if font_type.lower() == "angelic war" and font_size == font_size_14:
- font = FontTypes.Angelic_War_14_font
- elif font_type.lower() == "angelic war" and font_size == font_size_40:
- font = FontTypes.Angelic_War_40_font
- elif font_type.lower() == "times new roman" and font_size == font_size_18:
- font = FontTypes.Times_New_Roman_18_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_11:
- font = FontTypes.Free_Sans_Bold_10_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_12:
- font = FontTypes.Free_Sans_Bold_12_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_14:
- font = FontTypes.Free_Sans_Bold_14_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_16:
- font = FontTypes.Free_Sans_Bold_16_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_18:
- font = FontTypes.Free_Sans_Bold_18_font
- elif font_type.lower() == "free sans bold" and font_size == font_size_30:
- font = FontTypes.Free_Sans_Bold_30_font
- elif font_type.lower() == "birth of a hero" and font_size == font_size_30:
- font = FontTypes.Birth_Of_A_Hero_30_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_12:
- font = FontTypes.Sergeant_SixPack_12_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_14:
- font = FontTypes.Sergeant_SixPack_14_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_16:
- font = FontTypes.Sergeant_SixPack_16_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_18:
- font = FontTypes.Sergeant_SixPack_18_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_20:
- font = FontTypes.Sergeant_SixPack_20_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_22:
- font = FontTypes.Sergeant_SixPack_22_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_24:
- font = FontTypes.Sergeant_SixPack_24_font
- elif font_type.lower() == "sergeant sixpack" and font_size == font_size_26:
- font = FontTypes.Sergeant_SixPack_26_font
- else:
- path = pygame.font.match_font(font_type)
- font = pygame.font.Font(path, font_size)
- print("Font not found")
- if fromCentre:
- (width, height) = font.size(message)
- xpos -= width / 2
- ypos -= height / 2
- textsurface = font.render(message, True, font_colour)
- surface.blit(textsurface, (xpos, ypos))
- return
- def drawObjects(screen):
- padblue = Pads.padlist[0]
- padgreen = Pads.padlist[1]
- padred = Pads.padlist[2]
- padorange = Pads.padlist[3]
- screen.blit(padblue.surface, (padblue.x, padblue.y))
- screen.blit(padorange.surface, (padorange.x, padorange.y))
- screen.blit(padred.surface, (padred.x, padred.y))
- screen.blit(padgreen.surface, (padgreen.x, padgreen.y))
- ball = Balls.ball
- ballcirc = pygame.draw.circle(screen, white.toList(), (int(ball.x - ball_radius/2), int(ball.y - ball_radius/2)), ball_radius, 1)
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- (padxmid, padymid) = getPadMidpoint(i)
- mid_offset = 5
- if i == 0:
- padxmid -= mid_offset
- elif i == 1:
- padymid += mid_offset
- elif i == 2:
- padxmid += mid_offset
- else:
- padymid -= mid_offset
- renderText(screen, padxmid, padymid, "Free Sans Bold", font_size_18, white.toList(), str(pad.score))
- controller = {1:"ADAPTIVE COMP", 0.5:"STATIC COMP", 0:"HUMAN"}[pad.AI]
- offset = 5
- padxmin = pad.x
- padxmax = pad.x + pad_width * ((i+1) % 2) + pad_height * (i % 2)
- padymin = pad.y
- padymax = pad.y + pad_height * ((i+1) % 2) + pad_width * (i % 2)
- if i == 0:
- x_text = padxmin - offset
- y_text = padymin - offset
- elif i == 1:
- x_text = padxmin - offset
- y_text = padymax + offset
- elif i == 2:
- x_text = padxmax + offset
- y_text = padymin - offset
- else:
- x_text = padxmin - offset
- y_text = padymin - offset
- renderText(screen, x_text, y_text, "Free Sans Bold", font_size_11, [35,35,35], controller)
- drawLauncher(screen)
- drawSpeaker(screen)
- drawPauseButton(screen)
- drawLetterButton("i", screen)
- drawLetterButton("t", screen)
- drawFastForwardButton(screen)
- drawGearButton(screen)
- drawWeightInfo(screen)
- drawScore(screen)
- drawScoreMessage(screen)
- gravity_ring.construct()
- return
- def drawWeightInfo(screen):
- current_weight = "Weight: %.4f" % WeightData.current_weight
- current_collisions = "Current Score: %s" % WeightData.current_collisions
- current_game = "Current Game: %s" % WeightData.current_game
- current_generation = "Generation: %s" % WeightData.generation
- renderText(screen, window_width - 110, 20, "Free Sans Bold", font_size_16, white.toList(), current_weight, False)
- if GameState.toggle_genetic:
- renderText(screen, window_width - 110, 30, "Free Sans Bold", font_size_16, white.toList(), current_collisions, False)
- renderText(screen, window_width - 110, 40, "Free Sans Bold", font_size_16, white.toList(), current_game, False)
- renderText(screen, window_width - 110, 50, "Free Sans Bold", font_size_16, white.toList(), current_generation, False)
- return
- def drawFastForwardCover(screen, ticks, real_ticks):
- if not GameState.fast_forward_cover_loaded:
- loadFastForwardCover()
- elif not real_ticks % 500:
- loadFastForwardCover()
- drawSpeaker(screen)
- drawPauseButton(screen)
- drawFastForwardButton(screen)
- ticks = "Ticks: %s" % ticks
- renderText(screen, window_width / 2, 300, "Free Sans Bold", font_size_18, white.toList(), ticks)
- current_weight = "Weight: %.4f" % WeightData.current_weight
- current_collisions = "Current Score: %s" % WeightData.current_collisions
- current_game = "Current Game: %s" % WeightData.current_game
- current_generation = "Generation: %s" % WeightData.generation
- renderText(screen, window_width / 2, 320, "Free Sans Bold", font_size_18, white.toList(), current_weight)
- if GameState.toggle_genetic:
- renderText(screen, window_width / 2, 335, "Free Sans Bold", font_size_18, white.toList(), current_collisions)
- renderText(screen, window_width / 2, 350, "Free Sans Bold", font_size_18, white.toList(), current_game)
- renderText(screen, window_width / 2, 365, "Free Sans Bold", font_size_18, white.toList(), current_generation)
- padlist = Pads.padlist
- pad_green_score = "Green Pad Score: %s" % padlist[0].score
- pad_blue_score = "Blue Pad Score: %s" % padlist[1].score
- pad_orange_score = "Orange Pad Score: %s" % padlist[2].score
- pad_red_score = "Red Pad Score: %s" % padlist[3].score
- renderText(screen, window_width / 2, 420, "Free Sans Bold", font_size_18, white.toList(), pad_green_score)
- renderText(screen, window_width / 2, 435, "Free Sans Bold", font_size_18, white.toList(), pad_blue_score)
- renderText(screen, window_width / 2, 450, "Free Sans Bold", font_size_18, white.toList(), pad_orange_score)
- renderText(screen, window_width / 2, 465, "Free Sans Bold", font_size_18, white.toList(), pad_red_score)
- offset = 6
- update_area = [200 + offset, 200 + offset, 300 - offset * 2, 500 - offset * 2]
- pygame.display.update(update_area)
- return
- def drawLauncher(screen):
- launcher = Launchers.launcher
- launcher_colour = launcher.getColour()
- launcher_radius = launcher.getRadius()
- xlauncher = int(launcher.x)
- ylauncher = int(launcher.y)
- if not pause_button.active:
- launcher.coolDown()
- pygame.draw.circle(screen, launcher_colour, (xlauncher, ylauncher), launcher_radius, 1)
- return
- def drawSpeaker(screen):
- speaker_icon = sound_level.surface
- sound_level.setCoverTransparency()
- xspeaker = sound_level.pos_x
- yspeaker = sound_level.pos_y
- screen.blit(speaker_icon, (xspeaker, yspeaker))
- screen.blit(sound_level.cover, (xspeaker, yspeaker))
- return
- def drawPauseButton(screen):
- pause_surface.fill(black.toList())
- if pause_button.active:
- pause_surface.set_alpha(255 * 0.85)
- fill_val = 0
- elif pause_button.hovering:
- pause_surface.set_alpha(255 * 0.85)
- fill_val = 1
- else:
- pause_surface.set_alpha(255 * 0.5)
- fill_val = 1
- pygame.draw.rect(pause_surface, white.toList(), [0,
- 0,
- pause_button.width * (1/3),
- pause_button.height], fill_val)
- pygame.draw.rect(pause_surface, white.toList(), [pause_button.width * (2/3),
- 0,
- pause_button.width * (1/3),
- pause_button.height], fill_val)
- screen.blit(pause_surface, (pause_button.pos_x, pause_button.pos_y))
- return
- def drawLetterButton(letter, screen):
- if letter.lower() == "i":
- button = info_button
- elif letter.lower() == "t":
- button = print_button
- else:
- assert False
- letter_surface = button.surface
- letter_cover = button.cover
- if button.active:
- letter_cover.set_alpha(0)
- elif button.hovering:
- letter_cover.set_alpha(255 * 0.25)
- else:
- letter_cover.set_alpha(255 * 0.5)
- x_surface = button.pos_x
- y_surface = button.pos_y
- screen.blit(letter_surface, (x_surface, y_surface))
- screen.blit(letter_cover, (x_surface, y_surface))
- return
- def drawFastForwardButton(screen):
- surface = fast_forward.surface
- cover = fast_forward.cover
- if fast_forward.active:
- cover.set_alpha(0)
- elif fast_forward.hovering:
- cover.set_alpha(255 * 0.25)
- else:
- cover.set_alpha(255 * 0.5)
- x_surface = fast_forward.pos_x
- y_surface = fast_forward.pos_y
- screen.blit(surface, (x_surface, y_surface))
- screen.blit(cover, (x_surface, y_surface))
- return
- def drawGearButton(screen):
- surface = gear_button.surface
- cover = gear_button.cover
- if gear_button.active:
- cover.set_alpha(0)
- elif gear_button.hovering:
- cover.set_alpha(255 * 0.25)
- else:
- cover.set_alpha(255 * 0.5)
- x_surface = gear_button.pos_x
- y_surface = gear_button.pos_y
- screen.blit(surface, (x_surface, y_surface))
- screen.blit(cover, (x_surface, y_surface))
- return
- def drawSettingsMenu(screen):
- surface = SettingsMenu.modified_surface
- for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
- if i >= len(SettingOptions.options):
- break
- option = SettingOptions.options[i]
- option.construct()
- surface.blit(option.surface, (option.pos_x, option.pos_y))
- for arrow in [arrow_left, arrow_right]:
- surface.blit(arrow.surface, (arrow.pos_x, arrow.pos_y))
- x_text = surface.get_width() / 2 + 1
- y_text = arrow_left.pos_y + 6
- renderText(surface, x_text, y_text, "Free Sans Bold", font_size_14, white.toList(), str(SettingsMenu.current_page))
- x_surface = (window_width - surface.get_width()) / 2
- y_surface = (window_height - surface.get_height()) / 2
- screen.blit(surface, (x_surface, y_surface))
- ResetSettingsMenu()
- return
- def drawScore(screen):
- score = GameState.collision_count
- max_score = GameState.max_count
- if max_score:
- ratio = 1 - score / max_score
- else:
- ratio = 1
- colour = crossColours(red, green, ratio)
- offset = 25
- renderText(screen, window_width / 2, window_height / 2 + offset, "Free Sans Bold", font_size_16, colour.toList(), str(score))
- renderText(screen, window_width / 2, window_height / 2 - offset, "Free Sans Bold", font_size_16, white.toList(), str(max_score))
- return
- def drawScoreMessage(screen):
- score_message = ""
- message_threshold = 0
- message_frames_left = 0
- alpha = 255
- for i in range(len(GameState.score_messages_list)):
- message = GameState.score_messages_list[i][0]
- frames_left = GameState.score_messages[message]
- if frames_left:
- alpha = int(255 * frames_left / frames_per_score_message)
- GameState.score_messages[message] = frames_left - 1
- score_message = message
- message_index = i
- message_frames_left = frames_left
- if score_message:
- colour = crossColours(crossColours(green, white, message_index / 7), black, message_frames_left / frames_per_score_message)
- font_size = 12 + i * 2
- renderText(screen, window_width / 2, window_height / 2 - 100, "Sergeant SixPack", font_size, colour.toList(), score_message)
- return
- def handleScoreMessages():
- score = GameState.collision_count
- for message, threshold in score_message_thresholds.items():
- if score == threshold:
- GameState.score_messages[message] = frames_per_score_message
- return
- ############################################################
- #
- # H A N D L E M E N U S A N D C O V E R S
- #
- def ResetSettingsMenu():
- SettingsMenu.modified_surface = SettingsMenu.default_surface.copy()
- return
- def setFastForwardCoverStatus(boolean):
- GameState.fast_forward_cover_loaded = boolean
- if boolean:
- sound_level.pos_x = 214
- sound_level.pos_y = 210
- pause_button.pos_x = 250
- pause_button.pos_y = 214
- fast_forward.pos_x = 278
- fast_forward.pos_y = 211
- else:
- sound_level.pos_x = 10
- sound_level.pos_y = 10
- pause_button.pos_x = 50
- pause_button.pos_y = 14
- fast_forward.pos_x = 79
- fast_forward.pos_y = 11
- return
- def loadFastForwardCover():
- cover = FastForwardCover.surface
- Screen.screen.blit(cover, (0, 0))
- pygame.display.flip()
- setFastForwardCoverStatus(True)
- return
- ############################################################
- #
- # K E Y B O A R D H A N D L I N G
- #
- def keyPressed(key):
- if key == K_ESCAPE:
- if gear_button.active:
- gear_button.switch()
- pause_button.switch()
- else:
- GameState.done = True
- return
- if gear_button.active:
- if key == K_RIGHT:
- SettingsMenu.current_page = min(SettingsMenu.total_pages,
- SettingsMenu.current_page + 1)
- elif key == K_LEFT:
- SettingsMenu.current_page = max(1, SettingsMenu.current_page - 1)
- if key == K_1:
- sound_level.changeSoundLevel()
- return
- elif key == K_2:
- pause_button.switch()
- return
- elif key == K_3:
- fast_forward.switch()
- if not fast_forward.active:
- setFastForwardCoverStatus(False)
- return
- elif key == K_4 and not fast_forward.active:
- info_button.switch()
- return
- elif key == K_5 and not fast_forward.active:
- print_button.switch()
- return
- elif key == K_6 and not fast_forward.active:
- gear_button.switch()
- pause_button.active = gear_button.active
- return
- if gear_button.active:
- return
- if key == K_a:
- KeyBools.aPressed = True
- elif key == K_d:
- KeyBools.dPressed = True
- elif key == K_i:
- KeyBools.iPressed = True
- elif key == K_k:
- KeyBools.kPressed = True
- elif key == K_LEFT:
- KeyBools.leftPressed = True
- elif key == K_RIGHT:
- KeyBools.rightPressed = True
- elif key == K_KP2:
- KeyBools.kp2Pressed = True
- elif key == K_KP8:
- KeyBools.kp8Pressed = True
- return
- def keyReleased(key):
- if key == K_a:
- KeyBools.aPressed = False
- elif key == K_d:
- KeyBools.dPressed = False
- elif key == K_i:
- KeyBools.iPressed = False
- elif key == K_k:
- KeyBools.kPressed = False
- elif key == K_LEFT:
- KeyBools.leftPressed = False
- elif key == K_RIGHT:
- KeyBools.rightPressed = False
- elif key == K_KP2:
- KeyBools.kp2Pressed = False
- elif key == K_KP8:
- KeyBools.kp8Pressed = False
- return
- ############################################################
- #
- # M O T I O N H A N D L I N G
- #
- def handleMotion():
- padblue = Pads.padlist[0]
- padgreen = Pads.padlist[1]
- padred = Pads.padlist[2]
- padorange = Pads.padlist[3]
- if not padgreen.AI:
- if KeyBools.aPressed:
- padgreen.move(-pad_speed * GameState.pad_speed_mult, 0)
- if KeyBools.dPressed:
- padgreen.move(pad_speed * GameState.pad_speed_mult, 0)
- if not KeyBools.aPressed and not KeyBools.dPressed:
- padgreen.moving = (0, 0)
- if not padblue.AI:
- if KeyBools.iPressed:
- padblue.move(0, -pad_speed * GameState.pad_speed_mult)
- if KeyBools.kPressed:
- padblue.move(0, pad_speed * GameState.pad_speed_mult)
- if not KeyBools.iPressed and not KeyBools.kPressed:
- padblue.moving = (0, 0)
- if not padorange.AI:
- if KeyBools.leftPressed:
- padorange.move(-pad_speed * GameState.pad_speed_mult, 0)
- if KeyBools.rightPressed:
- padorange.move(pad_speed * GameState.pad_speed_mult, 0)
- if not KeyBools.leftPressed and not KeyBools.rightPressed:
- padorange.moving = (0, 0)
- if not padred.AI:
- if KeyBools.kp8Pressed:
- padred.move(0, -pad_speed * GameState.pad_speed_mult)
- if KeyBools.kp2Pressed:
- padred.move(0, pad_speed * GameState.pad_speed_mult)
- if not KeyBools.kp8Pressed and not KeyBools.kp2Pressed:
- padred.moving = (0, 0)
- Balls.ball.move()
- gravity_ring.pumpCircs()
- gravity_ring.modifyRadius()
- gravity_ring.handleGravFocals()
- return
- ############################################################
- #
- # C O L L I S I O N H A N D L I N G
- #
- def collisionHandling(ball):
- collision = False
- (x_translate, y_translate) = ball.getVelocityComponents()
- xnew = ball.x + x_translate
- ynew = ball.y + y_translate
- padlist = Pads.padlist
- for i in range(len(padlist)):
- pad = padlist[i]
- if ball.last_pad == pad:
- continue
- (padxmid, padymid) = getPadMidpoint(i)
- padxmin = pad.x
- padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
- padymin = pad.y
- padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
- inwards_reflect_normal = i * 90
- side_reflect_normal = (i+1) % 4 * 90
- tolerance = 0.01 * pad_height
- if objectWithinBounds(xnew, ynew, padxmin, padxmax, padymin, padymax):
- collision = pad
- if i == 0 or i == 2:
- #'side_reflect_normal' if ball collides with bottom / top side.
- #'inwards_reflect_normal' if ball collides with inwards-facing surface
- if (ynew > padymax - tolerance or ynew < padymin + tolerance):
- if i == 0 and xnew < padxmax and xnew > padxmax - tolerance:
- normal = inwards_reflect_normal
- elif i == 0:
- normal = side_reflect_normal
- elif i == 2 and xnew > padxmin and xnew < padxmin + tolerance:
- normal = inwards_reflect_normal
- else:
- normal = side_reflect_normal
- else:
- normal = inwards_reflect_normal
- reflected_angle = (plane_reflect_angles[normal] - ball.angle) % 360
- else:
- #'side_reflect_normal' if ball collides with left / right side.
- #'inwards_reflect_normal' if ball collides with inwards-facing surface
- if (xnew > padxmax - tolerance or xnew < padxmin + tolerance) and (i == 1 and ynew > padymin or i == 3 and ynew < padymax):
- if i == 1 and ynew > padymin and ynew < padymin + tolerance:
- normal = inwards_reflect_normal
- elif i == 1:
- normal = side_reflect_normal
- elif i == 3 and ynew < padymax and ynew > padymax - tolerance:
- normal = inwards_reflect_normal
- else:
- normal = side_reflect_normal
- else:
- normal = inwards_reflect_normal
- reflected_angle = (plane_reflect_angles[normal] - ball.angle) % 360
- break
- if collision:
- if WeightData.current_game < games_per_test:
- WeightData.current_collisions += 1
- GameState.collision_count += 1
- handleScoreMessages()
- if GameState.collision_count > GameState.max_count:
- GameState.max_count = GameState.collision_count
- modifier = 0.1
- (x_padmove, y_padmove) = collision.moving
- initial_angle = ball.angle
- initial_velocity = ball.velocity
- ball.angle = reflected_angle
- (x_ballmove, y_ballmove) = ball.getVelocityComponents()
- final_angle = angleFromComponents(x_ballmove + x_padmove * modifier, y_ballmove + y_padmove * modifier)
- final_velocity = velocityFromComponents(x_ballmove + x_padmove * modifier, y_ballmove + y_padmove * modifier)
- ball.angle = final_angle
- ball.velocity = final_velocity
- updateCollisionData(ball.x, ball.y, initial_angle, initial_velocity, final_angle, final_velocity)
- ball.last_pad = collision
- return
- def updateCollisionData(x, y, i_angle, i_speed, f_angle, f_speed):
- Collision.xpos = x
- Collision.ypos = y
- Collision.initial_angle = i_angle
- Collision.initial_speed = i_speed
- Collision.final_angle = f_angle
- Collision.final_speed = f_speed
- if gravity_ring.active:
- Collision.grav_coef = gravity_ring.grav_coef
- else:
- Collision.grav_coef = 0
- updatePadTargets()
- return
- def getCollisionData():
- collision_data = (Collision.xpos, Collision.ypos, Collision.initial_angle,
- Collision.initial_speed, Collision.final_angle,
- Collision.final_speed, Collision.grav_coef)
- return collision_data
- ############################################################
- #
- # B A L L G E N E R A T I O N
- #
- def genNewBall():
- if WeightData.current_game < games_per_test:
- WeightData.current_game += 1
- elif GameState.toggle_genetic:
- beginNewTest()
- velocity = random.random()*0.3+0.3
- angle = 0
- while angle % 90 < 20:
- angle = random.randrange(360)
- ball = Ball(window_width/2, window_height/2, velocity, angle)
- Balls.ball = ball
- printData("ANGLE BALL LAUNCHED AT: %s" % angle)
- updateCollisionData(ball.x, ball.y, angle, velocity, angle, velocity)
- return
- ############################################################
- #
- # M O U S E I N P U T
- #
- def cursorWithinBounds(low_x, high_x, low_y, high_y):
- (mouse_x, mouse_y) = pygame.mouse.get_pos()
- if low_x <= mouse_x and mouse_x <= high_x:
- if low_y <= mouse_y and mouse_y <= high_y:
- return True
- return False
- def hoverHandler(icon):
- hovering_over = cursorWithinBounds(icon.pos_x, icon.pos_x + icon.width,
- icon.pos_y, icon.pos_y + icon.height)
- return hovering_over
- def setHover(icon):
- if hoverHandler(icon):
- icon.hovering = True
- else:
- icon.hovering = False
- return
- def passiveMouseHandler():
- #(mouse_x, mouse_y) = pygame.mouse.get_pos()
- setHover(sound_level)
- setHover(pause_button)
- setHover(info_button)
- setHover(print_button)
- setHover(fast_forward)
- setHover(gear_button)
- setHover(arrow_left)
- setHover(arrow_right)
- if gear_button.active:
- x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
- y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
- for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
- if i >= len(SettingOptions.options):
- break
- option = SettingOptions.options[i]
- option.pos_x += x_offset
- option.pos_y += y_offset
- setHover(option)
- option.pos_x -= x_offset
- option.pos_y -= y_offset
- return
- def activeMouseHandler(position, button):
- if hoverHandler(sound_level):
- sound_level.changeSoundLevel()
- if hoverHandler(pause_button):
- pause_button.switch()
- if hoverHandler(info_button):
- info_button.switch()
- if hoverHandler(print_button):
- print_button.switch()
- if hoverHandler(fast_forward):
- fast_forward.switch()
- if not fast_forward.active:
- setFastForwardCoverStatus(False)
- if hoverHandler(gear_button):
- gear_button.switch()
- pause_button.active = gear_button.active
- if gear_button.active:
- x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
- y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
- for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
- if i >= len(SettingOptions.options):
- break
- option = SettingOptions.options[i]
- option.pos_x += x_offset
- option.pos_y += y_offset
- if hoverHandler(option):
- option.switch()
- option.pos_x -= x_offset
- option.pos_y -= y_offset
- for arrow in [arrow_left, arrow_right]:
- arrow.pos_x += x_offset
- arrow.pos_y += y_offset
- if hoverHandler(arrow):
- if arrow == arrow_left:
- SettingsMenu.current_page = max(1, SettingsMenu.current_page - 1)
- else:
- SettingsMenu.current_page = min(SettingsMenu.total_pages, SettingsMenu.current_page + 1)
- arrow.pos_x -= x_offset
- arrow.pos_y -= y_offset
- return
- ############################################################
- #
- # S E T T I N G S H A N D L I N G
- #
- def SettingOptionsHandler():
- for option in SettingOptions.options:
- option.adjustSize()
- option.adjustColour()
- return
- ############################################################
- #
- # M A I N G A M E L O O P
- #
- def main():
- pygame.init()
- screen = pygame.display.set_mode((window_width, window_height), 0, 32)
- Screen.screen = screen
- pygame.display.set_caption("PyBall")
- initialiseFonts()
- initialiseSession()
- beginNewGeneration()
- text_colour_val = 255
- demonstration_begun = False
- return_pressed = False
- ticks = 0
- real_ticks = 0
- while not GameState.done:
- real_ticks += 1
- screen.fill(black.toList())
- sound_level.adjustVolume()
- passiveMouseHandler()
- SettingOptionsHandler()
- events = pygame.event.get()
- for e in events:
- if e.type == QUIT:
- GameState.done = True
- break
- elif not demonstration_begun and e.type == KEYDOWN and e.key == K_RETURN:
- return_pressed = True
- elif e.type == KEYDOWN and demonstration_begun:
- keyPressed(e.key)
- elif e.type == KEYUP and demonstration_begun:
- keyReleased(e.key)
- elif e.type == MOUSEBUTTONDOWN:
- activeMouseHandler(e.pos, e.button)
- if text_colour_val:
- text_colour = [text_colour_val, text_colour_val, text_colour_val]
- renderText(screen, window_width / 2, 120, "Angelic War", font_size_40, text_colour, "PyBall")
- renderText(screen, window_width / 2, 160, "Angelic War", font_size_14, text_colour, "Created By Tag")
- if return_pressed:
- text_colour_val -= 1
- drawObjects(screen)
- pygame.display.flip()
- else:
- demonstration_begun = True
- if gear_button.active:
- pause_button.active = True
- fast_forward.active = False
- if info_button.active:
- circsAtTargets(screen)
- drawObjects(screen)
- drawSettingsMenu(screen)
- renderText(screen, window_width / 2, 160, "Birth Of A Hero", font_size_30, white.toList(), "S E T T I N G S")
- pygame.display.flip()
- elif pause_button.active and not fast_forward.active:
- renderText(screen, window_width / 2, 160, "Birth Of A Hero", font_size_30, white.toList(), "P A U S E D")
- if info_button.active:
- circsAtTargets(screen)
- drawObjects(screen)
- pygame.display.flip()
- elif fast_forward.active and not pause_button.active:
- ticks += 1
- drawFastForwardCover(screen, ticks, real_ticks)
- handleAI()
- handleMotion()
- elif fast_forward.active and pause_button.active:
- drawFastForwardCover(screen, ticks, real_ticks)
- else:
- if gravity_ring.active:
- renderText(screen, window_width / 2, 160, "Birth Of A Hero", font_size_30, white.toList(), "G R A V I T Y A C T I V E")
- ticks += 1
- handleAI()
- handleMotion()
- if info_button.active:
- circsAtTargets(screen)
- drawObjects(screen)
- pygame.display.flip()
- if GameState.done:
- pygame.quit()
- return
- if __name__ == "__main__":
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement