Advertisement
TAGC

PyBall

Feb 19th, 2012
181
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 292.68 KB | None | 0 0
  1. import os
  2. import io
  3. import shutil
  4. import mathimport os
  5. import io
  6. import shutilimport os
  7. import io
  8. import shutil
  9. import math
  10. import time
  11. import random
  12. import pygame
  13. from pygame.locals import *
  14.  
  15. ###################################
  16. # P Y B A L L
  17.  
  18. # PyBall - A Pong-Based Adaptive AI Demonstration / Game
  19. # Created by TAGC
  20. # Last update: 20/03/2012
  21. # Description: This program is developed as part of an
  22. #              assessed project at Imperial College, London
  23. #              to demonstrate the use of AI in gaming and
  24. #              contrast the use of static and dynamic
  25. #              AI techniques to allow further analysis into
  26. #              how different kinds of AI behaviour can affect
  27. #              replayability and game immersion
  28. # Notes:       This script will only run if it comes as part
  29. #              of the "PyBallStuff" package. This package
  30. #              contains the audio, image and font files used
  31. #              by this program
  32.  
  33. ###################################
  34. # I N S T A L L A T I O N
  35.  
  36. # This script is written in Python 3.2 and requires a Python 3
  37. # interpreter to run. This can be obtained from
  38. # http://www.python.org/download/ (it is strongly recommended
  39. # that the user obtains the 32-bit version even if they have
  40. # a 64-bit operating system)
  41.  
  42. # This script also requires an installation of the python
  43. # module "PyGame" available at
  44. # http://www.pygame.org/download.shtml (again, it is strongly
  45. # recommended that the user obtains the 32-bit version)
  46.  
  47. # In addition to these, the user will need to install the
  48. # following fonts:
  49. # > "Birth of a Hero"
  50. # > "Angelic War"
  51. # > "Hawaii Lover"
  52. # > "Sergeant SixPack"
  53. # These are all available at http://www.1001freefonts.com/
  54. # if the user does not possess the "Fonts" folder included
  55. # inside the folder "PyBallStuff"
  56.  
  57. # The "PyBallStuff" folder must be placed in the
  58. # "C:/Python32" directory
  59.  
  60. ###################################
  61. # L A U N C H I N G
  62.  
  63. # To execute the program, click on "Run" on the top bar
  64. # of the interpreter and select "Run Module" or press F5
  65.  
  66. ###################################
  67. # C O N T R O L S
  68.  
  69. # Special Controls
  70. ###################
  71. # "Enter"  - begins the game after the program launches
  72. # "Escape" - if the user has the Settings Menu open, this
  73. #            exits that screen, otherwise it quits the
  74. #            programs
  75.  
  76. # Setting Controls
  77. ###################
  78. # "1"      - Toggles through the sound level settings
  79. # "2"      - Pauses / unpauses the program
  80. # "3"      - Toggles Fast Forward mode on and off; when
  81. #            this mode is active, the logic of the game
  82. #            runs slightly faster
  83. # "4"      - Toggles Graphical Information mode on and off;
  84. #            when on, extra objects are rendered to the
  85. #            screen to display more information on the
  86. #            states of the pads' memories, etc
  87. # "5"      - Toggles Textual Information mode on and off;
  88. #            when on, information on the game is printed
  89. #            out to the console
  90. # "6"      - Opens or closes the Settings Menu
  91. # "Left"   - when the Settings Menu is open, this displays
  92. #            the previous page of options (if not already
  93. #            there)
  94. # "Right"  - when the Settings Menu is open, this displays
  95. #            the next page of options (if not already
  96. #            there)
  97.  
  98. # Pad Controls
  99. ###############
  100. # "A"      - if the green pad is under human control, moves
  101. #            that pad left
  102. # "D"      - if the green pad is under human control, moves
  103. #            that pad right
  104. # "I"      - if the blue pad is under human control, moves
  105. #            that pad up
  106. # "K"      - if the blue pad is under human control, moves
  107. #            that pad down
  108. # "Left"   - if the red pad is under human control, moves
  109. #            that pad left
  110. # "Right"  - if the red pad is under human control, moves
  111. #            that pad right
  112. # "Num 8"  - if the orange pad is under human control, moves
  113. #            that pad up
  114. # "Num 2"  - if the orange pad is under human control, moves
  115. #            that pad down
  116.  
  117. window_width  = 700
  118. window_height = 700
  119.  
  120. pad_width   = 25
  121. pad_height  = 100
  122. ball_radius = 2
  123.  
  124. corner_buffer_coef = 0.20
  125.  
  126. population_size      = 20
  127. games_per_test       = 50
  128.  
  129. xmin = window_width  * corner_buffer_coef     - pad_height / 2
  130. xmax = window_width  * (1-corner_buffer_coef) - pad_height / 2
  131. ymin = window_height * corner_buffer_coef     - pad_height / 2
  132. ymax = window_height * (1-corner_buffer_coef) - pad_height / 2
  133.  
  134. plane_reflect_angles = {0:180, 90:360, 180:180, 270:360}
  135.  
  136. pad_speed = 0.5
  137.  
  138. ball_accel = 0.00001
  139.  
  140. max_x_difference       = window_width
  141. max_y_difference       = window_height
  142. max_angular_difference = 180
  143. max_gravity_difference = 100
  144.  
  145. pyball_stuff_path = r"C:\Python32\PyBallStuff\\"
  146. pad_image_path    = pyball_stuff_path + r"Pictures\Pads"
  147. sound_level_path  = pyball_stuff_path + r"Pictures\SoundLevels"
  148. letter_path       = pyball_stuff_path + r"Pictures\Letters"
  149. picture_path      = pyball_stuff_path + r"Pictures"
  150. music_path        = pyball_stuff_path + r"Audio"
  151. pad_memory_path   = pyball_stuff_path + r"Memories"
  152. data_path         = pyball_stuff_path + r"Data"
  153.  
  154. font_size_11   = 11
  155. font_size_12   = 12
  156. font_size_14   = 14
  157. font_size_16   = 16
  158. font_size_18   = 18
  159. font_size_20   = 20
  160. font_size_22   = 22
  161. font_size_24   = 24
  162. font_size_26   = 26
  163. font_size_30   = 30
  164. font_size_40   = 40
  165.  
  166. frames_per_score_message = 1000
  167. score_message_thresholds = {"Okay":5, "Good":10, "Great":15, "Excellent!":20,
  168.                            "Superb!":25, "Amazing!":30, "Outstanding!":50,
  169.                            "Unbelievable!":100}
  170.  
  171. class Colour:
  172.     def __init__(self, r, g, b):
  173.         self.r = r
  174.         self.g = g
  175.         self.b = b
  176.         return
  177.     def toList(self):
  178.         listrep = [self.r, self.g, self.b]
  179.         return listrep
  180.  
  181. class Pad:
  182.     def __init__(self, x, y, surface):
  183.         self.x        = x
  184.         self.y        = y
  185.         self.x_target = x
  186.         self.y_target = y
  187.         self.surface  = surface
  188.         self.moving   = (0, 0)
  189.         self.AI       = 1
  190.         self.score    = 0
  191.         self.memory   = []
  192.         return
  193.     def move(self, x_translate, y_translate):
  194.         self.moving = (x_translate, y_translate)
  195.         self.xnew   = self.x + x_translate
  196.         self.ynew   = self.y + y_translate
  197.         if xmin <= self.xnew and self.xnew <= xmax:
  198.             self.x = self.xnew
  199.         else:
  200.             x_translate = 0
  201.         if ymin <= self.ynew and self.ynew <= ymax:
  202.             self.y = self.ynew
  203.         else:
  204.             y_translate = 0
  205.         self.moving = (x_translate, y_translate)
  206.         return
  207.     def losePoint(self, x_miss, y_miss):
  208.         self.score -= 1
  209.         if GameState.toggle_learning and Balls.ball.last_pad != self and self.AI == 1:
  210.             memory_tuple = MemoryTuple(x_miss, y_miss)
  211.             printData("SAVING TO MEMORY...")
  212.             printData(memory_tuple.getData())
  213.             printData("")
  214.             self.memory.append(memory_tuple)
  215.         return
  216.     def toString(self):
  217.         stringrep = ("(%s, %s)") % (self.x, self.y)
  218.         return stringrep
  219.    
  220. class Ball:
  221.     def __init__(self, x, y, velocity, angle):
  222.         self.x        = x
  223.         self.y        = y
  224.         self.velocity = velocity
  225.         self.angle    = angle
  226.         self.last_pad = -1
  227.         return
  228.     def move(self):
  229.         cut_off_modifier = 0.9
  230.         collisionHandling(self)
  231.  
  232.         (pad_0_x, pad_0_y) = getPadMidpoint(0)
  233.         (pad_1_x, pad_1_y) = getPadMidpoint(1)
  234.         (pad_2_x, pad_2_y) = getPadMidpoint(2)
  235.         (pad_3_x, pad_3_y) = getPadMidpoint(3)
  236.        
  237.         if self.x < pad_0_x * cut_off_modifier:
  238.             Pads.padlist[0].losePoint(self.x, self.y)
  239.             Launchers.launcher.launchBall()
  240.         elif self.x > window_width - (window_width - pad_2_x) * cut_off_modifier:
  241.             Pads.padlist[2].losePoint(self.x, self.y)
  242.             Launchers.launcher.launchBall()
  243.         elif self.y < pad_3_y * cut_off_modifier:
  244.             Pads.padlist[3].losePoint(self.x, self.y)
  245.             Launchers.launcher.launchBall()
  246.         elif self.y > window_height - (window_height - pad_1_y) * cut_off_modifier:
  247.             Pads.padlist[1].losePoint(self.x, self.y)
  248.             Launchers.launcher.launchBall()
  249.         else:
  250.             (self.x_translate, self.y_translate) = self.getVelocityComponents()
  251.             if gravity_ring.active:
  252.                 target = gravity_ring
  253.                 grav_pull_angle = degToRad(angleBetweenPoints((self.x, self.y),
  254.                                                               (target.pos_x, target.pos_y)))
  255.  
  256.                 distance = getPointDistance(self.x, self.y, target.pos_x, target.pos_y)
  257.                 grav_x = gravity_ring.grav_coef / 2000 * math.cos(grav_pull_angle) * math.pow(0.96, distance)
  258.                 grav_y = gravity_ring.grav_coef / 2000 * math.sin(grav_pull_angle) * math.pow(0.96, distance)
  259.                 if abs(self.x_translate + grav_x) < abs(self.x_translate):
  260.                     grav_x *= 0.3
  261.                 if abs(self.y_translate - grav_y) < abs(self.y_translate):
  262.                     grav_y *= 0.3
  263.                 self.velocity = velocityFromComponents(self.x_translate + grav_x,
  264.                                                        self.y_translate - grav_y)
  265.                 self.angle    = angleFromComponents(self.x_translate + grav_x,
  266.                                                     self.y_translate - grav_y)
  267.                
  268.         self.velocity += GameState.ball_accel
  269.         self.x += self.x_translate
  270.         self.y += self.y_translate
  271.         return
  272.     def getVelocityComponents(self):
  273.         x_translate =  math.cos(degToRad(self.angle)) * self.velocity
  274.         y_translate = -math.sin(degToRad(self.angle)) * self.velocity
  275.         components = (x_translate, y_translate)
  276.         return components
  277.     def toString(self):
  278.         stringrep = ("Position: (%s, %s), moving at %s units/frame at angle %s"
  279.                      ) % (self.x, self.y, self.velocity, self.angle)
  280.         return stringrep
  281.  
  282. class GravFocal:
  283.     def __init__(self, x, y, velocity, angle, grav_ring):
  284.         self.pos_x     = x
  285.         self.pos_y     = y
  286.         self.velocity  = velocity
  287.         self.angle     = angle
  288.         self.grav_ring = grav_ring
  289.         self.chasing   = ""
  290.  
  291.         i = 0
  292.         while self.chasing == "" or self.chasing == self:
  293.             if i < len(grav_ring.grav_focals):
  294.                 self.chasing = random.choice(grav_ring.grav_focals)
  295.                 i += 1
  296.             else:
  297.                 self.chasing = grav_ring
  298.                 break
  299.         return
  300.     def move(self):
  301.         (self.x_translate, self.y_translate) = self.getVelocityComponents()
  302.        
  303.         grav_pull_angle = degToRad(angleBetweenPoints((self.pos_x, self.pos_y),
  304.                                                       (self.chasing.pos_x, self.chasing.pos_y)))
  305.         grav_x = gravity_ring.grav_coef / 5000 * math.cos(grav_pull_angle)
  306.         grav_y = gravity_ring.grav_coef / 5000 * math.sin(grav_pull_angle)
  307.         self.velocity = velocityFromComponents(self.x_translate + grav_x, self.y_translate - grav_y)
  308.         self.angle    = angleFromComponents(self.x_translate + grav_x, self.y_translate - grav_y)
  309.        
  310.         self.pos_x += self.x_translate
  311.         self.pos_y += self.y_translate
  312.         return
  313.     def getVelocityComponents(self):
  314.         x_translate =  math.cos(degToRad(self.angle)) * self.velocity
  315.         y_translate = -math.sin(degToRad(self.angle)) * self.velocity
  316.         components = (x_translate, y_translate)
  317.         return components
  318.    
  319. class Launcher:
  320.     def __init__(self, radius, cool_down_rate, hot_colour, cool_colour):
  321.         self.x = window_width/2
  322.         self.y = window_height/2
  323.         self.radius = radius
  324.         self.heat = 0
  325.         self.cool_down_rate = cool_down_rate
  326.         self.hot_colour  = hot_colour
  327.         self.cool_colour = cool_colour
  328.     def launchBall(self):
  329.         GameState.data.append((GameState.game_count, GameState.collision_count))
  330.         if GameState.auto_saving:
  331.             if GameState.game_count > 0 and not GameState.game_count % 10:
  332.                 saveGameData()
  333.             if GameState.game_count > 0 and not GameState.game_count % 100:
  334.                 backUpGameData()
  335.         GameState.game_count += 1
  336.         GameState.collision_count = 0
  337.         genNewBall()
  338.         self.heat = 1
  339.         GameState.num_rounds += 1
  340.         if GameState.auto_saving and GameState.num_rounds > 0:
  341.             if GameState.num_rounds % GameState.auto_saving == 0:
  342.                 savePadMemories()
  343.     def coolDown(self):
  344.         if self.heat > 0:
  345.             self.heat -= self.cool_down_rate
  346.     def getColour(self):
  347.         self.hr = self.heat     * self.hot_colour[0]
  348.         self.cr = (1-self.heat) * self.cool_colour[0]
  349.         self.hg = self.heat     * self.hot_colour[1]
  350.         self.cg = (1-self.heat) * self.cool_colour[1]
  351.         self.hb = self.heat     * self.hot_colour[2]
  352.         self.cb = (1-self.heat) * self.cool_colour[2]
  353.         colour = [self.hr + self.cr, self.hg + self.cg, self.hb + self.cb]
  354.         return colour
  355.     def getRadius(self):
  356.         actual_radius = int(self.radius * math.pow(1.01, self.heat * 100))
  357.         return actual_radius
  358.  
  359. class SoundLevel:
  360.     def __init__(self):
  361.         self.levels = []
  362.         self.current_level = 3
  363.         self.volume    = 1
  364.         self.pos_x     = 0
  365.         self.pos_y     = 0
  366.         self.width     = 0
  367.         self.height    = 0
  368.         self.hovering  = False
  369.         self.surface   = ""
  370.         self.cover     = ""
  371.         self.channel   = ""
  372.         return
  373.     def addSurface(self, surface):
  374.         self.surface = surface
  375.         self.width = surface.get_width()
  376.         self.height = surface.get_height()
  377.         self.cover = pygame.Surface((self.width, self.height), 0, 32)
  378.         self.cover.fill(black.toList())
  379.         return
  380.     def addChannel(self, channel):
  381.         self.channel = channel
  382.         self.channel.set_volume(0.333 * self.current_level)
  383.         return
  384.     def changeSoundLevel(self):
  385.         self.current_level += 1
  386.         self.current_level %= 4
  387.         self.addSurface(self.levels[self.current_level])
  388.         return
  389.     def adjustVolume(self):
  390.         target = 0.333 * self.current_level
  391.         difference = target - self.volume
  392.         self.volume += difference * 0.002
  393.         self.channel.set_volume(self.volume)
  394.         return
  395.     def setCoverTransparency(self):
  396.         if self.hovering:
  397.             alpha = 255 * 0.15
  398.         else:
  399.             alpha = 255 * 0.5
  400.         self.cover.set_alpha(alpha)
  401.         return
  402.  
  403. class PauseButton:
  404.     def __init__(self, x, y):
  405.         self.active   = False
  406.         self.hovering = False
  407.         self.pos_x    = x
  408.         self.pos_y    = y
  409.         self.width    = 18
  410.         self.height   = 18
  411.         return
  412.     def switch(self):
  413.         self.active = not self.active
  414.         return
  415.  
  416. class GenericButton:
  417.     def __init__(self, x, y):
  418.         self.active   = False
  419.         self.hovering = False
  420.         self.pos_x    = x
  421.         self.pos_y    = y
  422.         self.width    = 0
  423.         self.height   = 0
  424.         return
  425.     def addSurface(self, surface):
  426.         self.surface = surface
  427.         self.width   = surface.get_width()
  428.         self.height  = surface.get_height()
  429.         self.cover   = pygame.Surface((self.width, self.height), 0, 32)
  430.         self.cover.fill(black.toList())
  431.         return
  432.     def switch(self):
  433.         self.active = not self.active
  434.         return
  435.    
  436. class MemoryTuple:
  437.     def __init__(self, x_miss, y_miss):
  438.         self.x_miss              = x_miss
  439.         self.y_miss              = y_miss
  440.         self.x_collision         = Collision.xpos
  441.         self.y_collision         = Collision.ypos
  442.         self.collision_i_angle   = Collision.initial_angle
  443.         self.collision_i_speed   = Collision.initial_speed
  444.         self.collision_f_angle   = Collision.final_angle
  445.         self.collision_f_speed   = Collision.final_speed
  446.         self.collision_grav_coef = Collision.grav_coef
  447.         return
  448.     def getData(self):
  449.         memory_tuple = (self.x_miss, self.y_miss, self.x_collision,
  450.                         self.y_collision, self.collision_i_angle,
  451.                         self.collision_i_speed, self.collision_f_angle,
  452.                         self.collision_f_speed, self.collision_grav_coef)
  453.         return memory_tuple
  454.  
  455. class SettingOption:
  456.     def __init__(self, y, name, info, modes):
  457.         self.pos_x      = 20
  458.         self.pos_y      = y
  459.         self.width      = 260
  460.         self.height     = 50
  461.         self.min_height = 50
  462.         self.max_height = 100
  463.         self.name       = name
  464.         self.info       = info
  465.         self.modes      = modes
  466.         self.colour     = green.toList()
  467.         self.active     = True
  468.         self.hovering   = False
  469.         self.surface    = pygame.Surface((self.width, self.height), pygame.SRCALPHA, 32)
  470.         return
  471.     def construct(self):
  472.         offset = 5
  473.         max_distance = getPointDistance(0, 0, offset, offset)
  474.  
  475.         self.surface.lock()
  476.         for x in range(self.width):
  477.             for y in range(self.height):
  478.                 alpha_coef    = 1 - math.pow(0.992, (self.width - x) * 2)
  479.                 alpha_subcoef = (abs(self.height / 2 - y) + (1 - x/self.width) * 20) * 5
  480.                 alpha_coef   *= 1 - math.pow(0.992, alpha_subcoef)
  481.  
  482.                 if x < offset and y < offset:
  483.                     distance    = getPointDistance(x, y, offset, offset)
  484.                     alpha_coef *= max(0, 1 - distance / offset)
  485.                 elif x < offset and y > self.height - offset:
  486.                     distance    = getPointDistance(x, y, offset, self.height - offset)
  487.                     alpha_coef *= max(0, 1 - distance / offset)
  488.                 elif x < offset:
  489.                     alpha_coef *= x / offset
  490.                 elif y < offset:
  491.                     alpha_coef *= y / offset
  492.                 elif y > self.height - offset:
  493.                     alpha_coef *= (self.height - y) / offset
  494.                    
  495.                 col_subcoef   = min(x, self.width - x) + min(y, self.height - y)
  496.                 col_coef      = math.pow(0.992, col_subcoef)
  497.  
  498.                 if self.hovering:
  499.                     alpha_coef = min(1, alpha_coef * 1.2)
  500.                     col_coef   = min(1, col_coef   * 1.2)
  501.                
  502.                 bg_colour      = [self.colour[0]*col_coef, self.colour[1]*col_coef, self.colour[2]*col_coef, 255 * alpha_coef]
  503.                 self.surface.set_at((x, y), bg_colour)
  504.  
  505.         self.surface.unlock()
  506.        
  507.         x_text = 0.1 * self.width
  508.         y_text = self.min_height / 2 - 5
  509.         renderText(self.surface, x_text, y_text, "Free Sans Bold", font_size_16, white.toList(), self.name, False)
  510.        
  511.         x_text = 0.1 * self.width
  512.         y_text = self.min_height - 5
  513.         textlen = len(self.info)
  514.         textsplit = self.info.split()
  515.         while textlen and y_text + 10 < self.surface.get_height():
  516.             text = []
  517.             if textsplit:
  518.                 next_word_length = len(textsplit[0])
  519.             else:
  520.                 next_word_length = 0
  521.             while len(" ".join(text)) + next_word_length < 43:
  522.                 if textsplit:
  523.                     word = textsplit.pop(0)
  524.                 else:
  525.                     textlen = 0
  526.                     break
  527.                 text.append(word)
  528.                 textlen -= len(word)
  529.  
  530.             text = " ".join(text)
  531.             renderText(self.surface, x_text, y_text, "Free Sans Bold", font_size_14, white.toList(), text, False)
  532.             y_text += 10
  533.  
  534.         if self.hovering:
  535.             x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
  536.             y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
  537.             radius = 2
  538.             x_circ = round(x_offset + self.pos_x - 8)
  539.             y_circ = round(y_offset + self.pos_y + self.height / 2 + radius / 2)
  540.             pygame.draw.circle(Screen.screen, white.toList(), (x_circ, y_circ), radius)
  541.         return
  542.     def adjustColour(self):
  543.         if self.modes[0] == 1 and self.active < 0:
  544.             self.colour = crossColours(white, green, -self.active/10).toList()
  545.             self.active += 1
  546.         elif self.modes[0] == 1 and self.active >= 0:
  547.             self.colour = green.toList()
  548.         elif self.modes[0] == 2:
  549.             self.colour = {False:red.toList(), True:green.toList()}[self.active]
  550.         elif self.modes[0] > 2:
  551.             self.colour = crossColours(green, red, self.active / (self.modes[0]-1)).toList()
  552.         return
  553.     def adjustSize(self):
  554.         lower_index = (SettingsMenu.current_page - 1) * 5
  555.         upper_index = SettingsMenu.current_page * 5
  556.         self_index  = SettingOptions.options.index(self)
  557.         if self_index < lower_index or self_index > upper_index:
  558.             return
  559.        
  560.         rate = 10
  561.         delta_height = 0
  562.         if self.hovering and self.height < self.max_height:
  563.             delta_height = rate
  564.         elif not self.hovering and self.height > self.min_height:
  565.             delta_height = -rate
  566.  
  567.         if delta_height:
  568.             if self.height + delta_height > self.max_height:
  569.                 actual_change = self.max_height - self.height
  570.             elif self.height + delta_height < self.min_height:
  571.                 actual_change = self.height - self.min_height
  572.             else:
  573.                 actual_change = delta_height
  574.                
  575.             self.height += actual_change
  576.             self.surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA, 32)
  577.            
  578.             found_self = False
  579.             for i in range(lower_index, upper_index):
  580.                 if i >= len(SettingOptions.options):
  581.                     break
  582.                 option = SettingOptions.options[i]
  583.                 if not found_self and self == option:
  584.                     found_self = True
  585.                 elif found_self:
  586.                     option.pos_y += actual_change
  587.         return        
  588.     def switch(self):
  589.         if self.modes[0] == 1:
  590.             self.active = -10
  591.         elif self.modes[0] == 2:
  592.             self.active = not self.active
  593.         elif self.modes[0] > 2:
  594.             self.active = (self.active + 1) % self.modes[0]
  595.  
  596.         if "TOGGLE_LEARNING" in self.modes:
  597.             GameState.toggle_learning = self.active
  598.  
  599.         if "TOGGLE_GENETIC" in self.modes:
  600.             GameState.toggle_genetic = self.active
  601.  
  602.         if "TOGGLE_GRAVITY" in self.modes:
  603.             gravity_ring.active = self.active
  604.  
  605.         if "SET_GRAV_COEF" in self.modes:
  606.             val = input("Please enter new gravity coefficient: ")
  607.             try:
  608.                 val = float(val)
  609.                 if 0 <= val and val <= 100:
  610.                     gravity_ring.grav_coef = val
  611.                 else:
  612.                     print("Invalid value")
  613.             except:
  614.                 print("Error parsing value")
  615.  
  616.         if "SET_WEIGHT" in self.modes:
  617.             val = input("Please enter new weight coefficient: ")
  618.             try:
  619.                 val = float(val)
  620.                 if 0 <= val and val <= 1:
  621.                     WeightData.current_weight = val
  622.                 else:
  623.                     print("Invalid value")
  624.             except:
  625.                 print("Error parsing value")
  626.  
  627.         if "SAVE_MEMORY" in self.modes:
  628.             savePadMemories()
  629.  
  630.         if "BACK_UP_MEMORY" in self.modes:
  631.             path = ""
  632.             i = 0
  633.             while path == "" or os.path.exists(path):
  634.                 i += 1
  635.                 path = os.path.join(pad_memory_path, r"BackUpMemories\Back-up %s" % i)
  636.             backUpPadMemories(path)
  637.  
  638.         if "LOAD_MEMORY" in self.modes:
  639.             loadPadMemories()
  640.  
  641.         if "RESET_MEMORY" in self.modes:
  642.             for pad in Pads.padlist:
  643.                 pad.memory = []
  644.  
  645.         if "RESET_SCORES" in self.modes:
  646.             for pad in Pads.padlist:
  647.                 pad.score = 0
  648.  
  649.         if "TOGGLE_AI_PAD1" in self.modes:
  650.             Pads.padlist[0].AI = self.active / 2
  651.  
  652.         if "TOGGLE_AI_PAD2" in self.modes:
  653.             Pads.padlist[1].AI = self.active / 2
  654.  
  655.         if "TOGGLE_AI_PAD3" in self.modes:
  656.             Pads.padlist[2].AI = self.active / 2
  657.  
  658.         if "TOGGLE_AI_PAD4" in self.modes:
  659.             Pads.padlist[3].AI = self.active / 2
  660.  
  661.         if "TOGGLE_ALL_AI" in self.modes:
  662.             option_list = []
  663.             for i in range(4):
  664.                 option_list.append("TOGGLE_AI_PAD%s" % (i+1))
  665.  
  666.             enable_all = False
  667.             active_set = -1
  668.            
  669.             for option in SettingOptions.options:
  670.                 for related_option in option_list:
  671.                     if related_option in option.modes and option.active != active_set:
  672.                         if active_set == -1:
  673.                             active_set = option.active
  674.                             continue
  675.                         else:
  676.                             enable_all = True
  677.                             break
  678.  
  679.             if enable_all:
  680.                 active_set = 2
  681.             else:
  682.                 active_set = (active_set + 1) % 3
  683.             for option in SettingOptions.options:
  684.                 for related_option in option_list:
  685.                     if related_option in option.modes:
  686.                         option.active = active_set
  687.  
  688.             for pad in Pads.padlist:
  689.                 pad.AI = active_set / 2
  690.  
  691.         if "ACCEL_BALL" in self.modes:
  692.             for pad in Pads.padlist:
  693.                 if self.active == 0:
  694.                     GameState.ball_accel = 0
  695.                 elif self.active == 1:
  696.                     GameState.ball_accel = ball_accel
  697.                 elif self.active == 2:
  698.                     GameState.ball_accel = ball_accel * 10
  699.                 else:
  700.                     GameState.ball_accel = ball_accel * 50
  701.  
  702.         if "FAST_PADS" in self.modes:
  703.             GameState.pad_speed_mult = {False:1, True:2}[self.active]
  704.        
  705.         if "AUTO_SAVE" in self.modes:
  706.             rounds = GameState.num_rounds
  707.             if self.active == 0:
  708.                 GameState.auto_saving = 0
  709.             elif self.active == 1:
  710.                 GameState.auto_saving = 100
  711.             elif self.active == 2:
  712.                 GameState.auto_saving = 50
  713.             else:
  714.                 GameState.auto_saving = 10
  715.                
  716.         return
  717.  
  718. class GravityRing:
  719.     def __init__(self):
  720.         self.pos_x          = int(window_width  / 2)
  721.         self.pos_y          = int(window_height / 2)
  722.         self.grav_coef      = 0
  723.         self.current_angle  = 0
  724.         self.current_radius = 0
  725.         self.end_radius     = 50
  726.         self.rings          = 8
  727.         self.pump_index     = 0
  728.         self.pump_timer     = 100
  729.         self.active         = False
  730.         self.pumpvals       = []
  731.         self.grav_focals    = []
  732.  
  733.         for i in range(10):
  734.             self.grav_focals.append("")
  735.             self.genNewGravFocal(i)
  736.        
  737.         for i in range(self.rings):
  738.             self.pumpvals.append(0)
  739.         return
  740.     def genNewGravFocal(self, index):
  741.         velocity = random.random()*0.2+0.2
  742.         angle = 0
  743.         while angle % 90 < 20:
  744.             angle = random.randrange(360)
  745.         grav_focal = GravFocal(window_width/2, window_height/2, velocity, angle, self)
  746.         self.grav_focals[index] = grav_focal
  747.         return
  748.     def construct(self):
  749.         if not self.current_radius > 0:
  750.             return
  751.        
  752.         delta_angle = 360 / self.rings
  753.         current_angle = 0
  754.  
  755.         for i in range(self.rings):
  756.             circ_x = self.pos_x + round(self.current_radius * math.cos(degToRad(current_angle)))
  757.             circ_y = self.pos_y + round(self.current_radius * math.sin(degToRad(current_angle)))
  758.             circ_rad = round(2 * (1 + self.pumpvals[i] / 100))
  759.             colour = crossColours(purple, grey, self.grav_coef / 180)
  760.             pygame.draw.circle(Screen.screen, colour.toList(), (circ_x, circ_y), circ_rad, 1)
  761.            
  762.             current_angle += delta_angle
  763.             if self.pumpvals[i]:
  764.                 self.pumpvals[i] -= 1
  765.  
  766.         return
  767.     def handleGravFocals(self):
  768.         if not self.current_radius > 0:
  769.             return
  770.  
  771.         for i in range(len(self.grav_focals)):
  772.             focal = self.grav_focals[i]
  773.             if getPointDistance(self.pos_x, self.pos_y, focal.pos_x, focal.pos_y) > self.current_radius:
  774.                 self.genNewGravFocal(i)
  775.                 return
  776.            
  777.             focal.move()
  778.             colour = crossColours(purple, grey, self.grav_coef / 180)
  779.             focal_radius = 2
  780.             focal_x = round(focal.pos_x - focal_radius / 2)
  781.             focal_y = round(focal.pos_y - focal_radius / 2)
  782.             pygame.draw.circle(Screen.screen, colour.toList(), (focal_x, focal_y), focal_radius, 1)
  783.  
  784.     def modifyRadius(self):
  785.         if self.active and 0.95 * self.end_radius <= self.current_radius and self.current_radius < self.end_radius:
  786.             self.current_radius = self.end_radius
  787.         elif self.active and self.current_radius < 0.95 * self.end_radius:
  788.             self.current_radius += self.end_radius / 250
  789.         elif not self.active and 0.05 * self.end_radius >= self.current_radius and self.current_radius >= 0:
  790.             self.current_radius = 0
  791.         else:
  792.             self.current_radius -= self.end_radius / 250
  793.         return
  794.     def pumpCircs(self):
  795.         if not self.pump_timer:
  796.             self.pumpvals[self.pump_index] = 100
  797.             self.pump_index = (self.pump_index + 1) % self.rings
  798.  
  799.         if self.pump_timer > 100 - 50 * (self.grav_coef - 50) / 50:
  800.             self.pump_timer = 0
  801.         else:
  802.             self.pump_timer += 1
  803.         return
  804.  
  805. class SettingOptions:
  806.     options = []
  807.    
  808. class WeightData:
  809.     weight_scores        = {}
  810.     current_generation   = []
  811.     current_weight       = 0
  812.     current_weight_index = 0
  813.     current_collisions   = 0
  814.     current_game         = 0
  815.     generation           = 0
  816.    
  817. class PadMemory:
  818.     memory_blue   = []
  819.     memory_green  = []
  820.     memory_red    = []
  821.     memory_orange = []
  822.  
  823. class Pads:
  824.     padlist = []
  825.  
  826. class Balls:
  827.     ball = ""    
  828.  
  829. class Screen:
  830.     screen = ""
  831.  
  832. class FastForwardCover:
  833.     surface = ""
  834.  
  835. class SettingsMenu:
  836.     modified_surface = ""
  837.     default_surface  = ""
  838.     total_pages      = 4
  839.     current_page     = 1
  840.  
  841. class GameState:
  842.     done                      = False
  843.     fast_forward_cover_loaded = False
  844.     toggle_learning           = True
  845.     toggle_genetic            = True
  846.     collision_count           = 0
  847.     max_count                 = 0
  848.     num_rounds                = 0
  849.     score_messages_list       = []
  850.     score_messages            = {"Okay":0, "Good":0, "Great":0, "Excellent!":0,
  851.                                  "Superb!":0, "Amazing!":0, "Outstanding!":0,
  852.                                  "Unbelievable!":0}
  853.     ball_accel                = 0
  854.     pad_speed_mult            = 1
  855.     auto_saving               = 0
  856.     data                      = []
  857.     game_count                = 0
  858.    
  859. class Collision:
  860.     xpos           = 0
  861.     ypos           = 0
  862.     initial_angle  = 0
  863.     initial_speed  = 0
  864.     final_angle    = 0
  865.     final_speed    = 0
  866.     grav_coef      = 0
  867.  
  868. class Launchers:
  869.     launcher = ""
  870.  
  871. class KeyBools:
  872.     aPressed     = False
  873.     dPressed     = False
  874.  
  875.     iPressed     = False
  876.     kPressed     = False
  877.  
  878.     leftPressed  = False
  879.     rightPressed = False
  880.  
  881.     kp8Pressed   = False
  882.     kp2Pressed   = False
  883.  
  884. class FontTypes:
  885.     Angelic_War_40_font              = ""
  886.     Angelic_War_14_font              = ""
  887.     Birth_Of_A_Hero_30_font          = ""
  888.     Times_New_Roman_18_font          = ""
  889.     Free_Sans_Bold_10_font           = ""
  890.     Free_Sans_Bold_12_font           = ""
  891.     Free_Sans_Bold_16_font           = ""
  892.     Free_Sans_Bold_18_font           = ""
  893.     Free_Sans_Bold_30_font           = ""
  894.     Sergeant_SixPack_12_font         = ""
  895.     Sergeant_SixPack_14_font         = ""
  896.     Sergeant_SixPack_16_font         = ""
  897.     Sergeant_SixPack_18_font         = ""
  898.     Sergeant_SixPack_20_font         = ""
  899.     Sergeant_SixPack_22_font         = ""
  900.     Sergeant_SixPack_24_font         = ""
  901.     Sergeant_SixPack_26_font         = ""
  902.  
  903. black      = Colour(0, 0, 0)
  904. grey       = Colour(100, 100, 100)
  905. white      = Colour(255, 255, 255)
  906. orange     = Colour(255, 165, 0)
  907. red        = Colour(255, 0, 0)
  908. green      = Colour(0, 255, 0)
  909. blue       = Colour(0, 0, 255)
  910. yellow     = Colour(255, 255, 0)
  911. cool_blue  = Colour(174, 238, 238)
  912. purple     = Colour(223, 0, 255)
  913. dark_red   = Colour(100, 0, 0)
  914. dark_green = Colour(0, 100, 0)
  915. dark_blue  = Colour(0, 0, 100)
  916.  
  917. sound_level  = SoundLevel()
  918. pause_button = PauseButton(50, 14)
  919. fast_forward = GenericButton(79, 11)
  920. info_button  = GenericButton(105, 10)
  921. print_button = GenericButton(135, 14)
  922. gear_button  = GenericButton(160, 13)
  923.  
  924. arrow_left   = GenericButton(0, 460)
  925. arrow_right  = GenericButton(0, 460)
  926.  
  927. pause_surface = pygame.Surface((pause_button.width, pause_button.height), 0, 32)
  928. gravity_ring  = GravityRing()
  929.  
  930. ############################################################
  931. #
  932. #           U T I L I T Y    F U N C T I O N S
  933. #
  934.  
  935. def degToRad(degrees):
  936.     rads = degrees * math.pi / 180
  937.     return rads
  938.  
  939. def radToDeg(radians):
  940.     degrees = radians * 180 / math.pi
  941.     return degrees
  942.  
  943. def crossColours(c1, c2, coef):
  944.     anti_coef = 1 - coef
  945.     c1 = c1.toList()
  946.     c2 = c2.toList()
  947.     result = Colour(c1[0]*coef + c2[0]*anti_coef,
  948.                     c1[1]*coef + c2[1]*anti_coef,
  949.                     c1[2]*coef + c2[2]*anti_coef)
  950.     return result
  951.  
  952. def objectWithinBounds(obj_x, obj_y, x_low, x_high, y_low, y_high):
  953.     ball = Balls.ball
  954.     within_bounds = x_low <= obj_x and obj_x <= x_high and y_low <= obj_y and obj_y <= y_high
  955.     return within_bounds
  956.  
  957. def angleBetweenPoints(p1, p2):
  958.     (p1_x, p1_y) = p1
  959.     (p2_x, p2_y) = p2
  960.     x_dif = p2_x - p1_x
  961.     y_dif = p2_y - p1_y
  962.     angle = angleFromComponents(x_dif, y_dif)
  963.     return angle
  964.  
  965. def angleFromComponents(x_translate, y_translate):
  966.     #quadrant 1, 2, 3, 4
  967.     y_translate *= -1
  968.     if x_translate == 0:
  969.         x_translate = 0.0001
  970.     if x_translate > 0 and y_translate > 0:
  971.         theta = radToDeg(math.atan(y_translate / x_translate))
  972.     elif x_translate < 0 and y_translate > 0:
  973.         theta = radToDeg(math.atan(y_translate / x_translate)) + 180
  974.     elif x_translate < 0 and y_translate < 0:
  975.         theta = radToDeg(math.atan(y_translate / x_translate)) + 180
  976.     else:
  977.         theta = radToDeg(math.atan(y_translate / x_translate)) + 360
  978.  
  979.     theta %= 360
  980.     return theta
  981.  
  982. def velocityFromComponents(x_translate, y_translate):
  983.     velocity = math.sqrt(x_translate * x_translate + y_translate * y_translate)
  984.     return velocity
  985.    
  986. def insidePad():
  987.     ball = Balls.ball
  988.     padlist = Pads.padlist
  989.     for i in range(len(padlist)):
  990.         pad = padlist[i]
  991.         padxmin = pad.x
  992.         padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
  993.         padymin = pad.y
  994.         padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
  995.         if padxmin <= ball.x and ball.x <= padxmax and padymin <= ball.y and ball.y <= padymax:
  996.             return True
  997.     return False
  998.  
  999. def averageAngles(first_angle, second_angle):
  1000.     average_angle = (first_angle + second_angle) / 2
  1001.     return average_angle
  1002.  
  1003. def getPointDistance(x1, y1, x2, y2):
  1004.     distance = math.sqrt(math.pow((x2 - x1), 2) + math.pow((y2 - y1), 2))
  1005.     return distance
  1006.  
  1007. def getPadMidpoint(padindex):
  1008.     i = padindex
  1009.     pad = Pads.padlist[i]
  1010.     padxmin = pad.x
  1011.     padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
  1012.     padymin = pad.y
  1013.     padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
  1014.     padxmid = (padxmin + padxmax) / 2
  1015.     padymid = (padymin + padymax) / 2
  1016.     midpoint = (padxmid, padymid)
  1017.     return midpoint
  1018.  
  1019. def getAngleDifference(angle1, angle2):
  1020.     angle_dif = 360 - max(angle1, angle2) + min(angle1, angle2)
  1021.     if angle_dif > 180:
  1022.         angle_dif = 360 - angle_dif
  1023.     return angle_dif
  1024.  
  1025. ############################################################
  1026. #
  1027. #           I N P U T / O U T P U T
  1028. #
  1029.  
  1030. def saveGameData():
  1031.     with open(os.path.join(data_path, "score_data.txt"), 'w') as file:
  1032.         for item in GameState.data:
  1033.             file.write(str(item) + "\n")
  1034.  
  1035.     padlist = Pads.padlist
  1036.     for i in range(len(padlist)):
  1037.         pad = padlist[i]
  1038.  
  1039.         with open(os.path.join(data_path, "pad_%s_memory.txt" % i), 'w') as file:
  1040.             for memory in pad.memory:
  1041.                 file.write(str(memory.getData()) + "\n")
  1042.  
  1043.     with open(os.path.join(data_path, "weight_coefficient.txt"), 'w') as file:
  1044.         file.write(str(WeightData.current_weight))
  1045.  
  1046.     with open(os.path.join(data_path, "grav_coefficient.txt"), 'w') as file:
  1047.         file.write(str(gravity_ring.grav_coef))
  1048.     return
  1049.  
  1050. def backUpGameData():
  1051.     path = ""
  1052.     i = 0
  1053.     while path == "" or os.path.exists(path):
  1054.         i += 1
  1055.         path = os.path.join(data_path, r"BackUpData\Back-up %s" % i)
  1056.  
  1057.     try:
  1058.         os.mkdir(path)
  1059.     except Exception as e:
  1060.         print("Error occured whilst making memory back up: %s" % e)
  1061.         return
  1062.  
  1063.     for filename in ["score_data.txt", "grav_coefficient.txt", "weight_coefficient.txt",
  1064.                      "pad_0_memory.txt", "pad_1_memory.txt", "pad_2_memory.txt",
  1065.                      "pad_3_memory.txt"]:
  1066.         file = os.path.join(data_path, filename)
  1067.         shutil.copy(file, path)
  1068.     print("Game data back-up created")
  1069.     return
  1070.    
  1071. def savePadMemories():
  1072.     padlist = Pads.padlist
  1073.     for i in range(len(padlist)):
  1074.         pad = padlist[i]
  1075.  
  1076.         with open(os.path.join(pad_memory_path, "pad_%s_memory.txt" % i), 'w') as file:
  1077.             for memory in pad.memory:
  1078.                 file.write(str(memory.getData()) + "\n")
  1079.  
  1080.     with open(os.path.join(pad_memory_path, "weight_coefficient.txt"), 'w') as file:
  1081.         file.write(str(WeightData.current_weight))
  1082.  
  1083.     return
  1084.  
  1085. def backUpPadMemories(path):
  1086.     if not os.path.exists(path):
  1087.         try:
  1088.             os.mkdir(path)
  1089.         except Exception as e:
  1090.             print("Error occurred whilst making memory back up: %s" % e)
  1091.             return
  1092.    
  1093.     padlist = Pads.padlist
  1094.     for i in range(len(padlist)):
  1095.         file = os.path.join(pad_memory_path, "pad_%s_memory.txt" % i)
  1096.         shutil.copy(file, path)
  1097.  
  1098.     file = os.path.join(pad_memory_path, "weight_coefficient.txt")
  1099.     shutil.copy(file, path)
  1100.     print("New directory created")
  1101.     return
  1102.  
  1103. def loadPadMemories():
  1104.     padlist = Pads.padlist
  1105.     for i in range(len(padlist)):
  1106.         pad = padlist[i]
  1107.  
  1108.         with open(os.path.join(pad_memory_path, "pad_%s_memory.txt" % i), 'r') as file:
  1109.             pad.memory = parseMemoryData(file.read())
  1110.  
  1111.     with open(os.path.join(pad_memory_path, "weight_coefficient.txt"), 'r') as file:
  1112.         WeightData.current_weight = float(file.read())
  1113.    
  1114.     return
  1115.  
  1116. def parseMemoryData(string_data):
  1117.     list_data = []
  1118.     for line in string_data.split("\n"):
  1119.         if not line:
  1120.             continue
  1121.         item_data = line.replace("(", "").replace(")", "").split(", ")
  1122.         x_miss            = float(item_data[0])
  1123.         y_miss            = float(item_data[1])
  1124.         memory_tuple = MemoryTuple(x_miss, y_miss)
  1125.        
  1126.         memory_tuple.x_collision         = float(item_data[2])
  1127.         memory_tuple.y_collision         = float(item_data[3])
  1128.         memory_tuple.collision_i_angle   = float(item_data[4])
  1129.         memory_tuple.collision_i_speed   = float(item_data[5])
  1130.         memory_tuple.collision_f_angle   = float(item_data[6])
  1131.         memory_tuple.collision_f_speed   = float(item_data[7])
  1132.         memory_tuple.collision_grav_coef = float(item_data[8])
  1133.        
  1134.         list_data.append(memory_tuple)
  1135.     return list_data
  1136.  
  1137. ############################################################
  1138. #
  1139. #                 A D A P T I V E    A I
  1140. #
  1141.  
  1142. def updatePadTargets():
  1143.     padlist = Pads.padlist
  1144.     for i in range(len(padlist)):
  1145.         pad = padlist[i]
  1146.         if pad.AI != 1:
  1147.             continue
  1148.         (x_target, y_target) = findBestApproximation(i)
  1149.         printData("%s: (%s, %s)" % (i, x_target, y_target))
  1150.         printMemory(i)
  1151.         pad.x_target = x_target
  1152.         pad.y_target = y_target
  1153.     printData("")
  1154.     return
  1155.  
  1156. def handleAI():
  1157.     padlist = Pads.padlist
  1158.     for i in range(len(padlist)):
  1159.         pad = padlist[i]
  1160.         if pad.AI == 0.5:
  1161.             pad.x_target = Balls.ball.x
  1162.             pad.y_target = Balls.ball.y
  1163.         elif pad.AI == 0:
  1164.             continue
  1165.  
  1166.         (padxmid, padymid)   = getPadMidpoint(i)
  1167.        
  1168.         if not i % 2:
  1169.             if padymid < pad.y_target:
  1170.                 pad.move(0, pad_speed * GameState.pad_speed_mult)
  1171.             elif padymid > pad.y_target:
  1172.                 pad.move(0, -pad_speed * GameState.pad_speed_mult)
  1173.         else:
  1174.             if padxmid < pad.x_target:
  1175.                 pad.move(pad_speed * GameState.pad_speed_mult, 0)
  1176.             elif padxmid > pad.x_target:
  1177.                 pad.move(-pad_speed * GameState.pad_speed_mult, 0)
  1178.     return
  1179.  
  1180. def findBestApproximation(padindex):
  1181.     printData("FINDING APPROXIMATION FOR PAD %s...\n" % padindex)
  1182.     pad = Pads.padlist[padindex]
  1183.     memory = pad.memory
  1184.     ball = Balls.ball
  1185.     if not memory:
  1186.         approximation = getPadMidpoint(padindex)
  1187.         return approximation
  1188.  
  1189.     collision_data = getCollisionData()
  1190.     (last_collision_x, last_collision_y, last_collision_i_angle,
  1191.      last_collision_i_speed, last_collision_f_angle,
  1192.      last_collision_f_speed, last_collision_grav_coef) = collision_data
  1193.    
  1194.     best_approx = 0
  1195.     strictness_coef = 1.03
  1196.    
  1197.     for memory_tuple in memory:
  1198.         (x_miss, y_miss, x_collision, y_collision, _, _, f_angle, _,
  1199.          collision_grav_coef) = memory_tuple.getData()
  1200.        
  1201.         (divergence, x_divergence, y_divergence, f_angular_divergence,
  1202.          grav_divergence) = calculateDivergence(memory_tuple, collision_data)
  1203.  
  1204.         approximation = (divergence, x_miss, y_miss)
  1205.        
  1206.         printData("\n\nPAD: %s" % padindex)
  1207.         printData("\nLAST COLLISION (X) = %s, CONSIDERED CASE (X) = %s" % (last_collision_x, x_collision))
  1208.         printData("pos_x DIVERGENCE: %s" % x_divergence)
  1209.  
  1210.         printData("\nLAST COLLISION (Y) = %s, CONSIDERED CASE (Y) = %s" % (last_collision_y, y_collision))
  1211.         printData("pos_y DIVERGENCE: %s" % y_divergence)
  1212.  
  1213.         printData("\nLAST COLLISION (fAngle) = %s, CONSIDERED CASE (fAngle) = %s" % (last_collision_f_angle, f_angle))
  1214.         printData("FINAL ANGLE DIVERGENCE: %s" % f_angular_divergence)
  1215.  
  1216.         printData("\nLAST COLLISION (grav) = %s, CONSIDERED CASE (grav) = %s" % (last_collision_grav_coef,
  1217.                                                                                  collision_grav_coef))
  1218.        
  1219.         printData("\nTOTAL DIVERGENCE: %s\n\n" % divergence)
  1220.        
  1221.         if not best_approx:
  1222.             best_approx = approximation
  1223.         else:
  1224.             (least_divergence, _, _) = best_approx
  1225.             if divergence < least_divergence:
  1226.                 best_approx = approximation
  1227.  
  1228.     (_, pos_xition, pos_yition) = best_approx
  1229.     approximation = (pos_xition, pos_yition)
  1230.     return approximation
  1231.  
  1232. def calculateDivergence(memory_tuple, collision_data):
  1233.     (last_collision_x, last_collision_y, last_collision_i_angle,
  1234.      last_collision_i_speed, last_collision_f_angle,
  1235.      last_collision_f_speed, last_collision_grav_coef) = collision_data
  1236.    
  1237.     (x_miss, y_miss, x_collision, y_collision,
  1238.      i_angle, i_speed, f_angle, f_speed,
  1239.      collision_grav_coef) = memory_tuple.getData()
  1240.  
  1241.     pos_x_dif   = abs(x_collision - last_collision_x)
  1242.     pos_y_dif   = abs(y_collision - last_collision_y)
  1243.     i_angle_dif = getAngleDifference(i_angle, last_collision_i_angle)
  1244.     i_speed_dif = abs(i_speed - last_collision_i_speed)
  1245.     f_angle_dif = getAngleDifference(f_angle, last_collision_f_angle)
  1246.     f_speed_dif = abs(f_speed - last_collision_f_speed)
  1247.     grav_dif    = abs(collision_grav_coef - last_collision_grav_coef)
  1248.  
  1249.     x_divergence         = 100 * pos_x_dif     / max_x_difference
  1250.     y_divergence         = 100 * pos_y_dif     / max_y_difference
  1251.     f_angular_divergence = 100 * f_angle_dif   / max_angular_difference
  1252.     grav_divergence      = 100 * grav_dif      / max_gravity_difference
  1253.  
  1254.     #Apply weights.
  1255.     x_divergence         *= WeightData.current_weight
  1256.     y_divergence         *= WeightData.current_weight
  1257.     f_angular_divergence *= (1 - WeightData.current_weight)
  1258.     grav_divergence      *= 0.5
  1259.  
  1260.     total_divergence = x_divergence +  y_divergence + f_angular_divergence + grav_divergence
  1261.    
  1262.     divergence_data = (total_divergence, x_divergence, y_divergence, f_angular_divergence, grav_divergence)
  1263.     return divergence_data  
  1264.  
  1265. ############################################################
  1266. #
  1267. #           G E N E T I C    A L G O R I T H M
  1268. #
  1269.  
  1270. def generateWeights():
  1271.     WeightData.generation += 1
  1272.     current_generation = produceChildren()
  1273.     while len(current_generation) < population_size:
  1274.         current_generation.append(random.random())
  1275.     WeightData.current_generation = current_generation
  1276.     print("NEW GENERATION: %s" % current_generation)
  1277.     return
  1278.  
  1279. def selectBestWeights():
  1280.     best_weights = []
  1281.     current_generation = WeightData.current_generation
  1282.     #Get the best three weights.
  1283.     for i in range(int(0.5 * population_size)):
  1284.         best_score  = -1
  1285.         best_weight = -1
  1286.         for weight, score in WeightData.weight_scores.items():
  1287.             if score > best_score and weight in current_generation and weight not in best_weights:
  1288.                 best_weight = weight
  1289.                 best_score  = score
  1290.         if best_weight != -1:
  1291.             best_weights.append(best_weight)
  1292.    
  1293.     return best_weights
  1294.  
  1295. def testNextWeight():
  1296.     WeightData.current_weight_index += 1
  1297.     index = WeightData.current_weight_index
  1298.     WeightData.current_weight = WeightData.current_generation[index]
  1299.     return
  1300.  
  1301. def produceChildren():
  1302.     best_weights = selectBestWeights()
  1303.     children = []
  1304.    
  1305.     for i in range(len(best_weights)):
  1306.         for j in range(i + 1, len(best_weights)):
  1307.             if len(children) == population_size:
  1308.                 break
  1309.             child = averageWeights(best_weights[i], best_weights[j])
  1310.             children.append(child)
  1311.  
  1312.     return children
  1313.  
  1314. def averageWeights(weight_1, weight_2):
  1315.     average_weight = (weight_1 + weight_2) / 2
  1316.     return average_weight
  1317.  
  1318. def scoreWeightValue():
  1319.     current_weight     = WeightData.current_weight
  1320.     current_collisions = WeightData.current_collisions
  1321.    
  1322.     WeightData.weight_scores[current_weight] = current_collisions
  1323.     printWeightScores()
  1324.     return
  1325.  
  1326. def printWeightScores():
  1327.     weight_scores = WeightData.weight_scores
  1328.     for weight, score in weight_scores.items():
  1329.         print("Weight %.4f: %s" % (weight, score))
  1330.     return
  1331.  
  1332. def beginNewTest():
  1333.     print("NEW TEST!")
  1334.     scoreWeightValue()
  1335.     WeightData.current_collisions    = 0
  1336.     WeightData.current_game          = 0
  1337.     if WeightData.current_weight_index < population_size - 1:
  1338.         WeightData.current_weight_index += 1
  1339.         index = WeightData.current_weight_index
  1340.         WeightData.current_weight = WeightData.current_generation[index]
  1341.     else:
  1342.         beginNewGeneration()
  1343.    
  1344.     padlist = Pads.padlist
  1345.     for i in range(len(padlist)):
  1346.         padlist[i].memory = []
  1347.  
  1348.     return
  1349.  
  1350. def beginNewGeneration():
  1351.     print("NEW GENERATION!")
  1352.     generateWeights()
  1353.     WeightData.current_weight_index = 0
  1354.     WeightData.current_weight = WeightData.current_generation[0]
  1355.     return
  1356.  
  1357. ############################################################
  1358. #
  1359. #        S U R F A C E S    F R O M    F I L E S
  1360. #
  1361.  
  1362. def getPadFromFile(padname):
  1363.     pad = pygame.image.load(os.path.join(pad_image_path, padname + ".png")).convert_alpha()
  1364.     return pad
  1365.  
  1366. def getSpeakerIcon(sound_level):
  1367.     scale = 0.12
  1368.     speaker = pygame.image.load(os.path.join(sound_level_path, "sound%s.png" % sound_level)).convert_alpha()
  1369.     speaker = scaleImage(speaker, scale)
  1370.     return speaker
  1371.  
  1372. def getLetterIcon(letter):
  1373.     scale = 0.09
  1374.     info_button = pygame.image.load(os.path.join(letter_path, "letter_" + letter + ".png")).convert_alpha()
  1375.     info_button = scaleImage(info_button, scale)
  1376.     return info_button
  1377.  
  1378. def getFastForwardIcon():
  1379.     scale = 0.2
  1380.     fast_forward = pygame.image.load(os.path.join(picture_path, "fast_forward.png")).convert_alpha()
  1381.     fast_forward = scaleImage(fast_forward, scale)
  1382.     return fast_forward
  1383.  
  1384. def getFastForwardCover():
  1385.     fast_forward_cover = pygame.image.load(os.path.join(picture_path, "pyballcover.png")).convert_alpha()
  1386.     return fast_forward_cover
  1387.  
  1388. def getGearIcon():
  1389.     scale = 0.1
  1390.     gear_button = pygame.image.load(os.path.join(picture_path, "gear.png")).convert_alpha()
  1391.     gear_button = scaleImage(gear_button, scale)
  1392.     return gear_button
  1393.  
  1394. def getArrowIcon(direction):
  1395.     scale = 0.1
  1396.     arrow_button = pygame.image.load(os.path.join(picture_path, "arrow_%s.png" % direction)).convert_alpha()
  1397.     arrow_button = scaleImage(arrow_button, scale)
  1398.     return arrow_button
  1399.  
  1400. def getSettingsMenu():
  1401.     settings_menu = pygame.image.load(os.path.join(picture_path, "settings_menu.png")).convert_alpha()
  1402.     return settings_menu
  1403.  
  1404. def scaleImage(image, scale_factor):
  1405.     scaled_image = pygame.transform.smoothscale(image, (int(image.get_width() * scale_factor), int(image.get_height() * scale_factor)))
  1406.     return scaled_image
  1407.  
  1408. ############################################################
  1409. #
  1410. #       S E S S I O N    I N I T I A L I S A T I O N
  1411. #
  1412.  
  1413. def initialiseMusic(sound_level):
  1414.     pygame.mixer.init()
  1415.     channel = pygame.mixer.Channel(0)
  1416.    
  1417.     song = "relaxing_space.wav"
  1418.     music = pygame.mixer.Sound(os.path.join(music_path, song))
  1419.     channel.play(music, -1)
  1420.     sound_level.addChannel(channel)
  1421.     return
  1422.  
  1423. def initialiseFonts():
  1424.     FontTypes.Angelic_War_14_font      = pygame.font.Font(pygame.font.match_font("Angelic War"),      font_size_14)
  1425.     FontTypes.Angelic_War_40_font      = pygame.font.Font(pygame.font.match_font("Angelic War"),      font_size_40)
  1426.     FontTypes.Birth_Of_A_Hero_30_font  = pygame.font.Font(pygame.font.match_font("Birth of a Hero"),  font_size_30)
  1427.     FontTypes.Times_New_Roman_18_font  = pygame.font.Font(pygame.font.match_font("Times New Roman"),  font_size_18)
  1428.     FontTypes.Free_Sans_Bold_10_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_11)
  1429.     FontTypes.Free_Sans_Bold_12_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_12)
  1430.     FontTypes.Free_Sans_Bold_14_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_14)
  1431.     FontTypes.Free_Sans_Bold_16_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_16)
  1432.     FontTypes.Free_Sans_Bold_18_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_18)
  1433.     FontTypes.Free_Sans_Bold_30_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_30)
  1434.     FontTypes.Sergeant_SixPack_12_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_12)
  1435.     FontTypes.Sergeant_SixPack_14_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_14)
  1436.     FontTypes.Sergeant_SixPack_16_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_16)
  1437.     FontTypes.Sergeant_SixPack_18_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_18)
  1438.     FontTypes.Sergeant_SixPack_20_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_20)
  1439.     FontTypes.Sergeant_SixPack_22_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_22)
  1440.     FontTypes.Sergeant_SixPack_24_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_24)
  1441.     FontTypes.Sergeant_SixPack_26_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_26)
  1442.     return
  1443.  
  1444. def initialiseSession():
  1445.     offset = 75
  1446.    
  1447.     padblue   = Pad(offset - pad_width/2,
  1448.                     window_height/2 - pad_height/2,
  1449.                     getPadFromFile("padblue"))
  1450.  
  1451.     padgreen  = Pad(window_width/2 - pad_height/2,
  1452.                     window_height - offset - pad_width/2,
  1453.                     pygame.transform.rotate(getPadFromFile("padgreen"),   90))
  1454.  
  1455.     padred    = Pad(window_width - offset - pad_width/2,
  1456.                     window_height/2 - pad_height/2,
  1457.                     pygame.transform.rotate(getPadFromFile("padred"),    180))
  1458.    
  1459.     padorange = Pad(window_width/2 - pad_height/2,
  1460.                     offset - pad_width/2,
  1461.                     pygame.transform.rotate(getPadFromFile("padorange"), 270))
  1462.  
  1463.     Pads.padlist = [padblue, padgreen, padred, padorange]
  1464.    
  1465.     info_button.addSurface(getLetterIcon("i"))
  1466.     print_button.addSurface(getLetterIcon("t"))
  1467.     fast_forward.addSurface(getFastForwardIcon())
  1468.     gear_button.addSurface(getGearIcon())
  1469.     arrow_left.addSurface(getArrowIcon("left"))
  1470.     arrow_right.addSurface(getArrowIcon("right"))
  1471.  
  1472.     sound_level.addSurface(getSpeakerIcon(sound_level.current_level))
  1473.    
  1474.     FastForwardCover.surface         = getFastForwardCover()
  1475.     SettingsMenu.default_surface     = getSettingsMenu()
  1476.     ResetSettingsMenu()
  1477.  
  1478.     Launchers.launcher = Launcher(3, 0.001, red.toList(), cool_blue.toList())
  1479.     Launchers.launcher.launchBall()
  1480.    
  1481.     for i in range(4):
  1482.         sound_level.levels.append(getSpeakerIcon(i))
  1483.  
  1484.     sound_level.pos_x = 14
  1485.     sound_level.pos_y = 10
  1486.  
  1487.     initialiseMusic(sound_level)
  1488.     initialiseSettings()
  1489.     for option in SettingOptions.options:
  1490.         if "TOGGLE_GENETIC" in option.modes and option.active:
  1491.             option.switch()
  1492.         elif "TOGGLE_GRAVITY" in option.modes and option.active:
  1493.             option.switch()
  1494.         elif "ACCEL_BALL" in option.modes:
  1495.             while option.active != 0:
  1496.                 option.switch()
  1497.         elif "FAST_PADS" in option.modes:
  1498.             while option.active != 0:
  1499.                 option.switch()
  1500.         elif "AUTO_SAVE" in option.modes:
  1501.             while option.active != 0:
  1502.                 option.switch()
  1503.         elif "TOGGLE_AI_PAD1" in option.modes:
  1504.             while option.active != 2:
  1505.                 option.switch()
  1506.         elif "TOGGLE_AI_PAD2" in option.modes:
  1507.             while option.active != 2:
  1508.                 option.switch()
  1509.         elif "TOGGLE_AI_PAD3" in option.modes:
  1510.             while option.active != 2:
  1511.                 option.switch()
  1512.         elif "TOGGLE_AI_PAD4" in option.modes:
  1513.             while option.active != 2:
  1514.                 option.switch()
  1515.  
  1516.     arrow_left.pos_x  = (SettingsMenu.default_surface.get_width() / 2) - 20 - arrow_left.width / 2
  1517.     arrow_right.pos_x = (SettingsMenu.default_surface.get_width() / 2) + 20 - arrow_right.width / 2
  1518.    
  1519.     GameState.score_messages_list = sorted(score_message_thresholds.items(), key=lambda item:item[1])
  1520.    
  1521.     return
  1522.  
  1523. def initialiseSettings():
  1524.     options = SettingOptions.options
  1525.     base   = 100
  1526.     offset = 60
  1527.     options.extend([SettingOption(base + offset * 0, "Toggle Learning",
  1528.                                   "When enabled, the pads learn from their mistakes and "     +
  1529.                                   "improve their behaviour over time",
  1530.                                   [2, "TOGGLE_LEARNING"]),
  1531.                     SettingOption(base + offset * 1, "Toggle Genetic Algorithm",
  1532.                                   "When enabled, the adaptive function will be put through " +
  1533.                                   "continuous testing to optimise pad learning efficiency",
  1534.                                   [2, "TOGGLE_GENETIC"]),
  1535.                     SettingOption(base + offset * 2, "Toggle Gravity",
  1536.                                   "When enabled, the ball will be attracted towards the gravity " +
  1537.                                   "ring in the centre of the window with a force determined by " +
  1538.                                   "the ring's gravity coefficient",
  1539.                                   [2, "TOGGLE_GRAVITY"]),
  1540.                     SettingOption(base + offset * 3, "Set Gravity Coefficient",
  1541.                                   "Sets the coefficient that determines how strongly the ball is " +
  1542.                                   "attracted towards the gravity ring when gravity is enabled. " +
  1543.                                   "Enter any number between 0 and 100 or an invalid value to quit",
  1544.                                   [0, "SET_GRAV_COEF"]),
  1545.                     SettingOption(base + offset * 4, "Set Weight Coefficient",
  1546.                                   "Adjusts the behaviour of the pads. Enter any floating value " +
  1547.                                   "between 0 and 1 in the shell after clicking this button or " +
  1548.                                   "an invalid value (e.g. -1) to exit without modifying the weight",
  1549.                                   [0, "SET_WEIGHT"]),
  1550.                     SettingOption(base + offset * 0, "Save Pad Memory",
  1551.                                   "Saves the data in each of the pad's memories to an "     +
  1552.                                   "external file, allowing the data to be preserved "       +
  1553.                                   "between different instances of the game",
  1554.                                   [1, "SAVE_MEMORY"]),
  1555.                     SettingOption(base + offset * 1, "Back-up Pad Memory",
  1556.                                   "Creates a new directory specified by a name you enter and " +
  1557.                                   "creates copies of the current external memory states of the " +
  1558.                                   "pads into the directory",
  1559.                                   [1, "BACK_UP_MEMORY"]),
  1560.                     SettingOption(base + offset * 2, "Load Pad Memory",
  1561.                                   "Loads data from an external file containing previously " +
  1562.                                   "stored data for each of the pads",
  1563.                                   [1, "LOAD_MEMORY"]),
  1564.                     SettingOption(base + offset * 3, "Reset Memory",
  1565.                                   "Resets the pads' memories, but leaves any data stored in " +
  1566.                                   "external files untouched",
  1567.                                   [1, "RESET_MEMORY"]),
  1568.                     SettingOption(base + offset * 4, "Reset Scores",
  1569.                                   "Resets the scores for each of the pads",
  1570.                                   [1, "RESET_SCORES"]),
  1571.                     SettingOption(base + offset * 0, "Toggle AI - Pad 1",
  1572.                                   "When fully or semi-enabled, the left pad plays with adaptive or " +
  1573.                                   "static behavioural patterns respectively. When disabled, the pad " +
  1574.                                   "is controlled by a human player using the keys I and K",
  1575.                                   [3, "TOGGLE_AI_PAD1"]),
  1576.                     SettingOption(base + offset * 1, "Toggle AI - Pad 2",
  1577.                                   "When fully or semi-enabled, the bottom pad plays with adaptive or " +
  1578.                                   "static behavioural patterns respectively. When disabled, the pad " +
  1579.                                   "is controlled by a human player using the keys A and D",
  1580.                                   [3, "TOGGLE_AI_PAD2"]),
  1581.                     SettingOption(base + offset * 2, "Toggle AI - Pad 3",
  1582.                                   "When fully or semi-enabled, the right pad plays with adaptive or " +
  1583.                                   "static behavioural patterns respectively. When disabled, the pad " +
  1584.                                   "is controlled by a human player using the numpad keys 8 and 2",
  1585.                                   [3, "TOGGLE_AI_PAD3"]),
  1586.                     SettingOption(base + offset * 3, "Toggle AI - Pad 4",
  1587.                                   "When fully or semi-enabled, the top pad plays with adaptive or " +
  1588.                                   "static behavioural patterns respectively. When disabled, the pad " +
  1589.                                   "is controlled by a human player using the arrow keys LEFT and RIGHT",
  1590.                                   [3, "TOGGLE_AI_PAD4"]),
  1591.                     SettingOption(base + offset * 4, "Toggle All AIs",
  1592.                                   "Cycles all the AI's through different AI states (ON, SEMI-, OFF)",
  1593.                                   [1, "TOGGLE_ALL_AI"]),
  1594.                     SettingOption(base + offset * 0, "Mode - Accelerating Ball",
  1595.                                   "Off - the ball does not automatically accelerate [1] - the ball " +
  1596.                                   "accelerates at a low rate [2] - the ball accelerates moderately " +
  1597.                                   "fast [3] - the ball accelerates at a high rate",
  1598.                                   [4, "ACCEL_BALL"]),
  1599.                     SettingOption(base + offset * 1, "Mode - Fast Pads",
  1600.                                   "When disabled, pads move at their normal rate. When enabled, " +
  1601.                                   "pads are able to move twice as fast",
  1602.                                   [2, "FAST_PADS"]),
  1603.                     SettingOption(base + offset * 2, "Auto-saving",
  1604.                                   "Off - auto-saving does not happen at all [1] - one save " +
  1605.                                   "is executed every 100 rounds [2] - one save is executed " +
  1606.                                   "every 50 rounds [3] - one save is executed every 10 rounds",
  1607.                                   [4, "AUTO_SAVE"])])
  1608.                    
  1609.        
  1610.     SettingOptions.options = options
  1611.     return
  1612.  
  1613. ############################################################
  1614. #
  1615. #                 T E X T    O U T P U T
  1616. #
  1617.  
  1618. def printData(data):
  1619.     if print_button.active:
  1620.         print(data)
  1621.     return
  1622.  
  1623. def printMemory(padindex):
  1624.     pad = Pads.padlist[padindex]
  1625.     printData("Pad %s" % padindex)
  1626.     if not pad.memory:
  1627.         printData("EMPTY MEMORY")
  1628.     for memory_tuple in pad.memory:
  1629.         printData(memory_tuple.getData())
  1630.     printData("")
  1631.     return
  1632.  
  1633. ############################################################
  1634. #
  1635. #                   R E N D E R I N G
  1636. #
  1637.  
  1638. def drawText(surface, pos_x, pos_y, font_type, font_size, colour, message):
  1639.     if info_button.active:
  1640.         renderText(surface, pos_x, pos_y, font_type, font_size, colour, message)
  1641.     return
  1642.  
  1643. def drawLine(surface, colour, start_pos, end_pos, width):
  1644.     if info_button.active:
  1645.         pygame.draw.aaline(surface, colour, start_pos, end_pos, width)
  1646.     return
  1647.  
  1648. def drawCircle(surface, colour, position, radius, width):
  1649.     if info_button.active:
  1650.         pygame.draw.circle(surface, colour, position, radius, width)
  1651.     return
  1652.  
  1653. def drawRect(surface, colour, area, width):
  1654.     if info_button.active:
  1655.         pygame.draw.rect(surface, colour, area, width)
  1656.     return
  1657.  
  1658. def circsAtTargets(screen):
  1659.     padlist = Pads.padlist
  1660.  
  1661.     collision_data = getCollisionData()
  1662.  
  1663.     (last_collision_x, last_collision_y, last_collision_i_angle,
  1664.      last_collision_i_speed, last_collision_f_angle,
  1665.      last_collision_f_speed, _) = collision_data
  1666.  
  1667.     drawRect(screen, purple.toList(), [last_collision_x - 3,
  1668.                                        last_collision_y - 3,
  1669.                                        6, 6], 1)    
  1670.     colours = [cool_blue.toList(), green.toList(), red.toList(), orange.toList()]
  1671.     miss_circ_radius = 4
  1672.     more_tolerant_circ_radius = miss_circ_radius * 75
  1673.     less_tolerant_circ_radius = miss_circ_radius * 5
  1674.  
  1675.     displaying_text = False
  1676.     for i in range(len(padlist)):
  1677.         pad = padlist[i]
  1678.         if not pad.AI == 1:
  1679.             continue
  1680.         drawCircle(screen, colours[i], (int(pad.x_target), int(pad.y_target)), 5, 1)
  1681.  
  1682.         for memory_tuple in pad.memory:
  1683.             (x_miss, y_miss, x_collision, y_collision,
  1684.              i_angle, i_speed, f_angle, f_speed,
  1685.              collision_grav_coef) = memory_tuple.getData()
  1686.            
  1687.             colour = [cool_blue.toList(), green.toList(), red.toList(), orange.toList()][i]
  1688.             drawCircle(Screen.screen, colour, (int(x_miss), int(y_miss)), miss_circ_radius, 1)
  1689.  
  1690.             scale = 20
  1691.             x_move = f_speed * math.cos(degToRad(f_angle))  * scale
  1692.             y_move = f_speed * -math.sin(degToRad(f_angle)) * scale
  1693.             drawLine(screen, colour, (int(x_miss), int(y_miss)), (int(x_miss + x_move), int(y_miss + y_move)), 4)
  1694.  
  1695.             within_bounds = cursorWithinBounds(x_miss - more_tolerant_circ_radius / 2,
  1696.                                                x_miss + more_tolerant_circ_radius / 2,
  1697.                                                y_miss - more_tolerant_circ_radius / 2,
  1698.                                                y_miss + more_tolerant_circ_radius / 2)
  1699.            
  1700.             if within_bounds:
  1701.                 drawLine(screen, colour, (int(x_collision), int(y_collision)), (int(x_miss), int(y_miss)), 4)
  1702.  
  1703.             within_bounds = cursorWithinBounds(x_miss - less_tolerant_circ_radius / 2,
  1704.                                                x_miss + less_tolerant_circ_radius / 2,
  1705.                                                y_miss - less_tolerant_circ_radius / 2,
  1706.                                                y_miss + less_tolerant_circ_radius / 2)
  1707.            
  1708.             if within_bounds and not displaying_text:
  1709.                 displaying_text = True
  1710.                 divergence_data = calculateDivergence(memory_tuple, collision_data)
  1711.                 (total_divergence, x_divergence, y_divergence, f_angular_divergence,
  1712.                  grav_divergence) = divergence_data
  1713.                
  1714.                 total_divergence_string     = "Total divergence: %.2f"   % total_divergence
  1715.                 x_divergence_string         = "X divergence: %.2f"       % x_divergence
  1716.                 y_divergence_string         = "Y divergence: %.2f"       % y_divergence
  1717.                 f_angular_divergence_string = "Angular divergence: %.2f" % f_angular_divergence
  1718.                 grav_divergence_string      = "Gravity divergence: %.2f" % grav_divergence
  1719.  
  1720.     if displaying_text:
  1721.         drawText(Screen.screen, window_width / 2, 200, "Free Sans Bold", font_size_16, white.toList(), total_divergence_string)
  1722.         drawText(Screen.screen, window_width / 2, 210, "Free Sans Bold", font_size_16, white.toList(), x_divergence_string)
  1723.         drawText(Screen.screen, window_width / 2, 220, "Free Sans Bold", font_size_16, white.toList(), y_divergence_string)
  1724.         drawText(Screen.screen, window_width / 2, 230, "Free Sans Bold", font_size_16, white.toList(), f_angular_divergence_string)
  1725.         drawText(Screen.screen, window_width / 2, 240, "Free Sans Bold", font_size_16, white.toList(), grav_divergence_string)
  1726.                
  1727.     return
  1728.  
  1729. def renderText(surface, xpos, ypos, font_type, font_size, font_colour, message, fromCentre=True):
  1730.     if font_type.lower() == "angelic war" and font_size == font_size_14:
  1731.         font = FontTypes.Angelic_War_14_font
  1732.     elif font_type.lower() == "angelic war" and font_size == font_size_40:
  1733.         font = FontTypes.Angelic_War_40_font
  1734.     elif font_type.lower() == "times new roman" and font_size == font_size_18:
  1735.         font = FontTypes.Times_New_Roman_18_font
  1736.     elif font_type.lower() == "free sans bold" and font_size == font_size_11:
  1737.         font = FontTypes.Free_Sans_Bold_10_font
  1738.     elif font_type.lower() == "free sans bold" and font_size == font_size_12:
  1739.         font = FontTypes.Free_Sans_Bold_12_font
  1740.     elif font_type.lower() == "free sans bold" and font_size == font_size_14:
  1741.         font = FontTypes.Free_Sans_Bold_14_font
  1742.     elif font_type.lower() == "free sans bold" and font_size == font_size_16:
  1743.         font = FontTypes.Free_Sans_Bold_16_font
  1744.     elif font_type.lower() == "free sans bold" and font_size == font_size_18:
  1745.         font = FontTypes.Free_Sans_Bold_18_font
  1746.     elif font_type.lower() == "free sans bold" and font_size == font_size_30:
  1747.         font = FontTypes.Free_Sans_Bold_30_font  
  1748.     elif font_type.lower() == "birth of a hero" and font_size == font_size_30:
  1749.         font = FontTypes.Birth_Of_A_Hero_30_font
  1750.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_12:
  1751.         font = FontTypes.Sergeant_SixPack_12_font
  1752.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_14:
  1753.         font = FontTypes.Sergeant_SixPack_14_font
  1754.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_16:
  1755.         font = FontTypes.Sergeant_SixPack_16_font
  1756.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_18:
  1757.         font = FontTypes.Sergeant_SixPack_18_font
  1758.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_20:
  1759.         font = FontTypes.Sergeant_SixPack_20_font
  1760.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_22:
  1761.         font = FontTypes.Sergeant_SixPack_22_font
  1762.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_24:
  1763.         font = FontTypes.Sergeant_SixPack_24_font
  1764.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_26:
  1765.         font = FontTypes.Sergeant_SixPack_26_font
  1766.     else:
  1767.         path = pygame.font.match_font(font_type)
  1768.         font = pygame.font.Font(path, font_size)
  1769.         print("Font not found")
  1770.  
  1771.     if fromCentre:
  1772.         (width, height) = font.size(message)
  1773.         xpos -= width / 2
  1774.         ypos -= height / 2
  1775.     textsurface = font.render(message, True, font_colour)
  1776.     surface.blit(textsurface, (xpos, ypos))
  1777.     return
  1778.    
  1779. def drawObjects(screen):
  1780.     padblue   = Pads.padlist[0]
  1781.     padgreen  = Pads.padlist[1]
  1782.     padred    = Pads.padlist[2]
  1783.     padorange = Pads.padlist[3]
  1784.    
  1785.     screen.blit(padblue.surface,   (padblue.x, padblue.y))
  1786.     screen.blit(padorange.surface, (padorange.x, padorange.y))
  1787.     screen.blit(padred.surface,    (padred.x, padred.y))
  1788.     screen.blit(padgreen.surface,  (padgreen.x, padgreen.y))
  1789.  
  1790.     ball = Balls.ball
  1791.     ballcirc = pygame.draw.circle(screen, white.toList(), (int(ball.x - ball_radius/2), int(ball.y - ball_radius/2)), ball_radius, 1)
  1792.  
  1793.     padlist = Pads.padlist
  1794.     for i in range(len(padlist)):
  1795.         pad = padlist[i]
  1796.         (padxmid, padymid) = getPadMidpoint(i)
  1797.         mid_offset = 5
  1798.         if i == 0:
  1799.             padxmid -= mid_offset
  1800.         elif i == 1:
  1801.             padymid += mid_offset
  1802.         elif i == 2:
  1803.             padxmid += mid_offset
  1804.         else:
  1805.             padymid -= mid_offset
  1806.         renderText(screen, padxmid, padymid, "Free Sans Bold", font_size_18, white.toList(), str(pad.score))
  1807.         controller = {1:"ADAPTIVE COMP", 0.5:"STATIC COMP", 0:"HUMAN"}[pad.AI]
  1808.        
  1809.         offset = 5
  1810.         padxmin = pad.x
  1811.         padxmax = pad.x + pad_width * ((i+1) % 2) + pad_height * (i % 2)
  1812.         padymin = pad.y
  1813.         padymax = pad.y + pad_height * ((i+1) % 2) + pad_width * (i % 2)
  1814.         if i == 0:
  1815.             x_text = padxmin - offset
  1816.             y_text = padymin - offset
  1817.         elif i == 1:
  1818.             x_text = padxmin - offset
  1819.             y_text = padymax + offset
  1820.         elif i == 2:
  1821.             x_text = padxmax + offset
  1822.             y_text = padymin - offset
  1823.         else:
  1824.             x_text = padxmin - offset
  1825.             y_text = padymin - offset
  1826.            
  1827.         renderText(screen, x_text, y_text, "Free Sans Bold", font_size_11, [35,35,35], controller)
  1828.  
  1829.     drawLauncher(screen)
  1830.     drawSpeaker(screen)
  1831.     drawPauseButton(screen)
  1832.     drawLetterButton("i", screen)
  1833.     drawLetterButton("t", screen)
  1834.     drawFastForwardButton(screen)
  1835.     drawGearButton(screen)
  1836.     drawWeightInfo(screen)
  1837.     drawScore(screen)
  1838.     drawScoreMessage(screen)
  1839.  
  1840.     gravity_ring.construct()
  1841.     return
  1842.  
  1843. def drawWeightInfo(screen):
  1844.     current_weight     = "Weight: %.4f"      % WeightData.current_weight
  1845.     current_collisions = "Current Score: %s" % WeightData.current_collisions
  1846.     current_game       = "Current Game: %s"  % WeightData.current_game
  1847.     current_generation = "Generation: %s"    % WeightData.generation
  1848.    
  1849.     renderText(screen, window_width - 110, 20, "Free Sans Bold", font_size_16, white.toList(), current_weight, False)
  1850.  
  1851.     if GameState.toggle_genetic:
  1852.         renderText(screen, window_width - 110, 30, "Free Sans Bold", font_size_16, white.toList(), current_collisions, False)
  1853.         renderText(screen, window_width - 110, 40, "Free Sans Bold", font_size_16, white.toList(), current_game, False)
  1854.         renderText(screen, window_width - 110, 50, "Free Sans Bold", font_size_16, white.toList(), current_generation, False)
  1855.     return
  1856.  
  1857. def drawFastForwardCover(screen, ticks, real_ticks):
  1858.     if not GameState.fast_forward_cover_loaded:
  1859.         loadFastForwardCover()
  1860.     elif not real_ticks % 500:
  1861.         loadFastForwardCover()
  1862.  
  1863.     drawSpeaker(screen)
  1864.     drawPauseButton(screen)
  1865.     drawFastForwardButton(screen)
  1866.  
  1867.     ticks = "Ticks: %s" % ticks
  1868.     renderText(screen, window_width / 2, 300, "Free Sans Bold", font_size_18, white.toList(), ticks)
  1869.  
  1870.     current_weight     = "Weight: %.4f"      % WeightData.current_weight
  1871.     current_collisions = "Current Score: %s" % WeightData.current_collisions
  1872.     current_game       = "Current Game: %s"  % WeightData.current_game
  1873.     current_generation = "Generation: %s"    % WeightData.generation
  1874.  
  1875.     renderText(screen, window_width / 2, 320, "Free Sans Bold", font_size_18, white.toList(), current_weight)
  1876.  
  1877.     if GameState.toggle_genetic:
  1878.         renderText(screen, window_width / 2, 335, "Free Sans Bold", font_size_18, white.toList(), current_collisions)
  1879.         renderText(screen, window_width / 2, 350, "Free Sans Bold", font_size_18, white.toList(), current_game)
  1880.         renderText(screen, window_width / 2, 365, "Free Sans Bold", font_size_18, white.toList(), current_generation)
  1881.  
  1882.     padlist = Pads.padlist
  1883.     pad_green_score   = "Green Pad Score: %s"  % padlist[0].score
  1884.     pad_blue_score    = "Blue Pad Score: %s"   % padlist[1].score
  1885.     pad_orange_score  = "Orange Pad Score: %s" % padlist[2].score
  1886.     pad_red_score     = "Red Pad Score: %s"    % padlist[3].score
  1887.  
  1888.     renderText(screen, window_width / 2, 420, "Free Sans Bold", font_size_18, white.toList(), pad_green_score)
  1889.     renderText(screen, window_width / 2, 435, "Free Sans Bold", font_size_18, white.toList(), pad_blue_score)
  1890.     renderText(screen, window_width / 2, 450, "Free Sans Bold", font_size_18, white.toList(), pad_orange_score)
  1891.     renderText(screen, window_width / 2, 465, "Free Sans Bold", font_size_18, white.toList(), pad_red_score)
  1892.  
  1893.     offset = 6
  1894.     update_area = [200 + offset, 200 + offset, 300 - offset * 2, 500 - offset * 2]
  1895.     pygame.display.update(update_area)
  1896.     return
  1897.  
  1898. def drawLauncher(screen):
  1899.     launcher = Launchers.launcher
  1900.     launcher_colour = launcher.getColour()
  1901.     launcher_radius = launcher.getRadius()
  1902.    
  1903.     xlauncher = int(launcher.x)
  1904.     ylauncher = int(launcher.y)
  1905.     if not pause_button.active:
  1906.         launcher.coolDown()
  1907.  
  1908.     pygame.draw.circle(screen, launcher_colour, (xlauncher, ylauncher), launcher_radius, 1)
  1909.     return
  1910.  
  1911. def drawSpeaker(screen):
  1912.     speaker_icon = sound_level.surface
  1913.     sound_level.setCoverTransparency()
  1914.    
  1915.     xspeaker = sound_level.pos_x
  1916.     yspeaker = sound_level.pos_y
  1917.    
  1918.     screen.blit(speaker_icon, (xspeaker, yspeaker))
  1919.     screen.blit(sound_level.cover, (xspeaker, yspeaker))
  1920.     return
  1921.  
  1922. def drawPauseButton(screen):
  1923.     pause_surface.fill(black.toList())
  1924.    
  1925.     if pause_button.active:
  1926.         pause_surface.set_alpha(255 * 0.85)
  1927.         fill_val = 0
  1928.     elif pause_button.hovering:
  1929.         pause_surface.set_alpha(255 * 0.85)
  1930.         fill_val = 1
  1931.     else:
  1932.         pause_surface.set_alpha(255 * 0.5)
  1933.         fill_val = 1
  1934.  
  1935.     pygame.draw.rect(pause_surface, white.toList(), [0,
  1936.                                                      0,
  1937.                                                      pause_button.width * (1/3),
  1938.                                                      pause_button.height], fill_val)
  1939.    
  1940.     pygame.draw.rect(pause_surface, white.toList(), [pause_button.width * (2/3),
  1941.                                                      0,
  1942.                                                      pause_button.width * (1/3),
  1943.                                                      pause_button.height], fill_val)
  1944.     screen.blit(pause_surface, (pause_button.pos_x, pause_button.pos_y))
  1945.     return
  1946.  
  1947. def drawLetterButton(letter, screen):
  1948.     if letter.lower() == "i":
  1949.         button = info_button
  1950.     elif letter.lower() == "t":
  1951.         button = print_button
  1952.     else:
  1953.         assert False
  1954.  
  1955.     letter_surface   = button.surface
  1956.     letter_cover     = button.cover
  1957.     if button.active:
  1958.         letter_cover.set_alpha(0)
  1959.     elif button.hovering:
  1960.         letter_cover.set_alpha(255 * 0.25)
  1961.     else:
  1962.         letter_cover.set_alpha(255 * 0.5)
  1963.        
  1964.     x_surface        = button.pos_x
  1965.     y_surface        = button.pos_y
  1966.  
  1967.     screen.blit(letter_surface, (x_surface, y_surface))
  1968.     screen.blit(letter_cover, (x_surface, y_surface))
  1969.     return    
  1970.  
  1971. def drawFastForwardButton(screen):
  1972.     surface = fast_forward.surface
  1973.     cover   = fast_forward.cover
  1974.     if fast_forward.active:
  1975.         cover.set_alpha(0)
  1976.     elif fast_forward.hovering:
  1977.         cover.set_alpha(255 * 0.25)
  1978.     else:
  1979.         cover.set_alpha(255 * 0.5)
  1980.  
  1981.     x_surface = fast_forward.pos_x
  1982.     y_surface = fast_forward.pos_y
  1983.  
  1984.     screen.blit(surface, (x_surface, y_surface))
  1985.     screen.blit(cover,   (x_surface, y_surface))
  1986.     return
  1987.  
  1988. def drawGearButton(screen):
  1989.     surface = gear_button.surface
  1990.     cover   = gear_button.cover
  1991.     if gear_button.active:
  1992.         cover.set_alpha(0)
  1993.     elif gear_button.hovering:
  1994.         cover.set_alpha(255 * 0.25)
  1995.     else:
  1996.         cover.set_alpha(255 * 0.5)
  1997.  
  1998.     x_surface = gear_button.pos_x
  1999.     y_surface = gear_button.pos_y
  2000.  
  2001.     screen.blit(surface, (x_surface, y_surface))
  2002.     screen.blit(cover,   (x_surface, y_surface))
  2003.     return
  2004.  
  2005. def drawSettingsMenu(screen):
  2006.     surface = SettingsMenu.modified_surface
  2007.     for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
  2008.         if i >= len(SettingOptions.options):
  2009.             break
  2010.         option = SettingOptions.options[i]
  2011.         option.construct()
  2012.         surface.blit(option.surface, (option.pos_x, option.pos_y))
  2013.  
  2014.     for arrow in [arrow_left, arrow_right]:
  2015.         surface.blit(arrow.surface, (arrow.pos_x, arrow.pos_y))
  2016.  
  2017.     x_text = surface.get_width() / 2 + 1
  2018.     y_text = arrow_left.pos_y + 6
  2019.     renderText(surface, x_text, y_text, "Free Sans Bold", font_size_14, white.toList(), str(SettingsMenu.current_page))
  2020.    
  2021.     x_surface = (window_width - surface.get_width()) / 2
  2022.     y_surface = (window_height - surface.get_height()) / 2
  2023.    
  2024.     screen.blit(surface, (x_surface, y_surface))
  2025.     ResetSettingsMenu()
  2026.     return
  2027.  
  2028. def drawScore(screen):
  2029.     score     = GameState.collision_count
  2030.     max_score = GameState.max_count
  2031.  
  2032.     if max_score:
  2033.         ratio = 1 - score / max_score
  2034.     else:
  2035.         ratio = 1
  2036.  
  2037.     colour = crossColours(red, green, ratio)
  2038.     offset = 25
  2039.     renderText(screen, window_width / 2, window_height / 2 + offset, "Free Sans Bold", font_size_16, colour.toList(), str(score))
  2040.  
  2041.     renderText(screen, window_width / 2, window_height / 2 - offset, "Free Sans Bold", font_size_16, white.toList(), str(max_score))
  2042.     return
  2043.  
  2044. def drawScoreMessage(screen):
  2045.     score_message = ""
  2046.     message_threshold   = 0
  2047.     message_frames_left = 0
  2048.     alpha = 255
  2049.     for i in range(len(GameState.score_messages_list)):
  2050.         message = GameState.score_messages_list[i][0]
  2051.         frames_left = GameState.score_messages[message]
  2052.         if frames_left:
  2053.             alpha = int(255 * frames_left / frames_per_score_message)
  2054.             GameState.score_messages[message] = frames_left - 1
  2055.             score_message = message
  2056.             message_index = i
  2057.             message_frames_left = frames_left
  2058.  
  2059.     if score_message:
  2060.         colour = crossColours(crossColours(green, white,  message_index / 7), black, message_frames_left / frames_per_score_message)
  2061.         font_size = 12 + i * 2
  2062.         renderText(screen, window_width / 2, window_height / 2 - 100, "Sergeant SixPack", font_size, colour.toList(), score_message)
  2063.  
  2064.     return
  2065.  
  2066. def handleScoreMessages():
  2067.     score = GameState.collision_count
  2068.     for message, threshold in score_message_thresholds.items():
  2069.         if score == threshold:
  2070.             GameState.score_messages[message] = frames_per_score_message
  2071.     return
  2072.  
  2073. ############################################################
  2074. #
  2075. #     H A N D L E    M E N U S    A N D    C O V E R S
  2076. #
  2077.  
  2078. def ResetSettingsMenu():
  2079.     SettingsMenu.modified_surface = SettingsMenu.default_surface.copy()
  2080.     return
  2081.  
  2082. def setFastForwardCoverStatus(boolean):
  2083.     GameState.fast_forward_cover_loaded = boolean
  2084.     if boolean:
  2085.         sound_level.pos_x  = 214
  2086.         sound_level.pos_y  = 210
  2087.        
  2088.         pause_button.pos_x = 250
  2089.         pause_button.pos_y = 214
  2090.  
  2091.         fast_forward.pos_x = 278
  2092.         fast_forward.pos_y = 211
  2093.     else:
  2094.         sound_level.pos_x = 10
  2095.         sound_level.pos_y = 10
  2096.        
  2097.         pause_button.pos_x = 50
  2098.         pause_button.pos_y = 14
  2099.  
  2100.         fast_forward.pos_x = 79
  2101.         fast_forward.pos_y = 11
  2102.     return
  2103.  
  2104. def loadFastForwardCover():
  2105.     cover = FastForwardCover.surface
  2106.     Screen.screen.blit(cover, (0, 0))
  2107.     pygame.display.flip()
  2108.     setFastForwardCoverStatus(True)
  2109.     return
  2110.  
  2111. ############################################################
  2112. #
  2113. #           K E Y B O A R D    H A N D L I N G
  2114. #
  2115.  
  2116. def keyPressed(key):
  2117.     if key == K_ESCAPE:
  2118.         if gear_button.active:
  2119.             gear_button.switch()
  2120.             pause_button.switch()
  2121.         else:
  2122.             GameState.done         = True
  2123.         return
  2124.  
  2125.     if gear_button.active:
  2126.         if key == K_RIGHT:
  2127.             SettingsMenu.current_page = min(SettingsMenu.total_pages,
  2128.                                               SettingsMenu.current_page + 1)
  2129.         elif key == K_LEFT:
  2130.             SettingsMenu.current_page = max(1, SettingsMenu.current_page - 1)
  2131.            
  2132.     if key == K_1:
  2133.         sound_level.changeSoundLevel()
  2134.         return
  2135.     elif key == K_2:
  2136.         pause_button.switch()
  2137.         return
  2138.     elif key == K_3:
  2139.         fast_forward.switch()
  2140.         if not fast_forward.active:
  2141.             setFastForwardCoverStatus(False)
  2142.         return
  2143.     elif key == K_4 and not fast_forward.active:
  2144.         info_button.switch()
  2145.         return
  2146.     elif key == K_5 and not fast_forward.active:
  2147.         print_button.switch()
  2148.         return
  2149.     elif key == K_6 and not fast_forward.active:
  2150.         gear_button.switch()
  2151.         pause_button.active = gear_button.active
  2152.         return
  2153.  
  2154.     if gear_button.active:
  2155.         return
  2156.    
  2157.     if key == K_a:
  2158.         KeyBools.aPressed     = True
  2159.     elif key == K_d:
  2160.         KeyBools.dPressed     = True
  2161.  
  2162.     elif key == K_i:
  2163.         KeyBools.iPressed     = True
  2164.     elif key == K_k:
  2165.         KeyBools.kPressed     = True
  2166.  
  2167.     elif key == K_LEFT:
  2168.         KeyBools.leftPressed  = True
  2169.     elif key == K_RIGHT:
  2170.         KeyBools.rightPressed = True
  2171.  
  2172.     elif key == K_KP2:
  2173.         KeyBools.kp2Pressed   = True
  2174.     elif key == K_KP8:
  2175.         KeyBools.kp8Pressed   = True
  2176.     return
  2177.  
  2178. def keyReleased(key):
  2179.     if key == K_a:
  2180.         KeyBools.aPressed     = False
  2181.     elif key == K_d:
  2182.         KeyBools.dPressed     = False
  2183.  
  2184.     elif key == K_i:
  2185.         KeyBools.iPressed     = False
  2186.     elif key == K_k:
  2187.         KeyBools.kPressed     = False
  2188.  
  2189.     elif key == K_LEFT:
  2190.         KeyBools.leftPressed  = False
  2191.     elif key == K_RIGHT:
  2192.         KeyBools.rightPressed = False
  2193.  
  2194.     elif key == K_KP2:
  2195.         KeyBools.kp2Pressed   = False
  2196.     elif key == K_KP8:
  2197.         KeyBools.kp8Pressed   = False
  2198.     return
  2199.  
  2200. ############################################################
  2201. #
  2202. #             M O T I O N    H A N D L I N G
  2203. #
  2204.  
  2205. def handleMotion():
  2206.     padblue   = Pads.padlist[0]
  2207.     padgreen  = Pads.padlist[1]
  2208.     padred    = Pads.padlist[2]
  2209.     padorange = Pads.padlist[3]
  2210.  
  2211.     if not padgreen.AI:
  2212.         if KeyBools.aPressed:
  2213.             padgreen.move(-pad_speed * GameState.pad_speed_mult, 0)
  2214.         if KeyBools.dPressed:        
  2215.             padgreen.move(pad_speed * GameState.pad_speed_mult, 0)
  2216.         if not KeyBools.aPressed and not KeyBools.dPressed:
  2217.             padgreen.moving = (0, 0)
  2218.  
  2219.     if not padblue.AI:
  2220.         if KeyBools.iPressed:
  2221.             padblue.move(0, -pad_speed * GameState.pad_speed_mult)
  2222.         if KeyBools.kPressed:
  2223.             padblue.move(0, pad_speed * GameState.pad_speed_mult)
  2224.         if not KeyBools.iPressed and not KeyBools.kPressed:
  2225.             padblue.moving = (0, 0)
  2226.  
  2227.     if not padorange.AI:
  2228.         if KeyBools.leftPressed:
  2229.             padorange.move(-pad_speed * GameState.pad_speed_mult, 0)
  2230.         if KeyBools.rightPressed:
  2231.             padorange.move(pad_speed * GameState.pad_speed_mult, 0)
  2232.         if not KeyBools.leftPressed and not KeyBools.rightPressed:
  2233.             padorange.moving = (0, 0)
  2234.  
  2235.     if not padred.AI:
  2236.         if KeyBools.kp8Pressed:
  2237.             padred.move(0, -pad_speed * GameState.pad_speed_mult)
  2238.         if KeyBools.kp2Pressed:
  2239.             padred.move(0, pad_speed * GameState.pad_speed_mult)
  2240.         if not KeyBools.kp8Pressed and not KeyBools.kp2Pressed:
  2241.             padred.moving = (0, 0)
  2242.  
  2243.     Balls.ball.move()
  2244.  
  2245.     gravity_ring.pumpCircs()
  2246.     gravity_ring.modifyRadius()
  2247.     gravity_ring.handleGravFocals()
  2248.     return
  2249.  
  2250. ############################################################
  2251. #
  2252. #           C O L L I S I O N    H A N D L I N G
  2253. #
  2254.  
  2255. def collisionHandling(ball):
  2256.     collision = False
  2257.     (x_translate, y_translate) = ball.getVelocityComponents()
  2258.     xnew = ball.x + x_translate
  2259.     ynew = ball.y + y_translate
  2260.     padlist = Pads.padlist
  2261.     for i in range(len(padlist)):
  2262.         pad = padlist[i]
  2263.         if ball.last_pad == pad:
  2264.             continue
  2265.         (padxmid, padymid) = getPadMidpoint(i)
  2266.        
  2267.         padxmin = pad.x
  2268.         padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
  2269.         padymin = pad.y
  2270.         padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
  2271.  
  2272.         inwards_reflect_normal = i * 90
  2273.         side_reflect_normal    = (i+1) % 4 * 90
  2274.  
  2275.         tolerance = 0.01 * pad_height
  2276.  
  2277.        
  2278.         if objectWithinBounds(xnew, ynew, padxmin, padxmax, padymin, padymax):
  2279.             collision = pad
  2280.             if i == 0 or i == 2:
  2281.                 #'side_reflect_normal' if ball collides with bottom / top side.
  2282.                 #'inwards_reflect_normal' if ball collides with inwards-facing surface
  2283.                 if (ynew > padymax - tolerance or ynew < padymin + tolerance):
  2284.                     if i == 0 and xnew < padxmax and xnew > padxmax - tolerance:
  2285.                         normal = inwards_reflect_normal
  2286.                     elif i == 0:
  2287.                         normal = side_reflect_normal
  2288.                     elif i == 2 and xnew > padxmin and xnew < padxmin + tolerance:
  2289.                         normal = inwards_reflect_normal
  2290.                     else:
  2291.                         normal = side_reflect_normal
  2292.                 else:
  2293.                     normal = inwards_reflect_normal
  2294.  
  2295.                 reflected_angle = (plane_reflect_angles[normal] - ball.angle) % 360
  2296.             else:
  2297.                 #'side_reflect_normal' if ball collides with left / right side.
  2298.                 #'inwards_reflect_normal' if ball collides with inwards-facing surface
  2299.                 if (xnew > padxmax - tolerance or xnew < padxmin + tolerance) and (i == 1 and ynew > padymin or i == 3 and ynew < padymax):
  2300.                     if i == 1 and ynew > padymin and ynew < padymin + tolerance:
  2301.                         normal = inwards_reflect_normal
  2302.                     elif i == 1:
  2303.                         normal = side_reflect_normal
  2304.                     elif i == 3 and ynew < padymax and ynew > padymax - tolerance:
  2305.                         normal = inwards_reflect_normal
  2306.                     else:
  2307.                         normal = side_reflect_normal
  2308.                 else:
  2309.                     normal = inwards_reflect_normal
  2310.  
  2311.                 reflected_angle = (plane_reflect_angles[normal] - ball.angle) % 360
  2312.             break
  2313.            
  2314.     if collision:
  2315.         if WeightData.current_game < games_per_test:
  2316.             WeightData.current_collisions += 1
  2317.  
  2318.         GameState.collision_count += 1
  2319.         handleScoreMessages()
  2320.         if GameState.collision_count > GameState.max_count:
  2321.             GameState.max_count = GameState.collision_count
  2322.        
  2323.         modifier = 0.1
  2324.         (x_padmove, y_padmove) = collision.moving
  2325.         initial_angle = ball.angle
  2326.         initial_velocity = ball.velocity
  2327.        
  2328.         ball.angle = reflected_angle
  2329.         (x_ballmove, y_ballmove) = ball.getVelocityComponents()
  2330.        
  2331.         final_angle = angleFromComponents(x_ballmove + x_padmove * modifier, y_ballmove + y_padmove * modifier)
  2332.         final_velocity = velocityFromComponents(x_ballmove + x_padmove * modifier, y_ballmove + y_padmove * modifier)
  2333.         ball.angle = final_angle
  2334.         ball.velocity = final_velocity
  2335.  
  2336.         updateCollisionData(ball.x, ball.y, initial_angle, initial_velocity, final_angle, final_velocity)
  2337.         ball.last_pad = collision
  2338.     return
  2339.  
  2340. def updateCollisionData(x, y, i_angle, i_speed, f_angle, f_speed):
  2341.     Collision.xpos = x
  2342.     Collision.ypos = y
  2343.     Collision.initial_angle = i_angle
  2344.     Collision.initial_speed = i_speed
  2345.     Collision.final_angle   = f_angle
  2346.     Collision.final_speed   = f_speed
  2347.    
  2348.     if gravity_ring.active:
  2349.         Collision.grav_coef     = gravity_ring.grav_coef
  2350.     else:
  2351.         Collision.grav_coef     = 0
  2352.  
  2353.     updatePadTargets()
  2354.     return
  2355.        
  2356. def getCollisionData():
  2357.     collision_data = (Collision.xpos, Collision.ypos, Collision.initial_angle,
  2358.                       Collision.initial_speed, Collision.final_angle,
  2359.                       Collision.final_speed, Collision.grav_coef)
  2360.     return collision_data
  2361.  
  2362. ############################################################
  2363. #
  2364. #             B A L L    G E N E R A T I O N
  2365. #
  2366.  
  2367. def genNewBall():
  2368.     if WeightData.current_game < games_per_test:
  2369.         WeightData.current_game += 1
  2370.     elif GameState.toggle_genetic:
  2371.         beginNewTest()
  2372.    
  2373.     velocity = random.random()*0.3+0.3
  2374.     angle = 0
  2375.     while angle % 90 < 20:
  2376.         angle = random.randrange(360)
  2377.     ball = Ball(window_width/2, window_height/2, velocity, angle)
  2378.     Balls.ball = ball
  2379.     printData("ANGLE BALL LAUNCHED AT: %s" % angle)
  2380.     updateCollisionData(ball.x, ball.y, angle, velocity, angle, velocity)
  2381.     return
  2382.  
  2383. ############################################################
  2384. #
  2385. #                  M O U S E    I N P U T
  2386. #
  2387.  
  2388. def cursorWithinBounds(low_x, high_x, low_y, high_y):
  2389.     (mouse_x, mouse_y) = pygame.mouse.get_pos()
  2390.    
  2391.     if low_x <= mouse_x and mouse_x <= high_x:
  2392.         if low_y <= mouse_y and mouse_y <= high_y:
  2393.             return True
  2394.     return False
  2395.  
  2396. def hoverHandler(icon):
  2397.     hovering_over = cursorWithinBounds(icon.pos_x, icon.pos_x + icon.width,
  2398.                                        icon.pos_y, icon.pos_y + icon.height)
  2399.     return hovering_over
  2400.  
  2401. def setHover(icon):
  2402.     if hoverHandler(icon):
  2403.         icon.hovering = True
  2404.     else:
  2405.         icon.hovering = False
  2406.     return
  2407.  
  2408. def passiveMouseHandler():
  2409.     #(mouse_x, mouse_y) = pygame.mouse.get_pos()
  2410.     setHover(sound_level)
  2411.     setHover(pause_button)
  2412.     setHover(info_button)
  2413.     setHover(print_button)
  2414.     setHover(fast_forward)
  2415.     setHover(gear_button)
  2416.     setHover(arrow_left)
  2417.     setHover(arrow_right)
  2418.  
  2419.     if gear_button.active:
  2420.         x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
  2421.         y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
  2422.         for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
  2423.             if i >= len(SettingOptions.options):
  2424.                 break
  2425.             option = SettingOptions.options[i]
  2426.             option.pos_x += x_offset
  2427.             option.pos_y += y_offset
  2428.             setHover(option)
  2429.             option.pos_x -= x_offset
  2430.             option.pos_y -= y_offset
  2431.     return
  2432.  
  2433. def activeMouseHandler(position, button):
  2434.     if hoverHandler(sound_level):
  2435.         sound_level.changeSoundLevel()
  2436.    
  2437.     if hoverHandler(pause_button):
  2438.         pause_button.switch()
  2439.  
  2440.     if hoverHandler(info_button):
  2441.         info_button.switch()
  2442.  
  2443.     if hoverHandler(print_button):
  2444.         print_button.switch()
  2445.  
  2446.     if hoverHandler(fast_forward):
  2447.         fast_forward.switch()
  2448.         if not fast_forward.active:
  2449.             setFastForwardCoverStatus(False)
  2450.  
  2451.     if hoverHandler(gear_button):
  2452.         gear_button.switch()
  2453.         pause_button.active = gear_button.active
  2454.  
  2455.     if gear_button.active:
  2456.         x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
  2457.         y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
  2458.         for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
  2459.             if i >= len(SettingOptions.options):
  2460.                 break
  2461.             option = SettingOptions.options[i]
  2462.             option.pos_x += x_offset
  2463.             option.pos_y += y_offset
  2464.             if hoverHandler(option):
  2465.                 option.switch()
  2466.             option.pos_x -= x_offset
  2467.             option.pos_y -= y_offset
  2468.  
  2469.         for arrow in [arrow_left, arrow_right]:
  2470.             arrow.pos_x += x_offset
  2471.             arrow.pos_y += y_offset
  2472.             if hoverHandler(arrow):
  2473.                 if arrow == arrow_left:
  2474.                     SettingsMenu.current_page = max(1, SettingsMenu.current_page - 1)
  2475.                 else:
  2476.                     SettingsMenu.current_page = min(SettingsMenu.total_pages, SettingsMenu.current_page + 1)
  2477.             arrow.pos_x -= x_offset
  2478.             arrow.pos_y -= y_offset
  2479.     return
  2480.  
  2481. ############################################################
  2482. #
  2483. #           S E T T I N G S    H A N D L I N G
  2484. #
  2485.  
  2486. def SettingOptionsHandler():
  2487.     for option in SettingOptions.options:
  2488.         option.adjustSize()
  2489.         option.adjustColour()
  2490.     return
  2491.  
  2492. ############################################################
  2493. #
  2494. #            M A I N    G A M E    L O O P
  2495. #
  2496.  
  2497. def main():
  2498.     pygame.init()
  2499.     screen = pygame.display.set_mode((window_width, window_height), 0, 32)
  2500.     Screen.screen = screen
  2501.     pygame.display.set_caption("PyBall")
  2502.     initialiseFonts()
  2503.     initialiseSession()
  2504.     beginNewGeneration()
  2505.     text_colour_val = 255
  2506.     demonstration_begun  = False
  2507.     return_pressed       = False
  2508.     ticks                = 0
  2509.     real_ticks           = 0
  2510.     while not GameState.done:
  2511.         real_ticks += 1
  2512.         screen.fill(black.toList())
  2513.         sound_level.adjustVolume()
  2514.         passiveMouseHandler()
  2515.         SettingOptionsHandler()
  2516.         events = pygame.event.get()
  2517.         for e in events:
  2518.             if e.type == QUIT:
  2519.                 GameState.done = True
  2520.                 break
  2521.             elif not demonstration_begun and e.type == KEYDOWN and e.key == K_RETURN:
  2522.                 return_pressed = True
  2523.             elif e.type == KEYDOWN and demonstration_begun:
  2524.                 keyPressed(e.key)
  2525.             elif e.type == KEYUP and demonstration_begun:
  2526.                 keyReleased(e.key)
  2527.             elif e.type == MOUSEBUTTONDOWN:
  2528.                 activeMouseHandler(e.pos, e.button)
  2529.  
  2530.         if text_colour_val:
  2531.             text_colour = [text_colour_val, text_colour_val, text_colour_val]
  2532.             renderText(screen, window_width / 2, 120, "Angelic War", font_size_40, text_colour, "PyBall")
  2533.             renderText(screen, window_width / 2, 160, "Angelic War", font_size_14, text_colour, "Created By Tag")
  2534.             if return_pressed:
  2535.                 text_colour_val -= 1
  2536.            
  2537.             drawObjects(screen)
  2538.             pygame.display.flip()
  2539.         else:
  2540.             demonstration_begun = True
  2541.             if gear_button.active:
  2542.                 pause_button.active = True
  2543.                 fast_forward.active = False
  2544.                 if info_button.active:
  2545.                     circsAtTargets(screen)
  2546.                 drawObjects(screen)
  2547.                 drawSettingsMenu(screen)
  2548.                 renderText(screen, window_width / 2, 160, "Birth Of A Hero", font_size_30, white.toList(), "S E T T I N G S")
  2549.                 pygame.display.flip()
  2550.             elif pause_button.active and not fast_forward.active:
  2551.                 renderText(screen, window_width / 2, 160, "Birth Of A Hero", font_size_30, white.toList(), "P A U S E D")
  2552.                 if info_button.active:
  2553.                     circsAtTargets(screen)
  2554.                 drawObjects(screen)
  2555.                 pygame.display.flip()
  2556.             elif fast_forward.active and not pause_button.active:
  2557.                 ticks += 1
  2558.                 drawFastForwardCover(screen, ticks, real_ticks)
  2559.                 handleAI()
  2560.                 handleMotion()
  2561.             elif fast_forward.active and pause_button.active:
  2562.                 drawFastForwardCover(screen, ticks, real_ticks)
  2563.             else:
  2564.                 if gravity_ring.active:
  2565.                     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")
  2566.                 ticks += 1
  2567.                 handleAI()
  2568.                 handleMotion()
  2569.                 if info_button.active:
  2570.                     circsAtTargets(screen)
  2571.                 drawObjects(screen)
  2572.                 pygame.display.flip()
  2573.  
  2574.     if GameState.done:
  2575.         pygame.quit()
  2576.     return
  2577.  
  2578. if __name__ == "__main__":
  2579.     main()
  2580.  
  2581. import math
  2582. import time
  2583. import random
  2584. import pygame
  2585. from pygame.locals import *
  2586.  
  2587. ###################################
  2588. # P Y B A L L
  2589.  
  2590. # PyBall - A Pong-Based Adaptive AI Demonstration / Game
  2591. # Created by TAGC
  2592. # Last update: 20/03/2012
  2593. # Description: This program is developed as part of an
  2594. #              assessed project at Imperial College, London
  2595. #              to demonstrate the use of AI in gaming and
  2596. #              contrast the use of static and dynamic
  2597. #              AI techniques to allow further analysis into
  2598. #              how different kinds of AI behaviour can affect
  2599. #              replayability and game immersion
  2600. # Notes:       This script will only run if it comes as part
  2601. #              of the "PyBallStuff" package. This package
  2602. #              contains the audio, image and font files used
  2603. #              by this program
  2604.  
  2605. ###################################
  2606. # I N S T A L L A T I O N
  2607.  
  2608. # This script is written in Python 3.2 and requires a Python 3
  2609. # interpreter to run. This can be obtained from
  2610. # http://www.python.org/download/ (it is strongly recommended
  2611. # that the user obtains the 32-bit version even if they have
  2612. # a 64-bit operating system)
  2613.  
  2614. # This script also requires an installation of the python
  2615. # module "PyGame" available at
  2616. # http://www.pygame.org/download.shtml (again, it is strongly
  2617. # recommended that the user obtains the 32-bit version)
  2618.  
  2619. # In addition to these, the user will need to install the
  2620. # following fonts:
  2621. # > "Birth of a Hero"
  2622. # > "Angelic War"
  2623. # > "Hawaii Lover"
  2624. # > "Sergeant SixPack"
  2625. # These are all available at http://www.1001freefonts.com/
  2626. # if the user does not possess the "Fonts" folder included
  2627. # inside the folder "PyBallStuff"
  2628.  
  2629. # The "PyBallStuff" folder must be placed in the
  2630. # "C:/Python32" directory
  2631.  
  2632. ###################################
  2633. # L A U N C H I N G
  2634.  
  2635. # To execute the program, click on "Run" on the top bar
  2636. # of the interpreter and select "Run Module" or press F5
  2637.  
  2638. ###################################
  2639. # C O N T R O L S
  2640.  
  2641. # Special Controls
  2642. ###################
  2643. # "Enter"  - begins the game after the program launches
  2644. # "Escape" - if the user has the Settings Menu open, this
  2645. #            exits that screen, otherwise it quits the
  2646. #            programs
  2647.  
  2648. # Setting Controls
  2649. ###################
  2650. # "1"      - Toggles through the sound level settings
  2651. # "2"      - Pauses / unpauses the program
  2652. # "3"      - Toggles Fast Forward mode on and off; when
  2653. #            this mode is active, the logic of the game
  2654. #            runs slightly faster
  2655. # "4"      - Toggles Graphical Information mode on and off;
  2656. #            when on, extra objects are rendered to the
  2657. #            screen to display more information on the
  2658. #            states of the pads' memories, etc
  2659. # "5"      - Toggles Textual Information mode on and off;
  2660. #            when on, information on the game is printed
  2661. #            out to the console
  2662. # "6"      - Opens or closes the Settings Menu
  2663. # "Left"   - when the Settings Menu is open, this displays
  2664. #            the previous page of options (if not already
  2665. #            there)
  2666. # "Right"  - when the Settings Menu is open, this displays
  2667. #            the next page of options (if not already
  2668. #            there)
  2669.  
  2670. # Pad Controls
  2671. ###############
  2672. # "A"      - if the green pad is under human control, moves
  2673. #            that pad left
  2674. # "D"      - if the green pad is under human control, moves
  2675. #            that pad right
  2676. # "I"      - if the blue pad is under human control, moves
  2677. #            that pad up
  2678. # "K"      - if the blue pad is under human control, moves
  2679. #            that pad down
  2680. # "Left"   - if the red pad is under human control, moves
  2681. #            that pad left
  2682. # "Right"  - if the red pad is under human control, moves
  2683. #            that pad right
  2684. # "Num 8"  - if the orange pad is under human control, moves
  2685. #            that pad up
  2686. # "Num 2"  - if the orange pad is under human control, moves
  2687. #            that pad down
  2688.  
  2689. window_width  = 700
  2690. window_height = 700
  2691.  
  2692. pad_width   = 25
  2693. pad_height  = 100
  2694. ball_radius = 2
  2695.  
  2696. corner_buffer_coef = 0.20
  2697.  
  2698. population_size      = 20
  2699. games_per_test       = 50
  2700.  
  2701. xmin = window_width  * corner_buffer_coef     - pad_height / 2
  2702. xmax = window_width  * (1-corner_buffer_coef) - pad_height / 2
  2703. ymin = window_height * corner_buffer_coef     - pad_height / 2
  2704. ymax = window_height * (1-corner_buffer_coef) - pad_height / 2
  2705.  
  2706. plane_reflect_angles = {0:180, 90:360, 180:180, 270:360}
  2707.  
  2708. pad_speed = 0.5
  2709.  
  2710. ball_accel = 0.00001
  2711.  
  2712. max_x_difference       = window_width
  2713. max_y_difference       = window_height
  2714. max_angular_difference = 180
  2715. max_gravity_difference = 100
  2716.  
  2717. pyball_stuff_path = r"C:\Python32\PyBallStuff\\"
  2718. pad_image_path    = pyball_stuff_path + r"Pictures\Pads"
  2719. sound_level_path  = pyball_stuff_path + r"Pictures\SoundLevels"
  2720. letter_path       = pyball_stuff_path + r"Pictures\Letters"
  2721. picture_path      = pyball_stuff_path + r"Pictures"
  2722. music_path        = pyball_stuff_path + r"Audio"
  2723. pad_memory_path   = pyball_stuff_path + r"Memories"
  2724. data_path         = pyball_stuff_path + r"Data"
  2725.  
  2726. font_size_11   = 11
  2727. font_size_12   = 12
  2728. font_size_14   = 14
  2729. font_size_16   = 16
  2730. font_size_18   = 18
  2731. font_size_20   = 20
  2732. font_size_22   = 22
  2733. font_size_24   = 24
  2734. font_size_26   = 26
  2735. font_size_30   = 30
  2736. font_size_40   = 40
  2737.  
  2738. frames_per_score_message = 1000
  2739. score_message_thresholds = {"Okay":5, "Good":10, "Great":15, "Excellent!":20,
  2740.                            "Superb!":25, "Amazing!":30, "Outstanding!":50,
  2741.                            "Unbelievable!":100}
  2742.  
  2743. class Colour:
  2744.     def __init__(self, r, g, b):
  2745.         self.r = r
  2746.         self.g = g
  2747.         self.b = b
  2748.         return
  2749.     def toList(self):
  2750.         listrep = [self.r, self.g, self.b]
  2751.         return listrep
  2752.  
  2753. class Pad:
  2754.     def __init__(self, x, y, surface):
  2755.         self.x        = x
  2756.         self.y        = y
  2757.         self.x_target = x
  2758.         self.y_target = y
  2759.         self.surface  = surface
  2760.         self.moving   = (0, 0)
  2761.         self.AI       = 1
  2762.         self.score    = 0
  2763.         self.memory   = []
  2764.         return
  2765.     def move(self, x_translate, y_translate):
  2766.         self.moving = (x_translate, y_translate)
  2767.         self.xnew   = self.x + x_translate
  2768.         self.ynew   = self.y + y_translate
  2769.         if xmin <= self.xnew and self.xnew <= xmax:
  2770.             self.x = self.xnew
  2771.         else:
  2772.             x_translate = 0
  2773.         if ymin <= self.ynew and self.ynew <= ymax:
  2774.             self.y = self.ynew
  2775.         else:
  2776.             y_translate = 0
  2777.         self.moving = (x_translate, y_translate)
  2778.         return
  2779.     def losePoint(self, x_miss, y_miss):
  2780.         self.score -= 1
  2781.         if GameState.toggle_learning and Balls.ball.last_pad != self and self.AI == 1:
  2782.             memory_tuple = MemoryTuple(x_miss, y_miss)
  2783.             printData("SAVING TO MEMORY...")
  2784.             printData(memory_tuple.getData())
  2785.             printData("")
  2786.             self.memory.append(memory_tuple)
  2787.         return
  2788.     def toString(self):
  2789.         stringrep = ("(%s, %s)") % (self.x, self.y)
  2790.         return stringrep
  2791.    
  2792. class Ball:
  2793.     def __init__(self, x, y, velocity, angle):
  2794.         self.x        = x
  2795.         self.y        = y
  2796.         self.velocity = velocity
  2797.         self.angle    = angle
  2798.         self.last_pad = -1
  2799.         return
  2800.     def move(self):
  2801.         cut_off_modifier = 0.9
  2802.         collisionHandling(self)
  2803.  
  2804.         (pad_0_x, pad_0_y) = getPadMidpoint(0)
  2805.         (pad_1_x, pad_1_y) = getPadMidpoint(1)
  2806.         (pad_2_x, pad_2_y) = getPadMidpoint(2)
  2807.         (pad_3_x, pad_3_y) = getPadMidpoint(3)
  2808.        
  2809.         if self.x < pad_0_x * cut_off_modifier:
  2810.             Pads.padlist[0].losePoint(self.x, self.y)
  2811.             Launchers.launcher.launchBall()
  2812.         elif self.x > window_width - (window_width - pad_2_x) * cut_off_modifier:
  2813.             Pads.padlist[2].losePoint(self.x, self.y)
  2814.             Launchers.launcher.launchBall()
  2815.         elif self.y < pad_3_y * cut_off_modifier:
  2816.             Pads.padlist[3].losePoint(self.x, self.y)
  2817.             Launchers.launcher.launchBall()
  2818.         elif self.y > window_height - (window_height - pad_1_y) * cut_off_modifier:
  2819.             Pads.padlist[1].losePoint(self.x, self.y)
  2820.             Launchers.launcher.launchBall()
  2821.         else:
  2822.             (self.x_translate, self.y_translate) = self.getVelocityComponents()
  2823.             if gravity_ring.active:
  2824.                 target = gravity_ring
  2825.                 grav_pull_angle = degToRad(angleBetweenPoints((self.x, self.y),
  2826.                                                               (target.pos_x, target.pos_y)))
  2827.  
  2828.                 distance = getPointDistance(self.x, self.y, target.pos_x, target.pos_y)
  2829.                 grav_x = gravity_ring.grav_coef / 2000 * math.cos(grav_pull_angle) * math.pow(0.96, distance)
  2830.                 grav_y = gravity_ring.grav_coef / 2000 * math.sin(grav_pull_angle) * math.pow(0.96, distance)
  2831.                 if abs(self.x_translate + grav_x) < abs(self.x_translate):
  2832.                     grav_x *= 0.3
  2833.                 if abs(self.y_translate - grav_y) < abs(self.y_translate):
  2834.                     grav_y *= 0.3
  2835.                 self.velocity = velocityFromComponents(self.x_translate + grav_x,
  2836.                                                        self.y_translate - grav_y)
  2837.                 self.angle    = angleFromComponents(self.x_translate + grav_x,
  2838.                                                     self.y_translate - grav_y)
  2839.                
  2840.         self.velocity += GameState.ball_accel
  2841.         self.x += self.x_translate
  2842.         self.y += self.y_translate
  2843.         return
  2844.     def getVelocityComponents(self):
  2845.         x_translate =  math.cos(degToRad(self.angle)) * self.velocity
  2846.         y_translate = -math.sin(degToRad(self.angle)) * self.velocity
  2847.         components = (x_translate, y_translate)
  2848.         return components
  2849.     def toString(self):
  2850.         stringrep = ("Position: (%s, %s), moving at %s units/frame at angle %s"
  2851.                      ) % (self.x, self.y, self.velocity, self.angle)
  2852.         return stringrep
  2853.  
  2854. class GravFocal:
  2855.     def __init__(self, x, y, velocity, angle, grav_ring):
  2856.         self.pos_x     = x
  2857.         self.pos_y     = y
  2858.         self.velocity  = velocity
  2859.         self.angle     = angle
  2860.         self.grav_ring = grav_ring
  2861.         self.chasing   = ""
  2862.  
  2863.         i = 0
  2864.         while self.chasing == "" or self.chasing == self:
  2865.             if i < len(grav_ring.grav_focals):
  2866.                 self.chasing = random.choice(grav_ring.grav_focals)
  2867.                 i += 1
  2868.             else:
  2869.                 self.chasing = grav_ring
  2870.                 break
  2871.         return
  2872.     def move(self):
  2873.         (self.x_translate, self.y_translate) = self.getVelocityComponents()
  2874.        
  2875.         grav_pull_angle = degToRad(angleBetweenPoints((self.pos_x, self.pos_y),
  2876.                                                       (self.chasing.pos_x, self.chasing.pos_y)))
  2877.         grav_x = gravity_ring.grav_coef / 5000 * math.cos(grav_pull_angle)
  2878.         grav_y = gravity_ring.grav_coef / 5000 * math.sin(grav_pull_angle)
  2879.         self.velocity = velocityFromComponents(self.x_translate + grav_x, self.y_translate - grav_y)
  2880.         self.angle    = angleFromComponents(self.x_translate + grav_x, self.y_translate - grav_y)
  2881.        
  2882.         self.pos_x += self.x_translate
  2883.         self.pos_y += self.y_translate
  2884.         return
  2885.     def getVelocityComponents(self):
  2886.         x_translate =  math.cos(degToRad(self.angle)) * self.velocity
  2887.         y_translate = -math.sin(degToRad(self.angle)) * self.velocity
  2888.         components = (x_translate, y_translate)
  2889.         return components
  2890.    
  2891. class Launcher:
  2892.     def __init__(self, radius, cool_down_rate, hot_colour, cool_colour):
  2893.         self.x = window_width/2
  2894.         self.y = window_height/2
  2895.         self.radius = radius
  2896.         self.heat = 0
  2897.         self.cool_down_rate = cool_down_rate
  2898.         self.hot_colour  = hot_colour
  2899.         self.cool_colour = cool_colour
  2900.     def launchBall(self):
  2901.         GameState.data.append((GameState.game_count, GameState.collision_count))
  2902.         if GameState.auto_saving:
  2903.             if GameState.game_count > 0 and not GameState.game_count % 10:
  2904.                 saveGameData()
  2905.             if GameState.game_count > 0 and not GameState.game_count % 100:
  2906.                 backUpGameData()
  2907.         GameState.game_count += 1
  2908.         GameState.collision_count = 0
  2909.         genNewBall()
  2910.         self.heat = 1
  2911.         GameState.num_rounds += 1
  2912.         if GameState.auto_saving and GameState.num_rounds > 0:
  2913.             if GameState.num_rounds % GameState.auto_saving == 0:
  2914.                 savePadMemories()
  2915.     def coolDown(self):
  2916.         if self.heat > 0:
  2917.             self.heat -= self.cool_down_rate
  2918.     def getColour(self):
  2919.         self.hr = self.heat     * self.hot_colour[0]
  2920.         self.cr = (1-self.heat) * self.cool_colour[0]
  2921.         self.hg = self.heat     * self.hot_colour[1]
  2922.         self.cg = (1-self.heat) * self.cool_colour[1]
  2923.         self.hb = self.heat     * self.hot_colour[2]
  2924.         self.cb = (1-self.heat) * self.cool_colour[2]
  2925.         colour = [self.hr + self.cr, self.hg + self.cg, self.hb + self.cb]
  2926.         return colour
  2927.     def getRadius(self):
  2928.         actual_radius = int(self.radius * math.pow(1.01, self.heat * 100))
  2929.         return actual_radius
  2930.  
  2931. class SoundLevel:
  2932.     def __init__(self):
  2933.         self.levels = []
  2934.         self.current_level = 3
  2935.         self.volume    = 1
  2936.         self.pos_x     = 0
  2937.         self.pos_y     = 0
  2938.         self.width     = 0
  2939.         self.height    = 0
  2940.         self.hovering  = False
  2941.         self.surface   = ""
  2942.         self.cover     = ""
  2943.         self.channel   = ""
  2944.         return
  2945.     def addSurface(self, surface):
  2946.         self.surface = surface
  2947.         self.width = surface.get_width()
  2948.         self.height = surface.get_height()
  2949.         self.cover = pygame.Surface((self.width, self.height), 0, 32)
  2950.         self.cover.fill(black.toList())
  2951.         return
  2952.     def addChannel(self, channel):
  2953.         self.channel = channel
  2954.         self.channel.set_volume(0.333 * self.current_level)
  2955.         return
  2956.     def changeSoundLevel(self):
  2957.         self.current_level += 1
  2958.         self.current_level %= 4
  2959.         self.addSurface(self.levels[self.current_level])
  2960.         return
  2961.     def adjustVolume(self):
  2962.         target = 0.333 * self.current_level
  2963.         difference = target - self.volume
  2964.         self.volume += difference * 0.002
  2965.         self.channel.set_volume(self.volume)
  2966.         return
  2967.     def setCoverTransparency(self):
  2968.         if self.hovering:
  2969.             alpha = 255 * 0.15
  2970.         else:
  2971.             alpha = 255 * 0.5
  2972.         self.cover.set_alpha(alpha)
  2973.         return
  2974.  
  2975. class PauseButton:
  2976.     def __init__(self, x, y):
  2977.         self.active   = False
  2978.         self.hovering = False
  2979.         self.pos_x    = x
  2980.         self.pos_y    = y
  2981.         self.width    = 18
  2982.         self.height   = 18
  2983.         return
  2984.     def switch(self):
  2985.         self.active = not self.active
  2986.         return
  2987.  
  2988. class GenericButton:
  2989.     def __init__(self, x, y):
  2990.         self.active   = False
  2991.         self.hovering = False
  2992.         self.pos_x    = x
  2993.         self.pos_y    = y
  2994.         self.width    = 0
  2995.         self.height   = 0
  2996.         return
  2997.     def addSurface(self, surface):
  2998.         self.surface = surface
  2999.         self.width   = surface.get_width()
  3000.         self.height  = surface.get_height()
  3001.         self.cover   = pygame.Surface((self.width, self.height), 0, 32)
  3002.         self.cover.fill(black.toList())
  3003.         return
  3004.     def switch(self):
  3005.         self.active = not self.active
  3006.         return
  3007.    
  3008. class MemoryTuple:
  3009.     def __init__(self, x_miss, y_miss):
  3010.         self.x_miss              = x_miss
  3011.         self.y_miss              = y_miss
  3012.         self.x_collision         = Collision.xpos
  3013.         self.y_collision         = Collision.ypos
  3014.         self.collision_i_angle   = Collision.initial_angle
  3015.         self.collision_i_speed   = Collision.initial_speed
  3016.         self.collision_f_angle   = Collision.final_angle
  3017.         self.collision_f_speed   = Collision.final_speed
  3018.         self.collision_grav_coef = Collision.grav_coef
  3019.         return
  3020.     def getData(self):
  3021.         memory_tuple = (self.x_miss, self.y_miss, self.x_collision,
  3022.                         self.y_collision, self.collision_i_angle,
  3023.                         self.collision_i_speed, self.collision_f_angle,
  3024.                         self.collision_f_speed, self.collision_grav_coef)
  3025.         return memory_tuple
  3026.  
  3027. class SettingOption:
  3028.     def __init__(self, y, name, info, modes):
  3029.         self.pos_x      = 20
  3030.         self.pos_y      = y
  3031.         self.width      = 260
  3032.         self.height     = 50
  3033.         self.min_height = 50
  3034.         self.max_height = 100
  3035.         self.name       = name
  3036.         self.info       = info
  3037.         self.modes      = modes
  3038.         self.colour     = green.toList()
  3039.         self.active     = True
  3040.         self.hovering   = False
  3041.         self.surface    = pygame.Surface((self.width, self.height), pygame.SRCALPHA, 32)
  3042.         return
  3043.     def construct(self):
  3044.         offset = 5
  3045.         max_distance = getPointDistance(0, 0, offset, offset)
  3046.  
  3047.         self.surface.lock()
  3048.         for x in range(self.width):
  3049.             for y in range(self.height):
  3050.                 alpha_coef    = 1 - math.pow(0.992, (self.width - x) * 2)
  3051.                 alpha_subcoef = (abs(self.height / 2 - y) + (1 - x/self.width) * 20) * 5
  3052.                 alpha_coef   *= 1 - math.pow(0.992, alpha_subcoef)
  3053.  
  3054.                 if x < offset and y < offset:
  3055.                     distance    = getPointDistance(x, y, offset, offset)
  3056.                     alpha_coef *= max(0, 1 - distance / offset)
  3057.                 elif x < offset and y > self.height - offset:
  3058.                     distance    = getPointDistance(x, y, offset, self.height - offset)
  3059.                     alpha_coef *= max(0, 1 - distance / offset)
  3060.                 elif x < offset:
  3061.                     alpha_coef *= x / offset
  3062.                 elif y < offset:
  3063.                     alpha_coef *= y / offset
  3064.                 elif y > self.height - offset:
  3065.                     alpha_coef *= (self.height - y) / offset
  3066.                    
  3067.                 col_subcoef   = min(x, self.width - x) + min(y, self.height - y)
  3068.                 col_coef      = math.pow(0.992, col_subcoef)
  3069.  
  3070.                 if self.hovering:
  3071.                     alpha_coef = min(1, alpha_coef * 1.2)
  3072.                     col_coef   = min(1, col_coef   * 1.2)
  3073.                
  3074.                 bg_colour      = [self.colour[0]*col_coef, self.colour[1]*col_coef, self.colour[2]*col_coef, 255 * alpha_coef]
  3075.                 self.surface.set_at((x, y), bg_colour)
  3076.  
  3077.         self.surface.unlock()
  3078.        
  3079.         x_text = 0.1 * self.width
  3080.         y_text = self.min_height / 2 - 5
  3081.         renderText(self.surface, x_text, y_text, "Free Sans Bold", font_size_16, white.toList(), self.name, False)
  3082.        
  3083.         x_text = 0.1 * self.width
  3084.         y_text = self.min_height - 5
  3085.         textlen = len(self.info)
  3086.         textsplit = self.info.split()
  3087.         while textlen and y_text + 10 < self.surface.get_height():
  3088.             text = []
  3089.             if textsplit:
  3090.                 next_word_length = len(textsplit[0])
  3091.             else:
  3092.                 next_word_length = 0
  3093.             while len(" ".join(text)) + next_word_length < 43:
  3094.                 if textsplit:
  3095.                     word = textsplit.pop(0)
  3096.                 else:
  3097.                     textlen = 0
  3098.                     break
  3099.                 text.append(word)
  3100.                 textlen -= len(word)
  3101.  
  3102.             text = " ".join(text)
  3103.             renderText(self.surface, x_text, y_text, "Free Sans Bold", font_size_14, white.toList(), text, False)
  3104.             y_text += 10
  3105.  
  3106.         if self.hovering:
  3107.             x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
  3108.             y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
  3109.             radius = 2
  3110.             x_circ = round(x_offset + self.pos_x - 8)
  3111.             y_circ = round(y_offset + self.pos_y + self.height / 2 + radius / 2)
  3112.             pygame.draw.circle(Screen.screen, white.toList(), (x_circ, y_circ), radius)
  3113.         return
  3114.     def adjustColour(self):
  3115.         if self.modes[0] == 1 and self.active < 0:
  3116.             self.colour = crossColours(white, green, -self.active/10).toList()
  3117.             self.active += 1
  3118.         elif self.modes[0] == 1 and self.active >= 0:
  3119.             self.colour = green.toList()
  3120.         elif self.modes[0] == 2:
  3121.             self.colour = {False:red.toList(), True:green.toList()}[self.active]
  3122.         elif self.modes[0] > 2:
  3123.             self.colour = crossColours(green, red, self.active / (self.modes[0]-1)).toList()
  3124.         return
  3125.     def adjustSize(self):
  3126.         lower_index = (SettingsMenu.current_page - 1) * 5
  3127.         upper_index = SettingsMenu.current_page * 5
  3128.         self_index  = SettingOptions.options.index(self)
  3129.         if self_index < lower_index or self_index > upper_index:
  3130.             return
  3131.        
  3132.         rate = 10
  3133.         delta_height = 0
  3134.         if self.hovering and self.height < self.max_height:
  3135.             delta_height = rate
  3136.         elif not self.hovering and self.height > self.min_height:
  3137.             delta_height = -rate
  3138.  
  3139.         if delta_height:
  3140.             if self.height + delta_height > self.max_height:
  3141.                 actual_change = self.max_height - self.height
  3142.             elif self.height + delta_height < self.min_height:
  3143.                 actual_change = self.height - self.min_height
  3144.             else:
  3145.                 actual_change = delta_height
  3146.                
  3147.             self.height += actual_change
  3148.             self.surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA, 32)
  3149.            
  3150.             found_self = False
  3151.             for i in range(lower_index, upper_index):
  3152.                 if i >= len(SettingOptions.options):
  3153.                     break
  3154.                 option = SettingOptions.options[i]
  3155.                 if not found_self and self == option:
  3156.                     found_self = True
  3157.                 elif found_self:
  3158.                     option.pos_y += actual_change
  3159.         return        
  3160.     def switch(self):
  3161.         if self.modes[0] == 1:
  3162.             self.active = -10
  3163.         elif self.modes[0] == 2:
  3164.             self.active = not self.active
  3165.         elif self.modes[0] > 2:
  3166.             self.active = (self.active + 1) % self.modes[0]
  3167.  
  3168.         if "TOGGLE_LEARNING" in self.modes:
  3169.             GameState.toggle_learning = self.active
  3170.  
  3171.         if "TOGGLE_GENETIC" in self.modes:
  3172.             GameState.toggle_genetic = self.active
  3173.  
  3174.         if "TOGGLE_GRAVITY" in self.modes:
  3175.             gravity_ring.active = self.active
  3176.  
  3177.         if "SET_GRAV_COEF" in self.modes:
  3178.             val = input("Please enter new gravity coefficient: ")
  3179.             try:
  3180.                 val = float(val)
  3181.                 if 0 <= val and val <= 100:
  3182.                     gravity_ring.grav_coef = val
  3183.                 else:
  3184.                     print("Invalid value")
  3185.             except:
  3186.                 print("Error parsing value")
  3187.  
  3188.         if "SET_WEIGHT" in self.modes:
  3189.             val = input("Please enter new weight coefficient: ")
  3190.             try:
  3191.                 val = float(val)
  3192.                 if 0 <= val and val <= 1:
  3193.                     WeightData.current_weight = val
  3194.                 else:
  3195.                     print("Invalid value")
  3196.             except:
  3197.                 print("Error parsing value")
  3198.  
  3199.         if "SAVE_MEMORY" in self.modes:
  3200.             savePadMemories()
  3201.  
  3202.         if "BACK_UP_MEMORY" in self.modes:
  3203.             path = ""
  3204.             i = 0
  3205.             while path == "" or os.path.exists(path):
  3206.                 i += 1
  3207.                 path = os.path.join(pad_memory_path, r"BackUpMemories\Back-up %s" % i)
  3208.             backUpPadMemories(path)
  3209.  
  3210.         if "LOAD_MEMORY" in self.modes:
  3211.             loadPadMemories()
  3212.  
  3213.         if "RESET_MEMORY" in self.modes:
  3214.             for pad in Pads.padlist:
  3215.                 pad.memory = []
  3216.  
  3217.         if "RESET_SCORES" in self.modes:
  3218.             for pad in Pads.padlist:
  3219.                 pad.score = 0
  3220.  
  3221.         if "TOGGLE_AI_PAD1" in self.modes:
  3222.             Pads.padlist[0].AI = self.active / 2
  3223.  
  3224.         if "TOGGLE_AI_PAD2" in self.modes:
  3225.             Pads.padlist[1].AI = self.active / 2
  3226.  
  3227.         if "TOGGLE_AI_PAD3" in self.modes:
  3228.             Pads.padlist[2].AI = self.active / 2
  3229.  
  3230.         if "TOGGLE_AI_PAD4" in self.modes:
  3231.             Pads.padlist[3].AI = self.active / 2
  3232.  
  3233.         if "TOGGLE_ALL_AI" in self.modes:
  3234.             option_list = []
  3235.             for i in range(4):
  3236.                 option_list.append("TOGGLE_AI_PAD%s" % (i+1))
  3237.  
  3238.             enable_all = False
  3239.             active_set = -1
  3240.            
  3241.             for option in SettingOptions.options:
  3242.                 for related_option in option_list:
  3243.                     if related_option in option.modes and option.active != active_set:
  3244.                         if active_set == -1:
  3245.                             active_set = option.active
  3246.                             continue
  3247.                         else:
  3248.                             enable_all = True
  3249.                             break
  3250.  
  3251.             if enable_all:
  3252.                 active_set = 2
  3253.             else:
  3254.                 active_set = (active_set + 1) % 3
  3255.             for option in SettingOptions.options:
  3256.                 for related_option in option_list:
  3257.                     if related_option in option.modes:
  3258.                         option.active = active_set
  3259.  
  3260.             for pad in Pads.padlist:
  3261.                 pad.AI = active_set / 2
  3262.  
  3263.         if "ACCEL_BALL" in self.modes:
  3264.             for pad in Pads.padlist:
  3265.                 if self.active == 0:
  3266.                     GameState.ball_accel = 0
  3267.                 elif self.active == 1:
  3268.                     GameState.ball_accel = ball_accel
  3269.                 elif self.active == 2:
  3270.                     GameState.ball_accel = ball_accel * 10
  3271.                 else:
  3272.                     GameState.ball_accel = ball_accel * 50
  3273.  
  3274.         if "FAST_PADS" in self.modes:
  3275.             GameState.pad_speed_mult = {False:1, True:2}[self.active]
  3276.        
  3277.         if "AUTO_SAVE" in self.modes:
  3278.             rounds = GameState.num_rounds
  3279.             if rounds == 0:
  3280.                 GameState.auto_saving = 0
  3281.             elif self.active == 1:
  3282.                 GameState.auto_saving = 100
  3283.             elif self.active == 2:
  3284.                 GameState.auto_saving = 50
  3285.             else:
  3286.                 GameState.auto_saving = 10
  3287.                
  3288.         return
  3289.  
  3290. class GravityRing:
  3291.     def __init__(self):
  3292.         self.pos_x          = int(window_width  / 2)
  3293.         self.pos_y          = int(window_height / 2)
  3294.         self.grav_coef      = 0
  3295.         self.current_angle  = 0
  3296.         self.current_radius = 0
  3297.         self.end_radius     = 50
  3298.         self.rings          = 8
  3299.         self.pump_index     = 0
  3300.         self.pump_timer     = 100
  3301.         self.active         = False
  3302.         self.pumpvals       = []
  3303.         self.grav_focals    = []
  3304.  
  3305.         for i in range(10):
  3306.             self.grav_focals.append("")
  3307.             self.genNewGravFocal(i)
  3308.        
  3309.         for i in range(self.rings):
  3310.             self.pumpvals.append(0)
  3311.         return
  3312.     def genNewGravFocal(self, index):
  3313.         velocity = random.random()*0.2+0.2
  3314.         angle = 0
  3315.         while angle % 90 < 20:
  3316.             angle = random.randrange(360)
  3317.         grav_focal = GravFocal(window_width/2, window_height/2, velocity, angle, self)
  3318.         self.grav_focals[index] = grav_focal
  3319.         return
  3320.     def construct(self):
  3321.         if not self.current_radius > 0:
  3322.             return
  3323.        
  3324.         delta_angle = 360 / self.rings
  3325.         current_angle = 0
  3326.  
  3327.         for i in range(self.rings):
  3328.             circ_x = self.pos_x + round(self.current_radius * math.cos(degToRad(current_angle)))
  3329.             circ_y = self.pos_y + round(self.current_radius * math.sin(degToRad(current_angle)))
  3330.             circ_rad = round(2 * (1 + self.pumpvals[i] / 100))
  3331.             colour = crossColours(purple, grey, self.grav_coef / 180)
  3332.             pygame.draw.circle(Screen.screen, colour.toList(), (circ_x, circ_y), circ_rad, 1)
  3333.            
  3334.             current_angle += delta_angle
  3335.             if self.pumpvals[i]:
  3336.                 self.pumpvals[i] -= 1
  3337.  
  3338.         return
  3339.     def handleGravFocals(self):
  3340.         if not self.current_radius > 0:
  3341.             return
  3342.  
  3343.         for i in range(len(self.grav_focals)):
  3344.             focal = self.grav_focals[i]
  3345.             if getPointDistance(self.pos_x, self.pos_y, focal.pos_x, focal.pos_y) > self.current_radius:
  3346.                 self.genNewGravFocal(i)
  3347.                 return
  3348.            
  3349.             focal.move()
  3350.             colour = crossColours(purple, grey, self.grav_coef / 180)
  3351.             focal_radius = 2
  3352.             focal_x = round(focal.pos_x - focal_radius / 2)
  3353.             focal_y = round(focal.pos_y - focal_radius / 2)
  3354.             pygame.draw.circle(Screen.screen, colour.toList(), (focal_x, focal_y), focal_radius, 1)
  3355.  
  3356.     def modifyRadius(self):
  3357.         if self.active and 0.95 * self.end_radius <= self.current_radius and self.current_radius < self.end_radius:
  3358.             self.current_radius = self.end_radius
  3359.         elif self.active and self.current_radius < 0.95 * self.end_radius:
  3360.             self.current_radius += self.end_radius / 250
  3361.         elif not self.active and 0.05 * self.end_radius >= self.current_radius and self.current_radius >= 0:
  3362.             self.current_radius = 0
  3363.         else:
  3364.             self.current_radius -= self.end_radius / 250
  3365.         return
  3366.     def pumpCircs(self):
  3367.         if not self.pump_timer:
  3368.             self.pumpvals[self.pump_index] = 100
  3369.             self.pump_index = (self.pump_index + 1) % self.rings
  3370.  
  3371.         if self.pump_timer > 100 - 50 * (self.grav_coef - 50) / 50:
  3372.             self.pump_timer = 0
  3373.         else:
  3374.             self.pump_timer += 1
  3375.         return
  3376.  
  3377. class SettingOptions:
  3378.     options = []
  3379.    
  3380. class WeightData:
  3381.     weight_scores        = {}
  3382.     current_generation   = []
  3383.     current_weight       = 0
  3384.     current_weight_index = 0
  3385.     current_collisions   = 0
  3386.     current_game         = 0
  3387.     generation           = 0
  3388.    
  3389. class PadMemory:
  3390.     memory_blue   = []
  3391.     memory_green  = []
  3392.     memory_red    = []
  3393.     memory_orange = []
  3394.  
  3395. class Pads:
  3396.     padlist = []
  3397.  
  3398. class Balls:
  3399.     ball = ""    
  3400.  
  3401. class Screen:
  3402.     screen = ""
  3403.  
  3404. class FastForwardCover:
  3405.     surface = ""
  3406.  
  3407. class SettingsMenu:
  3408.     modified_surface = ""
  3409.     default_surface  = ""
  3410.     total_pages      = 4
  3411.     current_page     = 1
  3412.  
  3413. class GameState:
  3414.     done                      = False
  3415.     fast_forward_cover_loaded = False
  3416.     toggle_learning           = True
  3417.     toggle_genetic            = True
  3418.     collision_count           = 0
  3419.     max_count                 = 0
  3420.     num_rounds                = 0
  3421.     score_messages_list       = []
  3422.     score_messages            = {"Okay":0, "Good":0, "Great":0, "Excellent!":0,
  3423.                                  "Superb!":0, "Amazing!":0, "Outstanding!":0,
  3424.                                  "Unbelievable!":0}
  3425.     ball_accel                = 0
  3426.     pad_speed_mult            = 1
  3427.     auto_saving               = 0
  3428.     data                      = []
  3429.     game_count                = 0
  3430.    
  3431. class Collision:
  3432.     xpos           = 0
  3433.     ypos           = 0
  3434.     initial_angle  = 0
  3435.     initial_speed  = 0
  3436.     final_angle    = 0
  3437.     final_speed    = 0
  3438.     grav_coef      = 0
  3439.  
  3440. class Launchers:
  3441.     launcher = ""
  3442.  
  3443. class KeyBools:
  3444.     aPressed     = False
  3445.     dPressed     = False
  3446.  
  3447.     iPressed     = False
  3448.     kPressed     = False
  3449.  
  3450.     leftPressed  = False
  3451.     rightPressed = False
  3452.  
  3453.     kp8Pressed   = False
  3454.     kp2Pressed   = False
  3455.  
  3456. class FontTypes:
  3457.     Angelic_War_40_font              = ""
  3458.     Angelic_War_14_font              = ""
  3459.     Birth_Of_A_Hero_30_font          = ""
  3460.     Times_New_Roman_18_font          = ""
  3461.     Free_Sans_Bold_10_font           = ""
  3462.     Free_Sans_Bold_12_font           = ""
  3463.     Free_Sans_Bold_16_font           = ""
  3464.     Free_Sans_Bold_18_font           = ""
  3465.     Free_Sans_Bold_30_font           = ""
  3466.     Sergeant_SixPack_12_font         = ""
  3467.     Sergeant_SixPack_14_font         = ""
  3468.     Sergeant_SixPack_16_font         = ""
  3469.     Sergeant_SixPack_18_font         = ""
  3470.     Sergeant_SixPack_20_font         = ""
  3471.     Sergeant_SixPack_22_font         = ""
  3472.     Sergeant_SixPack_24_font         = ""
  3473.     Sergeant_SixPack_26_font         = ""
  3474.  
  3475. black      = Colour(0, 0, 0)
  3476. grey       = Colour(100, 100, 100)
  3477. white      = Colour(255, 255, 255)
  3478. orange     = Colour(255, 165, 0)
  3479. red        = Colour(255, 0, 0)
  3480. green      = Colour(0, 255, 0)
  3481. blue       = Colour(0, 0, 255)
  3482. yellow     = Colour(255, 255, 0)
  3483. cool_blue  = Colour(174, 238, 238)
  3484. purple     = Colour(223, 0, 255)
  3485. dark_red   = Colour(100, 0, 0)
  3486. dark_green = Colour(0, 100, 0)
  3487. dark_blue  = Colour(0, 0, 100)
  3488.  
  3489. sound_level  = SoundLevel()
  3490. pause_button = PauseButton(50, 14)
  3491. fast_forward = GenericButton(79, 11)
  3492. info_button  = GenericButton(105, 10)
  3493. print_button = GenericButton(135, 14)
  3494. gear_button  = GenericButton(160, 13)
  3495.  
  3496. arrow_left   = GenericButton(0, 460)
  3497. arrow_right  = GenericButton(0, 460)
  3498.  
  3499. pause_surface = pygame.Surface((pause_button.width, pause_button.height), 0, 32)
  3500. gravity_ring  = GravityRing()
  3501.  
  3502. ############################################################
  3503. #
  3504. #           U T I L I T Y    F U N C T I O N S
  3505. #
  3506.  
  3507. def degToRad(degrees):
  3508.     rads = degrees * math.pi / 180
  3509.     return rads
  3510.  
  3511. def radToDeg(radians):
  3512.     degrees = radians * 180 / math.pi
  3513.     return degrees
  3514.  
  3515. def crossColours(c1, c2, coef):
  3516.     anti_coef = 1 - coef
  3517.     c1 = c1.toList()
  3518.     c2 = c2.toList()
  3519.     result = Colour(c1[0]*coef + c2[0]*anti_coef,
  3520.                     c1[1]*coef + c2[1]*anti_coef,
  3521.                     c1[2]*coef + c2[2]*anti_coef)
  3522.     return result
  3523.  
  3524. def objectWithinBounds(obj_x, obj_y, x_low, x_high, y_low, y_high):
  3525.     ball = Balls.ball
  3526.     within_bounds = x_low <= obj_x and obj_x <= x_high and y_low <= obj_y and obj_y <= y_high
  3527.     return within_bounds
  3528.  
  3529. def angleBetweenPoints(p1, p2):
  3530.     (p1_x, p1_y) = p1
  3531.     (p2_x, p2_y) = p2
  3532.     x_dif = p2_x - p1_x
  3533.     y_dif = p2_y - p1_y
  3534.     angle = angleFromComponents(x_dif, y_dif)
  3535.     return angle
  3536.  
  3537. def angleFromComponents(x_translate, y_translate):
  3538.     #quadrant 1, 2, 3, 4
  3539.     y_translate *= -1
  3540.     if x_translate == 0:
  3541.         x_translate = 0.0001
  3542.     if x_translate > 0 and y_translate > 0:
  3543.         theta = radToDeg(math.atan(y_translate / x_translate))
  3544.     elif x_translate < 0 and y_translate > 0:
  3545.         theta = radToDeg(math.atan(y_translate / x_translate)) + 180
  3546.     elif x_translate < 0 and y_translate < 0:
  3547.         theta = radToDeg(math.atan(y_translate / x_translate)) + 180
  3548.     else:
  3549.         theta = radToDeg(math.atan(y_translate / x_translate)) + 360
  3550.  
  3551.     theta %= 360
  3552.     return theta
  3553.  
  3554. def velocityFromComponents(x_translate, y_translate):
  3555.     velocity = math.sqrt(x_translate * x_translate + y_translate * y_translate)
  3556.     return velocity
  3557.    
  3558. def insidePad():
  3559.     ball = Balls.ball
  3560.     padlist = Pads.padlist
  3561.     for i in range(len(padlist)):
  3562.         pad = padlist[i]
  3563.         padxmin = pad.x
  3564.         padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
  3565.         padymin = pad.y
  3566.         padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
  3567.         if padxmin <= ball.x and ball.x <= padxmax and padymin <= ball.y and ball.y <= padymax:
  3568.             return True
  3569.     return False
  3570.  
  3571. def averageAngles(first_angle, second_angle):
  3572.     average_angle = (first_angle + second_angle) / 2
  3573.     return average_angle
  3574.  
  3575. def getPointDistance(x1, y1, x2, y2):
  3576.     distance = math.sqrt(math.pow((x2 - x1), 2) + math.pow((y2 - y1), 2))
  3577.     return distance
  3578.  
  3579. def getPadMidpoint(padindex):
  3580.     i = padindex
  3581.     pad = Pads.padlist[i]
  3582.     padxmin = pad.x
  3583.     padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
  3584.     padymin = pad.y
  3585.     padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
  3586.     padxmid = (padxmin + padxmax) / 2
  3587.     padymid = (padymin + padymax) / 2
  3588.     midpoint = (padxmid, padymid)
  3589.     return midpoint
  3590.  
  3591. def getAngleDifference(angle1, angle2):
  3592.     angle_dif = 360 - max(angle1, angle2) + min(angle1, angle2)
  3593.     if angle_dif > 180:
  3594.         angle_dif = 360 - angle_dif
  3595.     return angle_dif
  3596.  
  3597. ############################################################
  3598. #
  3599. #           I N P U T / O U T P U T
  3600. #
  3601.  
  3602. def saveGameData():
  3603.     with open(os.path.join(data_path, "score_data.txt"), 'w') as file:
  3604.         for item in GameState.data:
  3605.             file.write(str(item) + "\n")
  3606.  
  3607.     padlist = Pads.padlist
  3608.     for i in range(len(padlist)):
  3609.         pad = padlist[i]
  3610.  
  3611.         with open(os.path.join(data_path, "pad_%s_memory.txt" % i), 'w') as file:
  3612.             for memory in pad.memory:
  3613.                 file.write(str(memory.getData()) + "\n")
  3614.  
  3615.     with open(os.path.join(data_path, "weight_coefficient.txt"), 'w') as file:
  3616.         file.write(str(WeightData.current_weight))
  3617.  
  3618.     with open(os.path.join(data_path, "grav_coefficient.txt"), 'w') as file:
  3619.         file.write(str(gravity_ring.grav_coef))
  3620.     return
  3621.  
  3622. def backUpGameData():
  3623.     path = ""
  3624.     i = 0
  3625.     while path == "" or os.path.exists(path):
  3626.         i += 1
  3627.         path = os.path.join(data_path, r"BackUpData\Back-up %s" % i)
  3628.  
  3629.     try:
  3630.         os.mkdir(path)
  3631.     except Exception as e:
  3632.         print("Error occured whilst making memory back up: %s" % e)
  3633.         return
  3634.  
  3635.     for filename in ["score_data.txt", "grav_coefficient.txt", "weight_coefficient.txt",
  3636.                      "pad_0_memory.txt", "pad_1_memory.txt", "pad_2_memory.txt",
  3637.                      "pad_3_memory.txt"]:
  3638.         file = os.path.join(data_path, filename)
  3639.         shutil.copy(file, path)
  3640.     print("Game data back-up created")
  3641.     return
  3642.    
  3643. def savePadMemories():
  3644.     padlist = Pads.padlist
  3645.     for i in range(len(padlist)):
  3646.         pad = padlist[i]
  3647.  
  3648.         with open(os.path.join(pad_memory_path, "pad_%s_memory.txt" % i), 'w') as file:
  3649.             for memory in pad.memory:
  3650.                 file.write(str(memory.getData()) + "\n")
  3651.  
  3652.     with open(os.path.join(pad_memory_path, "weight_coefficient.txt"), 'w') as file:
  3653.         file.write(str(WeightData.current_weight))
  3654.  
  3655.     return
  3656.  
  3657. def backUpPadMemories(path):
  3658.     if not os.path.exists(path):
  3659.         try:
  3660.             os.mkdir(path)
  3661.         except Exception as e:
  3662.             print("Error occurred whilst making memory back up: %s" % e)
  3663.             return
  3664.    
  3665.     padlist = Pads.padlist
  3666.     for i in range(len(padlist)):
  3667.         file = os.path.join(pad_memory_path, "pad_%s_memory.txt" % i)
  3668.         shutil.copy(file, path)
  3669.  
  3670.     file = os.path.join(pad_memory_path, "weight_coefficient.txt")
  3671.     shutil.copy(file, path)
  3672.     print("New directory created")
  3673.     return
  3674.  
  3675. def loadPadMemories():
  3676.     padlist = Pads.padlist
  3677.     for i in range(len(padlist)):
  3678.         pad = padlist[i]
  3679.  
  3680.         with open(os.path.join(pad_memory_path, "pad_%s_memory.txt" % i), 'r') as file:
  3681.             pad.memory = parseMemoryData(file.read())
  3682.  
  3683.     with open(os.path.join(pad_memory_path, "weight_coefficient.txt"), 'r') as file:
  3684.         WeightData.current_weight = float(file.read())
  3685.    
  3686.     return
  3687.  
  3688. def parseMemoryData(string_data):
  3689.     list_data = []
  3690.     for line in string_data.split("\n"):
  3691.         if not line:
  3692.             continue
  3693.         item_data = line.replace("(", "").replace(")", "").split(", ")
  3694.         x_miss            = float(item_data[0])
  3695.         y_miss            = float(item_data[1])
  3696.         memory_tuple = MemoryTuple(x_miss, y_miss)
  3697.        
  3698.         memory_tuple.x_collision         = float(item_data[2])
  3699.         memory_tuple.y_collision         = float(item_data[3])
  3700.         memory_tuple.collision_i_angle   = float(item_data[4])
  3701.         memory_tuple.collision_i_speed   = float(item_data[5])
  3702.         memory_tuple.collision_f_angle   = float(item_data[6])
  3703.         memory_tuple.collision_f_speed   = float(item_data[7])
  3704.         memory_tuple.collision_grav_coef = float(item_data[8])
  3705.        
  3706.         list_data.append(memory_tuple)
  3707.     return list_data
  3708.  
  3709. ############################################################
  3710. #
  3711. #                 A D A P T I V E    A I
  3712. #
  3713.  
  3714. def updatePadTargets():
  3715.     padlist = Pads.padlist
  3716.     for i in range(len(padlist)):
  3717.         pad = padlist[i]
  3718.         if pad.AI != 1:
  3719.             continue
  3720.         (x_target, y_target) = findBestApproximation(i)
  3721.         printData("%s: (%s, %s)" % (i, x_target, y_target))
  3722.         printMemory(i)
  3723.         pad.x_target = x_target
  3724.         pad.y_target = y_target
  3725.     printData("")
  3726.     return
  3727.  
  3728. def handleAI():
  3729.     padlist = Pads.padlist
  3730.     for i in range(len(padlist)):
  3731.         pad = padlist[i]
  3732.         if pad.AI == 0.5:
  3733.             pad.x_target = Balls.ball.x
  3734.             pad.y_target = Balls.ball.y
  3735.         elif pad.AI == 0:
  3736.             continue
  3737.  
  3738.         (padxmid, padymid)   = getPadMidpoint(i)
  3739.        
  3740.         if not i % 2:
  3741.             if padymid < pad.y_target:
  3742.                 pad.move(0, pad_speed * GameState.pad_speed_mult)
  3743.             elif padymid > pad.y_target:
  3744.                 pad.move(0, -pad_speed * GameState.pad_speed_mult)
  3745.         else:
  3746.             if padxmid < pad.x_target:
  3747.                 pad.move(pad_speed * GameState.pad_speed_mult, 0)
  3748.             elif padxmid > pad.x_target:
  3749.                 pad.move(-pad_speed * GameState.pad_speed_mult, 0)
  3750.     return
  3751.  
  3752. def findBestApproximation(padindex):
  3753.     printData("FINDING APPROXIMATION FOR PAD %s...\n" % padindex)
  3754.     pad = Pads.padlist[padindex]
  3755.     memory = pad.memory
  3756.     ball = Balls.ball
  3757.     if not memory:
  3758.         approximation = getPadMidpoint(padindex)
  3759.         return approximation
  3760.  
  3761.     collision_data = getCollisionData()
  3762.     (last_collision_x, last_collision_y, last_collision_i_angle,
  3763.      last_collision_i_speed, last_collision_f_angle,
  3764.      last_collision_f_speed, last_collision_grav_coef) = collision_data
  3765.    
  3766.     best_approx = 0
  3767.     strictness_coef = 1.03
  3768.    
  3769.     for memory_tuple in memory:
  3770.         (x_miss, y_miss, x_collision, y_collision, _, _, f_angle, _,
  3771.          collision_grav_coef) = memory_tuple.getData()
  3772.        
  3773.         (divergence, x_divergence, y_divergence, f_angular_divergence,
  3774.          grav_divergence) = calculateDivergence(memory_tuple, collision_data)
  3775.  
  3776.         approximation = (divergence, x_miss, y_miss)
  3777.        
  3778.         printData("\n\nPAD: %s" % padindex)
  3779.         printData("\nLAST COLLISION (X) = %s, CONSIDERED CASE (X) = %s" % (last_collision_x, x_collision))
  3780.         printData("pos_x DIVERGENCE: %s" % x_divergence)
  3781.  
  3782.         printData("\nLAST COLLISION (Y) = %s, CONSIDERED CASE (Y) = %s" % (last_collision_y, y_collision))
  3783.         printData("pos_y DIVERGENCE: %s" % y_divergence)
  3784.  
  3785.         printData("\nLAST COLLISION (fAngle) = %s, CONSIDERED CASE (fAngle) = %s" % (last_collision_f_angle, f_angle))
  3786.         printData("FINAL ANGLE DIVERGENCE: %s" % f_angular_divergence)
  3787.  
  3788.         printData("\nLAST COLLISION (grav) = %s, CONSIDERED CASE (grav) = %s" % (last_collision_grav_coef,
  3789.                                                                                  collision_grav_coef))
  3790.        
  3791.         printData("\nTOTAL DIVERGENCE: %s\n\n" % divergence)
  3792.        
  3793.         if not best_approx:
  3794.             best_approx = approximation
  3795.         else:
  3796.             (least_divergence, _, _) = best_approx
  3797.             if divergence < least_divergence:
  3798.                 best_approx = approximation
  3799.  
  3800.     (_, pos_xition, pos_yition) = best_approx
  3801.     approximation = (pos_xition, pos_yition)
  3802.     return approximation
  3803.  
  3804. def calculateDivergence(memory_tuple, collision_data):
  3805.     (last_collision_x, last_collision_y, last_collision_i_angle,
  3806.      last_collision_i_speed, last_collision_f_angle,
  3807.      last_collision_f_speed, last_collision_grav_coef) = collision_data
  3808.    
  3809.     (x_miss, y_miss, x_collision, y_collision,
  3810.      i_angle, i_speed, f_angle, f_speed,
  3811.      collision_grav_coef) = memory_tuple.getData()
  3812.  
  3813.     pos_x_dif   = abs(x_collision - last_collision_x)
  3814.     pos_y_dif   = abs(y_collision - last_collision_y)
  3815.     i_angle_dif = getAngleDifference(i_angle, last_collision_i_angle)
  3816.     i_speed_dif = abs(i_speed - last_collision_i_speed)
  3817.     f_angle_dif = getAngleDifference(f_angle, last_collision_f_angle)
  3818.     f_speed_dif = abs(f_speed - last_collision_f_speed)
  3819.     grav_dif    = abs(collision_grav_coef - last_collision_grav_coef)
  3820.  
  3821.     x_divergence         = 100 * pos_x_dif     / max_x_difference
  3822.     y_divergence         = 100 * pos_y_dif     / max_y_difference
  3823.     f_angular_divergence = 100 * f_angle_dif   / max_angular_difference
  3824.     grav_divergence      = 100 * grav_dif      / max_gravity_difference
  3825.  
  3826.     #Apply weights.
  3827.     x_divergence         *= WeightData.current_weight
  3828.     y_divergence         *= WeightData.current_weight
  3829.     f_angular_divergence *= (1 - WeightData.current_weight)
  3830.     grav_divergence      *= 0.5
  3831.  
  3832.     total_divergence = x_divergence +  y_divergence + f_angular_divergence + grav_divergence
  3833.    
  3834.     divergence_data = (total_divergence, x_divergence, y_divergence, f_angular_divergence, grav_divergence)
  3835.     return divergence_data  
  3836.  
  3837. ############################################################
  3838. #
  3839. #           G E N E T I C    A L G O R I T H M
  3840. #
  3841.  
  3842. def generateWeights():
  3843.     WeightData.generation += 1
  3844.     current_generation = produceChildren()
  3845.     while len(current_generation) < population_size:
  3846.         current_generation.append(random.random())
  3847.     WeightData.current_generation = current_generation
  3848.     print("NEW GENERATION: %s" % current_generation)
  3849.     return
  3850.  
  3851. def selectBestWeights():
  3852.     best_weights = []
  3853.     current_generation = WeightData.current_generation
  3854.     #Get the best three weights.
  3855.     for i in range(int(0.5 * population_size)):
  3856.         best_score  = -1
  3857.         best_weight = -1
  3858.         for weight, score in WeightData.weight_scores.items():
  3859.             if score > best_score and weight in current_generation and weight not in best_weights:
  3860.                 best_weight = weight
  3861.                 best_score  = score
  3862.         if best_weight != -1:
  3863.             best_weights.append(best_weight)
  3864.    
  3865.     return best_weights
  3866.  
  3867. def testNextWeight():
  3868.     WeightData.current_weight_index += 1
  3869.     index = WeightData.current_weight_index
  3870.     WeightData.current_weight = WeightData.current_generation[index]
  3871.     return
  3872.  
  3873. def produceChildren():
  3874.     best_weights = selectBestWeights()
  3875.     children = []
  3876.    
  3877.     for i in range(len(best_weights)):
  3878.         for j in range(i + 1, len(best_weights)):
  3879.             if len(children) == population_size:
  3880.                 break
  3881.             child = averageWeights(best_weights[i], best_weights[j])
  3882.             children.append(child)
  3883.  
  3884.     return children
  3885.  
  3886. def averageWeights(weight_1, weight_2):
  3887.     average_weight = (weight_1 + weight_2) / 2
  3888.     return average_weight
  3889.  
  3890. def scoreWeightValue():
  3891.     current_weight     = WeightData.current_weight
  3892.     current_collisions = WeightData.current_collisions
  3893.    
  3894.     WeightData.weight_scores[current_weight] = current_collisions
  3895.     printWeightScores()
  3896.     return
  3897.  
  3898. def printWeightScores():
  3899.     weight_scores = WeightData.weight_scores
  3900.     for weight, score in weight_scores.items():
  3901.         print("Weight %.4f: %s" % (weight, score))
  3902.     return
  3903.  
  3904. def beginNewTest():
  3905.     print("NEW TEST!")
  3906.     scoreWeightValue()
  3907.     WeightData.current_collisions    = 0
  3908.     WeightData.current_game          = 0
  3909.     if WeightData.current_weight_index < population_size - 1:
  3910.         WeightData.current_weight_index += 1
  3911.         index = WeightData.current_weight_index
  3912.         WeightData.current_weight = WeightData.current_generation[index]
  3913.     else:
  3914.         beginNewGeneration()
  3915.    
  3916.     padlist = Pads.padlist
  3917.     for i in range(len(padlist)):
  3918.         padlist[i].memory = []
  3919.  
  3920.     return
  3921.  
  3922. def beginNewGeneration():
  3923.     print("NEW GENERATION!")
  3924.     generateWeights()
  3925.     WeightData.current_weight_index = 0
  3926.     WeightData.current_weight = WeightData.current_generation[0]
  3927.     return
  3928.  
  3929. ############################################################
  3930. #
  3931. #        S U R F A C E S    F R O M    F I L E S
  3932. #
  3933.  
  3934. def getPadFromFile(padname):
  3935.     pad = pygame.image.load(os.path.join(pad_image_path, padname + ".png")).convert_alpha()
  3936.     return pad
  3937.  
  3938. def getSpeakerIcon(sound_level):
  3939.     scale = 0.12
  3940.     speaker = pygame.image.load(os.path.join(sound_level_path, "sound%s.png" % sound_level)).convert_alpha()
  3941.     speaker = scaleImage(speaker, scale)
  3942.     return speaker
  3943.  
  3944. def getLetterIcon(letter):
  3945.     scale = 0.09
  3946.     info_button = pygame.image.load(os.path.join(letter_path, "letter_" + letter + ".png")).convert_alpha()
  3947.     info_button = scaleImage(info_button, scale)
  3948.     return info_button
  3949.  
  3950. def getFastForwardIcon():
  3951.     scale = 0.2
  3952.     fast_forward = pygame.image.load(os.path.join(picture_path, "fast_forward.png")).convert_alpha()
  3953.     fast_forward = scaleImage(fast_forward, scale)
  3954.     return fast_forward
  3955.  
  3956. def getFastForwardCover():
  3957.     fast_forward_cover = pygame.image.load(os.path.join(picture_path, "pyballcover.png")).convert_alpha()
  3958.     return fast_forward_cover
  3959.  
  3960. def getGearIcon():
  3961.     scale = 0.1
  3962.     gear_button = pygame.image.load(os.path.join(picture_path, "gear.png")).convert_alpha()
  3963.     gear_button = scaleImage(gear_button, scale)
  3964.     return gear_button
  3965.  
  3966. def getArrowIcon(direction):
  3967.     scale = 0.1
  3968.     arrow_button = pygame.image.load(os.path.join(picture_path, "arrow_%s.png" % direction)).convert_alpha()
  3969.     arrow_button = scaleImage(arrow_button, scale)
  3970.     return arrow_button
  3971.  
  3972. def getSettingsMenu():
  3973.     settings_menu = pygame.image.load(os.path.join(picture_path, "settings_menu.png")).convert_alpha()
  3974.     return settings_menu
  3975.  
  3976. def scaleImage(image, scale_factor):
  3977.     scaled_image = pygame.transform.smoothscale(image, (int(image.get_width() * scale_factor), int(image.get_height() * scale_factor)))
  3978.     return scaled_image
  3979.  
  3980. ############################################################
  3981. #
  3982. #       S E S S I O N    I N I T I A L I S A T I O N
  3983. #
  3984.  
  3985. def initialiseMusic(sound_level):
  3986.     pygame.mixer.init()
  3987.     channel = pygame.mixer.Channel(0)
  3988.    
  3989.     song = "relaxing_space.wav"
  3990.     music = pygame.mixer.Sound(os.path.join(music_path, song))
  3991.     channel.play(music, -1)
  3992.     sound_level.addChannel(channel)
  3993.     return
  3994.  
  3995. def initialiseFonts():
  3996.     FontTypes.Angelic_War_14_font      = pygame.font.Font(pygame.font.match_font("Angelic War"),      font_size_14)
  3997.     FontTypes.Angelic_War_40_font      = pygame.font.Font(pygame.font.match_font("Angelic War"),      font_size_40)
  3998.     FontTypes.Birth_Of_A_Hero_30_font  = pygame.font.Font(pygame.font.match_font("Birth of a Hero"),  font_size_30)
  3999.     FontTypes.Times_New_Roman_18_font  = pygame.font.Font(pygame.font.match_font("Times New Roman"),  font_size_18)
  4000.     FontTypes.Free_Sans_Bold_10_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_11)
  4001.     FontTypes.Free_Sans_Bold_12_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_12)
  4002.     FontTypes.Free_Sans_Bold_14_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_14)
  4003.     FontTypes.Free_Sans_Bold_16_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_16)
  4004.     FontTypes.Free_Sans_Bold_18_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_18)
  4005.     FontTypes.Free_Sans_Bold_30_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_30)
  4006.     FontTypes.Sergeant_SixPack_12_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_12)
  4007.     FontTypes.Sergeant_SixPack_14_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_14)
  4008.     FontTypes.Sergeant_SixPack_16_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_16)
  4009.     FontTypes.Sergeant_SixPack_18_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_18)
  4010.     FontTypes.Sergeant_SixPack_20_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_20)
  4011.     FontTypes.Sergeant_SixPack_22_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_22)
  4012.     FontTypes.Sergeant_SixPack_24_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_24)
  4013.     FontTypes.Sergeant_SixPack_26_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_26)
  4014.     return
  4015.  
  4016. def initialiseSession():
  4017.     offset = 75
  4018.    
  4019.     padblue   = Pad(offset - pad_width/2,
  4020.                     window_height/2 - pad_height/2,
  4021.                     getPadFromFile("padblue"))
  4022.  
  4023.     padgreen  = Pad(window_width/2 - pad_height/2,
  4024.                     window_height - offset - pad_width/2,
  4025.                     pygame.transform.rotate(getPadFromFile("padgreen"),   90))
  4026.  
  4027.     padred    = Pad(window_width - offset - pad_width/2,
  4028.                     window_height/2 - pad_height/2,
  4029.                     pygame.transform.rotate(getPadFromFile("padred"),    180))
  4030.    
  4031.     padorange = Pad(window_width/2 - pad_height/2,
  4032.                     offset - pad_width/2,
  4033.                     pygame.transform.rotate(getPadFromFile("padorange"), 270))
  4034.  
  4035.     Pads.padlist = [padblue, padgreen, padred, padorange]
  4036.    
  4037.     info_button.addSurface(getLetterIcon("i"))
  4038.     print_button.addSurface(getLetterIcon("t"))
  4039.     fast_forward.addSurface(getFastForwardIcon())
  4040.     gear_button.addSurface(getGearIcon())
  4041.     arrow_left.addSurface(getArrowIcon("left"))
  4042.     arrow_right.addSurface(getArrowIcon("right"))
  4043.  
  4044.     sound_level.addSurface(getSpeakerIcon(sound_level.current_level))
  4045.    
  4046.     FastForwardCover.surface         = getFastForwardCover()
  4047.     SettingsMenu.default_surface     = getSettingsMenu()
  4048.     ResetSettingsMenu()
  4049.  
  4050.     Launchers.launcher = Launcher(3, 0.001, red.toList(), cool_blue.toList())
  4051.     Launchers.launcher.launchBall()
  4052.    
  4053.     for i in range(4):
  4054.         sound_level.levels.append(getSpeakerIcon(i))
  4055.  
  4056.     sound_level.pos_x = 14
  4057.     sound_level.pos_y = 10
  4058.  
  4059.     initialiseMusic(sound_level)
  4060.     initialiseSettings()
  4061.     for option in SettingOptions.options:
  4062.         if "TOGGLE_GENETIC" in option.modes and option.active:
  4063.             option.switch()
  4064.         elif "TOGGLE_GRAVITY" in option.modes and option.active:
  4065.             option.switch()
  4066.         elif "ACCEL_BALL" in option.modes:
  4067.             while option.active != 0:
  4068.                 option.switch()
  4069.         elif "FAST_PADS" in option.modes:
  4070.             while option.active != 0:
  4071.                 option.switch()
  4072.         elif "AUTO_SAVE" in option.modes:
  4073.             while option.active != 0:
  4074.                 option.switch()
  4075.         elif "TOGGLE_AI_PAD1" in option.modes:
  4076.             while option.active != 2:
  4077.                 option.switch()
  4078.         elif "TOGGLE_AI_PAD2" in option.modes:
  4079.             while option.active != 2:
  4080.                 option.switch()
  4081.         elif "TOGGLE_AI_PAD3" in option.modes:
  4082.             while option.active != 2:
  4083.                 option.switch()
  4084.         elif "TOGGLE_AI_PAD4" in option.modes:
  4085.             while option.active != 2:
  4086.                 option.switch()
  4087.  
  4088.     arrow_left.pos_x  = (SettingsMenu.default_surface.get_width() / 2) - 20 - arrow_left.width / 2
  4089.     arrow_right.pos_x = (SettingsMenu.default_surface.get_width() / 2) + 20 - arrow_right.width / 2
  4090.    
  4091.     GameState.score_messages_list = sorted(score_message_thresholds.items(), key=lambda item:item[1])
  4092.    
  4093.     return
  4094.  
  4095. def initialiseSettings():
  4096.     options = SettingOptions.options
  4097.     base   = 100
  4098.     offset = 60
  4099.     options.extend([SettingOption(base + offset * 0, "Toggle Learning",
  4100.                                   "When enabled, the pads learn from their mistakes and "     +
  4101.                                   "improve their behaviour over time",
  4102.                                   [2, "TOGGLE_LEARNING"]),
  4103.                     SettingOption(base + offset * 1, "Toggle Genetic Algorithm",
  4104.                                   "When enabled, the adaptive function will be put through " +
  4105.                                   "continuous testing to optimise pad learning efficiency",
  4106.                                   [2, "TOGGLE_GENETIC"]),
  4107.                     SettingOption(base + offset * 2, "Toggle Gravity",
  4108.                                   "When enabled, the ball will be attracted towards the gravity " +
  4109.                                   "ring in the centre of the window with a force determined by " +
  4110.                                   "the ring's gravity coefficient",
  4111.                                   [2, "TOGGLE_GRAVITY"]),
  4112.                     SettingOption(base + offset * 3, "Set Gravity Coefficient",
  4113.                                   "Sets the coefficient that determines how strongly the ball is " +
  4114.                                   "attracted towards the gravity ring when gravity is enabled. " +
  4115.                                   "Enter any number between 0 and 100 or an invalid value to quit",
  4116.                                   [0, "SET_GRAV_COEF"]),
  4117.                     SettingOption(base + offset * 4, "Set Weight Coefficient",
  4118.                                   "Adjusts the behaviour of the pads. Enter any floating value " +
  4119.                                   "between 0 and 1 in the shell after clicking this button or " +
  4120.                                   "an invalid value (e.g. -1) to exit without modifying the weight",
  4121.                                   [0, "SET_WEIGHT"]),
  4122.                     SettingOption(base + offset * 0, "Save Pad Memory",
  4123.                                   "Saves the data in each of the pad's memories to an "     +
  4124.                                   "external file, allowing the data to be preserved "       +
  4125.                                   "between different instances of the game",
  4126.                                   [1, "SAVE_MEMORY"]),
  4127.                     SettingOption(base + offset * 1, "Back-up Pad Memory",
  4128.                                   "Creates a new directory specified by a name you enter and " +
  4129.                                   "creates copies of the current external memory states of the " +
  4130.                                   "pads into the directory",
  4131.                                   [1, "BACK_UP_MEMORY"]),
  4132.                     SettingOption(base + offset * 2, "Load Pad Memory",
  4133.                                   "Loads data from an external file containing previously " +
  4134.                                   "stored data for each of the pads",
  4135.                                   [1, "LOAD_MEMORY"]),
  4136.                     SettingOption(base + offset * 3, "Reset Memory",
  4137.                                   "Resets the pads' memories, but leaves any data stored in " +
  4138.                                   "external files untouched",
  4139.                                   [1, "RESET_MEMORY"]),
  4140.                     SettingOption(base + offset * 4, "Reset Scores",
  4141.                                   "Resets the scores for each of the pads",
  4142.                                   [1, "RESET_SCORES"]),
  4143.                     SettingOption(base + offset * 0, "Toggle AI - Pad 1",
  4144.                                   "When fully or semi-enabled, the left pad plays with adaptive or " +
  4145.                                   "static behavioural patterns respectively. When disabled, the pad " +
  4146.                                   "is controlled by a human player using the keys I and K",
  4147.                                   [3, "TOGGLE_AI_PAD1"]),
  4148.                     SettingOption(base + offset * 1, "Toggle AI - Pad 2",
  4149.                                   "When fully or semi-enabled, the bottom pad plays with adaptive or " +
  4150.                                   "static behavioural patterns respectively. When disabled, the pad " +
  4151.                                   "is controlled by a human player using the keys A and D",
  4152.                                   [3, "TOGGLE_AI_PAD2"]),
  4153.                     SettingOption(base + offset * 2, "Toggle AI - Pad 3",
  4154.                                   "When fully or semi-enabled, the right pad plays with adaptive or " +
  4155.                                   "static behavioural patterns respectively. When disabled, the pad " +
  4156.                                   "is controlled by a human player using the numpad keys 8 and 2",
  4157.                                   [3, "TOGGLE_AI_PAD3"]),
  4158.                     SettingOption(base + offset * 3, "Toggle AI - Pad 4",
  4159.                                   "When fully or semi-enabled, the top pad plays with adaptive or " +
  4160.                                   "static behavioural patterns respectively. When disabled, the pad " +
  4161.                                   "is controlled by a human player using the arrow keys LEFT and RIGHT",
  4162.                                   [3, "TOGGLE_AI_PAD4"]),
  4163.                     SettingOption(base + offset * 4, "Toggle All AIs",
  4164.                                   "Cycles all the AI's through different AI states (ON, SEMI-, OFF)",
  4165.                                   [1, "TOGGLE_ALL_AI"]),
  4166.                     SettingOption(base + offset * 0, "Mode - Accelerating Ball",
  4167.                                   "Off - the ball does not automatically accelerate [1] - the ball " +
  4168.                                   "accelerates at a low rate [2] - the ball accelerates moderately " +
  4169.                                   "fast [3] - the ball accelerates at a high rate",
  4170.                                   [4, "ACCEL_BALL"]),
  4171.                     SettingOption(base + offset * 1, "Mode - Fast Pads",
  4172.                                   "When disabled, pads move at their normal rate. When enabled, " +
  4173.                                   "pads are able to move twice as fast",
  4174.                                   [2, "FAST_PADS"]),
  4175.                     SettingOption(base + offset * 2, "Auto-saving",
  4176.                                   "Off - auto-saving does not happen at all [1] - one save " +
  4177.                                   "is executed every 100 rounds [2] - one save is executed " +
  4178.                                   "every 50 rounds [3] - one save is executed every 10 rounds",
  4179.                                   [4, "AUTO_SAVE"])])
  4180.                    
  4181.        
  4182.     SettingOptions.options = options
  4183.     return
  4184.  
  4185. ############################################################
  4186. #
  4187. #                 T E X T    O U T P U T
  4188. #
  4189.  
  4190. def printData(data):
  4191.     if print_button.active:
  4192.         print(data)
  4193.     return
  4194.  
  4195. def printMemory(padindex):
  4196.     pad = Pads.padlist[padindex]
  4197.     printData("Pad %s" % padindex)
  4198.     if not pad.memory:
  4199.         printData("EMPTY MEMORY")
  4200.     for memory_tuple in pad.memory:
  4201.         printData(memory_tuple.getData())
  4202.     printData("")
  4203.     return
  4204.  
  4205. ############################################################
  4206. #
  4207. #                   R E N D E R I N G
  4208. #
  4209.  
  4210. def drawText(surface, pos_x, pos_y, font_type, font_size, colour, message):
  4211.     if info_button.active:
  4212.         renderText(surface, pos_x, pos_y, font_type, font_size, colour, message)
  4213.     return
  4214.  
  4215. def drawLine(surface, colour, start_pos, end_pos, width):
  4216.     if info_button.active:
  4217.         pygame.draw.aaline(surface, colour, start_pos, end_pos, width)
  4218.     return
  4219.  
  4220. def drawCircle(surface, colour, position, radius, width):
  4221.     if info_button.active:
  4222.         pygame.draw.circle(surface, colour, position, radius, width)
  4223.     return
  4224.  
  4225. def drawRect(surface, colour, area, width):
  4226.     if info_button.active:
  4227.         pygame.draw.rect(surface, colour, area, width)
  4228.     return
  4229.  
  4230. def circsAtTargets(screen):
  4231.     padlist = Pads.padlist
  4232.  
  4233.     collision_data = getCollisionData()
  4234.  
  4235.     (last_collision_x, last_collision_y, last_collision_i_angle,
  4236.      last_collision_i_speed, last_collision_f_angle,
  4237.      last_collision_f_speed, _) = collision_data
  4238.  
  4239.     drawRect(screen, purple.toList(), [last_collision_x - 3,
  4240.                                        last_collision_y - 3,
  4241.                                        6, 6], 1)    
  4242.     colours = [cool_blue.toList(), green.toList(), red.toList(), orange.toList()]
  4243.     miss_circ_radius = 4
  4244.     more_tolerant_circ_radius = miss_circ_radius * 75
  4245.     less_tolerant_circ_radius = miss_circ_radius * 5
  4246.  
  4247.     displaying_text = False
  4248.     for i in range(len(padlist)):
  4249.         pad = padlist[i]
  4250.         if not pad.AI == 1:
  4251.             continue
  4252.         drawCircle(screen, colours[i], (int(pad.x_target), int(pad.y_target)), 5, 1)
  4253.  
  4254.         for memory_tuple in pad.memory:
  4255.             (x_miss, y_miss, x_collision, y_collision,
  4256.              i_angle, i_speed, f_angle, f_speed,
  4257.              collision_grav_coef) = memory_tuple.getData()
  4258.            
  4259.             colour = [cool_blue.toList(), green.toList(), red.toList(), orange.toList()][i]
  4260.             drawCircle(Screen.screen, colour, (int(x_miss), int(y_miss)), miss_circ_radius, 1)
  4261.  
  4262.             scale = 20
  4263.             x_move = f_speed * math.cos(degToRad(f_angle))  * scale
  4264.             y_move = f_speed * -math.sin(degToRad(f_angle)) * scale
  4265.             drawLine(screen, colour, (int(x_miss), int(y_miss)), (int(x_miss + x_move), int(y_miss + y_move)), 4)
  4266.  
  4267.             within_bounds = cursorWithinBounds(x_miss - more_tolerant_circ_radius / 2,
  4268.                                                x_miss + more_tolerant_circ_radius / 2,
  4269.                                                y_miss - more_tolerant_circ_radius / 2,
  4270.                                                y_miss + more_tolerant_circ_radius / 2)
  4271.            
  4272.             if within_bounds:
  4273.                 drawLine(screen, colour, (int(x_collision), int(y_collision)), (int(x_miss), int(y_miss)), 4)
  4274.  
  4275.             within_bounds = cursorWithinBounds(x_miss - less_tolerant_circ_radius / 2,
  4276.                                                x_miss + less_tolerant_circ_radius / 2,
  4277.                                                y_miss - less_tolerant_circ_radius / 2,
  4278.                                                y_miss + less_tolerant_circ_radius / 2)
  4279.            
  4280.             if within_bounds and not displaying_text:
  4281.                 displaying_text = True
  4282.                 divergence_data = calculateDivergence(memory_tuple, collision_data)
  4283.                 (total_divergence, x_divergence, y_divergence, f_angular_divergence,
  4284.                  grav_divergence) = divergence_data
  4285.                
  4286.                 total_divergence_string     = "Total divergence: %.2f"   % total_divergence
  4287.                 x_divergence_string         = "X divergence: %.2f"       % x_divergence
  4288.                 y_divergence_string         = "Y divergence: %.2f"       % y_divergence
  4289.                 f_angular_divergence_string = "Angular divergence: %.2f" % f_angular_divergence
  4290.                 grav_divergence_string      = "Gravity divergence: %.2f" % grav_divergence
  4291.  
  4292.     if displaying_text:
  4293.         drawText(Screen.screen, window_width / 2, 200, "Free Sans Bold", font_size_16, white.toList(), total_divergence_string)
  4294.         drawText(Screen.screen, window_width / 2, 210, "Free Sans Bold", font_size_16, white.toList(), x_divergence_string)
  4295.         drawText(Screen.screen, window_width / 2, 220, "Free Sans Bold", font_size_16, white.toList(), y_divergence_string)
  4296.         drawText(Screen.screen, window_width / 2, 230, "Free Sans Bold", font_size_16, white.toList(), f_angular_divergence_string)
  4297.         drawText(Screen.screen, window_width / 2, 240, "Free Sans Bold", font_size_16, white.toList(), grav_divergence_string)
  4298.                
  4299.     return
  4300.  
  4301. def renderText(surface, xpos, ypos, font_type, font_size, font_colour, message, fromCentre=True):
  4302.     if font_type.lower() == "angelic war" and font_size == font_size_14:
  4303.         font = FontTypes.Angelic_War_14_font
  4304.     elif font_type.lower() == "angelic war" and font_size == font_size_40:
  4305.         font = FontTypes.Angelic_War_40_font
  4306.     elif font_type.lower() == "times new roman" and font_size == font_size_18:
  4307.         font = FontTypes.Times_New_Roman_18_font
  4308.     elif font_type.lower() == "free sans bold" and font_size == font_size_11:
  4309.         font = FontTypes.Free_Sans_Bold_10_font
  4310.     elif font_type.lower() == "free sans bold" and font_size == font_size_12:
  4311.         font = FontTypes.Free_Sans_Bold_12_font
  4312.     elif font_type.lower() == "free sans bold" and font_size == font_size_14:
  4313.         font = FontTypes.Free_Sans_Bold_14_font
  4314.     elif font_type.lower() == "free sans bold" and font_size == font_size_16:
  4315.         font = FontTypes.Free_Sans_Bold_16_font
  4316.     elif font_type.lower() == "free sans bold" and font_size == font_size_18:
  4317.         font = FontTypes.Free_Sans_Bold_18_font
  4318.     elif font_type.lower() == "free sans bold" and font_size == font_size_30:
  4319.         font = FontTypes.Free_Sans_Bold_30_font  
  4320.     elif font_type.lower() == "birth of a hero" and font_size == font_size_30:
  4321.         font = FontTypes.Birth_Of_A_Hero_30_font
  4322.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_12:
  4323.         font = FontTypes.Sergeant_SixPack_12_font
  4324.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_14:
  4325.         font = FontTypes.Sergeant_SixPack_14_font
  4326.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_16:
  4327.         font = FontTypes.Sergeant_SixPack_16_font
  4328.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_18:
  4329.         font = FontTypes.Sergeant_SixPack_18_font
  4330.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_20:
  4331.         font = FontTypes.Sergeant_SixPack_20_font
  4332.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_22:
  4333.         font = FontTypes.Sergeant_SixPack_22_font
  4334.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_24:
  4335.         font = FontTypes.Sergeant_SixPack_24_font
  4336.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_26:
  4337.         font = FontTypes.Sergeant_SixPack_26_font
  4338.     else:
  4339.         path = pygame.font.match_font(font_type)
  4340.         font = pygame.font.Font(path, font_size)
  4341.         print("Font not found")
  4342.  
  4343.     if fromCentre:
  4344.         (width, height) = font.size(message)
  4345.         xpos -= width / 2
  4346.         ypos -= height / 2
  4347.     textsurface = font.render(message, True, font_colour)
  4348.     surface.blit(textsurface, (xpos, ypos))
  4349.     return
  4350.    
  4351. def drawObjects(screen):
  4352.     padblue   = Pads.padlist[0]
  4353.     padgreen  = Pads.padlist[1]
  4354.     padred    = Pads.padlist[2]
  4355.     padorange = Pads.padlist[3]
  4356.    
  4357.     screen.blit(padblue.surface,   (padblue.x, padblue.y))
  4358.     screen.blit(padorange.surface, (padorange.x, padorange.y))
  4359.     screen.blit(padred.surface,    (padred.x, padred.y))
  4360.     screen.blit(padgreen.surface,  (padgreen.x, padgreen.y))
  4361.  
  4362.     ball = Balls.ball
  4363.     ballcirc = pygame.draw.circle(screen, white.toList(), (int(ball.x - ball_radius/2), int(ball.y - ball_radius/2)), ball_radius, 1)
  4364.  
  4365.     padlist = Pads.padlist
  4366.     for i in range(len(padlist)):
  4367.         pad = padlist[i]
  4368.         (padxmid, padymid) = getPadMidpoint(i)
  4369.         mid_offset = 5
  4370.         if i == 0:
  4371.             padxmid -= mid_offset
  4372.         elif i == 1:
  4373.             padymid += mid_offset
  4374.         elif i == 2:
  4375.             padxmid += mid_offset
  4376.         else:
  4377.             padymid -= mid_offset
  4378.         renderText(screen, padxmid, padymid, "Free Sans Bold", font_size_18, white.toList(), str(pad.score))
  4379.         controller = {1:"ADAPTIVE COMP", 0.5:"STATIC COMP", 0:"HUMAN"}[pad.AI]
  4380.        
  4381.         offset = 5
  4382.         padxmin = pad.x
  4383.         padxmax = pad.x + pad_width * ((i+1) % 2) + pad_height * (i % 2)
  4384.         padymin = pad.y
  4385.         padymax = pad.y + pad_height * ((i+1) % 2) + pad_width * (i % 2)
  4386.         if i == 0:
  4387.             x_text = padxmin - offset
  4388.             y_text = padymin - offset
  4389.         elif i == 1:
  4390.             x_text = padxmin - offset
  4391.             y_text = padymax + offset
  4392.         elif i == 2:
  4393.             x_text = padxmax + offset
  4394.             y_text = padymin - offset
  4395.         else:
  4396.             x_text = padxmin - offset
  4397.             y_text = padymin - offset
  4398.            
  4399.         renderText(screen, x_text, y_text, "Free Sans Bold", font_size_11, [35,35,35], controller)
  4400.  
  4401.     drawLauncher(screen)
  4402.     drawSpeaker(screen)
  4403.     drawPauseButton(screen)
  4404.     drawLetterButton("i", screen)
  4405.     drawLetterButton("t", screen)
  4406.     drawFastForwardButton(screen)
  4407.     drawGearButton(screen)
  4408.     drawWeightInfo(screen)
  4409.     drawScore(screen)
  4410.     drawScoreMessage(screen)
  4411.  
  4412.     gravity_ring.construct()
  4413.     return
  4414.  
  4415. def drawWeightInfo(screen):
  4416.     current_weight     = "Weight: %.4f"      % WeightData.current_weight
  4417.     current_collisions = "Current Score: %s" % WeightData.current_collisions
  4418.     current_game       = "Current Game: %s"  % WeightData.current_game
  4419.     current_generation = "Generation: %s"    % WeightData.generation
  4420.    
  4421.     renderText(screen, window_width - 110, 20, "Free Sans Bold", font_size_16, white.toList(), current_weight, False)
  4422.  
  4423.     if GameState.toggle_genetic:
  4424.         renderText(screen, window_width - 110, 30, "Free Sans Bold", font_size_16, white.toList(), current_collisions, False)
  4425.         renderText(screen, window_width - 110, 40, "Free Sans Bold", font_size_16, white.toList(), current_game, False)
  4426.         renderText(screen, window_width - 110, 50, "Free Sans Bold", font_size_16, white.toList(), current_generation, False)
  4427.     return
  4428.  
  4429. def drawFastForwardCover(screen, ticks, real_ticks):
  4430.     if not GameState.fast_forward_cover_loaded:
  4431.         loadFastForwardCover()
  4432.     elif not real_ticks % 500:
  4433.         loadFastForwardCover()
  4434.  
  4435.     drawSpeaker(screen)
  4436.     drawPauseButton(screen)
  4437.     drawFastForwardButton(screen)
  4438.  
  4439.     ticks = "Ticks: %s" % ticks
  4440.     renderText(screen, window_width / 2, 300, "Free Sans Bold", font_size_18, white.toList(), ticks)
  4441.  
  4442.     current_weight     = "Weight: %.4f"      % WeightData.current_weight
  4443.     current_collisions = "Current Score: %s" % WeightData.current_collisions
  4444.     current_game       = "Current Game: %s"  % WeightData.current_game
  4445.     current_generation = "Generation: %s"    % WeightData.generation
  4446.  
  4447.     renderText(screen, window_width / 2, 320, "Free Sans Bold", font_size_18, white.toList(), current_weight)
  4448.  
  4449.     if GameState.toggle_genetic:
  4450.         renderText(screen, window_width / 2, 335, "Free Sans Bold", font_size_18, white.toList(), current_collisions)
  4451.         renderText(screen, window_width / 2, 350, "Free Sans Bold", font_size_18, white.toList(), current_game)
  4452.         renderText(screen, window_width / 2, 365, "Free Sans Bold", font_size_18, white.toList(), current_generation)
  4453.  
  4454.     padlist = Pads.padlist
  4455.     pad_green_score   = "Green Pad Score: %s"  % padlist[0].score
  4456.     pad_blue_score    = "Blue Pad Score: %s"   % padlist[1].score
  4457.     pad_orange_score  = "Orange Pad Score: %s" % padlist[2].score
  4458.     pad_red_score     = "Red Pad Score: %s"    % padlist[3].score
  4459.  
  4460.     renderText(screen, window_width / 2, 420, "Free Sans Bold", font_size_18, white.toList(), pad_green_score)
  4461.     renderText(screen, window_width / 2, 435, "Free Sans Bold", font_size_18, white.toList(), pad_blue_score)
  4462.     renderText(screen, window_width / 2, 450, "Free Sans Bold", font_size_18, white.toList(), pad_orange_score)
  4463.     renderText(screen, window_width / 2, 465, "Free Sans Bold", font_size_18, white.toList(), pad_red_score)
  4464.  
  4465.     offset = 6
  4466.     update_area = [200 + offset, 200 + offset, 300 - offset * 2, 500 - offset * 2]
  4467.     pygame.display.update(update_area)
  4468.     return
  4469.  
  4470. def drawLauncher(screen):
  4471.     launcher = Launchers.launcher
  4472.     launcher_colour = launcher.getColour()
  4473.     launcher_radius = launcher.getRadius()
  4474.    
  4475.     xlauncher = int(launcher.x)
  4476.     ylauncher = int(launcher.y)
  4477.     if not pause_button.active:
  4478.         launcher.coolDown()
  4479.  
  4480.     pygame.draw.circle(screen, launcher_colour, (xlauncher, ylauncher), launcher_radius, 1)
  4481.     return
  4482.  
  4483. def drawSpeaker(screen):
  4484.     speaker_icon = sound_level.surface
  4485.     sound_level.setCoverTransparency()
  4486.    
  4487.     xspeaker = sound_level.pos_x
  4488.     yspeaker = sound_level.pos_y
  4489.    
  4490.     screen.blit(speaker_icon, (xspeaker, yspeaker))
  4491.     screen.blit(sound_level.cover, (xspeaker, yspeaker))
  4492.     return
  4493.  
  4494. def drawPauseButton(screen):
  4495.     pause_surface.fill(black.toList())
  4496.    
  4497.     if pause_button.active:
  4498.         pause_surface.set_alpha(255 * 0.85)
  4499.         fill_val = 0
  4500.     elif pause_button.hovering:
  4501.         pause_surface.set_alpha(255 * 0.85)
  4502.         fill_val = 1
  4503.     else:
  4504.         pause_surface.set_alpha(255 * 0.5)
  4505.         fill_val = 1
  4506.  
  4507.     pygame.draw.rect(pause_surface, white.toList(), [0,
  4508.                                                      0,
  4509.                                                      pause_button.width * (1/3),
  4510.                                                      pause_button.height], fill_val)
  4511.    
  4512.     pygame.draw.rect(pause_surface, white.toList(), [pause_button.width * (2/3),
  4513.                                                      0,
  4514.                                                      pause_button.width * (1/3),
  4515.                                                      pause_button.height], fill_val)
  4516.     screen.blit(pause_surface, (pause_button.pos_x, pause_button.pos_y))
  4517.     return
  4518.  
  4519. def drawLetterButton(letter, screen):
  4520.     if letter.lower() == "i":
  4521.         button = info_button
  4522.     elif letter.lower() == "t":
  4523.         button = print_button
  4524.     else:
  4525.         assert False
  4526.  
  4527.     letter_surface   = button.surface
  4528.     letter_cover     = button.cover
  4529.     if button.active:
  4530.         letter_cover.set_alpha(0)
  4531.     elif button.hovering:
  4532.         letter_cover.set_alpha(255 * 0.25)
  4533.     else:
  4534.         letter_cover.set_alpha(255 * 0.5)
  4535.        
  4536.     x_surface        = button.pos_x
  4537.     y_surface        = button.pos_y
  4538.  
  4539.     screen.blit(letter_surface, (x_surface, y_surface))
  4540.     screen.blit(letter_cover, (x_surface, y_surface))
  4541.     return    
  4542.  
  4543. def drawFastForwardButton(screen):
  4544.     surface = fast_forward.surface
  4545.     cover   = fast_forward.cover
  4546.     if fast_forward.active:
  4547.         cover.set_alpha(0)
  4548.     elif fast_forward.hovering:
  4549.         cover.set_alpha(255 * 0.25)
  4550.     else:
  4551.         cover.set_alpha(255 * 0.5)
  4552.  
  4553.     x_surface = fast_forward.pos_x
  4554.     y_surface = fast_forward.pos_y
  4555.  
  4556.     screen.blit(surface, (x_surface, y_surface))
  4557.     screen.blit(cover,   (x_surface, y_surface))
  4558.     return
  4559.  
  4560. def drawGearButton(screen):
  4561.     surface = gear_button.surface
  4562.     cover   = gear_button.cover
  4563.     if gear_button.active:
  4564.         cover.set_alpha(0)
  4565.     elif gear_button.hovering:
  4566.         cover.set_alpha(255 * 0.25)
  4567.     else:
  4568.         cover.set_alpha(255 * 0.5)
  4569.  
  4570.     x_surface = gear_button.pos_x
  4571.     y_surface = gear_button.pos_y
  4572.  
  4573.     screen.blit(surface, (x_surface, y_surface))
  4574.     screen.blit(cover,   (x_surface, y_surface))
  4575.     return
  4576.  
  4577. def drawSettingsMenu(screen):
  4578.     surface = SettingsMenu.modified_surface
  4579.     for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
  4580.         if i >= len(SettingOptions.options):
  4581.             break
  4582.         option = SettingOptions.options[i]
  4583.         option.construct()
  4584.         surface.blit(option.surface, (option.pos_x, option.pos_y))
  4585.  
  4586.     for arrow in [arrow_left, arrow_right]:
  4587.         surface.blit(arrow.surface, (arrow.pos_x, arrow.pos_y))
  4588.  
  4589.     x_text = surface.get_width() / 2 + 1
  4590.     y_text = arrow_left.pos_y + 6
  4591.     renderText(surface, x_text, y_text, "Free Sans Bold", font_size_14, white.toList(), str(SettingsMenu.current_page))
  4592.    
  4593.     x_surface = (window_width - surface.get_width()) / 2
  4594.     y_surface = (window_height - surface.get_height()) / 2
  4595.    
  4596.     screen.blit(surface, (x_surface, y_surface))
  4597.     ResetSettingsMenu()
  4598.     return
  4599.  
  4600. def drawScore(screen):
  4601.     score     = GameState.collision_count
  4602.     max_score = GameState.max_count
  4603.  
  4604.     if max_score:
  4605.         ratio = 1 - score / max_score
  4606.     else:
  4607.         ratio = 1
  4608.  
  4609.     colour = crossColours(red, green, ratio)
  4610.     offset = 25
  4611.     renderText(screen, window_width / 2, window_height / 2 + offset, "Free Sans Bold", font_size_16, colour.toList(), str(score))
  4612.  
  4613.     renderText(screen, window_width / 2, window_height / 2 - offset, "Free Sans Bold", font_size_16, white.toList(), str(max_score))
  4614.     return
  4615.  
  4616. def drawScoreMessage(screen):
  4617.     score_message = ""
  4618.     message_threshold   = 0
  4619.     message_frames_left = 0
  4620.     alpha = 255
  4621.     for i in range(len(GameState.score_messages_list)):
  4622.         message = GameState.score_messages_list[i][0]
  4623.         frames_left = GameState.score_messages[message]
  4624.         if frames_left:
  4625.             alpha = int(255 * frames_left / frames_per_score_message)
  4626.             GameState.score_messages[message] = frames_left - 1
  4627.             score_message = message
  4628.             message_index = i
  4629.             message_frames_left = frames_left
  4630.  
  4631.     if score_message:
  4632.         colour = crossColours(crossColours(green, white,  message_index / 7), black, message_frames_left / frames_per_score_message)
  4633.         font_size = 12 + i * 2
  4634.         renderText(screen, window_width / 2, window_height / 2 - 100, "Sergeant SixPack", font_size, colour.toList(), score_message)
  4635.  
  4636.     return
  4637.  
  4638. def handleScoreMessages():
  4639.     score = GameState.collision_count
  4640.     for message, threshold in score_message_thresholds.items():
  4641.         if score == threshold:
  4642.             GameState.score_messages[message] = frames_per_score_message
  4643.     return
  4644.  
  4645. ############################################################
  4646. #
  4647. #     H A N D L E    M E N U S    A N D    C O V E R S
  4648. #
  4649.  
  4650. def ResetSettingsMenu():
  4651.     SettingsMenu.modified_surface = SettingsMenu.default_surface.copy()
  4652.     return
  4653.  
  4654. def setFastForwardCoverStatus(boolean):
  4655.     GameState.fast_forward_cover_loaded = boolean
  4656.     if boolean:
  4657.         sound_level.pos_x  = 214
  4658.         sound_level.pos_y  = 210
  4659.        
  4660.         pause_button.pos_x = 250
  4661.         pause_button.pos_y = 214
  4662.  
  4663.         fast_forward.pos_x = 278
  4664.         fast_forward.pos_y = 211
  4665.     else:
  4666.         sound_level.pos_x = 10
  4667.         sound_level.pos_y = 10
  4668.        
  4669.         pause_button.pos_x = 50
  4670.         pause_button.pos_y = 14
  4671.  
  4672.         fast_forward.pos_x = 79
  4673.         fast_forward.pos_y = 11
  4674.     return
  4675.  
  4676. def loadFastForwardCover():
  4677.     cover = FastForwardCover.surface
  4678.     Screen.screen.blit(cover, (0, 0))
  4679.     pygame.display.flip()
  4680.     setFastForwardCoverStatus(True)
  4681.     return
  4682.  
  4683. ############################################################
  4684. #
  4685. #           K E Y B O A R D    H A N D L I N G
  4686. #
  4687.  
  4688. def keyPressed(key):
  4689.     if key == K_ESCAPE:
  4690.         if gear_button.active:
  4691.             gear_button.switch()
  4692.             pause_button.switch()
  4693.         else:
  4694.             GameState.done         = True
  4695.         return
  4696.  
  4697.     if gear_button.active:
  4698.         if key == K_RIGHT:
  4699.             SettingsMenu.current_page = min(SettingsMenu.total_pages,
  4700.                                               SettingsMenu.current_page + 1)
  4701.         elif key == K_LEFT:
  4702.             SettingsMenu.current_page = max(1, SettingsMenu.current_page - 1)
  4703.            
  4704.     if key == K_1:
  4705.         sound_level.changeSoundLevel()
  4706.         return
  4707.     elif key == K_2:
  4708.         pause_button.switch()
  4709.         return
  4710.     elif key == K_3:
  4711.         fast_forward.switch()
  4712.         if not fast_forward.active:
  4713.             setFastForwardCoverStatus(False)
  4714.         return
  4715.     elif key == K_4 and not fast_forward.active:
  4716.         info_button.switch()
  4717.         return
  4718.     elif key == K_5 and not fast_forward.active:
  4719.         print_button.switch()
  4720.         return
  4721.     elif key == K_6 and not fast_forward.active:
  4722.         gear_button.switch()
  4723.         pause_button.active = gear_button.active
  4724.         return
  4725.  
  4726.     if gear_button.active:
  4727.         return
  4728.    
  4729.     if key == K_a:
  4730.         KeyBools.aPressed     = True
  4731.     elif key == K_d:
  4732.         KeyBools.dPressed     = True
  4733.  
  4734.     elif key == K_i:
  4735.         KeyBools.iPressed     = True
  4736.     elif key == K_k:
  4737.         KeyBools.kPressed     = True
  4738.  
  4739.     elif key == K_LEFT:
  4740.         KeyBools.leftPressed  = True
  4741.     elif key == K_RIGHT:
  4742.         KeyBools.rightPressed = True
  4743.  
  4744.     elif key == K_KP2:
  4745.         KeyBools.kp2Pressed   = True
  4746.     elif key == K_KP8:
  4747.         KeyBools.kp8Pressed   = True
  4748.     return
  4749.  
  4750. def keyReleased(key):
  4751.     if key == K_a:
  4752.         KeyBools.aPressed     = False
  4753.     elif key == K_d:
  4754.         KeyBools.dPressed     = False
  4755.  
  4756.     elif key == K_i:
  4757.         KeyBools.iPressed     = False
  4758.     elif key == K_k:
  4759.         KeyBools.kPressed     = False
  4760.  
  4761.     elif key == K_LEFT:
  4762.         KeyBools.leftPressed  = False
  4763.     elif key == K_RIGHT:
  4764.         KeyBools.rightPressed = False
  4765.  
  4766.     elif key == K_KP2:
  4767.         KeyBools.kp2Pressed   = False
  4768.     elif key == K_KP8:
  4769.         KeyBools.kp8Pressed   = False
  4770.     return
  4771.  
  4772. ############################################################
  4773. #
  4774. #             M O T I O N    H A N D L I N G
  4775. #
  4776.  
  4777. def handleMotion():
  4778.     padblue   = Pads.padlist[0]
  4779.     padgreen  = Pads.padlist[1]
  4780.     padred    = Pads.padlist[2]
  4781.     padorange = Pads.padlist[3]
  4782.  
  4783.     if not padgreen.AI:
  4784.         if KeyBools.aPressed:
  4785.             padgreen.move(-pad_speed * GameState.pad_speed_mult, 0)
  4786.         if KeyBools.dPressed:        
  4787.             padgreen.move(pad_speed * GameState.pad_speed_mult, 0)
  4788.         if not KeyBools.aPressed and not KeyBools.dPressed:
  4789.             padgreen.moving = (0, 0)
  4790.  
  4791.     if not padblue.AI:
  4792.         if KeyBools.iPressed:
  4793.             padblue.move(0, -pad_speed * GameState.pad_speed_mult)
  4794.         if KeyBools.kPressed:
  4795.             padblue.move(0, pad_speed * GameState.pad_speed_mult)
  4796.         if not KeyBools.iPressed and not KeyBools.kPressed:
  4797.             padblue.moving = (0, 0)
  4798.  
  4799.     if not padorange.AI:
  4800.         if KeyBools.leftPressed:
  4801.             padorange.move(-pad_speed * GameState.pad_speed_mult, 0)
  4802.         if KeyBools.rightPressed:
  4803.             padorange.move(pad_speed * GameState.pad_speed_mult, 0)
  4804.         if not KeyBools.leftPressed and not KeyBools.rightPressed:
  4805.             padorange.moving = (0, 0)
  4806.  
  4807.     if not padred.AI:
  4808.         if KeyBools.kp8Pressed:
  4809.             padred.move(0, -pad_speed * GameState.pad_speed_mult)
  4810.         if KeyBools.kp2Pressed:
  4811.             padred.move(0, pad_speed * GameState.pad_speed_mult)
  4812.         if not KeyBools.kp8Pressed and not KeyBools.kp2Pressed:
  4813.             padred.moving = (0, 0)
  4814.  
  4815.     Balls.ball.move()
  4816.  
  4817.     gravity_ring.pumpCircs()
  4818.     gravity_ring.modifyRadius()
  4819.     gravity_ring.handleGravFocals()
  4820.     return
  4821.  
  4822. ############################################################
  4823. #
  4824. #           C O L L I S I O N    H A N D L I N G
  4825. #
  4826.  
  4827. def collisionHandling(ball):
  4828.     collision = False
  4829.     (x_translate, y_translate) = ball.getVelocityComponents()
  4830.     xnew = ball.x + x_translate
  4831.     ynew = ball.y + y_translate
  4832.     padlist = Pads.padlist
  4833.     for i in range(len(padlist)):
  4834.         pad = padlist[i]
  4835.         if ball.last_pad == pad:
  4836.             continue
  4837.         (padxmid, padymid) = getPadMidpoint(i)
  4838.        
  4839.         padxmin = pad.x
  4840.         padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
  4841.         padymin = pad.y
  4842.         padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
  4843.  
  4844.         inwards_reflect_normal = i * 90
  4845.         side_reflect_normal    = (i+1) % 4 * 90
  4846.  
  4847.         tolerance = 0.01 * pad_height
  4848.  
  4849.        
  4850.         if objectWithinBounds(xnew, ynew, padxmin, padxmax, padymin, padymax):
  4851.             collision = pad
  4852.             if i == 0 or i == 2:
  4853.                 #'side_reflect_normal' if ball collides with bottom / top side.
  4854.                 #'inwards_reflect_normal' if ball collides with inwards-facing surface
  4855.                 if (ynew > padymax - tolerance or ynew < padymin + tolerance):
  4856.                     if i == 0 and xnew < padxmax and xnew > padxmax - tolerance:
  4857.                         normal = inwards_reflect_normal
  4858.                     elif i == 0:
  4859.                         normal = side_reflect_normal
  4860.                     elif i == 2 and xnew > padxmin and xnew < padxmin + tolerance:
  4861.                         normal = inwards_reflect_normal
  4862.                     else:
  4863.                         normal = side_reflect_normal
  4864.                 else:
  4865.                     normal = inwards_reflect_normal
  4866.  
  4867.                 reflected_angle = (plane_reflect_angles[normal] - ball.angle) % 360
  4868.             else:
  4869.                 #'side_reflect_normal' if ball collides with left / right side.
  4870.                 #'inwards_reflect_normal' if ball collides with inwards-facing surface
  4871.                 if (xnew > padxmax - tolerance or xnew < padxmin + tolerance) and (i == 1 and ynew > padymin or i == 3 and ynew < padymax):
  4872.                     if i == 1 and ynew > padymin and ynew < padymin + tolerance:
  4873.                         normal = inwards_reflect_normal
  4874.                     elif i == 1:
  4875.                         normal = side_reflect_normal
  4876.                     elif i == 3 and ynew < padymax and ynew > padymax - tolerance:
  4877.                         normal = inwards_reflect_normal
  4878.                     else:
  4879.                         normal = side_reflect_normal
  4880.                 else:
  4881.                     normal = inwards_reflect_normal
  4882.  
  4883.                 reflected_angle = (plane_reflect_angles[normal] - ball.angle) % 360
  4884.             break
  4885.            
  4886.     if collision:
  4887.         if WeightData.current_game < games_per_test:
  4888.             WeightData.current_collisions += 1
  4889.  
  4890.         GameState.collision_count += 1
  4891.         handleScoreMessages()
  4892.         if GameState.collision_count > GameState.max_count:
  4893.             GameState.max_count = GameState.collision_count
  4894.        
  4895.         modifier = 0.1
  4896.         (x_padmove, y_padmove) = collision.moving
  4897.         initial_angle = ball.angle
  4898.         initial_velocity = ball.velocity
  4899.        
  4900.         ball.angle = reflected_angle
  4901.         (x_ballmove, y_ballmove) = ball.getVelocityComponents()
  4902.        
  4903.         final_angle = angleFromComponents(x_ballmove + x_padmove * modifier, y_ballmove + y_padmove * modifier)
  4904.         final_velocity = velocityFromComponents(x_ballmove + x_padmove * modifier, y_ballmove + y_padmove * modifier)
  4905.         ball.angle = final_angle
  4906.         ball.velocity = final_velocity
  4907.  
  4908.         updateCollisionData(ball.x, ball.y, initial_angle, initial_velocity, final_angle, final_velocity)
  4909.         ball.last_pad = collision
  4910.     return
  4911.  
  4912. def updateCollisionData(x, y, i_angle, i_speed, f_angle, f_speed):
  4913.     Collision.xpos = x
  4914.     Collision.ypos = y
  4915.     Collision.initial_angle = i_angle
  4916.     Collision.initial_speed = i_speed
  4917.     Collision.final_angle   = f_angle
  4918.     Collision.final_speed   = f_speed
  4919.    
  4920.     if gravity_ring.active:
  4921.         Collision.grav_coef     = gravity_ring.grav_coef
  4922.     else:
  4923.         Collision.grav_coef     = 0
  4924.  
  4925.     updatePadTargets()
  4926.     return
  4927.        
  4928. def getCollisionData():
  4929.     collision_data = (Collision.xpos, Collision.ypos, Collision.initial_angle,
  4930.                       Collision.initial_speed, Collision.final_angle,
  4931.                       Collision.final_speed, Collision.grav_coef)
  4932.     return collision_data
  4933.  
  4934. ############################################################
  4935. #
  4936. #             B A L L    G E N E R A T I O N
  4937. #
  4938.  
  4939. def genNewBall():
  4940.     if WeightData.current_game < games_per_test:
  4941.         WeightData.current_game += 1
  4942.     elif GameState.toggle_genetic:
  4943.         beginNewTest()
  4944.    
  4945.     velocity = random.random()*0.3+0.3
  4946.     angle = 0
  4947.     while angle % 90 < 20:
  4948.         angle = random.randrange(360)
  4949.     ball = Ball(window_width/2, window_height/2, velocity, angle)
  4950.     Balls.ball = ball
  4951.     printData("ANGLE BALL LAUNCHED AT: %s" % angle)
  4952.     updateCollisionData(ball.x, ball.y, angle, velocity, angle, velocity)
  4953.     return
  4954.  
  4955. ############################################################
  4956. #
  4957. #                  M O U S E    I N P U T
  4958. #
  4959.  
  4960. def cursorWithinBounds(low_x, high_x, low_y, high_y):
  4961.     (mouse_x, mouse_y) = pygame.mouse.get_pos()
  4962.    
  4963.     if low_x <= mouse_x and mouse_x <= high_x:
  4964.         if low_y <= mouse_y and mouse_y <= high_y:
  4965.             return True
  4966.     return False
  4967.  
  4968. def hoverHandler(icon):
  4969.     hovering_over = cursorWithinBounds(icon.pos_x, icon.pos_x + icon.width,
  4970.                                        icon.pos_y, icon.pos_y + icon.height)
  4971.     return hovering_over
  4972.  
  4973. def setHover(icon):
  4974.     if hoverHandler(icon):
  4975.         icon.hovering = True
  4976.     else:
  4977.         icon.hovering = False
  4978.     return
  4979.  
  4980. def passiveMouseHandler():
  4981.     #(mouse_x, mouse_y) = pygame.mouse.get_pos()
  4982.     setHover(sound_level)
  4983.     setHover(pause_button)
  4984.     setHover(info_button)
  4985.     setHover(print_button)
  4986.     setHover(fast_forward)
  4987.     setHover(gear_button)
  4988.     setHover(arrow_left)
  4989.     setHover(arrow_right)
  4990.  
  4991.     if gear_button.active:
  4992.         x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
  4993.         y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
  4994.         for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
  4995.             if i >= len(SettingOptions.options):
  4996.                 break
  4997.             option = SettingOptions.options[i]
  4998.             option.pos_x += x_offset
  4999.             option.pos_y += y_offset
  5000.             setHover(option)
  5001.             option.pos_x -= x_offset
  5002.             option.pos_y -= y_offset
  5003.     return
  5004.  
  5005. def activeMouseHandler(position, button):
  5006.     if hoverHandler(sound_level):
  5007.         sound_level.changeSoundLevel()
  5008.    
  5009.     if hoverHandler(pause_button):
  5010.         pause_button.switch()
  5011.  
  5012.     if hoverHandler(info_button):
  5013.         info_button.switch()
  5014.  
  5015.     if hoverHandler(print_button):
  5016.         print_button.switch()
  5017.  
  5018.     if hoverHandler(fast_forward):
  5019.         fast_forward.switch()
  5020.         if not fast_forward.active:
  5021.             setFastForwardCoverStatus(False)
  5022.  
  5023.     if hoverHandler(gear_button):
  5024.         gear_button.switch()
  5025.         pause_button.active = gear_button.active
  5026.  
  5027.     if gear_button.active:
  5028.         x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
  5029.         y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
  5030.         for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
  5031.             if i >= len(SettingOptions.options):
  5032.                 break
  5033.             option = SettingOptions.options[i]
  5034.             option.pos_x += x_offset
  5035.             option.pos_y += y_offset
  5036.             if hoverHandler(option):
  5037.                 option.switch()
  5038.             option.pos_x -= x_offset
  5039.             option.pos_y -= y_offset
  5040.  
  5041.         for arrow in [arrow_left, arrow_right]:
  5042.             arrow.pos_x += x_offset
  5043.             arrow.pos_y += y_offset
  5044.             if hoverHandler(arrow):
  5045.                 if arrow == arrow_left:
  5046.                     SettingsMenu.current_page = max(1, SettingsMenu.current_page - 1)
  5047.                 else:
  5048.                     SettingsMenu.current_page = min(SettingsMenu.total_pages, SettingsMenu.current_page + 1)
  5049.             arrow.pos_x -= x_offset
  5050.             arrow.pos_y -= y_offset
  5051.     return
  5052.  
  5053. ############################################################
  5054. #
  5055. #           S E T T I N G S    H A N D L I N G
  5056. #
  5057.  
  5058. def SettingOptionsHandler():
  5059.     for option in SettingOptions.options:
  5060.         option.adjustSize()
  5061.         option.adjustColour()
  5062.     return
  5063.  
  5064. ############################################################
  5065. #
  5066. #            M A I N    G A M E    L O O P
  5067. #
  5068.  
  5069. def main():
  5070.     pygame.init()
  5071.     screen = pygame.display.set_mode((window_width, window_height), 0, 32)
  5072.     Screen.screen = screen
  5073.     pygame.display.set_caption("PyBall")
  5074.     initialiseFonts()
  5075.     initialiseSession()
  5076.     beginNewGeneration()
  5077.     text_colour_val = 255
  5078.     demonstration_begun  = False
  5079.     return_pressed       = False
  5080.     ticks                = 0
  5081.     real_ticks           = 0
  5082.     while not GameState.done:
  5083.         real_ticks += 1
  5084.         screen.fill(black.toList())
  5085.         sound_level.adjustVolume()
  5086.         passiveMouseHandler()
  5087.         SettingOptionsHandler()
  5088.         events = pygame.event.get()
  5089.         for e in events:
  5090.             if e.type == QUIT:
  5091.                 GameState.done = True
  5092.                 break
  5093.             elif not demonstration_begun and e.type == KEYDOWN and e.key == K_RETURN:
  5094.                 return_pressed = True
  5095.             elif e.type == KEYDOWN and demonstration_begun:
  5096.                 keyPressed(e.key)
  5097.             elif e.type == KEYUP and demonstration_begun:
  5098.                 keyReleased(e.key)
  5099.             elif e.type == MOUSEBUTTONDOWN:
  5100.                 activeMouseHandler(e.pos, e.button)
  5101.  
  5102.         if text_colour_val:
  5103.             text_colour = [text_colour_val, text_colour_val, text_colour_val]
  5104.             renderText(screen, window_width / 2, 120, "Angelic War", font_size_40, text_colour, "PyBall")
  5105.             renderText(screen, window_width / 2, 160, "Angelic War", font_size_14, text_colour, "Created By Tag")
  5106.             if return_pressed:
  5107.                 text_colour_val -= 1
  5108.            
  5109.             drawObjects(screen)
  5110.             pygame.display.flip()
  5111.         else:
  5112.             demonstration_begun = True
  5113.             if gear_button.active:
  5114.                 pause_button.active = True
  5115.                 fast_forward.active = False
  5116.                 if info_button.active:
  5117.                     circsAtTargets(screen)
  5118.                 drawObjects(screen)
  5119.                 drawSettingsMenu(screen)
  5120.                 renderText(screen, window_width / 2, 160, "Birth Of A Hero", font_size_30, white.toList(), "S E T T I N G S")
  5121.                 pygame.display.flip()
  5122.             elif pause_button.active and not fast_forward.active:
  5123.                 renderText(screen, window_width / 2, 160, "Birth Of A Hero", font_size_30, white.toList(), "P A U S E D")
  5124.                 if info_button.active:
  5125.                     circsAtTargets(screen)
  5126.                 drawObjects(screen)
  5127.                 pygame.display.flip()
  5128.             elif fast_forward.active and not pause_button.active:
  5129.                 ticks += 1
  5130.                 drawFastForwardCover(screen, ticks, real_ticks)
  5131.                 handleAI()
  5132.                 handleMotion()
  5133.             elif fast_forward.active and pause_button.active:
  5134.                 drawFastForwardCover(screen, ticks, real_ticks)
  5135.             else:
  5136.                 if gravity_ring.active:
  5137.                     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")
  5138.                 ticks += 1
  5139.                 handleAI()
  5140.                 handleMotion()
  5141.                 if info_button.active:
  5142.                     circsAtTargets(screen)
  5143.                 drawObjects(screen)
  5144.                 pygame.display.flip()
  5145.  
  5146.     if GameState.done:
  5147.         pygame.quit()
  5148.     return
  5149.  
  5150. if __name__ == "__main__":
  5151.     main()
  5152.  
  5153. import time
  5154. import random
  5155. import pygame
  5156. from pygame.locals import *
  5157.  
  5158. ###################################
  5159. # P Y B A L L
  5160.  
  5161. # PyBall - A Pong-Based Adaptive AI Demonstration / Game
  5162. # Created by TAGC
  5163. # Last update: 16/03/2012
  5164. # Description: This program is developed as part of an
  5165. #              assessed project at Imperial College, London
  5166. #              to demonstrate the use of AI in gaming and
  5167. #              contrast the use of static and dynamic
  5168. #              AI techniques to allow further analysis into
  5169. #              how different kinds of AI behaviour can affect
  5170. #              replayability and game immersion
  5171. # Notes:       This script will only run if it comes as part
  5172. #              of the "PyBallStuff" package. This package
  5173. #              contains the audio, image and font files used
  5174. #              by this program
  5175.  
  5176. ###################################
  5177. # I N S T A L L A T I O N
  5178.  
  5179. # This script is written in Python 3.2 and requires a Python 3
  5180. # interpreter to run. This can be obtained from
  5181. # http://www.python.org/download/ (it is strongly recommended
  5182. # that the user obtains the 32-bit version even if they have
  5183. # a 64-bit operating system)
  5184.  
  5185. # This script also requires an installation of the python
  5186. # module "PyGame" available at
  5187. # http://www.pygame.org/download.shtml (again, it is strongly
  5188. # recommended that the user obtains the 32-bit version)
  5189.  
  5190. # In addition to these, the user will need to install the
  5191. # following fonts:
  5192. # > "Birth of a Hero"
  5193. # > "Angelic War"
  5194. # > "Hawaii Lover"
  5195. # > "Sergeant SixPack"
  5196. # These are all available at http://www.1001freefonts.com/
  5197. # if the user does not possess the "Fonts" folder included
  5198. # inside the folder "PyBallStuff"
  5199.  
  5200. # The "PyBallStuff" folder must be placed in the
  5201. # "C:/Python32" directory
  5202.  
  5203. ###################################
  5204. # L A U N C H I N G
  5205.  
  5206. # To execute the program, click on "Run" on the top bar
  5207. # of the interpreter and select "Run Module" or press F5
  5208.  
  5209. ###################################
  5210. # C O N T R O L S
  5211.  
  5212. # Special Controls
  5213. ###################
  5214. # "Enter"  - begins the game after the program launches
  5215. # "Escape" - if the user has the Settings Menu open, this
  5216. #            exits that screen, otherwise it quits the
  5217. #            programs
  5218.  
  5219. # Setting Controls
  5220. ###################
  5221. # "1"      - Toggles through the sound level settings
  5222. # "2"      - Pauses / unpauses the program
  5223. # "3"      - Toggles Fast Forward mode on and off; when
  5224. #            this mode is active, the logic of the game
  5225. #            runs slightly faster
  5226. # "4"      - Toggles Graphical Information mode on and off;
  5227. #            when on, extra objects are rendered to the
  5228. #            screen to display more information on the
  5229. #            states of the pads' memories, etc
  5230. # "5"      - Toggles Textual Information mode on and off;
  5231. #            when on, information on the game is printed
  5232. #            out to the console
  5233. # "6"      - Opens or closes the Settings Menu
  5234. # "Left"   - when the Settings Menu is open, this displays
  5235. #            the previous page of options (if not already
  5236. #            there)
  5237. # "Right"  - when the Settings Menu is open, this displays
  5238. #            the next page of options (if not already
  5239. #            there)
  5240.  
  5241. # Pad Controls
  5242. ###############
  5243. # "A"      - if the green pad is under human control, moves
  5244. #            that pad left
  5245. # "D"      - if the green pad is under human control, moves
  5246. #            that pad right
  5247. # "I"      - if the blue pad is under human control, moves
  5248. #            that pad up
  5249. # "K"      - if the blue pad is under human control, moves
  5250. #            that pad down
  5251. # "Left"   - if the red pad is under human control, moves
  5252. #            that pad left
  5253. # "Right"  - if the red pad is under human control, moves
  5254. #            that pad right
  5255. # "Num 8"  - if the orange pad is under human control, moves
  5256. #            that pad up
  5257. # "Num 2"  - if the orange pad is under human control, moves
  5258. #            that pad down
  5259.  
  5260. window_width  = 700
  5261. window_height = 700
  5262.  
  5263. pad_width   = 25
  5264. pad_height  = 100
  5265. ball_radius = 2
  5266.  
  5267. corner_buffer_coef = 0.20
  5268.  
  5269. population_size      = 20
  5270. games_per_test       = 50
  5271.  
  5272. xmin = window_width  * corner_buffer_coef     - pad_height / 2
  5273. xmax = window_width  * (1-corner_buffer_coef) - pad_height / 2
  5274. ymin = window_height * corner_buffer_coef     - pad_height / 2
  5275. ymax = window_height * (1-corner_buffer_coef) - pad_height / 2
  5276.  
  5277. plane_reflect_angles = {0:180, 90:360, 180:180, 270:360}
  5278.  
  5279. pad_speed = 0.5
  5280.  
  5281. ball_accel = 0.00001
  5282.  
  5283. max_x_difference       = window_width
  5284. max_y_difference       = window_height
  5285. max_angular_difference = 180
  5286. max_gravity_difference = 100
  5287.  
  5288. pyball_stuff_path = r"C:\Python32\PyBallStuff\\"
  5289. pad_image_path    = pyball_stuff_path + r"Pictures\Pads"
  5290. sound_level_path  = pyball_stuff_path + r"Pictures\SoundLevels"
  5291. letter_path       = pyball_stuff_path + r"Pictures\Letters"
  5292. picture_path      = pyball_stuff_path + r"Pictures"
  5293. music_path        = pyball_stuff_path + r"Audio"
  5294. pad_memory_path   = pyball_stuff_path + r"Memories"
  5295. data_path         = pyball_stuff_path + r"Data"
  5296.  
  5297. font_size_11   = 11
  5298. font_size_12   = 12
  5299. font_size_14   = 14
  5300. font_size_16   = 16
  5301. font_size_18   = 18
  5302. font_size_20   = 20
  5303. font_size_22   = 22
  5304. font_size_24   = 24
  5305. font_size_26   = 26
  5306. font_size_30   = 30
  5307. font_size_40   = 40
  5308.  
  5309. frames_per_score_message = 1000
  5310. score_message_thresholds = {"Okay":5, "Good":10, "Great":15, "Excellent!":20,
  5311.                            "Superb!":25, "Amazing!":30, "Outstanding!":50,
  5312.                            "Unbelievable!":100}
  5313.  
  5314. class Colour:
  5315.     def __init__(self, r, g, b):
  5316.         self.r = r
  5317.         self.g = g
  5318.         self.b = b
  5319.         return
  5320.     def toList(self):
  5321.         listrep = [self.r, self.g, self.b]
  5322.         return listrep
  5323.  
  5324. class Pad:
  5325.     def __init__(self, x, y, surface):
  5326.         self.x        = x
  5327.         self.y        = y
  5328.         self.x_target = x
  5329.         self.y_target = y
  5330.         self.surface  = surface
  5331.         self.moving   = (0, 0)
  5332.         self.AI       = 1
  5333.         self.score    = 0
  5334.         self.memory   = []
  5335.         return
  5336.     def move(self, x_translate, y_translate):
  5337.         self.moving = (x_translate, y_translate)
  5338.         self.xnew   = self.x + x_translate
  5339.         self.ynew   = self.y + y_translate
  5340.         if xmin <= self.xnew and self.xnew <= xmax:
  5341.             self.x = self.xnew
  5342.         else:
  5343.             x_translate = 0
  5344.         if ymin <= self.ynew and self.ynew <= ymax:
  5345.             self.y = self.ynew
  5346.         else:
  5347.             y_translate = 0
  5348.         self.moving = (x_translate, y_translate)
  5349.         return
  5350.     def losePoint(self, x_miss, y_miss):
  5351.         self.score -= 1
  5352.         if GameState.toggle_learning and Balls.ball.last_pad != self and self.AI == 1:
  5353.             memory_tuple = MemoryTuple(x_miss, y_miss)
  5354.             printData("SAVING TO MEMORY...")
  5355.             printData(memory_tuple.getData())
  5356.             printData("")
  5357.             self.memory.append(memory_tuple)
  5358.         return
  5359.     def toString(self):
  5360.         stringrep = ("(%s, %s)") % (self.x, self.y)
  5361.         return stringrep
  5362.    
  5363. class Ball:
  5364.     def __init__(self, x, y, velocity, angle):
  5365.         self.x        = x
  5366.         self.y        = y
  5367.         self.velocity = velocity
  5368.         self.angle    = angle
  5369.         self.last_pad = -1
  5370.         return
  5371.     def move(self):
  5372.         cut_off_modifier = 0.9
  5373.         collisionHandling(self)
  5374.  
  5375.         (pad_0_x, pad_0_y) = getPadMidpoint(0)
  5376.         (pad_1_x, pad_1_y) = getPadMidpoint(1)
  5377.         (pad_2_x, pad_2_y) = getPadMidpoint(2)
  5378.         (pad_3_x, pad_3_y) = getPadMidpoint(3)
  5379.        
  5380.         if self.x < pad_0_x * cut_off_modifier:
  5381.             Pads.padlist[0].losePoint(self.x, self.y)
  5382.             Launchers.launcher.launchBall()
  5383.         elif self.x > window_width - (window_width - pad_2_x) * cut_off_modifier:
  5384.             Pads.padlist[2].losePoint(self.x, self.y)
  5385.             Launchers.launcher.launchBall()
  5386.         elif self.y < pad_3_y * cut_off_modifier:
  5387.             Pads.padlist[3].losePoint(self.x, self.y)
  5388.             Launchers.launcher.launchBall()
  5389.         elif self.y > window_height - (window_height - pad_1_y) * cut_off_modifier:
  5390.             Pads.padlist[1].losePoint(self.x, self.y)
  5391.             Launchers.launcher.launchBall()
  5392.         else:
  5393.             (self.x_translate, self.y_translate) = self.getVelocityComponents()
  5394.             if gravity_ring.active:
  5395.                 target = gravity_ring
  5396.                 grav_pull_angle = degToRad(angleBetweenPoints((self.x, self.y),
  5397.                                                               (target.pos_x, target.pos_y)))
  5398.  
  5399.                 distance = getPointDistance(self.x, self.y, target.pos_x, target.pos_y)
  5400.                 grav_x = gravity_ring.grav_coef / 2000 * math.cos(grav_pull_angle) * math.pow(0.96, distance)
  5401.                 grav_y = gravity_ring.grav_coef / 2000 * math.sin(grav_pull_angle) * math.pow(0.96, distance)
  5402.                 if abs(self.x_translate + grav_x) < abs(self.x_translate):
  5403.                     grav_x *= 0.3
  5404.                 if abs(self.y_translate - grav_y) < abs(self.y_translate):
  5405.                     grav_y *= 0.3
  5406.                 self.velocity = velocityFromComponents(self.x_translate + grav_x,
  5407.                                                        self.y_translate - grav_y)
  5408.                 self.angle    = angleFromComponents(self.x_translate + grav_x,
  5409.                                                     self.y_translate - grav_y)
  5410.                
  5411.         self.velocity += GameState.ball_accel
  5412.         self.x += self.x_translate
  5413.         self.y += self.y_translate
  5414.         return
  5415.     def getVelocityComponents(self):
  5416.         x_translate =  math.cos(degToRad(self.angle)) * self.velocity
  5417.         y_translate = -math.sin(degToRad(self.angle)) * self.velocity
  5418.         components = (x_translate, y_translate)
  5419.         return components
  5420.     def toString(self):
  5421.         stringrep = ("Position: (%s, %s), moving at %s units/frame at angle %s"
  5422.                      ) % (self.x, self.y, self.velocity, self.angle)
  5423.         return stringrep
  5424.  
  5425. class GravFocal:
  5426.     def __init__(self, x, y, velocity, angle, grav_ring):
  5427.         self.pos_x     = x
  5428.         self.pos_y     = y
  5429.         self.velocity  = velocity
  5430.         self.angle     = angle
  5431.         self.grav_ring = grav_ring
  5432.         self.chasing   = ""
  5433.  
  5434.         i = 0
  5435.         while self.chasing == "" or self.chasing == self:
  5436.             if i < len(grav_ring.grav_focals):
  5437.                 self.chasing = random.choice(grav_ring.grav_focals)
  5438.                 i += 1
  5439.             else:
  5440.                 self.chasing = grav_ring
  5441.                 break
  5442.         return
  5443.     def move(self):
  5444.         (self.x_translate, self.y_translate) = self.getVelocityComponents()
  5445.        
  5446.         grav_pull_angle = degToRad(angleBetweenPoints((self.pos_x, self.pos_y),
  5447.                                                       (self.chasing.pos_x, self.chasing.pos_y)))
  5448.         grav_x = gravity_ring.grav_coef / 5000 * math.cos(grav_pull_angle)
  5449.         grav_y = gravity_ring.grav_coef / 5000 * math.sin(grav_pull_angle)
  5450.         self.velocity = velocityFromComponents(self.x_translate + grav_x, self.y_translate - grav_y)
  5451.         self.angle    = angleFromComponents(self.x_translate + grav_x, self.y_translate - grav_y)
  5452.        
  5453.         self.pos_x += self.x_translate
  5454.         self.pos_y += self.y_translate
  5455.         return
  5456.     def getVelocityComponents(self):
  5457.         x_translate =  math.cos(degToRad(self.angle)) * self.velocity
  5458.         y_translate = -math.sin(degToRad(self.angle)) * self.velocity
  5459.         components = (x_translate, y_translate)
  5460.         return components
  5461.    
  5462. class Launcher:
  5463.     def __init__(self, radius, cool_down_rate, hot_colour, cool_colour):
  5464.         self.x = window_width/2
  5465.         self.y = window_height/2
  5466.         self.radius = radius
  5467.         self.heat = 0
  5468.         self.cool_down_rate = cool_down_rate
  5469.         self.hot_colour  = hot_colour
  5470.         self.cool_colour = cool_colour
  5471.     def launchBall(self):
  5472.         GameState.data.append((GameState.game_count, GameState.collision_count))
  5473.         if GameState.auto_saving:
  5474.             if GameState.game_count > 0 and not GameState.game_count % 10:
  5475.                 saveGameData()
  5476.             if GameState.game_count > 0 and not GameState.game_count % 100:
  5477.                 backUpGameData()
  5478.         GameState.game_count += 1
  5479.         GameState.collision_count = 0
  5480.         genNewBall()
  5481.         self.heat = 1
  5482.         GameState.num_rounds += 1
  5483.         if GameState.auto_saving and GameState.num_rounds > 0:
  5484.             if GameState.num_rounds % GameState.auto_saving == 0:
  5485.                 savePadMemories()
  5486.     def coolDown(self):
  5487.         if self.heat > 0:
  5488.             self.heat -= self.cool_down_rate
  5489.     def getColour(self):
  5490.         self.hr = self.heat     * self.hot_colour[0]
  5491.         self.cr = (1-self.heat) * self.cool_colour[0]
  5492.         self.hg = self.heat     * self.hot_colour[1]
  5493.         self.cg = (1-self.heat) * self.cool_colour[1]
  5494.         self.hb = self.heat     * self.hot_colour[2]
  5495.         self.cb = (1-self.heat) * self.cool_colour[2]
  5496.         colour = [self.hr + self.cr, self.hg + self.cg, self.hb + self.cb]
  5497.         return colour
  5498.     def getRadius(self):
  5499.         actual_radius = int(self.radius * math.pow(1.01, self.heat * 100))
  5500.         return actual_radius
  5501.  
  5502. class SoundLevel:
  5503.     def __init__(self):
  5504.         self.levels = []
  5505.         self.current_level = 3
  5506.         self.volume    = 1
  5507.         self.pos_x     = 0
  5508.         self.pos_y     = 0
  5509.         self.width     = 0
  5510.         self.height    = 0
  5511.         self.hovering  = False
  5512.         self.surface   = ""
  5513.         self.cover     = ""
  5514.         self.channel   = ""
  5515.         return
  5516.     def addSurface(self, surface):
  5517.         self.surface = surface
  5518.         self.width = surface.get_width()
  5519.         self.height = surface.get_height()
  5520.         self.cover = pygame.Surface((self.width, self.height), 0, 32)
  5521.         self.cover.fill(black.toList())
  5522.         return
  5523.     def addChannel(self, channel):
  5524.         self.channel = channel
  5525.         self.channel.set_volume(0.333 * self.current_level)
  5526.         return
  5527.     def changeSoundLevel(self):
  5528.         self.current_level += 1
  5529.         self.current_level %= 4
  5530.         self.addSurface(self.levels[self.current_level])
  5531.         return
  5532.     def adjustVolume(self):
  5533.         target = 0.333 * self.current_level
  5534.         difference = target - self.volume
  5535.         self.volume += difference * 0.002
  5536.         self.channel.set_volume(self.volume)
  5537.         return
  5538.     def setCoverTransparency(self):
  5539.         if self.hovering:
  5540.             alpha = 255 * 0.15
  5541.         else:
  5542.             alpha = 255 * 0.5
  5543.         self.cover.set_alpha(alpha)
  5544.         return
  5545.  
  5546. class PauseButton:
  5547.     def __init__(self, x, y):
  5548.         self.active   = False
  5549.         self.hovering = False
  5550.         self.pos_x    = x
  5551.         self.pos_y    = y
  5552.         self.width    = 18
  5553.         self.height   = 18
  5554.         return
  5555.     def switch(self):
  5556.         self.active = not self.active
  5557.         return
  5558.  
  5559. class GenericButton:
  5560.     def __init__(self, x, y):
  5561.         self.active   = False
  5562.         self.hovering = False
  5563.         self.pos_x    = x
  5564.         self.pos_y    = y
  5565.         self.width    = 0
  5566.         self.height   = 0
  5567.         return
  5568.     def addSurface(self, surface):
  5569.         self.surface = surface
  5570.         self.width   = surface.get_width()
  5571.         self.height  = surface.get_height()
  5572.         self.cover   = pygame.Surface((self.width, self.height), 0, 32)
  5573.         self.cover.fill(black.toList())
  5574.         return
  5575.     def switch(self):
  5576.         self.active = not self.active
  5577.         return
  5578.    
  5579. class MemoryTuple:
  5580.     def __init__(self, x_miss, y_miss):
  5581.         self.x_miss              = x_miss
  5582.         self.y_miss              = y_miss
  5583.         self.x_collision         = Collision.xpos
  5584.         self.y_collision         = Collision.ypos
  5585.         self.collision_i_angle   = Collision.initial_angle
  5586.         self.collision_i_speed   = Collision.initial_speed
  5587.         self.collision_f_angle   = Collision.final_angle
  5588.         self.collision_f_speed   = Collision.final_speed
  5589.         self.collision_grav_coef = Collision.grav_coef
  5590.         return
  5591.     def getData(self):
  5592.         memory_tuple = (self.x_miss, self.y_miss, self.x_collision,
  5593.                         self.y_collision, self.collision_i_angle,
  5594.                         self.collision_i_speed, self.collision_f_angle,
  5595.                         self.collision_f_speed, self.collision_grav_coef)
  5596.         return memory_tuple
  5597.  
  5598. class SettingOption:
  5599.     def __init__(self, y, name, info, modes):
  5600.         self.pos_x      = 20
  5601.         self.pos_y      = y
  5602.         self.width      = 260
  5603.         self.height     = 50
  5604.         self.min_height = 50
  5605.         self.max_height = 100
  5606.         self.name       = name
  5607.         self.info       = info
  5608.         self.modes      = modes
  5609.         self.colour     = green.toList()
  5610.         self.active     = True
  5611.         self.hovering   = False
  5612.         self.surface    = pygame.Surface((self.width, self.height), pygame.SRCALPHA, 32)
  5613.         return
  5614.     def construct(self):
  5615.         offset = 5
  5616.         max_distance = getPointDistance(0, 0, offset, offset)
  5617.  
  5618.         self.surface.lock()
  5619.         for x in range(self.width):
  5620.             for y in range(self.height):
  5621.                 alpha_coef    = 1 - math.pow(0.992, (self.width - x) * 2)
  5622.                 alpha_subcoef = (abs(self.height / 2 - y) + (1 - x/self.width) * 20) * 5
  5623.                 alpha_coef   *= 1 - math.pow(0.992, alpha_subcoef)
  5624.  
  5625.                 if x < offset and y < offset:
  5626.                     distance    = getPointDistance(x, y, offset, offset)
  5627.                     alpha_coef *= max(0, 1 - distance / offset)
  5628.                 elif x < offset and y > self.height - offset:
  5629.                     distance    = getPointDistance(x, y, offset, self.height - offset)
  5630.                     alpha_coef *= max(0, 1 - distance / offset)
  5631.                 elif x < offset:
  5632.                     alpha_coef *= x / offset
  5633.                 elif y < offset:
  5634.                     alpha_coef *= y / offset
  5635.                 elif y > self.height - offset:
  5636.                     alpha_coef *= (self.height - y) / offset
  5637.                    
  5638.                 col_subcoef   = min(x, self.width - x) + min(y, self.height - y)
  5639.                 col_coef      = math.pow(0.992, col_subcoef)
  5640.  
  5641.                 if self.hovering:
  5642.                     alpha_coef = min(1, alpha_coef * 1.2)
  5643.                     col_coef   = min(1, col_coef   * 1.2)
  5644.                
  5645.                 bg_colour      = [self.colour[0]*col_coef, self.colour[1]*col_coef, self.colour[2]*col_coef, 255 * alpha_coef]
  5646.                 self.surface.set_at((x, y), bg_colour)
  5647.  
  5648.         self.surface.unlock()
  5649.        
  5650.         x_text = 0.1 * self.width
  5651.         y_text = self.min_height / 2 - 5
  5652.         renderText(self.surface, x_text, y_text, "Free Sans Bold", font_size_16, white.toList(), self.name, False)
  5653.        
  5654.         x_text = 0.1 * self.width
  5655.         y_text = self.min_height - 5
  5656.         textlen = len(self.info)
  5657.         textsplit = self.info.split()
  5658.         while textlen and y_text + 10 < self.surface.get_height():
  5659.             text = []
  5660.             if textsplit:
  5661.                 next_word_length = len(textsplit[0])
  5662.             else:
  5663.                 next_word_length = 0
  5664.             while len(" ".join(text)) + next_word_length < 43:
  5665.                 if textsplit:
  5666.                     word = textsplit.pop(0)
  5667.                 else:
  5668.                     textlen = 0
  5669.                     break
  5670.                 text.append(word)
  5671.                 textlen -= len(word)
  5672.  
  5673.             text = " ".join(text)
  5674.             renderText(self.surface, x_text, y_text, "Free Sans Bold", font_size_14, white.toList(), text, False)
  5675.             y_text += 10
  5676.  
  5677.         if self.hovering:
  5678.             x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
  5679.             y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
  5680.             radius = 2
  5681.             x_circ = round(x_offset + self.pos_x - 8)
  5682.             y_circ = round(y_offset + self.pos_y + self.height / 2 + radius / 2)
  5683.             pygame.draw.circle(Screen.screen, white.toList(), (x_circ, y_circ), radius)
  5684.         return
  5685.     def adjustColour(self):
  5686.         if self.modes[0] == 1 and self.active < 0:
  5687.             self.colour = crossColours(white, green, -self.active/10).toList()
  5688.             self.active += 1
  5689.         elif self.modes[0] == 1 and self.active >= 0:
  5690.             self.colour = green.toList()
  5691.         elif self.modes[0] == 2:
  5692.             self.colour = {False:red.toList(), True:green.toList()}[self.active]
  5693.         elif self.modes[0] > 2:
  5694.             self.colour = crossColours(green, red, self.active / (self.modes[0]-1)).toList()
  5695.         return
  5696.     def adjustSize(self):
  5697.         lower_index = (SettingsMenu.current_page - 1) * 5
  5698.         upper_index = SettingsMenu.current_page * 5
  5699.         self_index  = SettingOptions.options.index(self)
  5700.         if self_index < lower_index or self_index > upper_index:
  5701.             return
  5702.        
  5703.         rate = 10
  5704.         delta_height = 0
  5705.         if self.hovering and self.height < self.max_height:
  5706.             delta_height = rate
  5707.         elif not self.hovering and self.height > self.min_height:
  5708.             delta_height = -rate
  5709.  
  5710.         if delta_height:
  5711.             if self.height + delta_height > self.max_height:
  5712.                 actual_change = self.max_height - self.height
  5713.             elif self.height + delta_height < self.min_height:
  5714.                 actual_change = self.height - self.min_height
  5715.             else:
  5716.                 actual_change = delta_height
  5717.                
  5718.             self.height += actual_change
  5719.             self.surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA, 32)
  5720.            
  5721.             found_self = False
  5722.             for i in range(lower_index, upper_index):
  5723.                 if i >= len(SettingOptions.options):
  5724.                     break
  5725.                 option = SettingOptions.options[i]
  5726.                 if not found_self and self == option:
  5727.                     found_self = True
  5728.                 elif found_self:
  5729.                     option.pos_y += actual_change
  5730.         return        
  5731.     def switch(self):
  5732.         if self.modes[0] == 1:
  5733.             self.active = -10
  5734.         elif self.modes[0] == 2:
  5735.             self.active = not self.active
  5736.         elif self.modes[0] > 2:
  5737.             self.active = (self.active + 1) % self.modes[0]
  5738.  
  5739.         if "TOGGLE_LEARNING" in self.modes:
  5740.             GameState.toggle_learning = self.active
  5741.  
  5742.         if "TOGGLE_GENETIC" in self.modes:
  5743.             GameState.toggle_genetic = self.active
  5744.  
  5745.         if "TOGGLE_GRAVITY" in self.modes:
  5746.             gravity_ring.active = self.active
  5747.  
  5748.         if "SET_GRAV_COEF" in self.modes:
  5749.             val = input("Please enter new gravity coefficient: ")
  5750.             try:
  5751.                 val = float(val)
  5752.                 if 0 <= val and val <= 100:
  5753.                     gravity_ring.grav_coef = val
  5754.                 else:
  5755.                     print("Invalid value")
  5756.             except:
  5757.                 print("Error parsing value")
  5758.  
  5759.         if "SET_WEIGHT" in self.modes:
  5760.             val = input("Please enter new weight coefficient: ")
  5761.             try:
  5762.                 val = float(val)
  5763.                 if 0 <= val and val <= 1:
  5764.                     WeightData.current_weight = val
  5765.                 else:
  5766.                     print("Invalid value")
  5767.             except:
  5768.                 print("Error parsing value")
  5769.  
  5770.         if "SAVE_MEMORY" in self.modes:
  5771.             savePadMemories()
  5772.  
  5773.         if "BACK_UP_MEMORY" in self.modes:
  5774.             path = ""
  5775.             i = 0
  5776.             while path == "" or os.path.exists(path):
  5777.                 i += 1
  5778.                 path = os.path.join(pad_memory_path, r"BackUpMemories\Back-up %s" % i)
  5779.             backUpPadMemories(path)
  5780.  
  5781.         if "LOAD_MEMORY" in self.modes:
  5782.             loadPadMemories()
  5783.  
  5784.         if "RESET_MEMORY" in self.modes:
  5785.             for pad in Pads.padlist:
  5786.                 pad.memory = []
  5787.  
  5788.         if "RESET_SCORES" in self.modes:
  5789.             for pad in Pads.padlist:
  5790.                 pad.score = 0
  5791.  
  5792.         if "TOGGLE_AI_PAD1" in self.modes:
  5793.             Pads.padlist[0].AI = self.active / 2
  5794.  
  5795.         if "TOGGLE_AI_PAD2" in self.modes:
  5796.             Pads.padlist[1].AI = self.active / 2
  5797.  
  5798.         if "TOGGLE_AI_PAD3" in self.modes:
  5799.             Pads.padlist[2].AI = self.active / 2
  5800.  
  5801.         if "TOGGLE_AI_PAD4" in self.modes:
  5802.             Pads.padlist[3].AI = self.active / 2
  5803.  
  5804.         if "TOGGLE_ALL_AI" in self.modes:
  5805.             option_list = []
  5806.             for i in range(4):
  5807.                 option_list.append("TOGGLE_AI_PAD%s" % (i+1))
  5808.  
  5809.             enable_all = False
  5810.             active_set = -1
  5811.            
  5812.             for option in SettingOptions.options:
  5813.                 for related_option in option_list:
  5814.                     if related_option in option.modes and option.active != active_set:
  5815.                         if active_set == -1:
  5816.                             active_set = option.active
  5817.                             continue
  5818.                         else:
  5819.                             enable_all = True
  5820.                             break
  5821.  
  5822.             if enable_all:
  5823.                 active_set = 2
  5824.             else:
  5825.                 active_set = (active_set + 1) % 3
  5826.             for option in SettingOptions.options:
  5827.                 for related_option in option_list:
  5828.                     if related_option in option.modes:
  5829.                         option.active = active_set
  5830.  
  5831.             for pad in Pads.padlist:
  5832.                 pad.AI = active_set / 2
  5833.  
  5834.         if "ACCEL_BALL" in self.modes:
  5835.             for pad in Pads.padlist:
  5836.                 if self.active == 0:
  5837.                     GameState.ball_accel = 0
  5838.                 elif self.active == 1:
  5839.                     GameState.ball_accel = ball_accel
  5840.                 elif self.active == 2:
  5841.                     GameState.ball_accel = ball_accel * 10
  5842.                 else:
  5843.                     GameState.ball_accel = ball_accel * 50
  5844.  
  5845.         if "FAST_PADS" in self.modes:
  5846.             GameState.pad_speed_mult = {False:1, True:2}[self.active]
  5847.        
  5848.         if "AUTO_SAVE" in self.modes:
  5849.             rounds = GameState.num_rounds
  5850.             if rounds == 0:
  5851.                 GameState.auto_saving = 0
  5852.             elif self.active == 1:
  5853.                 GameState.auto_saving = 100
  5854.             elif self.active == 2:
  5855.                 GameState.auto_saving = 50
  5856.             else:
  5857.                 GameState.auto_saving = 10
  5858.                
  5859.         return
  5860.  
  5861. class GravityRing:
  5862.     def __init__(self):
  5863.         self.pos_x          = int(window_width  / 2)
  5864.         self.pos_y          = int(window_height / 2)
  5865.         self.grav_coef      = 0
  5866.         self.current_angle  = 0
  5867.         self.current_radius = 0
  5868.         self.end_radius     = 50
  5869.         self.rings          = 8
  5870.         self.pump_index     = 0
  5871.         self.pump_timer     = 100
  5872.         self.active         = False
  5873.         self.pumpvals       = []
  5874.         self.grav_focals    = []
  5875.  
  5876.         for i in range(10):
  5877.             self.grav_focals.append("")
  5878.             self.genNewGravFocal(i)
  5879.        
  5880.         for i in range(self.rings):
  5881.             self.pumpvals.append(0)
  5882.         return
  5883.     def genNewGravFocal(self, index):
  5884.         velocity = random.random()*0.2+0.2
  5885.         angle = 0
  5886.         while angle % 90 < 20:
  5887.             angle = random.randrange(360)
  5888.         grav_focal = GravFocal(window_width/2, window_height/2, velocity, angle, self)
  5889.         self.grav_focals[index] = grav_focal
  5890.         return
  5891.     def construct(self):
  5892.         if not self.current_radius > 0:
  5893.             return
  5894.        
  5895.         delta_angle = 360 / self.rings
  5896.         current_angle = 0
  5897.  
  5898.         for i in range(self.rings):
  5899.             circ_x = self.pos_x + round(self.current_radius * math.cos(degToRad(current_angle)))
  5900.             circ_y = self.pos_y + round(self.current_radius * math.sin(degToRad(current_angle)))
  5901.             circ_rad = round(2 * (1 + self.pumpvals[i] / 100))
  5902.             colour = crossColours(purple, grey, self.grav_coef / 180)
  5903.             pygame.draw.circle(Screen.screen, colour.toList(), (circ_x, circ_y), circ_rad, 1)
  5904.            
  5905.             current_angle += delta_angle
  5906.             if self.pumpvals[i]:
  5907.                 self.pumpvals[i] -= 1
  5908.  
  5909.         return
  5910.     def handleGravFocals(self):
  5911.         if not self.current_radius > 0:
  5912.             return
  5913.  
  5914.         for i in range(len(self.grav_focals)):
  5915.             focal = self.grav_focals[i]
  5916.             if getPointDistance(self.pos_x, self.pos_y, focal.pos_x, focal.pos_y) > self.current_radius:
  5917.                 self.genNewGravFocal(i)
  5918.                 return
  5919.            
  5920.             focal.move()
  5921.             colour = crossColours(purple, grey, self.grav_coef / 180)
  5922.             focal_radius = 2
  5923.             focal_x = round(focal.pos_x - focal_radius / 2)
  5924.             focal_y = round(focal.pos_y - focal_radius / 2)
  5925.             pygame.draw.circle(Screen.screen, colour.toList(), (focal_x, focal_y), focal_radius, 1)
  5926.  
  5927.     def modifyRadius(self):
  5928.         if self.active and 0.95 * self.end_radius <= self.current_radius and self.current_radius < self.end_radius:
  5929.             self.current_radius = self.end_radius
  5930.         elif self.active and self.current_radius < 0.95 * self.end_radius:
  5931.             self.current_radius += self.end_radius / 250
  5932.         elif not self.active and 0.05 * self.end_radius >= self.current_radius and self.current_radius >= 0:
  5933.             self.current_radius = 0
  5934.         else:
  5935.             self.current_radius -= self.end_radius / 250
  5936.         return
  5937.     def pumpCircs(self):
  5938.         if not self.pump_timer:
  5939.             self.pumpvals[self.pump_index] = 100
  5940.             self.pump_index = (self.pump_index + 1) % self.rings
  5941.  
  5942.         if self.pump_timer > 100 - 50 * (self.grav_coef - 50) / 50:
  5943.             self.pump_timer = 0
  5944.         else:
  5945.             self.pump_timer += 1
  5946.         return
  5947.  
  5948. class SettingOptions:
  5949.     options = []
  5950.    
  5951. class WeightData:
  5952.     weight_scores        = {}
  5953.     current_generation   = []
  5954.     current_weight       = 0
  5955.     current_weight_index = 0
  5956.     current_collisions   = 0
  5957.     current_game         = 0
  5958.     generation           = 0
  5959.    
  5960. class PadMemory:
  5961.     memory_blue   = []
  5962.     memory_green  = []
  5963.     memory_red    = []
  5964.     memory_orange = []
  5965.  
  5966. class Pads:
  5967.     padlist = []
  5968.  
  5969. class Balls:
  5970.     ball = ""    
  5971.  
  5972. class Screen:
  5973.     screen = ""
  5974.  
  5975. class FastForwardCover:
  5976.     surface = ""
  5977.  
  5978. class SettingsMenu:
  5979.     modified_surface = ""
  5980.     default_surface  = ""
  5981.     total_pages      = 4
  5982.     current_page     = 1
  5983.  
  5984. class GameState:
  5985.     done                      = False
  5986.     fast_forward_cover_loaded = False
  5987.     toggle_learning           = True
  5988.     toggle_genetic            = True
  5989.     collision_count           = 0
  5990.     max_count                 = 0
  5991.     num_rounds                = 0
  5992.     score_messages_list       = []
  5993.     score_messages            = {"Okay":0, "Good":0, "Great":0, "Excellent!":0,
  5994.                                  "Superb!":0, "Amazing!":0, "Outstanding!":0,
  5995.                                  "Unbelievable!":0}
  5996.     ball_accel                = 0
  5997.     pad_speed_mult            = 1
  5998.     auto_saving               = 0
  5999.     data                      = []
  6000.     game_count                = 0
  6001.    
  6002. class Collision:
  6003.     xpos           = 0
  6004.     ypos           = 0
  6005.     initial_angle  = 0
  6006.     initial_speed  = 0
  6007.     final_angle    = 0
  6008.     final_speed    = 0
  6009.     grav_coef      = 0
  6010.  
  6011. class Launchers:
  6012.     launcher = ""
  6013.  
  6014. class KeyBools:
  6015.     aPressed     = False
  6016.     dPressed     = False
  6017.  
  6018.     iPressed     = False
  6019.     kPressed     = False
  6020.  
  6021.     leftPressed  = False
  6022.     rightPressed = False
  6023.  
  6024.     kp8Pressed   = False
  6025.     kp2Pressed   = False
  6026.  
  6027. class FontTypes:
  6028.     Angelic_War_40_font              = ""
  6029.     Angelic_War_14_font              = ""
  6030.     Birth_Of_A_Hero_30_font          = ""
  6031.     Times_New_Roman_18_font          = ""
  6032.     Free_Sans_Bold_10_font           = ""
  6033.     Free_Sans_Bold_12_font           = ""
  6034.     Free_Sans_Bold_16_font           = ""
  6035.     Free_Sans_Bold_18_font           = ""
  6036.     Free_Sans_Bold_30_font           = ""
  6037.     Sergeant_SixPack_12_font         = ""
  6038.     Sergeant_SixPack_14_font         = ""
  6039.     Sergeant_SixPack_16_font         = ""
  6040.     Sergeant_SixPack_18_font         = ""
  6041.     Sergeant_SixPack_20_font         = ""
  6042.     Sergeant_SixPack_22_font         = ""
  6043.     Sergeant_SixPack_24_font         = ""
  6044.     Sergeant_SixPack_26_font         = ""
  6045.  
  6046. black      = Colour(0, 0, 0)
  6047. grey       = Colour(100, 100, 100)
  6048. white      = Colour(255, 255, 255)
  6049. orange     = Colour(255, 165, 0)
  6050. red        = Colour(255, 0, 0)
  6051. green      = Colour(0, 255, 0)
  6052. blue       = Colour(0, 0, 255)
  6053. yellow     = Colour(255, 255, 0)
  6054. cool_blue  = Colour(174, 238, 238)
  6055. purple     = Colour(223, 0, 255)
  6056. dark_red   = Colour(100, 0, 0)
  6057. dark_green = Colour(0, 100, 0)
  6058. dark_blue  = Colour(0, 0, 100)
  6059.  
  6060. sound_level  = SoundLevel()
  6061. pause_button = PauseButton(50, 14)
  6062. fast_forward = GenericButton(79, 11)
  6063. info_button  = GenericButton(105, 10)
  6064. print_button = GenericButton(135, 14)
  6065. gear_button  = GenericButton(160, 13)
  6066.  
  6067. arrow_left   = GenericButton(0, 460)
  6068. arrow_right  = GenericButton(0, 460)
  6069.  
  6070. pause_surface = pygame.Surface((pause_button.width, pause_button.height), 0, 32)
  6071. gravity_ring  = GravityRing()
  6072.  
  6073. ############################################################
  6074. #
  6075. #           U T I L I T Y    F U N C T I O N S
  6076. #
  6077.  
  6078. def degToRad(degrees):
  6079.     rads = degrees * math.pi / 180
  6080.     return rads
  6081.  
  6082. def radToDeg(radians):
  6083.     degrees = radians * 180 / math.pi
  6084.     return degrees
  6085.  
  6086. def crossColours(c1, c2, coef):
  6087.     anti_coef = 1 - coef
  6088.     c1 = c1.toList()
  6089.     c2 = c2.toList()
  6090.     result = Colour(c1[0]*coef + c2[0]*anti_coef,
  6091.                     c1[1]*coef + c2[1]*anti_coef,
  6092.                     c1[2]*coef + c2[2]*anti_coef)
  6093.     return result
  6094.  
  6095. def objectWithinBounds(obj_x, obj_y, x_low, x_high, y_low, y_high):
  6096.     ball = Balls.ball
  6097.     within_bounds = x_low <= obj_x and obj_x <= x_high and y_low <= obj_y and obj_y <= y_high
  6098.     return within_bounds
  6099.  
  6100. def angleBetweenPoints(p1, p2):
  6101.     (p1_x, p1_y) = p1
  6102.     (p2_x, p2_y) = p2
  6103.     x_dif = p2_x - p1_x
  6104.     y_dif = p2_y - p1_y
  6105.     angle = angleFromComponents(x_dif, y_dif)
  6106.     return angle
  6107.  
  6108. def angleFromComponents(x_translate, y_translate):
  6109.     #quadrant 1, 2, 3, 4
  6110.     y_translate *= -1
  6111.     if x_translate == 0:
  6112.         x_translate = 0.0001
  6113.     if x_translate > 0 and y_translate > 0:
  6114.         theta = radToDeg(math.atan(y_translate / x_translate))
  6115.     elif x_translate < 0 and y_translate > 0:
  6116.         theta = radToDeg(math.atan(y_translate / x_translate)) + 180
  6117.     elif x_translate < 0 and y_translate < 0:
  6118.         theta = radToDeg(math.atan(y_translate / x_translate)) + 180
  6119.     else:
  6120.         theta = radToDeg(math.atan(y_translate / x_translate)) + 360
  6121.  
  6122.     theta %= 360
  6123.     return theta
  6124.  
  6125. def velocityFromComponents(x_translate, y_translate):
  6126.     velocity = math.sqrt(x_translate * x_translate + y_translate * y_translate)
  6127.     return velocity
  6128.    
  6129. def insidePad():
  6130.     ball = Balls.ball
  6131.     padlist = Pads.padlist
  6132.     for i in range(len(padlist)):
  6133.         pad = padlist[i]
  6134.         padxmin = pad.x
  6135.         padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
  6136.         padymin = pad.y
  6137.         padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
  6138.         if padxmin <= ball.x and ball.x <= padxmax and padymin <= ball.y and ball.y <= padymax:
  6139.             return True
  6140.     return False
  6141.  
  6142. def averageAngles(first_angle, second_angle):
  6143.     average_angle = (first_angle + second_angle) / 2
  6144.     return average_angle
  6145.  
  6146. def getPointDistance(x1, y1, x2, y2):
  6147.     distance = math.sqrt(math.pow((x2 - x1), 2) + math.pow((y2 - y1), 2))
  6148.     return distance
  6149.  
  6150. def getPadMidpoint(padindex):
  6151.     i = padindex
  6152.     pad = Pads.padlist[i]
  6153.     padxmin = pad.x
  6154.     padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
  6155.     padymin = pad.y
  6156.     padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
  6157.     padxmid = (padxmin + padxmax) / 2
  6158.     padymid = (padymin + padymax) / 2
  6159.     midpoint = (padxmid, padymid)
  6160.     return midpoint
  6161.  
  6162. def getAngleDifference(angle1, angle2):
  6163.     angle_dif = 360 - max(angle1, angle2) + min(angle1, angle2)
  6164.     if angle_dif > 180:
  6165.         angle_dif = 360 - angle_dif
  6166.     return angle_dif
  6167.  
  6168. ############################################################
  6169. #
  6170. #           I N P U T / O U T P U T
  6171. #
  6172.  
  6173. def saveGameData():
  6174.     with open(os.path.join(data_path, "score_data.txt"), 'w') as file:
  6175.         for item in GameState.data:
  6176.             file.write(str(item) + "\n")
  6177.  
  6178.     padlist = Pads.padlist
  6179.     for i in range(len(padlist)):
  6180.         pad = padlist[i]
  6181.  
  6182.         with open(os.path.join(data_path, "pad_%s_memory.txt" % i), 'w') as file:
  6183.             for memory in pad.memory:
  6184.                 file.write(str(memory.getData()) + "\n")
  6185.  
  6186.     with open(os.path.join(data_path, "weight_coefficient.txt"), 'w') as file:
  6187.         file.write(str(WeightData.current_weight))
  6188.  
  6189.     with open(os.path.join(data_path, "grav_coefficient.txt"), 'w') as file:
  6190.         file.write(str(gravity_ring.grav_coef))
  6191.     return
  6192.  
  6193. def backUpGameData():
  6194.     path = ""
  6195.     i = 0
  6196.     while path == "" or os.path.exists(path):
  6197.         i += 1
  6198.         path = os.path.join(data_path, r"BackUpData\Back-up %s" % i)
  6199.  
  6200.     try:
  6201.         os.mkdir(path)
  6202.     except Exception as e:
  6203.         print("Error occured whilst making memory back up: %s" % e)
  6204.         return
  6205.  
  6206.     for filename in ["score_data.txt", "grav_coefficient.txt", "weight_coefficient.txt",
  6207.                      "pad_0_memory.txt", "pad_1_memory.txt", "pad_2_memory.txt",
  6208.                      "pad_3_memory.txt"]:
  6209.         file = os.path.join(data_path, filename)
  6210.         shutil.copy(file, path)
  6211.     print("Game data back-up created")
  6212.     return
  6213.    
  6214. def savePadMemories():
  6215.     padlist = Pads.padlist
  6216.     for i in range(len(padlist)):
  6217.         pad = padlist[i]
  6218.  
  6219.         with open(os.path.join(pad_memory_path, "pad_%s_memory.txt" % i), 'w') as file:
  6220.             for memory in pad.memory:
  6221.                 file.write(str(memory.getData()) + "\n")
  6222.  
  6223.     with open(os.path.join(pad_memory_path, "weight_coefficient.txt"), 'w') as file:
  6224.         file.write(str(WeightData.current_weight))
  6225.  
  6226.     return
  6227.  
  6228. def backUpPadMemories(path):
  6229.     if not os.path.exists(path):
  6230.         try:
  6231.             os.mkdir(path)
  6232.         except Exception as e:
  6233.             print("Error occurred whilst making memory back up: %s" % e)
  6234.             return
  6235.    
  6236.     padlist = Pads.padlist
  6237.     for i in range(len(padlist)):
  6238.         file = os.path.join(pad_memory_path, "pad_%s_memory.txt" % i)
  6239.         shutil.copy(file, path)
  6240.  
  6241.     file = os.path.join(pad_memory_path, "weight_coefficient.txt")
  6242.     shutil.copy(file, path)
  6243.     print("New directory created")
  6244.     return
  6245.  
  6246. def loadPadMemories():
  6247.     padlist = Pads.padlist
  6248.     for i in range(len(padlist)):
  6249.         pad = padlist[i]
  6250.  
  6251.         with open(os.path.join(pad_memory_path, "pad_%s_memory.txt" % i), 'r') as file:
  6252.             pad.memory = parseMemoryData(file.read())
  6253.  
  6254.     with open(os.path.join(pad_memory_path, "weight_coefficient.txt"), 'r') as file:
  6255.         WeightData.current_weight = float(file.read())
  6256.    
  6257.     return
  6258.  
  6259. def parseMemoryData(string_data):
  6260.     list_data = []
  6261.     for line in string_data.split("\n"):
  6262.         if not line:
  6263.             continue
  6264.         item_data = line.replace("(", "").replace(")", "").split(", ")
  6265.         x_miss            = float(item_data[0])
  6266.         y_miss            = float(item_data[1])
  6267.         memory_tuple = MemoryTuple(x_miss, y_miss)
  6268.        
  6269.         memory_tuple.x_collision         = float(item_data[2])
  6270.         memory_tuple.y_collision         = float(item_data[3])
  6271.         memory_tuple.collision_i_angle   = float(item_data[4])
  6272.         memory_tuple.collision_i_speed   = float(item_data[5])
  6273.         memory_tuple.collision_f_angle   = float(item_data[6])
  6274.         memory_tuple.collision_f_speed   = float(item_data[7])
  6275.         memory_tuple.collision_grav_coef = float(item_data[8])
  6276.        
  6277.         list_data.append(memory_tuple)
  6278.     return list_data
  6279.  
  6280. ############################################################
  6281. #
  6282. #                 A D A P T I V E    A I
  6283. #
  6284.  
  6285. def updatePadTargets():
  6286.     padlist = Pads.padlist
  6287.     for i in range(len(padlist)):
  6288.         pad = padlist[i]
  6289.         if pad.AI != 1:
  6290.             continue
  6291.         (x_target, y_target) = findBestApproximation(i)
  6292.         printData("%s: (%s, %s)" % (i, x_target, y_target))
  6293.         printMemory(i)
  6294.         pad.x_target = x_target
  6295.         pad.y_target = y_target
  6296.     printData("")
  6297.     return
  6298.  
  6299. def handleAI():
  6300.     padlist = Pads.padlist
  6301.     for i in range(len(padlist)):
  6302.         pad = padlist[i]
  6303.         if pad.AI == 0.5:
  6304.             pad.x_target = Balls.ball.x
  6305.             pad.y_target = Balls.ball.y
  6306.         elif pad.AI == 0:
  6307.             continue
  6308.  
  6309.         (padxmid, padymid)   = getPadMidpoint(i)
  6310.        
  6311.         if not i % 2:
  6312.             if padymid < pad.y_target:
  6313.                 pad.move(0, pad_speed * GameState.pad_speed_mult)
  6314.             elif padymid > pad.y_target:
  6315.                 pad.move(0, -pad_speed * GameState.pad_speed_mult)
  6316.         else:
  6317.             if padxmid < pad.x_target:
  6318.                 pad.move(pad_speed * GameState.pad_speed_mult, 0)
  6319.             elif padxmid > pad.x_target:
  6320.                 pad.move(-pad_speed * GameState.pad_speed_mult, 0)
  6321.     return
  6322.  
  6323. def findBestApproximation(padindex):
  6324.     printData("FINDING APPROXIMATION FOR PAD %s...\n" % padindex)
  6325.     pad = Pads.padlist[padindex]
  6326.     memory = pad.memory
  6327.     ball = Balls.ball
  6328.     if not memory:
  6329.         approximation = getPadMidpoint(padindex)
  6330.         return approximation
  6331.  
  6332.     collision_data = getCollisionData()
  6333.     (last_collision_x, last_collision_y, last_collision_i_angle,
  6334.      last_collision_i_speed, last_collision_f_angle,
  6335.      last_collision_f_speed, last_collision_grav_coef) = collision_data
  6336.    
  6337.     best_approx = 0
  6338.     strictness_coef = 1.03
  6339.    
  6340.     for memory_tuple in memory:
  6341.         (x_miss, y_miss, x_collision, y_collision, _, _, f_angle, _,
  6342.          collision_grav_coef) = memory_tuple.getData()
  6343.        
  6344.         (divergence, x_divergence, y_divergence, f_angular_divergence,
  6345.          grav_divergence) = calculateDivergence(memory_tuple, collision_data)
  6346.  
  6347.         approximation = (divergence, x_miss, y_miss)
  6348.        
  6349.         printData("\n\nPAD: %s" % padindex)
  6350.         printData("\nLAST COLLISION (X) = %s, CONSIDERED CASE (X) = %s" % (last_collision_x, x_collision))
  6351.         printData("pos_x DIVERGENCE: %s" % x_divergence)
  6352.  
  6353.         printData("\nLAST COLLISION (Y) = %s, CONSIDERED CASE (Y) = %s" % (last_collision_y, y_collision))
  6354.         printData("pos_y DIVERGENCE: %s" % y_divergence)
  6355.  
  6356.         printData("\nLAST COLLISION (fAngle) = %s, CONSIDERED CASE (fAngle) = %s" % (last_collision_f_angle, f_angle))
  6357.         printData("FINAL ANGLE DIVERGENCE: %s" % f_angular_divergence)
  6358.  
  6359.         printData("\nLAST COLLISION (grav) = %s, CONSIDERED CASE (grav) = %s" % (last_collision_grav_coef,
  6360.                                                                                  collision_grav_coef))
  6361.        
  6362.         printData("\nTOTAL DIVERGENCE: %s\n\n" % divergence)
  6363.        
  6364.         if not best_approx:
  6365.             best_approx = approximation
  6366.         else:
  6367.             (least_divergence, _, _) = best_approx
  6368.             if divergence < least_divergence:
  6369.                 best_approx = approximation
  6370.  
  6371.     (_, pos_xition, pos_yition) = best_approx
  6372.     approximation = (pos_xition, pos_yition)
  6373.     return approximation
  6374.  
  6375. def calculateDivergence(memory_tuple, collision_data):
  6376.     (last_collision_x, last_collision_y, last_collision_i_angle,
  6377.      last_collision_i_speed, last_collision_f_angle,
  6378.      last_collision_f_speed, last_collision_grav_coef) = collision_data
  6379.    
  6380.     (x_miss, y_miss, x_collision, y_collision,
  6381.      i_angle, i_speed, f_angle, f_speed,
  6382.      collision_grav_coef) = memory_tuple.getData()
  6383.  
  6384.     pos_x_dif   = abs(x_collision - last_collision_x)
  6385.     pos_y_dif   = abs(y_collision - last_collision_y)
  6386.     i_angle_dif = getAngleDifference(i_angle, last_collision_i_angle)
  6387.     i_speed_dif = abs(i_speed - last_collision_i_speed)
  6388.     f_angle_dif = getAngleDifference(f_angle, last_collision_f_angle)
  6389.     f_speed_dif = abs(f_speed - last_collision_f_speed)
  6390.     grav_dif    = abs(collision_grav_coef - last_collision_grav_coef)
  6391.  
  6392.     x_divergence         = 100 * pos_x_dif     / max_x_difference
  6393.     y_divergence         = 100 * pos_y_dif     / max_y_difference
  6394.     f_angular_divergence = 100 * f_angle_dif   / max_angular_difference
  6395.     grav_divergence      = 100 * grav_dif      / max_gravity_difference
  6396.  
  6397.     #Apply weights.
  6398.     x_divergence         *= WeightData.current_weight
  6399.     y_divergence         *= WeightData.current_weight
  6400.     f_angular_divergence *= (1 - WeightData.current_weight)
  6401.     grav_divergence      *= 0.5
  6402.  
  6403.     total_divergence = x_divergence +  y_divergence + f_angular_divergence + grav_divergence
  6404.    
  6405.     divergence_data = (total_divergence, x_divergence, y_divergence, f_angular_divergence, grav_divergence)
  6406.     return divergence_data  
  6407.  
  6408. ############################################################
  6409. #
  6410. #           G E N E T I C    A L G O R I T H M
  6411. #
  6412.  
  6413. def generateWeights():
  6414.     WeightData.generation += 1
  6415.     current_generation = produceChildren()
  6416.     while len(current_generation) < population_size:
  6417.         current_generation.append(random.random())
  6418.     WeightData.current_generation = current_generation
  6419.     print("NEW GENERATION: %s" % current_generation)
  6420.     return
  6421.  
  6422. def selectBestWeights():
  6423.     best_weights = []
  6424.     current_generation = WeightData.current_generation
  6425.     #Get the best three weights.
  6426.     for i in range(int(0.5 * population_size)):
  6427.         best_score  = -1
  6428.         best_weight = -1
  6429.         for weight, score in WeightData.weight_scores.items():
  6430.             if score > best_score and weight in current_generation and weight not in best_weights:
  6431.                 best_weight = weight
  6432.                 best_score  = score
  6433.         if best_weight != -1:
  6434.             best_weights.append(best_weight)
  6435.    
  6436.     return best_weights
  6437.  
  6438. def testNextWeight():
  6439.     WeightData.current_weight_index += 1
  6440.     index = WeightData.current_weight_index
  6441.     WeightData.current_weight = WeightData.current_generation[index]
  6442.     return
  6443.  
  6444. def produceChildren():
  6445.     best_weights = selectBestWeights()
  6446.     children = []
  6447.    
  6448.     for i in range(len(best_weights)):
  6449.         for j in range(i + 1, len(best_weights)):
  6450.             if len(children) == population_size:
  6451.                 break
  6452.             child = averageWeights(best_weights[i], best_weights[j])
  6453.             children.append(child)
  6454.  
  6455.     return children
  6456.  
  6457. def averageWeights(weight_1, weight_2):
  6458.     average_weight = (weight_1 + weight_2) / 2
  6459.     return average_weight
  6460.  
  6461. def scoreWeightValue():
  6462.     current_weight     = WeightData.current_weight
  6463.     current_collisions = WeightData.current_collisions
  6464.    
  6465.     WeightData.weight_scores[current_weight] = current_collisions
  6466.     printWeightScores()
  6467.     return
  6468.  
  6469. def printWeightScores():
  6470.     weight_scores = WeightData.weight_scores
  6471.     for weight, score in weight_scores.items():
  6472.         print("Weight %.4f: %s" % (weight, score))
  6473.     return
  6474.  
  6475. def beginNewTest():
  6476.     print("NEW TEST!")
  6477.     scoreWeightValue()
  6478.     WeightData.current_collisions    = 0
  6479.     WeightData.current_game          = 0
  6480.     if WeightData.current_weight_index < population_size - 1:
  6481.         WeightData.current_weight_index += 1
  6482.         index = WeightData.current_weight_index
  6483.         WeightData.current_weight = WeightData.current_generation[index]
  6484.     else:
  6485.         beginNewGeneration()
  6486.    
  6487.     padlist = Pads.padlist
  6488.     for i in range(len(padlist)):
  6489.         padlist[i].memory = []
  6490.  
  6491.     return
  6492.  
  6493. def beginNewGeneration():
  6494.     print("NEW GENERATION!")
  6495.     generateWeights()
  6496.     WeightData.current_weight_index = 0
  6497.     WeightData.current_weight = WeightData.current_generation[0]
  6498.     return
  6499.  
  6500. ############################################################
  6501. #
  6502. #        S U R F A C E S    F R O M    F I L E S
  6503. #
  6504.  
  6505. def getPadFromFile(padname):
  6506.     pad = pygame.image.load(os.path.join(pad_image_path, padname + ".png")).convert_alpha()
  6507.     return pad
  6508.  
  6509. def getSpeakerIcon(sound_level):
  6510.     scale = 0.12
  6511.     speaker = pygame.image.load(os.path.join(sound_level_path, "sound%s.png" % sound_level)).convert_alpha()
  6512.     speaker = scaleImage(speaker, scale)
  6513.     return speaker
  6514.  
  6515. def getLetterIcon(letter):
  6516.     scale = 0.09
  6517.     info_button = pygame.image.load(os.path.join(letter_path, "letter_" + letter + ".png")).convert_alpha()
  6518.     info_button = scaleImage(info_button, scale)
  6519.     return info_button
  6520.  
  6521. def getFastForwardIcon():
  6522.     scale = 0.2
  6523.     fast_forward = pygame.image.load(os.path.join(picture_path, "fast_forward.png")).convert_alpha()
  6524.     fast_forward = scaleImage(fast_forward, scale)
  6525.     return fast_forward
  6526.  
  6527. def getFastForwardCover():
  6528.     fast_forward_cover = pygame.image.load(os.path.join(picture_path, "pyballcover.png")).convert_alpha()
  6529.     return fast_forward_cover
  6530.  
  6531. def getGearIcon():
  6532.     scale = 0.1
  6533.     gear_button = pygame.image.load(os.path.join(picture_path, "gear.png")).convert_alpha()
  6534.     gear_button = scaleImage(gear_button, scale)
  6535.     return gear_button
  6536.  
  6537. def getArrowIcon(direction):
  6538.     scale = 0.1
  6539.     arrow_button = pygame.image.load(os.path.join(picture_path, "arrow_%s.png" % direction)).convert_alpha()
  6540.     arrow_button = scaleImage(arrow_button, scale)
  6541.     return arrow_button
  6542.  
  6543. def getSettingsMenu():
  6544.     settings_menu = pygame.image.load(os.path.join(picture_path, "settings_menu.png")).convert_alpha()
  6545.     return settings_menu
  6546.  
  6547. def scaleImage(image, scale_factor):
  6548.     scaled_image = pygame.transform.smoothscale(image, (int(image.get_width() * scale_factor), int(image.get_height() * scale_factor)))
  6549.     return scaled_image
  6550.  
  6551. ############################################################
  6552. #
  6553. #       S E S S I O N    I N I T I A L I S A T I O N
  6554. #
  6555.  
  6556. def initialiseMusic(sound_level):
  6557.     pygame.mixer.init()
  6558.     channel = pygame.mixer.Channel(0)
  6559.    
  6560.     song = "relaxing_space.wav"
  6561.     music = pygame.mixer.Sound(os.path.join(music_path, song))
  6562.     channel.play(music, -1)
  6563.     sound_level.addChannel(channel)
  6564.     return
  6565.  
  6566. def initialiseFonts():
  6567.     FontTypes.Angelic_War_14_font      = pygame.font.Font(pygame.font.match_font("Angelic War"),      font_size_14)
  6568.     FontTypes.Angelic_War_40_font      = pygame.font.Font(pygame.font.match_font("Angelic War"),      font_size_40)
  6569.     FontTypes.Birth_Of_A_Hero_30_font  = pygame.font.Font(pygame.font.match_font("Birth of a Hero"),  font_size_30)
  6570.     FontTypes.Times_New_Roman_18_font  = pygame.font.Font(pygame.font.match_font("Times New Roman"),  font_size_18)
  6571.     FontTypes.Free_Sans_Bold_10_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_11)
  6572.     FontTypes.Free_Sans_Bold_12_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_12)
  6573.     FontTypes.Free_Sans_Bold_14_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_14)
  6574.     FontTypes.Free_Sans_Bold_16_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_16)
  6575.     FontTypes.Free_Sans_Bold_18_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_18)
  6576.     FontTypes.Free_Sans_Bold_30_font   = pygame.font.Font(pygame.font.match_font("Free Sans Bold"),   font_size_30)
  6577.     FontTypes.Sergeant_SixPack_12_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_12)
  6578.     FontTypes.Sergeant_SixPack_14_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_14)
  6579.     FontTypes.Sergeant_SixPack_16_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_16)
  6580.     FontTypes.Sergeant_SixPack_18_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_18)
  6581.     FontTypes.Sergeant_SixPack_20_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_20)
  6582.     FontTypes.Sergeant_SixPack_22_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_22)
  6583.     FontTypes.Sergeant_SixPack_24_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_24)
  6584.     FontTypes.Sergeant_SixPack_26_font = pygame.font.Font(pygame.font.match_font("Sergeant SixPack"), font_size_26)
  6585.     return
  6586.  
  6587. def initialiseSession():
  6588.     offset = 75
  6589.    
  6590.     padblue   = Pad(offset - pad_width/2,
  6591.                     window_height/2 - pad_height/2,
  6592.                     getPadFromFile("padblue"))
  6593.  
  6594.     padgreen  = Pad(window_width/2 - pad_height/2,
  6595.                     window_height - offset - pad_width/2,
  6596.                     pygame.transform.rotate(getPadFromFile("padgreen"),   90))
  6597.  
  6598.     padred    = Pad(window_width - offset - pad_width/2,
  6599.                     window_height/2 - pad_height/2,
  6600.                     pygame.transform.rotate(getPadFromFile("padred"),    180))
  6601.    
  6602.     padorange = Pad(window_width/2 - pad_height/2,
  6603.                     offset - pad_width/2,
  6604.                     pygame.transform.rotate(getPadFromFile("padorange"), 270))
  6605.  
  6606.     Pads.padlist = [padblue, padgreen, padred, padorange]
  6607.    
  6608.     info_button.addSurface(getLetterIcon("i"))
  6609.     print_button.addSurface(getLetterIcon("t"))
  6610.     fast_forward.addSurface(getFastForwardIcon())
  6611.     gear_button.addSurface(getGearIcon())
  6612.     arrow_left.addSurface(getArrowIcon("left"))
  6613.     arrow_right.addSurface(getArrowIcon("right"))
  6614.  
  6615.     sound_level.addSurface(getSpeakerIcon(sound_level.current_level))
  6616.    
  6617.     FastForwardCover.surface         = getFastForwardCover()
  6618.     SettingsMenu.default_surface     = getSettingsMenu()
  6619.     ResetSettingsMenu()
  6620.  
  6621.     Launchers.launcher = Launcher(3, 0.001, red.toList(), cool_blue.toList())
  6622.     Launchers.launcher.launchBall()
  6623.    
  6624.     for i in range(4):
  6625.         sound_level.levels.append(getSpeakerIcon(i))
  6626.  
  6627.     sound_level.pos_x = 14
  6628.     sound_level.pos_y = 10
  6629.  
  6630.     initialiseMusic(sound_level)
  6631.     initialiseSettings()
  6632.     for option in SettingOptions.options:
  6633.         if "TOGGLE_GENETIC" in option.modes and option.active:
  6634.             option.switch()
  6635.         elif "TOGGLE_GRAVITY" in option.modes and option.active:
  6636.             option.switch()
  6637.         elif "ACCEL_BALL" in option.modes:
  6638.             while option.active != 0:
  6639.                 option.switch()
  6640.         elif "FAST_PADS" in option.modes:
  6641.             while option.active != 0:
  6642.                 option.switch()
  6643.         elif "AUTO_SAVE" in option.modes:
  6644.             while option.active != 0:
  6645.                 option.switch()
  6646.         elif "TOGGLE_AI_PAD1" in option.modes:
  6647.             while option.active != 2:
  6648.                 option.switch()
  6649.         elif "TOGGLE_AI_PAD2" in option.modes:
  6650.             while option.active != 2:
  6651.                 option.switch()
  6652.         elif "TOGGLE_AI_PAD3" in option.modes:
  6653.             while option.active != 2:
  6654.                 option.switch()
  6655.         elif "TOGGLE_AI_PAD4" in option.modes:
  6656.             while option.active != 2:
  6657.                 option.switch()
  6658.  
  6659.     arrow_left.pos_x  = (SettingsMenu.default_surface.get_width() / 2) - 20 - arrow_left.width / 2
  6660.     arrow_right.pos_x = (SettingsMenu.default_surface.get_width() / 2) + 20 - arrow_right.width / 2
  6661.    
  6662.     GameState.score_messages_list = sorted(score_message_thresholds.items(), key=lambda item:item[1])
  6663.    
  6664.     return
  6665.  
  6666. def initialiseSettings():
  6667.     options = SettingOptions.options
  6668.     base   = 100
  6669.     offset = 60
  6670.     options.extend([SettingOption(base + offset * 0, "Toggle Learning",
  6671.                                   "When enabled, the pads learn from their mistakes and "     +
  6672.                                   "improve their behaviour over time",
  6673.                                   [2, "TOGGLE_LEARNING"]),
  6674.                     SettingOption(base + offset * 1, "Toggle Genetic Algorithm",
  6675.                                   "When enabled, the adaptive function will be put through " +
  6676.                                   "continuous testing to optimise pad learning efficiency",
  6677.                                   [2, "TOGGLE_GENETIC"]),
  6678.                     SettingOption(base + offset * 2, "Toggle Gravity",
  6679.                                   "When enabled, the ball will be attracted towards the gravity " +
  6680.                                   "ring in the centre of the window with a force determined by " +
  6681.                                   "the ring's gravity coefficient",
  6682.                                   [2, "TOGGLE_GRAVITY"]),
  6683.                     SettingOption(base + offset * 3, "Set Gravity Coefficient",
  6684.                                   "Sets the coefficient that determines how strongly the ball is " +
  6685.                                   "attracted towards the gravity ring when gravity is enabled. " +
  6686.                                   "Enter any number between 0 and 100 or an invalid value to quit",
  6687.                                   [0, "SET_GRAV_COEF"]),
  6688.                     SettingOption(base + offset * 4, "Set Weight Coefficient",
  6689.                                   "Adjusts the behaviour of the pads. Enter any floating value " +
  6690.                                   "between 0 and 1 in the shell after clicking this button or " +
  6691.                                   "an invalid value (e.g. -1) to exit without modifying the weight",
  6692.                                   [0, "SET_WEIGHT"]),
  6693.                     SettingOption(base + offset * 0, "Save Pad Memory",
  6694.                                   "Saves the data in each of the pad's memories to an "     +
  6695.                                   "external file, allowing the data to be preserved "       +
  6696.                                   "between different instances of the game",
  6697.                                   [1, "SAVE_MEMORY"]),
  6698.                     SettingOption(base + offset * 1, "Back-up Pad Memory",
  6699.                                   "Creates a new directory specified by a name you enter and " +
  6700.                                   "creates copies of the current external memory states of the " +
  6701.                                   "pads into the directory",
  6702.                                   [1, "BACK_UP_MEMORY"]),
  6703.                     SettingOption(base + offset * 2, "Load Pad Memory",
  6704.                                   "Loads data from an external file containing previously " +
  6705.                                   "stored data for each of the pads",
  6706.                                   [1, "LOAD_MEMORY"]),
  6707.                     SettingOption(base + offset * 3, "Reset Memory",
  6708.                                   "Resets the pads' memories, but leaves any data stored in " +
  6709.                                   "external files untouched",
  6710.                                   [1, "RESET_MEMORY"]),
  6711.                     SettingOption(base + offset * 4, "Reset Scores",
  6712.                                   "Resets the scores for each of the pads",
  6713.                                   [1, "RESET_SCORES"]),
  6714.                     SettingOption(base + offset * 0, "Toggle AI - Pad 1",
  6715.                                   "When fully or semi-enabled, the left pad plays with adaptive or " +
  6716.                                   "static behavioural patterns respectively. When disabled, the pad " +
  6717.                                   "is controlled by a human player using the keys I and K",
  6718.                                   [3, "TOGGLE_AI_PAD1"]),
  6719.                     SettingOption(base + offset * 1, "Toggle AI - Pad 2",
  6720.                                   "When fully or semi-enabled, the bottom pad plays with adaptive or " +
  6721.                                   "static behavioural patterns respectively. When disabled, the pad " +
  6722.                                   "is controlled by a human player using the keys A and D",
  6723.                                   [3, "TOGGLE_AI_PAD2"]),
  6724.                     SettingOption(base + offset * 2, "Toggle AI - Pad 3",
  6725.                                   "When fully or semi-enabled, the right pad plays with adaptive or " +
  6726.                                   "static behavioural patterns respectively. When disabled, the pad " +
  6727.                                   "is controlled by a human player using the numpad keys 8 and 2",
  6728.                                   [3, "TOGGLE_AI_PAD3"]),
  6729.                     SettingOption(base + offset * 3, "Toggle AI - Pad 4",
  6730.                                   "When fully or semi-enabled, the top pad plays with adaptive or " +
  6731.                                   "static behavioural patterns respectively. When disabled, the pad " +
  6732.                                   "is controlled by a human player using the arrow keys LEFT and RIGHT",
  6733.                                   [3, "TOGGLE_AI_PAD4"]),
  6734.                     SettingOption(base + offset * 4, "Toggle All AIs",
  6735.                                   "Cycles all the AI's through different AI states (ON, SEMI-, OFF)",
  6736.                                   [1, "TOGGLE_ALL_AI"]),
  6737.                     SettingOption(base + offset * 0, "Mode - Accelerating Ball",
  6738.                                   "Off - the ball does not automatically accelerate [1] - the ball " +
  6739.                                   "accelerates at a low rate [2] - the ball accelerates moderately " +
  6740.                                   "fast [3] - the ball accelerates at a high rate",
  6741.                                   [4, "ACCEL_BALL"]),
  6742.                     SettingOption(base + offset * 1, "Mode - Fast Pads",
  6743.                                   "When disabled, pads move at their normal rate. When enabled, " +
  6744.                                   "pads are able to move twice as fast",
  6745.                                   [2, "FAST_PADS"]),
  6746.                     SettingOption(base + offset * 2, "Auto-saving",
  6747.                                   "Off - auto-saving does not happen at all [1] - one save " +
  6748.                                   "is executed every 100 rounds [2] - one save is executed " +
  6749.                                   "every 50 rounds [3] - one save is executed every 10 rounds",
  6750.                                   [4, "AUTO_SAVE"])])
  6751.                    
  6752.        
  6753.     SettingOptions.options = options
  6754.     return
  6755.  
  6756. ############################################################
  6757. #
  6758. #                 T E X T    O U T P U T
  6759. #
  6760.  
  6761. def printData(data):
  6762.     if print_button.active:
  6763.         print(data)
  6764.     return
  6765.  
  6766. def printMemory(padindex):
  6767.     pad = Pads.padlist[padindex]
  6768.     printData("Pad %s" % padindex)
  6769.     if not pad.memory:
  6770.         printData("EMPTY MEMORY")
  6771.     for memory_tuple in pad.memory:
  6772.         printData(memory_tuple.getData())
  6773.     printData("")
  6774.     return
  6775.  
  6776. ############################################################
  6777. #
  6778. #                   R E N D E R I N G
  6779. #
  6780.  
  6781. def drawText(surface, pos_x, pos_y, font_type, font_size, colour, message):
  6782.     if info_button.active:
  6783.         renderText(surface, pos_x, pos_y, font_type, font_size, colour, message)
  6784.     return
  6785.  
  6786. def drawLine(surface, colour, start_pos, end_pos, width):
  6787.     if info_button.active:
  6788.         pygame.draw.aaline(surface, colour, start_pos, end_pos, width)
  6789.     return
  6790.  
  6791. def drawCircle(surface, colour, position, radius, width):
  6792.     if info_button.active:
  6793.         pygame.draw.circle(surface, colour, position, radius, width)
  6794.     return
  6795.  
  6796. def drawRect(surface, colour, area, width):
  6797.     if info_button.active:
  6798.         pygame.draw.rect(surface, colour, area, width)
  6799.     return
  6800.  
  6801. def circsAtTargets(screen):
  6802.     padlist = Pads.padlist
  6803.  
  6804.     collision_data = getCollisionData()
  6805.  
  6806.     (last_collision_x, last_collision_y, last_collision_i_angle,
  6807.      last_collision_i_speed, last_collision_f_angle,
  6808.      last_collision_f_speed, _) = collision_data
  6809.  
  6810.     drawRect(screen, purple.toList(), [last_collision_x - 3,
  6811.                                        last_collision_y - 3,
  6812.                                        6, 6], 1)    
  6813.     colours = [cool_blue.toList(), green.toList(), red.toList(), orange.toList()]
  6814.     miss_circ_radius = 4
  6815.     more_tolerant_circ_radius = miss_circ_radius * 75
  6816.     less_tolerant_circ_radius = miss_circ_radius * 5
  6817.  
  6818.     displaying_text = False
  6819.     for i in range(len(padlist)):
  6820.         pad = padlist[i]
  6821.         if not pad.AI == 1:
  6822.             continue
  6823.         drawCircle(screen, colours[i], (int(pad.x_target), int(pad.y_target)), 5, 1)
  6824.  
  6825.         for memory_tuple in pad.memory:
  6826.             (x_miss, y_miss, x_collision, y_collision,
  6827.              i_angle, i_speed, f_angle, f_speed,
  6828.              collision_grav_coef) = memory_tuple.getData()
  6829.            
  6830.             colour = [cool_blue.toList(), green.toList(), red.toList(), orange.toList()][i]
  6831.             drawCircle(Screen.screen, colour, (int(x_miss), int(y_miss)), miss_circ_radius, 1)
  6832.  
  6833.             scale = 20
  6834.             x_move = f_speed * math.cos(degToRad(f_angle))  * scale
  6835.             y_move = f_speed * -math.sin(degToRad(f_angle)) * scale
  6836.             drawLine(screen, colour, (int(x_miss), int(y_miss)), (int(x_miss + x_move), int(y_miss + y_move)), 4)
  6837.  
  6838.             within_bounds = cursorWithinBounds(x_miss - more_tolerant_circ_radius / 2,
  6839.                                                x_miss + more_tolerant_circ_radius / 2,
  6840.                                                y_miss - more_tolerant_circ_radius / 2,
  6841.                                                y_miss + more_tolerant_circ_radius / 2)
  6842.            
  6843.             if within_bounds:
  6844.                 drawLine(screen, colour, (int(x_collision), int(y_collision)), (int(x_miss), int(y_miss)), 4)
  6845.  
  6846.             within_bounds = cursorWithinBounds(x_miss - less_tolerant_circ_radius / 2,
  6847.                                                x_miss + less_tolerant_circ_radius / 2,
  6848.                                                y_miss - less_tolerant_circ_radius / 2,
  6849.                                                y_miss + less_tolerant_circ_radius / 2)
  6850.            
  6851.             if within_bounds and not displaying_text:
  6852.                 displaying_text = True
  6853.                 divergence_data = calculateDivergence(memory_tuple, collision_data)
  6854.                 (total_divergence, x_divergence, y_divergence, f_angular_divergence,
  6855.                  grav_divergence) = divergence_data
  6856.                
  6857.                 total_divergence_string     = "Total divergence: %.2f"   % total_divergence
  6858.                 x_divergence_string         = "X divergence: %.2f"       % x_divergence
  6859.                 y_divergence_string         = "Y divergence: %.2f"       % y_divergence
  6860.                 f_angular_divergence_string = "Angular divergence: %.2f" % f_angular_divergence
  6861.                 grav_divergence_string      = "Gravity divergence: %.2f" % grav_divergence
  6862.  
  6863.     if displaying_text:
  6864.         drawText(Screen.screen, window_width / 2, 200, "Free Sans Bold", font_size_16, white.toList(), total_divergence_string)
  6865.         drawText(Screen.screen, window_width / 2, 210, "Free Sans Bold", font_size_16, white.toList(), x_divergence_string)
  6866.         drawText(Screen.screen, window_width / 2, 220, "Free Sans Bold", font_size_16, white.toList(), y_divergence_string)
  6867.         drawText(Screen.screen, window_width / 2, 230, "Free Sans Bold", font_size_16, white.toList(), f_angular_divergence_string)
  6868.         drawText(Screen.screen, window_width / 2, 240, "Free Sans Bold", font_size_16, white.toList(), grav_divergence_string)
  6869.                
  6870.     return
  6871.  
  6872. def renderText(surface, xpos, ypos, font_type, font_size, font_colour, message, fromCentre=True):
  6873.     if font_type.lower() == "angelic war" and font_size == font_size_14:
  6874.         font = FontTypes.Angelic_War_14_font
  6875.     elif font_type.lower() == "angelic war" and font_size == font_size_40:
  6876.         font = FontTypes.Angelic_War_40_font
  6877.     elif font_type.lower() == "times new roman" and font_size == font_size_18:
  6878.         font = FontTypes.Times_New_Roman_18_font
  6879.     elif font_type.lower() == "free sans bold" and font_size == font_size_11:
  6880.         font = FontTypes.Free_Sans_Bold_10_font
  6881.     elif font_type.lower() == "free sans bold" and font_size == font_size_12:
  6882.         font = FontTypes.Free_Sans_Bold_12_font
  6883.     elif font_type.lower() == "free sans bold" and font_size == font_size_14:
  6884.         font = FontTypes.Free_Sans_Bold_14_font
  6885.     elif font_type.lower() == "free sans bold" and font_size == font_size_16:
  6886.         font = FontTypes.Free_Sans_Bold_16_font
  6887.     elif font_type.lower() == "free sans bold" and font_size == font_size_18:
  6888.         font = FontTypes.Free_Sans_Bold_18_font
  6889.     elif font_type.lower() == "free sans bold" and font_size == font_size_30:
  6890.         font = FontTypes.Free_Sans_Bold_30_font  
  6891.     elif font_type.lower() == "birth of a hero" and font_size == font_size_30:
  6892.         font = FontTypes.Birth_Of_A_Hero_30_font
  6893.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_12:
  6894.         font = FontTypes.Sergeant_SixPack_12_font
  6895.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_14:
  6896.         font = FontTypes.Sergeant_SixPack_14_font
  6897.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_16:
  6898.         font = FontTypes.Sergeant_SixPack_16_font
  6899.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_18:
  6900.         font = FontTypes.Sergeant_SixPack_18_font
  6901.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_20:
  6902.         font = FontTypes.Sergeant_SixPack_20_font
  6903.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_22:
  6904.         font = FontTypes.Sergeant_SixPack_22_font
  6905.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_24:
  6906.         font = FontTypes.Sergeant_SixPack_24_font
  6907.     elif font_type.lower() == "sergeant sixpack" and font_size == font_size_26:
  6908.         font = FontTypes.Sergeant_SixPack_26_font
  6909.     else:
  6910.         path = pygame.font.match_font(font_type)
  6911.         font = pygame.font.Font(path, font_size)
  6912.         print("Font not found")
  6913.  
  6914.     if fromCentre:
  6915.         (width, height) = font.size(message)
  6916.         xpos -= width / 2
  6917.         ypos -= height / 2
  6918.     textsurface = font.render(message, True, font_colour)
  6919.     surface.blit(textsurface, (xpos, ypos))
  6920.     return
  6921.    
  6922. def drawObjects(screen):
  6923.     padblue   = Pads.padlist[0]
  6924.     padgreen  = Pads.padlist[1]
  6925.     padred    = Pads.padlist[2]
  6926.     padorange = Pads.padlist[3]
  6927.    
  6928.     screen.blit(padblue.surface,   (padblue.x, padblue.y))
  6929.     screen.blit(padorange.surface, (padorange.x, padorange.y))
  6930.     screen.blit(padred.surface,    (padred.x, padred.y))
  6931.     screen.blit(padgreen.surface,  (padgreen.x, padgreen.y))
  6932.  
  6933.     ball = Balls.ball
  6934.     ballcirc = pygame.draw.circle(screen, white.toList(), (int(ball.x - ball_radius/2), int(ball.y - ball_radius/2)), ball_radius, 1)
  6935.  
  6936.     padlist = Pads.padlist
  6937.     for i in range(len(padlist)):
  6938.         pad = padlist[i]
  6939.         (padxmid, padymid) = getPadMidpoint(i)
  6940.         mid_offset = 5
  6941.         if i == 0:
  6942.             padxmid -= mid_offset
  6943.         elif i == 1:
  6944.             padymid += mid_offset
  6945.         elif i == 2:
  6946.             padxmid += mid_offset
  6947.         else:
  6948.             padymid -= mid_offset
  6949.         renderText(screen, padxmid, padymid, "Free Sans Bold", font_size_18, white.toList(), str(pad.score))
  6950.         controller = {1:"ADAPTIVE COMP", 0.5:"STATIC COMP", 0:"HUMAN"}[pad.AI]
  6951.        
  6952.         offset = 5
  6953.         padxmin = pad.x
  6954.         padxmax = pad.x + pad_width * ((i+1) % 2) + pad_height * (i % 2)
  6955.         padymin = pad.y
  6956.         padymax = pad.y + pad_height * ((i+1) % 2) + pad_width * (i % 2)
  6957.         if i == 0:
  6958.             x_text = padxmin - offset
  6959.             y_text = padymin - offset
  6960.         elif i == 1:
  6961.             x_text = padxmin - offset
  6962.             y_text = padymax + offset
  6963.         elif i == 2:
  6964.             x_text = padxmax + offset
  6965.             y_text = padymin - offset
  6966.         else:
  6967.             x_text = padxmin - offset
  6968.             y_text = padymin - offset
  6969.            
  6970.         renderText(screen, x_text, y_text, "Free Sans Bold", font_size_11, [35,35,35], controller)
  6971.  
  6972.     drawLauncher(screen)
  6973.     drawSpeaker(screen)
  6974.     drawPauseButton(screen)
  6975.     drawLetterButton("i", screen)
  6976.     drawLetterButton("t", screen)
  6977.     drawFastForwardButton(screen)
  6978.     drawGearButton(screen)
  6979.     drawWeightInfo(screen)
  6980.     drawScore(screen)
  6981.     drawScoreMessage(screen)
  6982.  
  6983.     gravity_ring.construct()
  6984.     return
  6985.  
  6986. def drawWeightInfo(screen):
  6987.     current_weight     = "Weight: %.4f"      % WeightData.current_weight
  6988.     current_collisions = "Current Score: %s" % WeightData.current_collisions
  6989.     current_game       = "Current Game: %s"  % WeightData.current_game
  6990.     current_generation = "Generation: %s"    % WeightData.generation
  6991.    
  6992.     renderText(screen, window_width - 110, 20, "Free Sans Bold", font_size_16, white.toList(), current_weight, False)
  6993.  
  6994.     if GameState.toggle_genetic:
  6995.         renderText(screen, window_width - 110, 30, "Free Sans Bold", font_size_16, white.toList(), current_collisions, False)
  6996.         renderText(screen, window_width - 110, 40, "Free Sans Bold", font_size_16, white.toList(), current_game, False)
  6997.         renderText(screen, window_width - 110, 50, "Free Sans Bold", font_size_16, white.toList(), current_generation, False)
  6998.     return
  6999.  
  7000. def drawFastForwardCover(screen, ticks, real_ticks):
  7001.     if not GameState.fast_forward_cover_loaded:
  7002.         loadFastForwardCover()
  7003.     elif not real_ticks % 500:
  7004.         loadFastForwardCover()
  7005.  
  7006.     drawSpeaker(screen)
  7007.     drawPauseButton(screen)
  7008.     drawFastForwardButton(screen)
  7009.  
  7010.     ticks = "Ticks: %s" % ticks
  7011.     renderText(screen, window_width / 2, 300, "Free Sans Bold", font_size_18, white.toList(), ticks)
  7012.  
  7013.     current_weight     = "Weight: %.4f"      % WeightData.current_weight
  7014.     current_collisions = "Current Score: %s" % WeightData.current_collisions
  7015.     current_game       = "Current Game: %s"  % WeightData.current_game
  7016.     current_generation = "Generation: %s"    % WeightData.generation
  7017.  
  7018.     renderText(screen, window_width / 2, 320, "Free Sans Bold", font_size_18, white.toList(), current_weight)
  7019.  
  7020.     if GameState.toggle_genetic:
  7021.         renderText(screen, window_width / 2, 335, "Free Sans Bold", font_size_18, white.toList(), current_collisions)
  7022.         renderText(screen, window_width / 2, 350, "Free Sans Bold", font_size_18, white.toList(), current_game)
  7023.         renderText(screen, window_width / 2, 365, "Free Sans Bold", font_size_18, white.toList(), current_generation)
  7024.  
  7025.     padlist = Pads.padlist
  7026.     pad_green_score   = "Green Pad Score: %s"  % padlist[0].score
  7027.     pad_blue_score    = "Blue Pad Score: %s"   % padlist[1].score
  7028.     pad_orange_score  = "Orange Pad Score: %s" % padlist[2].score
  7029.     pad_red_score     = "Red Pad Score: %s"    % padlist[3].score
  7030.  
  7031.     renderText(screen, window_width / 2, 420, "Free Sans Bold", font_size_18, white.toList(), pad_green_score)
  7032.     renderText(screen, window_width / 2, 435, "Free Sans Bold", font_size_18, white.toList(), pad_blue_score)
  7033.     renderText(screen, window_width / 2, 450, "Free Sans Bold", font_size_18, white.toList(), pad_orange_score)
  7034.     renderText(screen, window_width / 2, 465, "Free Sans Bold", font_size_18, white.toList(), pad_red_score)
  7035.  
  7036.     offset = 6
  7037.     update_area = [200 + offset, 200 + offset, 300 - offset * 2, 500 - offset * 2]
  7038.     pygame.display.update(update_area)
  7039.     return
  7040.  
  7041. def drawLauncher(screen):
  7042.     launcher = Launchers.launcher
  7043.     launcher_colour = launcher.getColour()
  7044.     launcher_radius = launcher.getRadius()
  7045.    
  7046.     xlauncher = int(launcher.x)
  7047.     ylauncher = int(launcher.y)
  7048.     if not pause_button.active:
  7049.         launcher.coolDown()
  7050.  
  7051.     pygame.draw.circle(screen, launcher_colour, (xlauncher, ylauncher), launcher_radius, 1)
  7052.     return
  7053.  
  7054. def drawSpeaker(screen):
  7055.     speaker_icon = sound_level.surface
  7056.     sound_level.setCoverTransparency()
  7057.    
  7058.     xspeaker = sound_level.pos_x
  7059.     yspeaker = sound_level.pos_y
  7060.    
  7061.     screen.blit(speaker_icon, (xspeaker, yspeaker))
  7062.     screen.blit(sound_level.cover, (xspeaker, yspeaker))
  7063.     return
  7064.  
  7065. def drawPauseButton(screen):
  7066.     pause_surface.fill(black.toList())
  7067.    
  7068.     if pause_button.active:
  7069.         pause_surface.set_alpha(255 * 0.85)
  7070.         fill_val = 0
  7071.     elif pause_button.hovering:
  7072.         pause_surface.set_alpha(255 * 0.85)
  7073.         fill_val = 1
  7074.     else:
  7075.         pause_surface.set_alpha(255 * 0.5)
  7076.         fill_val = 1
  7077.  
  7078.     pygame.draw.rect(pause_surface, white.toList(), [0,
  7079.                                                      0,
  7080.                                                      pause_button.width * (1/3),
  7081.                                                      pause_button.height], fill_val)
  7082.    
  7083.     pygame.draw.rect(pause_surface, white.toList(), [pause_button.width * (2/3),
  7084.                                                      0,
  7085.                                                      pause_button.width * (1/3),
  7086.                                                      pause_button.height], fill_val)
  7087.     screen.blit(pause_surface, (pause_button.pos_x, pause_button.pos_y))
  7088.     return
  7089.  
  7090. def drawLetterButton(letter, screen):
  7091.     if letter.lower() == "i":
  7092.         button = info_button
  7093.     elif letter.lower() == "t":
  7094.         button = print_button
  7095.     else:
  7096.         assert False
  7097.  
  7098.     letter_surface   = button.surface
  7099.     letter_cover     = button.cover
  7100.     if button.active:
  7101.         letter_cover.set_alpha(0)
  7102.     elif button.hovering:
  7103.         letter_cover.set_alpha(255 * 0.25)
  7104.     else:
  7105.         letter_cover.set_alpha(255 * 0.5)
  7106.        
  7107.     x_surface        = button.pos_x
  7108.     y_surface        = button.pos_y
  7109.  
  7110.     screen.blit(letter_surface, (x_surface, y_surface))
  7111.     screen.blit(letter_cover, (x_surface, y_surface))
  7112.     return    
  7113.  
  7114. def drawFastForwardButton(screen):
  7115.     surface = fast_forward.surface
  7116.     cover   = fast_forward.cover
  7117.     if fast_forward.active:
  7118.         cover.set_alpha(0)
  7119.     elif fast_forward.hovering:
  7120.         cover.set_alpha(255 * 0.25)
  7121.     else:
  7122.         cover.set_alpha(255 * 0.5)
  7123.  
  7124.     x_surface = fast_forward.pos_x
  7125.     y_surface = fast_forward.pos_y
  7126.  
  7127.     screen.blit(surface, (x_surface, y_surface))
  7128.     screen.blit(cover,   (x_surface, y_surface))
  7129.     return
  7130.  
  7131. def drawGearButton(screen):
  7132.     surface = gear_button.surface
  7133.     cover   = gear_button.cover
  7134.     if gear_button.active:
  7135.         cover.set_alpha(0)
  7136.     elif gear_button.hovering:
  7137.         cover.set_alpha(255 * 0.25)
  7138.     else:
  7139.         cover.set_alpha(255 * 0.5)
  7140.  
  7141.     x_surface = gear_button.pos_x
  7142.     y_surface = gear_button.pos_y
  7143.  
  7144.     screen.blit(surface, (x_surface, y_surface))
  7145.     screen.blit(cover,   (x_surface, y_surface))
  7146.     return
  7147.  
  7148. def drawSettingsMenu(screen):
  7149.     surface = SettingsMenu.modified_surface
  7150.     for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
  7151.         if i >= len(SettingOptions.options):
  7152.             break
  7153.         option = SettingOptions.options[i]
  7154.         option.construct()
  7155.         surface.blit(option.surface, (option.pos_x, option.pos_y))
  7156.  
  7157.     for arrow in [arrow_left, arrow_right]:
  7158.         surface.blit(arrow.surface, (arrow.pos_x, arrow.pos_y))
  7159.  
  7160.     x_text = surface.get_width() / 2 + 1
  7161.     y_text = arrow_left.pos_y + 6
  7162.     renderText(surface, x_text, y_text, "Free Sans Bold", font_size_14, white.toList(), str(SettingsMenu.current_page))
  7163.    
  7164.     x_surface = (window_width - surface.get_width()) / 2
  7165.     y_surface = (window_height - surface.get_height()) / 2
  7166.    
  7167.     screen.blit(surface, (x_surface, y_surface))
  7168.     ResetSettingsMenu()
  7169.     return
  7170.  
  7171. def drawScore(screen):
  7172.     score     = GameState.collision_count
  7173.     max_score = GameState.max_count
  7174.  
  7175.     if max_score:
  7176.         ratio = 1 - score / max_score
  7177.     else:
  7178.         ratio = 1
  7179.  
  7180.     colour = crossColours(red, green, ratio)
  7181.     offset = 25
  7182.     renderText(screen, window_width / 2, window_height / 2 + offset, "Free Sans Bold", font_size_16, colour.toList(), str(score))
  7183.  
  7184.     renderText(screen, window_width / 2, window_height / 2 - offset, "Free Sans Bold", font_size_16, white.toList(), str(max_score))
  7185.     return
  7186.  
  7187. def drawScoreMessage(screen):
  7188.     score_message = ""
  7189.     message_threshold   = 0
  7190.     message_frames_left = 0
  7191.     alpha = 255
  7192.     for i in range(len(GameState.score_messages_list)):
  7193.         message = GameState.score_messages_list[i][0]
  7194.         frames_left = GameState.score_messages[message]
  7195.         if frames_left:
  7196.             alpha = int(255 * frames_left / frames_per_score_message)
  7197.             GameState.score_messages[message] = frames_left - 1
  7198.             score_message = message
  7199.             message_index = i
  7200.             message_frames_left = frames_left
  7201.  
  7202.     if score_message:
  7203.         colour = crossColours(crossColours(green, white,  message_index / 7), black, message_frames_left / frames_per_score_message)
  7204.         font_size = 12 + i * 2
  7205.         renderText(screen, window_width / 2, window_height / 2 - 100, "Sergeant SixPack", font_size, colour.toList(), score_message)
  7206.  
  7207.     return
  7208.  
  7209. def handleScoreMessages():
  7210.     score = GameState.collision_count
  7211.     for message, threshold in score_message_thresholds.items():
  7212.         if score == threshold:
  7213.             GameState.score_messages[message] = frames_per_score_message
  7214.     return
  7215.  
  7216. ############################################################
  7217. #
  7218. #     H A N D L E    M E N U S    A N D    C O V E R S
  7219. #
  7220.  
  7221. def ResetSettingsMenu():
  7222.     SettingsMenu.modified_surface = SettingsMenu.default_surface.copy()
  7223.     return
  7224.  
  7225. def setFastForwardCoverStatus(boolean):
  7226.     GameState.fast_forward_cover_loaded = boolean
  7227.     if boolean:
  7228.         sound_level.pos_x  = 214
  7229.         sound_level.pos_y  = 210
  7230.        
  7231.         pause_button.pos_x = 250
  7232.         pause_button.pos_y = 214
  7233.  
  7234.         fast_forward.pos_x = 278
  7235.         fast_forward.pos_y = 211
  7236.     else:
  7237.         sound_level.pos_x = 10
  7238.         sound_level.pos_y = 10
  7239.        
  7240.         pause_button.pos_x = 50
  7241.         pause_button.pos_y = 14
  7242.  
  7243.         fast_forward.pos_x = 79
  7244.         fast_forward.pos_y = 11
  7245.     return
  7246.  
  7247. def loadFastForwardCover():
  7248.     cover = FastForwardCover.surface
  7249.     Screen.screen.blit(cover, (0, 0))
  7250.     pygame.display.flip()
  7251.     setFastForwardCoverStatus(True)
  7252.     return
  7253.  
  7254. ############################################################
  7255. #
  7256. #           K E Y B O A R D    H A N D L I N G
  7257. #
  7258.  
  7259. def keyPressed(key):
  7260.     if key == K_ESCAPE:
  7261.         if gear_button.active:
  7262.             gear_button.switch()
  7263.             pause_button.switch()
  7264.         else:
  7265.             GameState.done         = True
  7266.         return
  7267.  
  7268.     if gear_button.active:
  7269.         if key == K_RIGHT:
  7270.             SettingsMenu.current_page = min(SettingsMenu.total_pages,
  7271.                                               SettingsMenu.current_page + 1)
  7272.         elif key == K_LEFT:
  7273.             SettingsMenu.current_page = max(1, SettingsMenu.current_page - 1)
  7274.            
  7275.     if key == K_1:
  7276.         sound_level.changeSoundLevel()
  7277.         return
  7278.     elif key == K_2:
  7279.         pause_button.switch()
  7280.         return
  7281.     elif key == K_3:
  7282.         fast_forward.switch()
  7283.         if not fast_forward.active:
  7284.             setFastForwardCoverStatus(False)
  7285.         return
  7286.     elif key == K_4 and not fast_forward.active:
  7287.         info_button.switch()
  7288.         return
  7289.     elif key == K_5 and not fast_forward.active:
  7290.         print_button.switch()
  7291.         return
  7292.     elif key == K_6 and not fast_forward.active:
  7293.         gear_button.switch()
  7294.         pause_button.active = gear_button.active
  7295.         return
  7296.  
  7297.     if gear_button.active:
  7298.         return
  7299.    
  7300.     if key == K_a:
  7301.         KeyBools.aPressed     = True
  7302.     elif key == K_d:
  7303.         KeyBools.dPressed     = True
  7304.  
  7305.     elif key == K_i:
  7306.         KeyBools.iPressed     = True
  7307.     elif key == K_k:
  7308.         KeyBools.kPressed     = True
  7309.  
  7310.     elif key == K_LEFT:
  7311.         KeyBools.leftPressed  = True
  7312.     elif key == K_RIGHT:
  7313.         KeyBools.rightPressed = True
  7314.  
  7315.     elif key == K_KP2:
  7316.         KeyBools.kp2Pressed   = True
  7317.     elif key == K_KP8:
  7318.         KeyBools.kp8Pressed   = True
  7319.     return
  7320.  
  7321. def keyReleased(key):
  7322.     if key == K_a:
  7323.         KeyBools.aPressed     = False
  7324.     elif key == K_d:
  7325.         KeyBools.dPressed     = False
  7326.  
  7327.     elif key == K_i:
  7328.         KeyBools.iPressed     = False
  7329.     elif key == K_k:
  7330.         KeyBools.kPressed     = False
  7331.  
  7332.     elif key == K_LEFT:
  7333.         KeyBools.leftPressed  = False
  7334.     elif key == K_RIGHT:
  7335.         KeyBools.rightPressed = False
  7336.  
  7337.     elif key == K_KP2:
  7338.         KeyBools.kp2Pressed   = False
  7339.     elif key == K_KP8:
  7340.         KeyBools.kp8Pressed   = False
  7341.     return
  7342.  
  7343. ############################################################
  7344. #
  7345. #             M O T I O N    H A N D L I N G
  7346. #
  7347.  
  7348. def handleMotion():
  7349.     padblue   = Pads.padlist[0]
  7350.     padgreen  = Pads.padlist[1]
  7351.     padred    = Pads.padlist[2]
  7352.     padorange = Pads.padlist[3]
  7353.  
  7354.     if not padgreen.AI:
  7355.         if KeyBools.aPressed:
  7356.             padgreen.move(-pad_speed * GameState.pad_speed_mult, 0)
  7357.         if KeyBools.dPressed:        
  7358.             padgreen.move(pad_speed * GameState.pad_speed_mult, 0)
  7359.         if not KeyBools.aPressed and not KeyBools.dPressed:
  7360.             padgreen.moving = (0, 0)
  7361.  
  7362.     if not padblue.AI:
  7363.         if KeyBools.iPressed:
  7364.             padblue.move(0, -pad_speed * GameState.pad_speed_mult)
  7365.         if KeyBools.kPressed:
  7366.             padblue.move(0, pad_speed * GameState.pad_speed_mult)
  7367.         if not KeyBools.iPressed and not KeyBools.kPressed:
  7368.             padblue.moving = (0, 0)
  7369.  
  7370.     if not padorange.AI:
  7371.         if KeyBools.leftPressed:
  7372.             padorange.move(-pad_speed * GameState.pad_speed_mult, 0)
  7373.         if KeyBools.rightPressed:
  7374.             padorange.move(pad_speed * GameState.pad_speed_mult, 0)
  7375.         if not KeyBools.leftPressed and not KeyBools.rightPressed:
  7376.             padorange.moving = (0, 0)
  7377.  
  7378.     if not padred.AI:
  7379.         if KeyBools.kp8Pressed:
  7380.             padred.move(0, -pad_speed * GameState.pad_speed_mult)
  7381.         if KeyBools.kp2Pressed:
  7382.             padred.move(0, pad_speed * GameState.pad_speed_mult)
  7383.         if not KeyBools.kp8Pressed and not KeyBools.kp2Pressed:
  7384.             padred.moving = (0, 0)
  7385.  
  7386.     Balls.ball.move()
  7387.  
  7388.     gravity_ring.pumpCircs()
  7389.     gravity_ring.modifyRadius()
  7390.     gravity_ring.handleGravFocals()
  7391.     return
  7392.  
  7393. ############################################################
  7394. #
  7395. #           C O L L I S I O N    H A N D L I N G
  7396. #
  7397.  
  7398. def collisionHandling(ball):
  7399.     collision = False
  7400.     (x_translate, y_translate) = ball.getVelocityComponents()
  7401.     xnew = ball.x + x_translate
  7402.     ynew = ball.y + y_translate
  7403.     padlist = Pads.padlist
  7404.     for i in range(len(padlist)):
  7405.         pad = padlist[i]
  7406.         if ball.last_pad == pad:
  7407.             continue
  7408.         (padxmid, padymid) = getPadMidpoint(i)
  7409.        
  7410.         padxmin = pad.x
  7411.         padxmax = pad.x + pad_width * ((i + 1) % 2) + pad_height * (i % 2)
  7412.         padymin = pad.y
  7413.         padymax = pad.y + pad_width * (i % 2) + pad_height * ((i + 1) % 2)
  7414.  
  7415.         inwards_reflect_normal = i * 90
  7416.         side_reflect_normal    = (i+1) % 4 * 90
  7417.  
  7418.         tolerance = 0.01 * pad_height
  7419.  
  7420.        
  7421.         if objectWithinBounds(xnew, ynew, padxmin, padxmax, padymin, padymax):
  7422.             collision = pad
  7423.             if i == 0 or i == 2:
  7424.                 #'side_reflect_normal' if ball collides with bottom / top side.
  7425.                 #'inwards_reflect_normal' if ball collides with inwards-facing surface
  7426.                 if (ynew > padymax - tolerance or ynew < padymin + tolerance):
  7427.                     if i == 0 and xnew < padxmax and xnew > padxmax - tolerance:
  7428.                         normal = inwards_reflect_normal
  7429.                     elif i == 0:
  7430.                         normal = side_reflect_normal
  7431.                     elif i == 2 and xnew > padxmin and xnew < padxmin + tolerance:
  7432.                         normal = inwards_reflect_normal
  7433.                     else:
  7434.                         normal = side_reflect_normal
  7435.                 else:
  7436.                     normal = inwards_reflect_normal
  7437.  
  7438.                 reflected_angle = (plane_reflect_angles[normal] - ball.angle) % 360
  7439.             else:
  7440.                 #'side_reflect_normal' if ball collides with left / right side.
  7441.                 #'inwards_reflect_normal' if ball collides with inwards-facing surface
  7442.                 if (xnew > padxmax - tolerance or xnew < padxmin + tolerance) and (i == 1 and ynew > padymin or i == 3 and ynew < padymax):
  7443.                     if i == 1 and ynew > padymin and ynew < padymin + tolerance:
  7444.                         normal = inwards_reflect_normal
  7445.                     elif i == 1:
  7446.                         normal = side_reflect_normal
  7447.                     elif i == 3 and ynew < padymax and ynew > padymax - tolerance:
  7448.                         normal = inwards_reflect_normal
  7449.                     else:
  7450.                         normal = side_reflect_normal
  7451.                 else:
  7452.                     normal = inwards_reflect_normal
  7453.  
  7454.                 reflected_angle = (plane_reflect_angles[normal] - ball.angle) % 360
  7455.             break
  7456.            
  7457.     if collision:
  7458.         if WeightData.current_game < games_per_test:
  7459.             WeightData.current_collisions += 1
  7460.  
  7461.         GameState.collision_count += 1
  7462.         handleScoreMessages()
  7463.         if GameState.collision_count > GameState.max_count:
  7464.             GameState.max_count = GameState.collision_count
  7465.        
  7466.         modifier = 0.1
  7467.         (x_padmove, y_padmove) = collision.moving
  7468.         initial_angle = ball.angle
  7469.         initial_velocity = ball.velocity
  7470.        
  7471.         ball.angle = reflected_angle
  7472.         (x_ballmove, y_ballmove) = ball.getVelocityComponents()
  7473.        
  7474.         final_angle = angleFromComponents(x_ballmove + x_padmove * modifier, y_ballmove + y_padmove * modifier)
  7475.         final_velocity = velocityFromComponents(x_ballmove + x_padmove * modifier, y_ballmove + y_padmove * modifier)
  7476.         ball.angle = final_angle
  7477.         ball.velocity = final_velocity
  7478.  
  7479.         updateCollisionData(ball.x, ball.y, initial_angle, initial_velocity, final_angle, final_velocity)
  7480.         ball.last_pad = collision
  7481.     return
  7482.  
  7483. def updateCollisionData(x, y, i_angle, i_speed, f_angle, f_speed):
  7484.     Collision.xpos = x
  7485.     Collision.ypos = y
  7486.     Collision.initial_angle = i_angle
  7487.     Collision.initial_speed = i_speed
  7488.     Collision.final_angle   = f_angle
  7489.     Collision.final_speed   = f_speed
  7490.    
  7491.     if gravity_ring.active:
  7492.         Collision.grav_coef     = gravity_ring.grav_coef
  7493.     else:
  7494.         Collision.grav_coef     = 0
  7495.  
  7496.     updatePadTargets()
  7497.     return
  7498.        
  7499. def getCollisionData():
  7500.     collision_data = (Collision.xpos, Collision.ypos, Collision.initial_angle,
  7501.                       Collision.initial_speed, Collision.final_angle,
  7502.                       Collision.final_speed, Collision.grav_coef)
  7503.     return collision_data
  7504.  
  7505. ############################################################
  7506. #
  7507. #             B A L L    G E N E R A T I O N
  7508. #
  7509.  
  7510. def genNewBall():
  7511.     if WeightData.current_game < games_per_test:
  7512.         WeightData.current_game += 1
  7513.     elif GameState.toggle_genetic:
  7514.         beginNewTest()
  7515.    
  7516.     velocity = random.random()*0.3+0.3
  7517.     angle = 0
  7518.     while angle % 90 < 20:
  7519.         angle = random.randrange(360)
  7520.     ball = Ball(window_width/2, window_height/2, velocity, angle)
  7521.     Balls.ball = ball
  7522.     printData("ANGLE BALL LAUNCHED AT: %s" % angle)
  7523.     updateCollisionData(ball.x, ball.y, angle, velocity, angle, velocity)
  7524.     return
  7525.  
  7526. ############################################################
  7527. #
  7528. #                  M O U S E    I N P U T
  7529. #
  7530.  
  7531. def cursorWithinBounds(low_x, high_x, low_y, high_y):
  7532.     (mouse_x, mouse_y) = pygame.mouse.get_pos()
  7533.    
  7534.     if low_x <= mouse_x and mouse_x <= high_x:
  7535.         if low_y <= mouse_y and mouse_y <= high_y:
  7536.             return True
  7537.     return False
  7538.  
  7539. def hoverHandler(icon):
  7540.     hovering_over = cursorWithinBounds(icon.pos_x, icon.pos_x + icon.width,
  7541.                                        icon.pos_y, icon.pos_y + icon.height)
  7542.     return hovering_over
  7543.  
  7544. def setHover(icon):
  7545.     if hoverHandler(icon):
  7546.         icon.hovering = True
  7547.     else:
  7548.         icon.hovering = False
  7549.     return
  7550.  
  7551. def passiveMouseHandler():
  7552.     #(mouse_x, mouse_y) = pygame.mouse.get_pos()
  7553.     setHover(sound_level)
  7554.     setHover(pause_button)
  7555.     setHover(info_button)
  7556.     setHover(print_button)
  7557.     setHover(fast_forward)
  7558.     setHover(gear_button)
  7559.     setHover(arrow_left)
  7560.     setHover(arrow_right)
  7561.  
  7562.     if gear_button.active:
  7563.         x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
  7564.         y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
  7565.         for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
  7566.             if i >= len(SettingOptions.options):
  7567.                 break
  7568.             option = SettingOptions.options[i]
  7569.             option.pos_x += x_offset
  7570.             option.pos_y += y_offset
  7571.             setHover(option)
  7572.             option.pos_x -= x_offset
  7573.             option.pos_y -= y_offset
  7574.     return
  7575.  
  7576. def activeMouseHandler(position, button):
  7577.     if hoverHandler(sound_level):
  7578.         sound_level.changeSoundLevel()
  7579.    
  7580.     if hoverHandler(pause_button):
  7581.         pause_button.switch()
  7582.  
  7583.     if hoverHandler(info_button):
  7584.         info_button.switch()
  7585.  
  7586.     if hoverHandler(print_button):
  7587.         print_button.switch()
  7588.  
  7589.     if hoverHandler(fast_forward):
  7590.         fast_forward.switch()
  7591.         if not fast_forward.active:
  7592.             setFastForwardCoverStatus(False)
  7593.  
  7594.     if hoverHandler(gear_button):
  7595.         gear_button.switch()
  7596.         pause_button.active = gear_button.active
  7597.  
  7598.     if gear_button.active:
  7599.         x_offset = (window_width - SettingsMenu.default_surface.get_width()) / 2
  7600.         y_offset = (window_height - SettingsMenu.default_surface.get_height()) / 2
  7601.         for i in range((SettingsMenu.current_page - 1) * 5, SettingsMenu.current_page * 5):
  7602.             if i >= len(SettingOptions.options):
  7603.                 break
  7604.             option = SettingOptions.options[i]
  7605.             option.pos_x += x_offset
  7606.             option.pos_y += y_offset
  7607.             if hoverHandler(option):
  7608.                 option.switch()
  7609.             option.pos_x -= x_offset
  7610.             option.pos_y -= y_offset
  7611.  
  7612.         for arrow in [arrow_left, arrow_right]:
  7613.             arrow.pos_x += x_offset
  7614.             arrow.pos_y += y_offset
  7615.             if hoverHandler(arrow):
  7616.                 if arrow == arrow_left:
  7617.                     SettingsMenu.current_page = max(1, SettingsMenu.current_page - 1)
  7618.                 else:
  7619.                     SettingsMenu.current_page = min(SettingsMenu.total_pages, SettingsMenu.current_page + 1)
  7620.             arrow.pos_x -= x_offset
  7621.             arrow.pos_y -= y_offset
  7622.     return
  7623.  
  7624. ############################################################
  7625. #
  7626. #           S E T T I N G S    H A N D L I N G
  7627. #
  7628.  
  7629. def SettingOptionsHandler():
  7630.     for option in SettingOptions.options:
  7631.         option.adjustSize()
  7632.         option.adjustColour()
  7633.     return
  7634.  
  7635. ############################################################
  7636. #
  7637. #            M A I N    G A M E    L O O P
  7638. #
  7639.  
  7640. def main():
  7641.     pygame.init()
  7642.     screen = pygame.display.set_mode((window_width, window_height), 0, 32)
  7643.     Screen.screen = screen
  7644.     pygame.display.set_caption("PyBall")
  7645.     initialiseFonts()
  7646.     initialiseSession()
  7647.     beginNewGeneration()
  7648.     text_colour_val = 255
  7649.     demonstration_begun  = False
  7650.     return_pressed       = False
  7651.     ticks                = 0
  7652.     real_ticks           = 0
  7653.     while not GameState.done:
  7654.         real_ticks += 1
  7655.         screen.fill(black.toList())
  7656.         sound_level.adjustVolume()
  7657.         passiveMouseHandler()
  7658.         SettingOptionsHandler()
  7659.         events = pygame.event.get()
  7660.         for e in events:
  7661.             if e.type == QUIT:
  7662.                 GameState.done = True
  7663.                 break
  7664.             elif not demonstration_begun and e.type == KEYDOWN and e.key == K_RETURN:
  7665.                 return_pressed = True
  7666.             elif e.type == KEYDOWN and demonstration_begun:
  7667.                 keyPressed(e.key)
  7668.             elif e.type == KEYUP and demonstration_begun:
  7669.                 keyReleased(e.key)
  7670.             elif e.type == MOUSEBUTTONDOWN:
  7671.                 activeMouseHandler(e.pos, e.button)
  7672.  
  7673.         if text_colour_val:
  7674.             text_colour = [text_colour_val, text_colour_val, text_colour_val]
  7675.             renderText(screen, window_width / 2, 120, "Angelic War", font_size_40, text_colour, "PyBall")
  7676.             renderText(screen, window_width / 2, 160, "Angelic War", font_size_14, text_colour, "Created By Tag")
  7677.             if return_pressed:
  7678.                 text_colour_val -= 1
  7679.            
  7680.             drawObjects(screen)
  7681.             pygame.display.flip()
  7682.         else:
  7683.             demonstration_begun = True
  7684.             if gear_button.active:
  7685.                 pause_button.active = True
  7686.                 fast_forward.active = False
  7687.                 if info_button.active:
  7688.                     circsAtTargets(screen)
  7689.                 drawObjects(screen)
  7690.                 drawSettingsMenu(screen)
  7691.                 renderText(screen, window_width / 2, 160, "Birth Of A Hero", font_size_30, white.toList(), "S E T T I N G S")
  7692.                 pygame.display.flip()
  7693.             elif pause_button.active and not fast_forward.active:
  7694.                 renderText(screen, window_width / 2, 160, "Birth Of A Hero", font_size_30, white.toList(), "P A U S E D")
  7695.                 if info_button.active:
  7696.                     circsAtTargets(screen)
  7697.                 drawObjects(screen)
  7698.                 pygame.display.flip()
  7699.             elif fast_forward.active and not pause_button.active:
  7700.                 ticks += 1
  7701.                 drawFastForwardCover(screen, ticks, real_ticks)
  7702.                 handleAI()
  7703.                 handleMotion()
  7704.             elif fast_forward.active and pause_button.active:
  7705.                 drawFastForwardCover(screen, ticks, real_ticks)
  7706.             else:
  7707.                 if gravity_ring.active:
  7708.                     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")
  7709.                 ticks += 1
  7710.                 handleAI()
  7711.                 handleMotion()
  7712.                 if info_button.active:
  7713.                     circsAtTargets(screen)
  7714.                 drawObjects(screen)
  7715.                 pygame.display.flip()
  7716.  
  7717.     if GameState.done:
  7718.         pygame.quit()
  7719.     return
  7720.  
  7721. if __name__ == "__main__":
  7722.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement