├── .gitignore ├── LICENSE ├── README.md ├── The Game ├── Sprites │ ├── Bird.py │ ├── CactusDouble.py │ ├── CactusSingle.py │ ├── CactusTriple.py │ ├── GameImages │ │ ├── birdDown.png │ │ ├── birdUp.png │ │ ├── doubleCactus.png │ │ ├── smallCactus.png │ │ ├── tRex.png │ │ ├── tRexDuck.png │ │ ├── tRexDuckRight.png │ │ ├── tRexLeftLeg.png │ │ ├── tRexRightLeg.png │ │ └── tripleCactus.png │ └── Player.py └── test.py └── TheGameAndTheGeneticAI ├── GameForAi.py ├── GameTrainer.py ├── GameTrainerNo-UI.py ├── Sprites ├── Bird.py ├── CactusDouble.py ├── CactusSingle.py ├── CactusTriple.py ├── Clouds.py ├── Dashes.py ├── GameImages │ ├── Cloud.png │ ├── birdDown.png │ ├── birdUp.png │ ├── concave.png │ ├── convex.png │ ├── doubleCactus.png │ ├── plain.png │ ├── smallCactus.png │ ├── tRex.png │ ├── tRexDuck.png │ ├── tRexDuckRight.png │ ├── tRexLeftLeg.png │ ├── tRexRightLeg.png │ └── tripleCactus.png └── Player.py ├── bestTRex_better.pickle ├── config └── visualize.py /.gitignore: -------------------------------------------------------------------------------- 1 | */*.pyc 2 | *.gv* 3 | *.yml 4 | */__pycache__ 5 | */Sprites/__pycache__/ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Sparsha Saha 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Obstacle-Avoiding-AI-bot 2 | A self-playing bot built with NEAT-python for Chrome T-Rex game recreated in pygame 3 | 4 | ## Requirements 5 | 1. Python3 6 | 2. NEAT-python 7 | 3. Pygame 8 | 9 | ## Algorithm 10 | We have used **NEURO EVOLUTION OF AUGMENTED TOPOLOGIES (NEAT)** for designing the AI-bot. NEAT is a very handy algorithm and is actively used in AI design. More information about **NEAT** can be found [here](http://nn.cs.utexas.edu/downloads/papers/stanley.ec02.pdf). 11 | 12 | There is a great python library for **NEAT** in python developed by [CodeReclaimers](https://github.com/CodeReclaimers). We have used **NEAT Python** for implementing **NEAT** for the evolution of our AI-bot. 13 | 14 | ## Folder and Files Description 15 | 1> TheGame 16 | This contains the Game written in pygame and playbale by humans 17 | 18 | 2> TheGameAndTheGeneticAI 19 | 20 | i> Game.py contains the code to train the AI 21 | 22 | ii> GameForAi.py contains the game played by the best trained AI 23 | 24 | Video Link: 25 | https://youtu.be/fLJsuVCr5L0 26 | -------------------------------------------------------------------------------- /The Game/Sprites/Bird.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | class Bird(object): 3 | 4 | def __init__(self, x, y): 5 | self.x = x 6 | self.y = y 7 | self.hitbox = (0, 0, 0, 0) 8 | self.frameCount = 0; 9 | self.images = ["Sprites/GameImages/birdUp.png", "Sprites/GameImages/birdDown.png"] 10 | self.flipState = 0 11 | 12 | def drawCharacter(self, canvas): 13 | if self.frameCount % 50 == 0: 14 | self.flipState = self.flip(self.flipState) 15 | 16 | canvas.blit(pygame.image.load(self.images[self.flipState]), (self.x,self.y)) 17 | self.hitbox = (self.x, self.y, 45, 27) 18 | self.frameCount += 1 19 | 20 | def propagate(self, step): 21 | self.x -= step 22 | 23 | def flip(self, index): 24 | if index == 0: 25 | return 1 26 | else: 27 | return 0 28 | -------------------------------------------------------------------------------- /The Game/Sprites/CactusDouble.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | class CactusDouble(object): 3 | 4 | def __init__(self, x, y): 5 | self.x = x 6 | self.y = y 7 | self.hitbox = (0, 0, 0, 0) 8 | self.imageName = "Sprites/GameImages/doubleCactus.png" 9 | self.loadedImage = pygame.image.load(self.imageName) 10 | 11 | def drawCharacter(self, canvas): 12 | canvas.blit(self.loadedImage, (self.x,self.y)) 13 | self.hitbox = (self.x + 3, self.y + 3, 30, 30) 14 | #pygame.draw.rect(canvas, (255, 0, 0), self.hitbox, 2) 15 | 16 | def propagate(self, step): 17 | self.x -= step 18 | -------------------------------------------------------------------------------- /The Game/Sprites/CactusSingle.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | class CactusSingle(object): 3 | 4 | def __init__(self, x, y): 5 | self.x = x 6 | self.y = y 7 | self.hitbox = (0, 0, 0, 0) 8 | self.imageName = "Sprites/GameImages/smallCactus.png" 9 | self.loadedImage = pygame.image.load(self.imageName) 10 | 11 | def drawCharacter(self, canvas): 12 | canvas.blit(self.loadedImage, (self.x,self.y)) 13 | self.hitbox = (self.x + 3, self.y + 3, 15, 30) 14 | #pygame.draw.rect(canvas, (255, 0, 0), self.hitbox, 2) 15 | 16 | def propagate(self, step): 17 | self.x -= step 18 | -------------------------------------------------------------------------------- /The Game/Sprites/CactusTriple.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | class CactusTriple(object): 3 | 4 | def __init__(self, x, y): 5 | self.x = x 6 | self.y = y 7 | self.hitbox = (self.x, self.y, 51, 30) 8 | self.imageName = "Sprites/GameImages/tripleCactus.png" 9 | self.loadedImage = pygame.image.load(self.imageName) 10 | 11 | 12 | def drawCharacter(self, canvas): 13 | canvas.blit(self.loadedImage, (self.x,self.y)) 14 | self.hitbox = (self.x + 3, self.y + 3, 45, 30) 15 | #pygame.draw.rect(canvas, (255, 0, 0), self.hitbox, 2) 16 | 17 | def propagate(self, step): 18 | self.x -= step 19 | -------------------------------------------------------------------------------- /The Game/Sprites/GameImages/birdDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/The Game/Sprites/GameImages/birdDown.png -------------------------------------------------------------------------------- /The Game/Sprites/GameImages/birdUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/The Game/Sprites/GameImages/birdUp.png -------------------------------------------------------------------------------- /The Game/Sprites/GameImages/doubleCactus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/The Game/Sprites/GameImages/doubleCactus.png -------------------------------------------------------------------------------- /The Game/Sprites/GameImages/smallCactus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/The Game/Sprites/GameImages/smallCactus.png -------------------------------------------------------------------------------- /The Game/Sprites/GameImages/tRex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/The Game/Sprites/GameImages/tRex.png -------------------------------------------------------------------------------- /The Game/Sprites/GameImages/tRexDuck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/The Game/Sprites/GameImages/tRexDuck.png -------------------------------------------------------------------------------- /The Game/Sprites/GameImages/tRexDuckRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/The Game/Sprites/GameImages/tRexDuckRight.png -------------------------------------------------------------------------------- /The Game/Sprites/GameImages/tRexLeftLeg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/The Game/Sprites/GameImages/tRexLeftLeg.png -------------------------------------------------------------------------------- /The Game/Sprites/GameImages/tRexRightLeg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/The Game/Sprites/GameImages/tRexRightLeg.png -------------------------------------------------------------------------------- /The Game/Sprites/GameImages/tripleCactus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/The Game/Sprites/GameImages/tripleCactus.png -------------------------------------------------------------------------------- /The Game/Sprites/Player.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | class Player(object): 3 | def __init__(self, x, y): 4 | self.x = x 5 | self.y = [y, y + 20, y] 6 | self.hitbox = (self.x, self.y[0], 38, 40) 7 | self.hitboxRectStanding = pygame.Rect(self.x, self.y[0], 41, 40) 8 | self.hitboxRectDucking = pygame.Rect(self.x, self.y[1], 58, 30) 9 | self.frameCount = 0 10 | self.index = 0 11 | self.currentImageIndex = 0 12 | self.imageName = ["Sprites/GameImages/tRexLeftLeg.png", "Sprites/GameImages/tRexDuck.png", "Sprites/GameImages/tRexRightLeg.png", "Sprites/GameImages/tRexDuckRight.png"] 13 | 14 | def drawCharacter(self, canvas, index): 15 | if self.frameCount % 10 == 0 or index != self.index: 16 | self.currentImageIndex = self.flip(index) 17 | self.frameCount = 0 18 | self.index = index 19 | 20 | if index == 0 or index == 2: 21 | self.hitbox = (self.x, self.y[0], 38, 40) 22 | self.hitboxRectStanding = pygame.Rect(self.x, self.y[0], 41, 40) 23 | self.hitboxRect = self.hitboxRectStanding 24 | else: 25 | self.hitboxRectDucking = pygame.Rect(self.x, self.y[1], 58, 30) 26 | self.hitbox = (self.x, self.y[1], 58, 30) 27 | self.hitboxRect = self.hitboxRectDucking 28 | 29 | self.frameCount += 1 30 | 31 | canvas.blit(pygame.image.load(self.imageName[self.currentImageIndex]), (self.x,self.y[index])) 32 | 33 | #pygame.draw.rect(canvas, (255, 0, 0), self.hitbox,2) 34 | 35 | def jump(self, jump, direction, jumpSpeed): 36 | self.y[0] += jumpSpeed * direction 37 | if self.y[0] < 410 : 38 | direction = 1 39 | return True, direction 40 | elif self.y[0] >= 500: 41 | direction = -1 42 | return False, direction 43 | else: 44 | return True, direction 45 | 46 | def detectCollision(self, sprite): 47 | return self.hitboxRect.colliderect(sprite.hitbox) 48 | 49 | def flip(self, index): 50 | if index == 0: 51 | if self.currentImageIndex == 0: 52 | return 2 53 | else: 54 | return 0 55 | else: 56 | if self.currentImageIndex == 1: 57 | return 3 58 | else: 59 | return 1 60 | -------------------------------------------------------------------------------- /The Game/test.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import random 3 | from Sprites.Player import Player 4 | from Sprites.CactusSingle import CactusSingle 5 | from Sprites.CactusDouble import CactusDouble 6 | from Sprites.CactusTriple import CactusTriple 7 | from Sprites.Bird import Bird 8 | from Logger import Logger 9 | 10 | # Game Variables 11 | obstacleProbability = 0.01 12 | obstaclesOnScreen = [] 13 | speed = 3.0 14 | lastQuotient = 0 15 | score = 0 16 | tRexIndex = 0 17 | direction = -1 18 | running = True 19 | jump = False 20 | gameOver = False 21 | jumpSpeed = 3.2 22 | fontName = pygame.font.match_font('arial') 23 | clock = pygame.time.Clock() 24 | background_colour = (255,255,255) 25 | (width, height) = (900, 600) 26 | 27 | # Display Score on Screen 28 | def drawText(surf, text, size, x, y): 29 | font = pygame.font.Font(fontName, size) 30 | textSurface = font.render(text, True, (0, 0, 0)) 31 | textRect = textSurface.get_rect() 32 | textRect.midtop = (x, y) 33 | surf.blit(textSurface, textRect) 34 | 35 | # Draw the game background 36 | def drawGameBackground(): 37 | screen.blit(background_colour) 38 | 39 | # Draw TRex and all obstacles on Screen 40 | def drawCharacter(): 41 | tRex.drawCharacter(screen, tRexIndex) 42 | for obstacles in obstaclesOnScreen: 43 | obstacles.drawCharacter(screen) 44 | 45 | # Randomly generate game obstacles depending on obstacle probability 46 | def generateGameObstacles(): 47 | if len(obstaclesOnScreen) == 0 or obstaclesOnScreen[len(obstaclesOnScreen) - 1].x < 650: 48 | if random.uniform(0,1) < obstacleProbability: 49 | obstacleNumber = random.randint(0,11) 50 | if obstacleNumber <= 4: 51 | obstaclesOnScreen.append(CactusSingle(900, 515)) 52 | elif obstacleNumber <= 6: 53 | obstaclesOnScreen.append(CactusDouble(900, 515)) 54 | elif obstacleNumber <= 8: 55 | obstaclesOnScreen.append(CactusTriple(900, 515)) 56 | elif obstacleNumber <= 9 and score >= 25: 57 | obstaclesOnScreen.append(Bird(900, 490)) 58 | elif score >= 30: 59 | obstaclesOnScreen.append(Bird(900, 515)) 60 | 61 | # Remove dead obstacles from obstacle array 62 | def cleanDeadObstaclesAndPropagate(obstacles, score): 63 | index = 0 64 | for obstacle in obstacles: 65 | if obstacle.x > 100: 66 | lastDistance = 900 67 | break 68 | else: 69 | score += 1 70 | index+=1 71 | 72 | obstacles = obstacles[index : ] 73 | for obstacle in obstacles: 74 | obstacle.propagate(speed) 75 | return obstacles, score 76 | 77 | 78 | # 0 - jump 79 | # 1 - duck 80 | # 2 - nothing 81 | 82 | pygame.init() 83 | logger = Logger(1) 84 | action = 0; 85 | screen = pygame.display.set_mode((width, height)) 86 | pygame.display.set_caption('T-Rex Runner') 87 | drawGameBackground() 88 | pygame.display.flip() 89 | 90 | lastpos = 900 91 | 92 | tRex = Player(90, 500) 93 | while running: 94 | action = 2 95 | clock.tick(100) 96 | for event in pygame.event.get(): 97 | if event.type == pygame.QUIT: 98 | running = False 99 | break 100 | 101 | keys = pygame.key.get_pressed() 102 | 103 | if keys[pygame.K_r]: 104 | gameOver = False 105 | score = 0 106 | obstaclesOnScreen = [] 107 | 108 | if keys[pygame.K_DOWN] and not jump: 109 | tRexIndex = 1 110 | action = 1 111 | else: 112 | tRexIndex = 0 113 | 114 | if not jump: 115 | if keys[pygame.K_UP]: 116 | jump = True 117 | action = 0 118 | else: 119 | jump, direction = tRex.jump(jump, direction, jumpSpeed) 120 | action = -1 121 | 122 | if not gameOver: 123 | drawGameBackground() 124 | generateGameObstacles() 125 | obstaclesOnScreen, score = cleanDeadObstaclesAndPropagate(obstaclesOnScreen, score) 126 | drawCharacter() 127 | drawText(screen, 'score: ' + str(score), 20, 700, 50) 128 | pygame.display.update() 129 | 130 | if len(obstaclesOnScreen) > 0 and tRex.detectCollision(obstaclesOnScreen[0]): 131 | gameOver = True 132 | speed = 1.5 133 | break 134 | 135 | if score // 10 > lastQuotient: 136 | lastQuotient += 1 137 | speed += 0.5 138 | jumpSpeed += 0.1 139 | 140 | # Logging 141 | if len(obstaclesOnScreen) != 0: 142 | if (lastpos - obstaclesOnScreen[0].x) > speed * 15.0 or action != 2: 143 | if obstaclesOnScreen[0].x - 120 > 0 and action != -1: 144 | logger.logData(1, speed, action, obstaclesOnScreen[0].x - 115) 145 | lastpos = obstaclesOnScreen[0].x 146 | -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/GameForAi.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import random 3 | import pickle 4 | import numpy as np 5 | import neat 6 | import os 7 | import visualize 8 | 9 | from Sprites.CactusSingle import CactusSingle 10 | from Sprites.Player import Player 11 | from Sprites.CactusDouble import CactusDouble 12 | from Sprites.CactusTriple import CactusTriple 13 | from Sprites.Bird import Bird 14 | from Sprites.Clouds import Cloud 15 | from Sprites.Dashes import Dashes 16 | 17 | 18 | class Game(object): 19 | 20 | def __init__(self, tRexArray, config): 21 | self.obstacleProbability = 0.01 22 | self.obstaclesOnScreen = [] 23 | self.speed = 3.0 24 | self.lastQuotient = 0 25 | self.score = 0 26 | self.direction = -1 27 | self.running = True 28 | self.gameOver = False 29 | self.jumpSpeed = 3.2 30 | self.fontName = pygame.font.match_font('arial') 31 | self.clock = pygame.time.Clock() 32 | self.background_colour = (255,255,255) 33 | self.width = 900 34 | self.height = 600 35 | self.frameCount = 0 36 | self.screen = pygame.display.set_mode((self.width, self.height)) 37 | self.trexs = tRexArray 38 | self.config = config 39 | self.dashes = [] 40 | self.clouds = [] 41 | cloud1 = Cloud(930, 50, 930, 0.7) 42 | cloud2 = Cloud(1030, 50, 1030, 0.65) 43 | cloud3 = Cloud(1030, 100, 1030, 0.5) 44 | cloud4 = Cloud(1130, 100, 1130, 0.45) 45 | self.clouds = [cloud1, cloud2, cloud3, cloud4] 46 | for trex in self.trexs: 47 | trex.net = neat.nn.FeedForwardNetwork.create(trex, config) 48 | 49 | 50 | # Display Score on Screen 51 | def drawText(self, text, size, x, y): 52 | font = pygame.font.Font(self.fontName, size) 53 | textSurface = font.render(text, True, (0, 0, 0)) 54 | textRect = textSurface.get_rect() 55 | textRect.midtop = (x, y) 56 | self.screen.blit(textSurface, textRect) 57 | 58 | # Draw the game background 59 | def drawGameBackground(self): 60 | self.screen.fill(self.background_colour) 61 | pygame.draw.rect(self.screen, (0, 0, 0), (0, 550, 900, 1), 1) 62 | 63 | 64 | # Draw obstacles and trexs on screen 65 | def drawCharacter(self): 66 | 67 | for trex in self.trexs: 68 | if trex.isJumping: 69 | trex.drawCharacter(self.screen, 0) 70 | elif trex.alive: 71 | if trex.predictedAction == 2: 72 | trex.drawCharacter(self.screen, 1) 73 | else: 74 | trex.drawCharacter(self.screen, 0) 75 | 76 | for obstacles in self.obstaclesOnScreen: 77 | obstacles.drawCharacter(self.screen) 78 | 79 | for cloud in self.clouds: 80 | cloud.drawCharacter(self.screen) 81 | 82 | # Randomly generate game obstacles depending on obstacle probability 83 | def generateGameObstacles(self): 84 | if len(self.obstaclesOnScreen) == 0 or self.obstaclesOnScreen[len(self.obstaclesOnScreen) - 1].x < 600: 85 | if random.uniform(0,1) < self.obstacleProbability: 86 | obstacleNumber = random.randint(0, 3) 87 | if obstacleNumber <= 0: 88 | self.obstaclesOnScreen.append(CactusSingle(900, 515)) 89 | elif obstacleNumber <= 1: 90 | self.obstaclesOnScreen.append(CactusDouble(900, 515)) 91 | elif obstacleNumber <= 2: 92 | self.obstaclesOnScreen.append(CactusTriple(900, 515)) 93 | elif obstacleNumber <= 3: 94 | self.obstaclesOnScreen.append(Bird(900, 485)) 95 | 96 | 97 | # Kill trexs on collision 98 | def detectCollisionAndKillTRex(self): 99 | for trex in self.trexs: 100 | if trex.detectCollision(self.obstaclesOnScreen[0]) and trex.alive: 101 | trex.fitness = self.score 102 | trex.alive = False 103 | 104 | # Get Obstacle Info 105 | def getObstacleIndex(self, name): 106 | if name == "CactusSingle": 107 | return (15, 30) 108 | 109 | if name == "CactusDouble": 110 | return (30, 30) 111 | 112 | if name == "CactusTriple": 113 | return (45, 30) 114 | 115 | else: 116 | return (45, 27) 117 | 118 | 119 | 120 | # Predict actions for all trexs which are alive 121 | def predictActionsForTRexs(self): 122 | 123 | if len(self.obstaclesOnScreen) > 0: 124 | obstacleNumber = self.getObstacleIndex(self.obstaclesOnScreen[0].__class__.__name__) 125 | if obstacleNumber[1] != 27: 126 | input = (float(obstacleNumber[0]),float(obstacleNumber[1]), 0, float(self.obstaclesOnScreen[0].x - 120), float(self.speed*100)) 127 | else: 128 | input = (float(obstacleNumber[0]),float(obstacleNumber[1]), 100, float(self.obstaclesOnScreen[0].x - 120), float(self.speed*100)) 129 | 130 | for trex in self.trexs: 131 | if trex.alive: 132 | output = trex.net.activate(input) 133 | trex.predictedAction = (output.index(max(output))) 134 | else: 135 | input = (float(30),float(30), 0, float(9500), float(self.speed*100)) 136 | for trex in self.trexs: 137 | if trex.alive: 138 | output = trex.net.activate(input) 139 | trex.predictedAction = (output.index(max(output))) 140 | 141 | 142 | 143 | # Check if generation of Trexs are extinct 144 | def allDead(self): 145 | for trex in self.trexs: 146 | if trex.alive: 147 | return False 148 | self.gameOver = True 149 | return True 150 | 151 | 152 | # Make the TRrexs to jump 153 | def makeTrexsJump(self): 154 | 155 | for trex in self.trexs: 156 | if trex.alive: 157 | if not trex.isJumping: 158 | if trex.predictedAction == 1: 159 | trex.isJumping = True 160 | else: 161 | trex.isJumping, trex.direction = trex.jump(trex.isJumping, trex.direction, self.jumpSpeed) 162 | 163 | 164 | # Clean obstacles which has passed player 165 | def cleanDeadObstaclesAndPropagate(self): 166 | index = 0 167 | for obstacle in self.obstaclesOnScreen: 168 | if obstacle.x > 30: 169 | break 170 | else: 171 | self.score += 1 172 | index += 1 173 | 174 | self.obstaclesOnScreen = self.obstaclesOnScreen[index : ] 175 | for obstacle in self.obstaclesOnScreen: 176 | obstacle.propagate(self.speed) 177 | 178 | for cloud in self.clouds: 179 | cloud.propagate() 180 | 181 | # Increase Game Speed 182 | def increaseGameSpeed(self): 183 | if int(self.score/ 10) != self.lastQuotient: 184 | self.lastQuotient = int(self.score/ 10) 185 | self.speed += 0.15 186 | self.jumpSpeed += 0.05 187 | 188 | # Create dashes which signify ground 189 | def createDashes(self): 190 | possibleYCoords = [555, 565, 575] 191 | chosenYCoord = random.choice(possibleYCoords) 192 | self.dashes.append(Dashes(899, chosenYCoord)) 193 | 194 | # Remove dashes which have passed the screen and propagate dashes 195 | def removeDeadDashesAndPropagate(self): 196 | index = 0 197 | for dash in self.dashes: 198 | if dash.x > 0: 199 | break 200 | index += 1 201 | self.dashes = self.dashes[index : ] 202 | for dash in self.dashes: 203 | dash.propagate(self.speed) 204 | 205 | # Draw dashes on screen 206 | def drawDashes(self): 207 | for dash in self.dashes: 208 | dash.drawCharacter(self.screen) 209 | 210 | 211 | 212 | # Run the game 213 | def game(self): 214 | pygame.init() 215 | pygame.display.set_caption('T-Rex Runner') 216 | self.drawGameBackground() 217 | pygame.display.flip() 218 | 219 | while self.running: 220 | self.clock.tick(110) 221 | 222 | for event in pygame.event.get(): 223 | if event.type == pygame.QUIT: 224 | self.running = False 225 | break 226 | 227 | self.predictActionsForTRexs() 228 | 229 | self.makeTrexsJump() 230 | 231 | self.drawGameBackground() 232 | self.generateGameObstacles() 233 | self.cleanDeadObstaclesAndPropagate() 234 | self.drawCharacter() 235 | self.drawText('score: ' + str(self.score), 20, 700, 50) 236 | 237 | if len(self.dashes) == 0: 238 | self.createDashes() 239 | elif self.dashes[0].x < 890: 240 | self.createDashes() 241 | 242 | self.removeDeadDashesAndPropagate() 243 | self.drawDashes() 244 | 245 | pygame.display.update() 246 | 247 | if len(self.obstaclesOnScreen) > 0: 248 | self.detectCollisionAndKillTRex() 249 | 250 | if self.allDead(): 251 | print(self.trexs[0].fitness) 252 | return 253 | 254 | self.increaseGameSpeed() 255 | 256 | local_dir = os.path.dirname(__file__) 257 | config_path = os.path.join(local_dir, 'config') 258 | config = neat.Config(Player, neat.DefaultReproduction, neat.DefaultSpeciesSet, neat.DefaultStagnation, config_path) 259 | 260 | player = None 261 | 262 | with open('bestTRex_better.pickle', 'rb') as handle: 263 | player = pickle.load(handle) 264 | 265 | print(player) 266 | player.alive = True 267 | visualize.draw_net(config, player, True) 268 | 269 | game = Game([player], config) 270 | 271 | game.game() 272 | -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/GameTrainer.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import random 3 | import pickle 4 | import numpy as np 5 | import neat 6 | import os 7 | from Sprites.CactusSingle import CactusSingle 8 | from Sprites.Player import Player 9 | from Sprites.CactusDouble import CactusDouble 10 | from Sprites.CactusTriple import CactusTriple 11 | from Sprites.Bird import Bird 12 | import pickle 13 | 14 | class Game(object): 15 | 16 | def __init__(self, tRexArray, config): 17 | self.obstacleProbability = 0.01 18 | self.obstaclesOnScreen = [] 19 | self.speed = 3.0 20 | self.lastQuotient = 0 21 | self.score = 0 22 | self.direction = -1 23 | self.running = True 24 | self.gameOver = False 25 | self.jumpSpeed = 3.2 26 | self.fontName = pygame.font.match_font('arial') 27 | self.clock = pygame.time.Clock() 28 | self.background_colour = (255,255,255) 29 | self.width = 900 30 | self.height = 600 31 | self.frameCount = 0 32 | self.screen = pygame.display.set_mode((self.width, self.height)) 33 | self.trexs = tRexArray 34 | self.config = config 35 | for trexId, trex in self.trexs: 36 | trex.net = neat.nn.FeedForwardNetwork.create(trex, config) 37 | 38 | 39 | # Display Score on Screen 40 | def drawText(self, text, size, x, y): 41 | font = pygame.font.Font(self.fontName, size) 42 | textSurface = font.render(text, True, (0, 0, 0)) 43 | textRect = textSurface.get_rect() 44 | textRect.midtop = (x, y) 45 | self.screen.blit(textSurface, textRect) 46 | 47 | # Draw the game background 48 | def drawGameBackground(self): 49 | self.screen.fill(self.background_colour) 50 | 51 | 52 | # Draw obstacles and trexs on screen 53 | def drawCharacter(self): 54 | 55 | for trexId, trex in self.trexs: 56 | 57 | if trex.alive: 58 | if trex.isJumping: 59 | trex.drawCharacter(self.screen, 0) 60 | else: 61 | if trex.predictedAction == 2: 62 | trex.drawCharacter(self.screen, 1) 63 | else: 64 | trex.drawCharacter(self.screen, 0) 65 | 66 | for obstacles in self.obstaclesOnScreen: 67 | obstacles.drawCharacter(self.screen) 68 | 69 | # Randomly generate game obstacles depending on obstacle probability 70 | def generateGameObstacles(self): 71 | if len(self.obstaclesOnScreen) == 0 or self.obstaclesOnScreen[len(self.obstaclesOnScreen) - 1].x < 600: 72 | if random.uniform(0,1) < self.obstacleProbability: 73 | obstacleNumber = random.randint(0, 3) 74 | if obstacleNumber <= 0: 75 | self.obstaclesOnScreen.append(CactusSingle(900, 515)) 76 | elif obstacleNumber <= 1: 77 | self.obstaclesOnScreen.append(CactusDouble(900, 515)) 78 | elif obstacleNumber <= 2: 79 | self.obstaclesOnScreen.append(CactusTriple(900, 515)) 80 | elif obstacleNumber <= 3: 81 | self.obstaclesOnScreen.append(Bird(900, 485)) 82 | 83 | 84 | # Kill trexs on collision 85 | def detectCollisionAndKillTRex(self): 86 | for trexId, trex in self.trexs: 87 | if trex.detectCollision(self.obstaclesOnScreen[0]) and trex.alive: 88 | trex.fitness = self.score 89 | trex.alive = False 90 | 91 | 92 | def getObstacleIndex(self, name): 93 | if name == "CactusSingle": 94 | return (15, 30) 95 | 96 | if name == "CactusDouble": 97 | return (30, 30) 98 | 99 | if name == "CactusTriple": 100 | return (45, 30) 101 | 102 | else: 103 | return (45, 27) 104 | 105 | 106 | # Predict actions for all trexs which are alive 107 | def predictActionsForTRexs(self): 108 | 109 | if len(self.obstaclesOnScreen) > 0: 110 | obstacleNumber = self.getObstacleIndex(self.obstaclesOnScreen[0].__class__.__name__) 111 | if obstacleNumber[1] != 27: 112 | input = (float(obstacleNumber[0]),float(obstacleNumber[1]), 0, float(self.obstaclesOnScreen[0].x - 120), float(self.speed*100)) 113 | else: 114 | input = (float(obstacleNumber[0]),float(obstacleNumber[1]), 100, float(self.obstaclesOnScreen[0].x - 120), float(self.speed*100)) 115 | 116 | for trexId, trex in self.trexs: 117 | if trex.alive: 118 | output = trex.net.activate(input) 119 | trex.predictedAction = (output.index(max(output))) 120 | else: 121 | 122 | input = (float(30),float(30), 0, float(9500), float(self.speed*100)) 123 | 124 | for trexId, trex in self.trexs: 125 | if trex.alive: 126 | output = trex.net.activate(input) 127 | trex.predictedAction = (output.index(max(output))) 128 | 129 | 130 | 131 | # Check if generation of Trexs are extinct 132 | def allDead(self): 133 | for trexId, trex in self.trexs: 134 | if trex.alive: 135 | return False 136 | self.gameOver = True 137 | return True 138 | 139 | 140 | def makeTrexsJump(self): 141 | 142 | for trexId, trex in self.trexs: 143 | if trex.alive: 144 | if not trex.isJumping: 145 | if trex.predictedAction == 1: 146 | trex.isJumping = True 147 | else: 148 | trex.isJumping, trex.direction = trex.jump(trex.isJumping, trex.direction, self.jumpSpeed) 149 | 150 | 151 | 152 | def cleanDeadObstaclesAndPropagate(self): 153 | index = 0 154 | for obstacle in self.obstaclesOnScreen: 155 | if obstacle.x > 30: 156 | break 157 | else: 158 | self.score += 1 159 | index += 1 160 | 161 | self.obstaclesOnScreen = self.obstaclesOnScreen[index : ] 162 | for obstacle in self.obstaclesOnScreen: 163 | obstacle.propagate(self.speed) 164 | 165 | 166 | def increaseGameSpeed(self): 167 | if int(self.score/ 5) != self.lastQuotient: 168 | self.lastQuotient = int(self.score/ 5) 169 | self.speed += 0.15 170 | self.jumpSpeed += 0.05 171 | 172 | 173 | 174 | 175 | def game(self): 176 | pygame.init() 177 | pygame.display.set_caption('T-Rex Runner') 178 | self.drawGameBackground() 179 | pygame.display.flip() 180 | 181 | while self.running: 182 | self.clock.tick(100) 183 | 184 | for event in pygame.event.get(): 185 | if event.type == pygame.QUIT: 186 | self.running = False 187 | break 188 | 189 | self.predictActionsForTRexs() 190 | 191 | self.makeTrexsJump() 192 | 193 | self.drawGameBackground() 194 | self.generateGameObstacles() 195 | self.cleanDeadObstaclesAndPropagate() 196 | self.drawCharacter() 197 | self.drawText('score: ' + str(self.score), 20, 700, 50) 198 | 199 | pygame.display.update() 200 | 201 | if len(self.obstaclesOnScreen) > 0: 202 | self.detectCollisionAndKillTRex() 203 | 204 | if self.allDead(): 205 | return 206 | 207 | self.increaseGameSpeed() 208 | 209 | 210 | 211 | 212 | 213 | 214 | class Simulate(object): 215 | def __init__(self): 216 | self.GENERATION_NUMBER = 0 217 | 218 | def main(self): 219 | local_dir = os.path.dirname(__file__) 220 | config_path = os.path.join(local_dir, 'config') 221 | config = neat.Config(Player, neat.DefaultReproduction, neat.DefaultSpeciesSet, neat.DefaultStagnation, config_path) 222 | 223 | pop = neat.Population(config) 224 | stats = neat.StatisticsReporter() 225 | pop.add_reporter(stats) 226 | 227 | winner = pop.run(self.eval_genomes, 50) 228 | 229 | # Save winner in a file 230 | with open('bestTRex_better.pickle', 'wb') as handle: 231 | pickle.dump(winner, handle, protocol = pickle.HIGHEST_PROTOCOL) 232 | 233 | def eval_genomes(self, genomes, config): 234 | 235 | self.GENERATION_NUMBER += 1 236 | for _, trex in genomes: 237 | trex.alive = True 238 | g = Game(genomes, config) 239 | g.game() 240 | 241 | maxScore = 0 242 | for _, trex in genomes: 243 | if trex.fitness > maxScore: 244 | maxScore = trex.fitness 245 | 246 | print("Max score for generation : "+str(self.GENERATION_NUMBER)+ " is "+str(maxScore)) 247 | 248 | sim = Simulate() 249 | sim.main() -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/GameTrainerNo-UI.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import random 3 | import pickle 4 | import numpy as np 5 | import neat 6 | import os 7 | from Sprites.CactusSingle import CactusSingle 8 | from Sprites.Player import Player 9 | from Sprites.CactusDouble import CactusDouble 10 | from Sprites.CactusTriple import CactusTriple 11 | from Sprites.Bird import Bird 12 | import pickle 13 | 14 | global GENERATION_NUMBER = 1 15 | class Game(object): 16 | 17 | def __init__(self, tRexArray, config): 18 | self.obstacleProbability = 0.01 19 | self.obstaclesOnScreen = [] 20 | self.speed = 3.0 21 | self.lastQuotient = 0 22 | self.score = 0 23 | self.direction = -1 24 | self.running = True 25 | self.gameOver = False 26 | self.jumpSpeed = 3.2 27 | self.fontName = pygame.font.match_font('arial') 28 | self.clock = pygame.time.Clock() 29 | self.background_colour = (255,255,255) 30 | self.width = 900 31 | self.height = 600 32 | self.frameCount = 0 33 | self.screen = pygame.display.set_mode((self.width, self.height)) 34 | self.trexs = tRexArray 35 | self.config = config 36 | for trexId, trex in self.trexs: 37 | trex.net = neat.nn.FeedForwardNetwork.create(trex, config) 38 | 39 | 40 | # Display Score on Screen 41 | def drawText(self, text, size, x, y): 42 | font = pygame.font.Font(self.fontName, size) 43 | textSurface = font.render(text, True, (0, 0, 0)) 44 | textRect = textSurface.get_rect() 45 | textRect.midtop = (x, y) 46 | self.screen.blit(textSurface, textRect) 47 | 48 | # Draw the game background 49 | def drawGameBackground(self): 50 | self.screen.fill(self.background_colour) 51 | 52 | 53 | # Draw obstacles and trexs on screen 54 | def drawCharacter(self): 55 | 56 | for trexId, trex in self.trexs: 57 | 58 | if trex.alive: 59 | if trex.isJumping: 60 | trex.drawCharacter(self.screen, 0) 61 | else: 62 | if trex.predictedAction == 2: 63 | trex.drawCharacter(self.screen, 1) 64 | else: 65 | trex.drawCharacter(self.screen, 0) 66 | 67 | for obstacles in self.obstaclesOnScreen: 68 | obstacles.drawCharacter(self.screen) 69 | 70 | # Randomly generate game obstacles depending on obstacle probability 71 | def generateGameObstacles(self): 72 | if len(self.obstaclesOnScreen) == 0 or self.obstaclesOnScreen[len(self.obstaclesOnScreen) - 1].x < 600: 73 | if random.uniform(0,1) < self.obstacleProbability: 74 | obstacleNumber = random.randint(0, 3) 75 | if obstacleNumber <= 0: 76 | self.obstaclesOnScreen.append(CactusSingle(900, 515)) 77 | elif obstacleNumber <= 1: 78 | self.obstaclesOnScreen.append(CactusDouble(900, 515)) 79 | elif obstacleNumber <= 2: 80 | self.obstaclesOnScreen.append(CactusTriple(900, 515)) 81 | elif obstacleNumber <= 3: 82 | self.obstaclesOnScreen.append(Bird(900, 485)) 83 | 84 | 85 | # Kill trexs on collision 86 | def detectCollisionAndKillTRex(self): 87 | for trexId, trex in self.trexs: 88 | if trex.detectCollision(self.obstaclesOnScreen[0]) and trex.alive: 89 | trex.fitness = self.score 90 | trex.alive = False 91 | 92 | 93 | def getObstacleIndex(self, name): 94 | if name == "CactusSingle": 95 | return (15, 30) 96 | 97 | if name == "CactusDouble": 98 | return (30, 30) 99 | 100 | if name == "CactusTriple": 101 | return (45, 30) 102 | 103 | else: 104 | return (45, 27) 105 | 106 | 107 | # Predict actions for all trexs which are alive 108 | def predictActionsForTRexs(self): 109 | 110 | if len(self.obstaclesOnScreen) > 0: 111 | obstacleNumber = self.getObstacleIndex(self.obstaclesOnScreen[0].__class__.__name__) 112 | if obstacleNumber != 4: 113 | input = (float(obstacleNumber[0]),float(obstacleNumber[1]), 0, float(self.obstaclesOnScreen[0].x - 120), float(self.speed*100)) 114 | else: 115 | input = (float(obstacleNumber[0]),float(obstacleNumber[1]), 100, float(self.obstaclesOnScreen[0].x - 120), float(self.speed*100)) 116 | 117 | for trexId, trex in self.trexs: 118 | if trex.alive: 119 | output = trex.net.activate(input) 120 | trex.predictedAction = (output.index(max(output))) 121 | else: 122 | 123 | input = (float(30),float(30), 0, float(9500), float(self.speed*100)) 124 | 125 | for trexId, trex in self.trexs: 126 | if trex.alive: 127 | output = trex.net.activate(input) 128 | trex.predictedAction = (output.index(max(output))) 129 | 130 | 131 | 132 | # Check if generation of Trexs are extinct 133 | def allDead(self): 134 | for trexId, trex in self.trexs: 135 | if trex.alive: 136 | return False 137 | self.gameOver = True 138 | return True 139 | 140 | 141 | def makeTrexsJump(self): 142 | 143 | for trexId, trex in self.trexs: 144 | if trex.alive: 145 | if not trex.isJumping: 146 | if trex.predictedAction == 1: 147 | trex.isJumping = True 148 | else: 149 | trex.isJumping, trex.direction = trex.jump(trex.isJumping, trex.direction, self.jumpSpeed) 150 | 151 | 152 | 153 | def cleanDeadObstaclesAndPropagate(self): 154 | index = 0 155 | for obstacle in self.obstaclesOnScreen: 156 | if obstacle.x > 30: 157 | break 158 | else: 159 | self.score += 1 160 | index += 1 161 | 162 | self.obstaclesOnScreen = self.obstaclesOnScreen[index : ] 163 | for obstacle in self.obstaclesOnScreen: 164 | obstacle.propagate(self.speed) 165 | 166 | 167 | def increaseGameSpeed(self): 168 | if int(self.score/ 5) != self.lastQuotient: 169 | self.lastQuotient = int(self.score/ 5) 170 | self.speed += 0.15 171 | self.jumpSpeed += 0.05 172 | 173 | 174 | 175 | 176 | def game(self): 177 | 178 | while self.running: 179 | 180 | self.predictActionsForTRexs() 181 | 182 | self.makeTrexsJump() 183 | 184 | 185 | 186 | self.generateGameObstacles() 187 | self.cleanDeadObstaclesAndPropagate() 188 | 189 | if len(self.obstaclesOnScreen) > 0: 190 | self.detectCollisionAndKillTRex() 191 | 192 | if self.allDead(): 193 | return 194 | 195 | self.increaseGameSpeed() 196 | 197 | 198 | 199 | def eval_genomes(genomes, config): 200 | GENERATION_NUMBER += 1 201 | g = Game(genomes, config) 202 | maxScore = 0 203 | for _, trex in genomes: 204 | if trex.score > maxScore: 205 | maxScore = trex.score 206 | 207 | print("Max Score For Generation : " + str(GENERATION_NUMBER) + " is " + str(maxScore) ) 208 | g.game() 209 | 210 | 211 | 212 | local_dir = os.path.dirname(__file__) 213 | config_path = os.path.join(local_dir, 'config') 214 | config = neat.Config(Player, neat.DefaultReproduction, neat.DefaultSpeciesSet, neat.DefaultStagnation, config_path) 215 | 216 | pop = neat.Population(config) 217 | stats = neat.StatisticsReporter() 218 | pop.add_reporter(stats) 219 | 220 | winner = pop.run(eval_genomes, 100) 221 | 222 | # Save winner in a file 223 | with open('bestTRex_better.pickle', 'wb') as handle: 224 | pickle.dump(winner, handle, protocol = pickle.HIGHEST_PROTOCOL) 225 | 226 | -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/Bird.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | class Bird(object): 3 | 4 | def __init__(self, x, y): 5 | self.x = x 6 | self.y = y 7 | self.hitbox = (0, 0, 0, 0) 8 | self.frameCount = 0; 9 | self.images = ["Sprites/GameImages/birdUp.png", "Sprites/GameImages/birdDown.png"] 10 | self.flipState = 0 11 | 12 | def drawCharacter(self, canvas): 13 | if self.frameCount % 50 == 0: 14 | self.flipState = self.flip(self.flipState) 15 | 16 | canvas.blit(pygame.image.load(self.images[self.flipState]), (self.x,self.y)) 17 | self.hitbox = (self.x, self.y, 45, 27) 18 | self.frameCount += 1 19 | 20 | def propagate(self, step): 21 | self.x -= step 22 | 23 | def flip(self, index): 24 | if index == 0: 25 | return 1 26 | else: 27 | return 0 28 | -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/CactusDouble.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | class CactusDouble(object): 3 | 4 | def __init__(self, x, y): 5 | self.x = x 6 | self.y = y 7 | self.hitbox = (0, 0, 0, 0) 8 | self.imageName = "Sprites/GameImages/doubleCactus.png" 9 | self.loadedImage = pygame.image.load(self.imageName) 10 | 11 | def drawCharacter(self, canvas): 12 | canvas.blit(self.loadedImage, (self.x,self.y)) 13 | self.hitbox = (self.x + 3, self.y + 3, 30, 30) 14 | #pygame.draw.rect(canvas, (255, 0, 0), self.hitbox, 2) 15 | 16 | def propagate(self, step): 17 | self.x -= step 18 | -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/CactusSingle.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | class CactusSingle(object): 3 | 4 | def __init__(self, x, y): 5 | self.x = x 6 | self.y = y 7 | self.hitbox = (0, 0, 0, 0) 8 | self.imageName = "Sprites/GameImages/smallCactus.png" 9 | self.loadedImage = pygame.image.load(self.imageName) 10 | 11 | def drawCharacter(self, canvas): 12 | canvas.blit(self.loadedImage, (self.x,self.y)) 13 | self.hitbox = (self.x + 3, self.y + 3, 15, 30) 14 | #pygame.draw.rect(canvas, (255, 0, 0), self.hitbox, 2) 15 | 16 | def propagate(self, step): 17 | self.x -= step 18 | -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/CactusTriple.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | class CactusTriple(object): 3 | 4 | def __init__(self, x, y): 5 | self.x = x 6 | self.y = y 7 | self.hitbox = (self.x, self.y, 51, 30) 8 | self.imageName = "Sprites/GameImages/tripleCactus.png" 9 | self.loadedImage = pygame.image.load(self.imageName) 10 | 11 | 12 | def drawCharacter(self, canvas): 13 | canvas.blit(self.loadedImage, (self.x,self.y)) 14 | self.hitbox = (self.x + 3, self.y + 3, 45, 30) 15 | #pygame.draw.rect(canvas, (255, 0, 0), self.hitbox, 2) 16 | 17 | def propagate(self, step): 18 | self.x -= step 19 | -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/Clouds.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | 3 | class Cloud(object): 4 | 5 | def __init__(self, x, y, regenarate, speed): 6 | self.speed = speed 7 | self.y = y 8 | self.x = regenarate 9 | self.regenarate = regenarate 10 | self.image = pygame.image.load("Sprites/GameImages/Cloud.png") 11 | 12 | def propagate(self): 13 | if self.x < -10: 14 | self.x = self.regenarate 15 | 16 | self.x -= self.speed 17 | 18 | def drawCharacter(self, canvas): 19 | canvas.blit(self.image, (self.x,self.y)) 20 | 21 | 22 | -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/Dashes.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | 3 | class Dashes(object): 4 | 5 | def __init__(self,x ,y): 6 | self.x = x 7 | self.y = y 8 | self.hitbox = (self.x, self.y, 7, 5) 9 | 10 | 11 | def propagate(self, speed): 12 | self.x -= speed 13 | 14 | def drawCharacter(self, screen): 15 | self.hitbox = (self.x, self.y, 2, 1) 16 | pygame.draw.rect(screen, (0, 0, 0), self.hitbox, 2) 17 | 18 | -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/GameImages/Cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/TheGameAndTheGeneticAI/Sprites/GameImages/Cloud.png -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/GameImages/birdDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/TheGameAndTheGeneticAI/Sprites/GameImages/birdDown.png -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/GameImages/birdUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/TheGameAndTheGeneticAI/Sprites/GameImages/birdUp.png -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/GameImages/concave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/TheGameAndTheGeneticAI/Sprites/GameImages/concave.png -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/GameImages/convex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/TheGameAndTheGeneticAI/Sprites/GameImages/convex.png -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/GameImages/doubleCactus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/TheGameAndTheGeneticAI/Sprites/GameImages/doubleCactus.png -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/GameImages/plain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/TheGameAndTheGeneticAI/Sprites/GameImages/plain.png -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/GameImages/smallCactus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/TheGameAndTheGeneticAI/Sprites/GameImages/smallCactus.png -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/GameImages/tRex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/TheGameAndTheGeneticAI/Sprites/GameImages/tRex.png -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/GameImages/tRexDuck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/TheGameAndTheGeneticAI/Sprites/GameImages/tRexDuck.png -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/GameImages/tRexDuckRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/TheGameAndTheGeneticAI/Sprites/GameImages/tRexDuckRight.png -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/GameImages/tRexLeftLeg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/TheGameAndTheGeneticAI/Sprites/GameImages/tRexLeftLeg.png -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/GameImages/tRexRightLeg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/TheGameAndTheGeneticAI/Sprites/GameImages/tRexRightLeg.png -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/GameImages/tripleCactus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/TheGameAndTheGeneticAI/Sprites/GameImages/tripleCactus.png -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/Sprites/Player.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import random 3 | import neat 4 | class Player(neat.DefaultGenome): 5 | def __init__(self, key): 6 | super().__init__(key) 7 | 8 | # Define player properties 9 | self.x = 90 10 | self.y = [500, 500 + 20, 500] 11 | self.hitbox = (self.x, self.y[0], 38, 40) 12 | self.hitboxRectStanding = pygame.Rect(self.x, self.y[0], 41, 40) 13 | self.hitboxRectDucking = pygame.Rect(self.x, self.y[1], 58, 30) 14 | self.frameCount = 0 15 | self.index = 0 16 | self.currentImageIndex = 0 17 | self.score = 0 18 | self.alive = True 19 | self.isJumping = False 20 | self.direction = -1 21 | self.predictedAction = 0 22 | self.net = None 23 | self.imageName = ["Sprites/GameImages/tRexLeftLeg.png", "Sprites/GameImages/tRexDuck.png", "Sprites/GameImages/tRexRightLeg.png", "Sprites/GameImages/tRexDuckRight.png"] 24 | 25 | def configure_new(self, config): 26 | super().configure_new(config) 27 | 28 | 29 | def configure_crossover(self, genome1, genome2, config): 30 | super().configure_crossover(genome1, genome2, config) 31 | 32 | 33 | def mutate(self, config): 34 | super().mutate(config) 35 | 36 | 37 | def distance(self, other, config): 38 | return super().distance(other, config) 39 | 40 | 41 | 42 | def drawCharacter(self, canvas, index): 43 | if self.frameCount % 10 == 0 or index != self.index: 44 | self.currentImageIndex = self.flip(index) 45 | self.frameCount = 0 46 | self.index = index 47 | 48 | if index == 0 or index == 2 or self.isJumping: 49 | self.hitbox = (self.x, self.y[0], 38, 40) 50 | self.hitboxRectStanding = pygame.Rect(self.x, self.y[0], 41, 40) 51 | self.hitboxRect = self.hitboxRectStanding 52 | 53 | else: 54 | self.hitboxRectDucking = pygame.Rect(self.x, self.y[1], 58, 30) 55 | self.hitbox = (self.x, self.y[1], 58, 30) 56 | self.hitboxRect = self.hitboxRectDucking 57 | 58 | self.frameCount += 1 59 | 60 | canvas.blit(pygame.image.load(self.imageName[self.currentImageIndex]), (self.x, self.y[index])) 61 | 62 | #pygame.draw.rect(canvas, (255, 0, 0), self.hitbox, 2) 63 | 64 | def jump(self, jump, direction, jumpSpeed): 65 | self.y[0] += jumpSpeed * direction 66 | if self.y[0] < 410 : 67 | direction = 1 68 | return True, direction 69 | elif self.y[0] >= 500: 70 | direction = -1 71 | return False, direction 72 | else: 73 | return True, direction 74 | 75 | def detectCollision(self, sprite): 76 | return self.hitboxRect.colliderect(sprite.hitbox) 77 | 78 | def flip(self, index): 79 | if index == 0: 80 | if self.currentImageIndex == 0: 81 | return 2 82 | else: 83 | return 0 84 | else: 85 | if self.currentImageIndex == 1: 86 | return 3 87 | else: 88 | return 1 89 | -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/bestTRex_better.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SparshaSaha/Self-Playing-AI-Bot-using-Evolutionary-Algorithms/e67348903cbfae252b672ba6041daeb30a54b353/TheGameAndTheGeneticAI/bestTRex_better.pickle -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/config: -------------------------------------------------------------------------------- 1 | # NEAT configuration for the bit-sequence memory experiment. 2 | 3 | # The `NEAT` section specifies parameters particular to the NEAT algorithm 4 | # or the experiment itself. This is the only required section. 5 | [NEAT] 6 | fitness_criterion = max 7 | fitness_threshold = 10000.0 8 | pop_size = 300 9 | reset_on_extinction = 1 10 | 11 | [Player] 12 | num_inputs = 5 13 | num_hidden = 6 14 | num_outputs = 3 15 | initial_connection = partial_direct 0.5 16 | feed_forward = True 17 | compatibility_disjoint_coefficient = 1.0 18 | compatibility_weight_coefficient = 0.6 19 | conn_add_prob = 0.2 20 | conn_delete_prob = 0.2 21 | node_add_prob = 0.2 22 | node_delete_prob = 0.2 23 | activation_default = sigmoid 24 | activation_options = sigmoid 25 | activation_mutate_rate = 0.0 26 | aggregation_default = sum 27 | aggregation_options = sum 28 | aggregation_mutate_rate = 0.0 29 | bias_init_mean = 0.0 30 | bias_init_stdev = 1.0 31 | bias_replace_rate = 0.1 32 | bias_mutate_rate = 0.7 33 | bias_mutate_power = 0.5 34 | bias_max_value = 30.0 35 | bias_min_value = -30.0 36 | response_init_mean = 1.0 37 | response_init_stdev = 0.0 38 | response_replace_rate = 0.0 39 | response_mutate_rate = 0.0 40 | response_mutate_power = 0.0 41 | response_max_value = 30.0 42 | response_min_value = -30.0 43 | 44 | weight_max_value = 30 45 | weight_min_value = -30 46 | weight_init_mean = 0.0 47 | weight_init_stdev = 1.0 48 | weight_mutate_rate = 0.8 49 | weight_replace_rate = 0.1 50 | weight_mutate_power = 0.5 51 | enabled_default = True 52 | enabled_mutate_rate = 0.01 53 | 54 | [DefaultSpeciesSet] 55 | compatibility_threshold = 3.0 56 | 57 | [DefaultStagnation] 58 | species_fitness_func = max 59 | max_stagnation = 20 60 | 61 | [DefaultReproduction] 62 | elitism = 2 63 | survival_threshold = 0.2 64 | 65 | -------------------------------------------------------------------------------- /TheGameAndTheGeneticAI/visualize.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import copy 4 | import warnings 5 | 6 | import graphviz 7 | import matplotlib.pyplot as plt 8 | import numpy as np 9 | 10 | 11 | def plot_stats(statistics, ylog=False, view=False, filename='avg_fitness.svg'): 12 | """ Plots the population's average and best fitness. """ 13 | if plt is None: 14 | warnings.warn("This display is not available due to a missing optional dependency (matplotlib)") 15 | return 16 | 17 | generation = range(len(statistics.most_fit_genomes)) 18 | best_fitness = [c.fitness for c in statistics.most_fit_genomes] 19 | avg_fitness = np.array(statistics.get_fitness_mean()) 20 | stdev_fitness = np.array(statistics.get_fitness_stdev()) 21 | 22 | plt.plot(generation, avg_fitness, 'b-', label="average") 23 | plt.plot(generation, avg_fitness - stdev_fitness, 'g-.', label="-1 sd") 24 | plt.plot(generation, avg_fitness + stdev_fitness, 'g-.', label="+1 sd") 25 | plt.plot(generation, best_fitness, 'r-', label="best") 26 | 27 | plt.title("Population's average and best fitness") 28 | plt.xlabel("Generations") 29 | plt.ylabel("Fitness") 30 | plt.grid() 31 | plt.legend(loc="best") 32 | if ylog: 33 | plt.gca().set_yscale('symlog') 34 | 35 | plt.savefig(filename) 36 | if view: 37 | plt.show() 38 | 39 | plt.close() 40 | 41 | 42 | def plot_spikes(spikes, view=False, filename=None, title=None): 43 | """ Plots the trains for a single spiking neuron. """ 44 | t_values = [t for t, I, v, u, f in spikes] 45 | v_values = [v for t, I, v, u, f in spikes] 46 | u_values = [u for t, I, v, u, f in spikes] 47 | I_values = [I for t, I, v, u, f in spikes] 48 | f_values = [f for t, I, v, u, f in spikes] 49 | 50 | fig = plt.figure() 51 | plt.subplot(4, 1, 1) 52 | plt.ylabel("Potential (mv)") 53 | plt.xlabel("Time (in ms)") 54 | plt.grid() 55 | plt.plot(t_values, v_values, "g-") 56 | 57 | if title is None: 58 | plt.title("Izhikevich's spiking neuron model") 59 | else: 60 | plt.title("Izhikevich's spiking neuron model ({0!s})".format(title)) 61 | 62 | plt.subplot(4, 1, 2) 63 | plt.ylabel("Fired") 64 | plt.xlabel("Time (in ms)") 65 | plt.grid() 66 | plt.plot(t_values, f_values, "r-") 67 | 68 | plt.subplot(4, 1, 3) 69 | plt.ylabel("Recovery (u)") 70 | plt.xlabel("Time (in ms)") 71 | plt.grid() 72 | plt.plot(t_values, u_values, "r-") 73 | 74 | plt.subplot(4, 1, 4) 75 | plt.ylabel("Current (I)") 76 | plt.xlabel("Time (in ms)") 77 | plt.grid() 78 | plt.plot(t_values, I_values, "r-o") 79 | 80 | if filename is not None: 81 | plt.savefig(filename) 82 | 83 | if view: 84 | plt.show() 85 | plt.close() 86 | fig = None 87 | 88 | return fig 89 | 90 | 91 | def plot_species(statistics, view=False, filename='speciation.svg'): 92 | """ Visualizes speciation throughout evolution. """ 93 | if plt is None: 94 | warnings.warn("This display is not available due to a missing optional dependency (matplotlib)") 95 | return 96 | 97 | species_sizes = statistics.get_species_sizes() 98 | num_generations = len(species_sizes) 99 | curves = np.array(species_sizes).T 100 | 101 | fig, ax = plt.subplots() 102 | ax.stackplot(range(num_generations), *curves) 103 | 104 | plt.title("Speciation") 105 | plt.ylabel("Size per Species") 106 | plt.xlabel("Generations") 107 | 108 | plt.savefig(filename) 109 | 110 | if view: 111 | plt.show() 112 | 113 | plt.close() 114 | 115 | 116 | def draw_net(config, genome, view=False, filename=None, node_names=None, show_disabled=True, prune_unused=False, 117 | node_colors=None, fmt='svg'): 118 | """ Receives a genome and draws a neural network with arbitrary topology. """ 119 | # Attributes for network nodes. 120 | if graphviz is None: 121 | warnings.warn("This display is not available due to a missing optional dependency (graphviz)") 122 | return 123 | 124 | if node_names is None: 125 | node_names = {} 126 | 127 | assert type(node_names) is dict 128 | 129 | if node_colors is None: 130 | node_colors = {} 131 | 132 | assert type(node_colors) is dict 133 | 134 | node_attrs = { 135 | 'shape': 'circle', 136 | 'fontsize': '9', 137 | 'height': '0.2', 138 | 'width': '0.2'} 139 | 140 | dot = graphviz.Digraph(format=fmt, node_attr=node_attrs) 141 | 142 | inputs = set() 143 | for k in config.genome_config.input_keys: 144 | inputs.add(k) 145 | name = node_names.get(k, str(k)) 146 | input_attrs = {'style': 'filled', 147 | 'shape': 'box'} 148 | input_attrs['fillcolor'] = node_colors.get(k, 'lightgray') 149 | dot.node(name, _attributes=input_attrs) 150 | 151 | outputs = set() 152 | for k in config.genome_config.output_keys: 153 | outputs.add(k) 154 | name = node_names.get(k, str(k)) 155 | node_attrs = {'style': 'filled'} 156 | node_attrs['fillcolor'] = node_colors.get(k, 'lightblue') 157 | 158 | dot.node(name, _attributes=node_attrs) 159 | 160 | if prune_unused: 161 | connections = set() 162 | for cg in genome.connections.values(): 163 | if cg.enabled or show_disabled: 164 | connections.add((cg.in_node_id, cg.out_node_id)) 165 | 166 | used_nodes = copy.copy(outputs) 167 | pending = copy.copy(outputs) 168 | while pending: 169 | new_pending = set() 170 | for a, b in connections: 171 | if b in pending and a not in used_nodes: 172 | new_pending.add(a) 173 | used_nodes.add(a) 174 | pending = new_pending 175 | else: 176 | used_nodes = set(genome.nodes.keys()) 177 | 178 | for n in used_nodes: 179 | if n in inputs or n in outputs: 180 | continue 181 | 182 | attrs = {'style': 'filled', 183 | 'fillcolor': node_colors.get(n, 'white')} 184 | dot.node(str(n), _attributes=attrs) 185 | 186 | for cg in genome.connections.values(): 187 | if cg.enabled or show_disabled: 188 | #if cg.input not in used_nodes or cg.output not in used_nodes: 189 | # continue 190 | input, output = cg.key 191 | a = node_names.get(input, str(input)) 192 | b = node_names.get(output, str(output)) 193 | style = 'solid' if cg.enabled else 'dotted' 194 | color = 'green' if cg.weight > 0 else 'red' 195 | width = str(0.1 + abs(cg.weight / 5.0)) 196 | dot.edge(a, b, _attributes={'style': style, 'color': color, 'penwidth': width}) 197 | 198 | dot.render(filename, view=view) 199 | 200 | return dot --------------------------------------------------------------------------------