Amazing-Python-Scripts
289 строк · 7.6 Кб
1# AI-driven AI Flappy Bird
2import pygame3import neat4import os5import random6
7# Game window size
8WINDOW_WIDTH = 5009WINDOW_HEIGHT = 80010
11# Bird images
12BIRD_IMAGES = [13pygame.transform.scale2x(pygame.image.load(14os.path.join("images", "bird1.png"))),15pygame.transform.scale2x(pygame.image.load(16os.path.join("images", "bird2.png"))),17pygame.transform.scale2x(pygame.image.load(18os.path.join("images", "bird3.png")))19]
20
21# Pipe image
22PIPE_IMAGE = pygame.transform.scale2x(23pygame.image.load(os.path.join("images", "pipe.png")))24
25# Base image
26BASE_IMAGE = pygame.transform.scale2x(27pygame.image.load(os.path.join("images", "base.png")))28
29# Background image
30BG_IMAGE = pygame.transform.scale2x(31pygame.image.load(os.path.join("images", "bg.png")))32
33
34class Bird:35IMAGES = BIRD_IMAGES36MAX_ROTATION = 2537ROTATION_VELOCITY = 2038ANIMATION_TIME = 539
40def __init__(self, x, y):41self.x = x42self.y = y43self.tilt = 044self.tick_count = 045self.velocity = 046self.height = self.y47self.image_count = 048self.image = self.IMAGES[0]49
50def jump(self):51self.velocity = -10.552self.tick_count = 053self.height = self.y54
55def move(self):56self.tick_count += 157displacement = self.velocity * self.tick_count + 1.5 * self.tick_count ** 258
59if displacement >= 16:60displacement = 1661
62if displacement < 0:63displacement -= 264
65self.y = self.y + displacement66
67if displacement < 0 or self.y < self.height + 50:68if self.tilt < self.MAX_ROTATION:69self.tilt = self.MAX_ROTATION70else:71if self.tilt > -90:72self.tilt -= self.ROTATION_VELOCITY73
74def draw(self, win):75self.image_count += 176
77if self.image_count < self.ANIMATION_TIME:78self.image = self.IMAGES[0]79elif self.image_count < self.ANIMATION_TIME * 2:80self.image = self.IMAGES[1]81elif self.image_count < self.ANIMATION_TIME * 3:82self.image = self.IMAGES[2]83elif self.image_count < self.ANIMATION_TIME * 4:84self.image = self.IMAGES[1]85elif self.image_count == self.ANIMATION_TIME * 4 + 1:86self.image = self.IMAGES[0]87self.image_count = 088
89if self.tilt <= -80:90self.image = self.IMAGES[1]91self.image_count = self.ANIMATION_TIME * 292
93rotated_image = pygame.transform.rotate(self.image, self.tilt)94new_rect = rotated_image.get_rect(95center=self.image.get_rect(topleft=(self.x, self.y)).center)96win.blit(rotated_image, new_rect.topleft)97
98def get_mask(self):99return pygame.mask.from_surface(self.image)100
101
102class Pipe:103GAP = 200104VEL = 5105
106def __init__(self, x):107self.x = x108self.height = 0109
110self.top = 0111self.bottom = 0112self.PIPE_TOP = pygame.transform.flip(PIPE_IMAGE, False, True)113self.PIPE_BOTTOM = PIPE_IMAGE114
115self.passed = False116self.set_height()117
118def set_height(self):119self.height = random.randrange(50, 450)120self.top = self.height - self.PIPE_TOP.get_height()121self.bottom = self.height + self.GAP122
123def move(self):124self.x -= self.VEL125
126def draw(self, win):127win.blit(self.PIPE_TOP, (self.x, self.top))128win.blit(self.PIPE_BOTTOM, (self.x, self.bottom))129
130def collide(self, bird):131bird_mask = bird.get_mask()132top_mask = pygame.mask.from_surface(self.PIPE_TOP)133bottom_mask = pygame.mask.from_surface(self.PIPE_BOTTOM)134
135top_offset = (self.x - bird.x, self.top - round(bird.y))136bottom_offset = (self.x - bird.x, self.bottom - round(bird.y))137
138t_point = bird_mask.overlap(top_mask, top_offset)139b_point = bird_mask.overlap(bottom_mask, bottom_offset)140
141if t_point or b_point:142return True143
144return False145
146
147class Base:148VEL = 5149WIDTH = BASE_IMAGE.get_width()150IMG = BASE_IMAGE151
152def __init__(self, y):153self.y = y154self.x1 = 0155self.x2 = self.WIDTH156
157def move(self):158self.x1 -= self.VEL159self.x2 -= self.VEL160
161if self.x1 + self.WIDTH < 0:162self.x1 = self.x2 + self.WIDTH163
164if self.x2 + self.WIDTH < 0:165self.x2 = self.x1 + self.WIDTH166
167def draw(self, win):168win.blit(self.IMG, (self.x1, self.y))169win.blit(self.IMG, (self.x2, self.y))170
171
172def draw_window(win, birds, pipes, base, score):173win.blit(BG_IMAGE, (0, 0))174for pipe in pipes:175pipe.draw(win)176
177text = STAT_FONT.render("Score: " + str(score), 1, (255, 255, 255))178win.blit(text, (WINDOW_WIDTH - 10 - text.get_width(), 10))179
180base.draw(win)181for bird in birds:182bird.draw(win)183pygame.display.update()184
185
186def main(genomes, config):187nets = []188ge = []189birds = []190
191for _, g in genomes:192net = neat.nn.FeedForwardNetwork.create(g, config)193nets.append(net)194birds.append(Bird(230, 350))195g.fitness = 0196ge.append(g)197
198base = Base(730)199pipes = [Pipe(700)]200win = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))201clock = pygame.time.Clock()202score = 0203
204run = True205while run:206clock.tick(30)207for event in pygame.event.get():208if event.type == pygame.QUIT:209run = False210pygame.quit()211quit()212
213pipe_ind = 0214if len(birds) > 0:215if len(pipes) > 1 and birds[0].x > pipes[0].x + pipes[0].PIPE_TOP.get_width():216pipe_ind = 1217else:218run = False219break220
221for x, bird in enumerate(birds):222bird.move()223ge[x].fitness += 0.1224
225output = nets[x].activate((bird.y, abs(226bird.y - pipes[pipe_ind].height), abs(bird.y - pipes[pipe_ind].bottom)))227
228if output[0] > 0.5:229bird.jump()230
231add_pipe = False232remove_pipes = []233for pipe in pipes:234for x, bird in enumerate(birds):235if pipe.collide(bird):236ge[x].fitness -= 1237birds.pop(x)238nets.pop(x)239ge.pop(x)240
241if not pipe.passed and pipe.x < bird.x:242pipe.passed = True243add_pipe = True244
245if pipe.x + pipe.PIPE_TOP.get_width() < 0:246remove_pipes.append(pipe)247
248pipe.move()249
250if add_pipe:251score += 1252for g in ge:253g.fitness += 5254pipes.append(Pipe(700))255
256for pipe in remove_pipes:257pipes.remove(pipe)258
259for x, bird in enumerate(birds):260if bird.y + bird.image.get_height() >= 730 or bird.y < 0:261birds.pop(x)262nets.pop(x)263ge.pop(x)264
265base.move()266draw_window(win, birds, pipes, base, score)267
268
269def run_neat():270local_dir = os.path.dirname(__file__)271config_path = os.path.join(local_dir, "config-feedforward.txt")272config = neat.Config(neat.DefaultGenome, neat.DefaultReproduction,273neat.DefaultSpeciesSet, neat.DefaultStagnation,274config_path)275
276population = neat.Population(config)277population.add_reporter(neat.StdOutReporter(True))278stats = neat.StatisticsReporter()279population.add_reporter(stats)280
281winner = population.run(main, 50)282
283print("\nBest genome:\n{!s}".format(winner))284
285
286if __name__ == "__main__":287pygame.init()288STAT_FONT = pygame.font.SysFont("comicsans", 50)289run_neat()290