Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit fc0e1ab

Browse files
Flappy_bird.py
1 parent c0b68a7 commit fc0e1ab

File tree

1 file changed

+282
-0
lines changed

1 file changed

+282
-0
lines changed
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
#AI-driven AI Flappy Bird
2+
import pygame
3+
import neat
4+
import os
5+
import random
6+
7+
# Game window size
8+
WINDOW_WIDTH = 500
9+
WINDOW_HEIGHT = 800
10+
11+
# Bird images
12+
BIRD_IMAGES = [
13+
pygame.transform.scale2x(pygame.image.load(os.path.join("images", "bird1.png"))),
14+
pygame.transform.scale2x(pygame.image.load(os.path.join("images", "bird2.png"))),
15+
pygame.transform.scale2x(pygame.image.load(os.path.join("images", "bird3.png")))
16+
]
17+
18+
# Pipe image
19+
PIPE_IMAGE = pygame.transform.scale2x(pygame.image.load(os.path.join("images", "pipe.png")))
20+
21+
# Base image
22+
BASE_IMAGE = pygame.transform.scale2x(pygame.image.load(os.path.join("images", "base.png")))
23+
24+
# Background image
25+
BG_IMAGE = pygame.transform.scale2x(pygame.image.load(os.path.join("images", "bg.png")))
26+
27+
28+
class Bird:
29+
IMAGES = BIRD_IMAGES
30+
MAX_ROTATION = 25
31+
ROTATION_VELOCITY = 20
32+
ANIMATION_TIME = 5
33+
34+
def __init__(self, x, y):
35+
self.x = x
36+
self.y = y
37+
self.tilt = 0
38+
self.tick_count = 0
39+
self.velocity = 0
40+
self.height = self.y
41+
self.image_count = 0
42+
self.image = self.IMAGES[0]
43+
44+
def jump(self):
45+
self.velocity = -10.5
46+
self.tick_count = 0
47+
self.height = self.y
48+
49+
def move(self):
50+
self.tick_count += 1
51+
displacement = self.velocity * self.tick_count + 1.5 * self.tick_count ** 2
52+
53+
if displacement >= 16:
54+
displacement = 16
55+
56+
if displacement < 0:
57+
displacement -= 2
58+
59+
self.y = self.y + displacement
60+
61+
if displacement < 0 or self.y < self.height + 50:
62+
if self.tilt < self.MAX_ROTATION:
63+
self.tilt = self.MAX_ROTATION
64+
else:
65+
if self.tilt > -90:
66+
self.tilt -= self.ROTATION_VELOCITY
67+
68+
def draw(self, win):
69+
self.image_count += 1
70+
71+
if self.image_count < self.ANIMATION_TIME:
72+
self.image = self.IMAGES[0]
73+
elif self.image_count < self.ANIMATION_TIME * 2:
74+
self.image = self.IMAGES[1]
75+
elif self.image_count < self.ANIMATION_TIME * 3:
76+
self.image = self.IMAGES[2]
77+
elif self.image_count < self.ANIMATION_TIME * 4:
78+
self.image = self.IMAGES[1]
79+
elif self.image_count == self.ANIMATION_TIME * 4 + 1:
80+
self.image = self.IMAGES[0]
81+
self.image_count = 0
82+
83+
if self.tilt <= -80:
84+
self.image = self.IMAGES[1]
85+
self.image_count = self.ANIMATION_TIME * 2
86+
87+
rotated_image = pygame.transform.rotate(self.image, self.tilt)
88+
new_rect = rotated_image.get_rect(center=self.image.get_rect(topleft=(self.x, self.y)).center)
89+
win.blit(rotated_image, new_rect.topleft)
90+
91+
def get_mask(self):
92+
return pygame.mask.from_surface(self.image)
93+
94+
95+
class Pipe:
96+
GAP = 200
97+
VEL = 5
98+
99+
def __init__(self, x):
100+
self.x = x
101+
self.height = 0
102+
103+
self.top = 0
104+
self.bottom = 0
105+
self.PIPE_TOP = pygame.transform.flip(PIPE_IMAGE, False, True)
106+
self.PIPE_BOTTOM = PIPE_IMAGE
107+
108+
self.passed = False
109+
self.set_height()
110+
111+
def set_height(self):
112+
self.height = random.randrange(50, 450)
113+
self.top = self.height - self.PIPE_TOP.get_height()
114+
self.bottom = self.height + self.GAP
115+
116+
def move(self):
117+
self.x -= self.VEL
118+
119+
def draw(self, win):
120+
win.blit(self.PIPE_TOP, (self.x, self.top))
121+
win.blit(self.PIPE_BOTTOM, (self.x, self.bottom))
122+
123+
def collide(self, bird):
124+
bird_mask = bird.get_mask()
125+
top_mask = pygame.mask.from_surface(self.PIPE_TOP)
126+
bottom_mask = pygame.mask.from_surface(self.PIPE_BOTTOM)
127+
128+
top_offset = (self.x - bird.x, self.top - round(bird.y))
129+
bottom_offset = (self.x - bird.x, self.bottom - round(bird.y))
130+
131+
t_point = bird_mask.overlap(top_mask, top_offset)
132+
b_point = bird_mask.overlap(bottom_mask, bottom_offset)
133+
134+
if t_point or b_point:
135+
return True
136+
137+
return False
138+
139+
140+
class Base:
141+
VEL = 5
142+
WIDTH = BASE_IMAGE.get_width()
143+
IMG = BASE_IMAGE
144+
145+
def __init__(self, y):
146+
self.y = y
147+
self.x1 = 0
148+
self.x2 = self.WIDTH
149+
150+
def move(self):
151+
self.x1 -= self.VEL
152+
self.x2 -= self.VEL
153+
154+
if self.x1 + self.WIDTH < 0:
155+
self.x1 = self.x2 + self.WIDTH
156+
157+
if self.x2 + self.WIDTH < 0:
158+
self.x2 = self.x1 + self.WIDTH
159+
160+
def draw(self, win):
161+
win.blit(self.IMG, (self.x1, self.y))
162+
win.blit(self.IMG, (self.x2, self.y))
163+
164+
165+
def draw_window(win, birds, pipes, base, score):
166+
win.blit(BG_IMAGE, (0, 0))
167+
for pipe in pipes:
168+
pipe.draw(win)
169+
170+
text = STAT_FONT.render("Score: " + str(score), 1, (255, 255, 255))
171+
win.blit(text, (WINDOW_WIDTH - 10 - text.get_width(), 10))
172+
173+
base.draw(win)
174+
for bird in birds:
175+
bird.draw(win)
176+
pygame.display.update()
177+
178+
179+
def main(genomes, config):
180+
nets = []
181+
ge = []
182+
birds = []
183+
184+
for _, g in genomes:
185+
net = neat.nn.FeedForwardNetwork.create(g, config)
186+
nets.append(net)
187+
birds.append(Bird(230, 350))
188+
g.fitness = 0
189+
ge.append(g)
190+
191+
base = Base(730)
192+
pipes = [Pipe(700)]
193+
win = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
194+
clock = pygame.time.Clock()
195+
score = 0
196+
197+
run = True
198+
while run:
199+
clock.tick(30)
200+
for event in pygame.event.get():
201+
if event.type == pygame.QUIT:
202+
run = False
203+
pygame.quit()
204+
quit()
205+
206+
pipe_ind = 0
207+
if len(birds) > 0:
208+
if len(pipes) > 1 and birds[0].x > pipes[0].x + pipes[0].PIPE_TOP.get_width():
209+
pipe_ind = 1
210+
else:
211+
run = False
212+
break
213+
214+
for x, bird in enumerate(birds):
215+
bird.move()
216+
ge[x].fitness += 0.1
217+
218+
output = nets[x].activate((bird.y, abs(bird.y - pipes[pipe_ind].height), abs(bird.y - pipes[pipe_ind].bottom)))
219+
220+
if output[0] > 0.5:
221+
bird.jump()
222+
223+
add_pipe = False
224+
remove_pipes = []
225+
for pipe in pipes:
226+
for x, bird in enumerate(birds):
227+
if pipe.collide(bird):
228+
ge[x].fitness -= 1
229+
birds.pop(x)
230+
nets.pop(x)
231+
ge.pop(x)
232+
233+
if not pipe.passed and pipe.x < bird.x:
234+
pipe.passed = True
235+
add_pipe = True
236+
237+
if pipe.x + pipe.PIPE_TOP.get_width() < 0:
238+
remove_pipes.append(pipe)
239+
240+
pipe.move()
241+
242+
if add_pipe:
243+
score += 1
244+
for g in ge:
245+
g.fitness += 5
246+
pipes.append(Pipe(700))
247+
248+
for pipe in remove_pipes:
249+
pipes.remove(pipe)
250+
251+
for x, bird in enumerate(birds):
252+
if bird.y + bird.image.get_height() >= 730 or bird.y < 0:
253+
birds.pop(x)
254+
nets.pop(x)
255+
ge.pop(x)
256+
257+
base.move()
258+
draw_window(win, birds, pipes, base, score)
259+
260+
261+
def run_neat():
262+
local_dir = os.path.dirname(__file__)
263+
config_path = os.path.join(local_dir, "config-feedforward.txt")
264+
config = neat.Config(neat.DefaultGenome, neat.DefaultReproduction,
265+
neat.DefaultSpeciesSet, neat.DefaultStagnation,
266+
config_path)
267+
268+
population = neat.Population(config)
269+
population.add_reporter(neat.StdOutReporter(True))
270+
stats = neat.StatisticsReporter()
271+
population.add_reporter(stats)
272+
273+
winner = population.run(main, 50)
274+
275+
print("\nBest genome:\n{!s}".format(winner))
276+
277+
278+
if __name__ == "__main__":
279+
pygame.init()
280+
STAT_FONT = pygame.font.SysFont("comicsans", 50)
281+
run_neat()
282+

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /