Amazing-Python-Scripts

Форк
0
289 строк · 7.6 Кб
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(
14
        os.path.join("images", "bird1.png"))),
15
    pygame.transform.scale2x(pygame.image.load(
16
        os.path.join("images", "bird2.png"))),
17
    pygame.transform.scale2x(pygame.image.load(
18
        os.path.join("images", "bird3.png")))
19
]
20

21
# Pipe image
22
PIPE_IMAGE = pygame.transform.scale2x(
23
    pygame.image.load(os.path.join("images", "pipe.png")))
24

25
# Base image
26
BASE_IMAGE = pygame.transform.scale2x(
27
    pygame.image.load(os.path.join("images", "base.png")))
28

29
# Background image
30
BG_IMAGE = pygame.transform.scale2x(
31
    pygame.image.load(os.path.join("images", "bg.png")))
32

33

34
class Bird:
35
    IMAGES = BIRD_IMAGES
36
    MAX_ROTATION = 25
37
    ROTATION_VELOCITY = 20
38
    ANIMATION_TIME = 5
39

40
    def __init__(self, x, y):
41
        self.x = x
42
        self.y = y
43
        self.tilt = 0
44
        self.tick_count = 0
45
        self.velocity = 0
46
        self.height = self.y
47
        self.image_count = 0
48
        self.image = self.IMAGES[0]
49

50
    def jump(self):
51
        self.velocity = -10.5
52
        self.tick_count = 0
53
        self.height = self.y
54

55
    def move(self):
56
        self.tick_count += 1
57
        displacement = self.velocity * self.tick_count + 1.5 * self.tick_count ** 2
58

59
        if displacement >= 16:
60
            displacement = 16
61

62
        if displacement < 0:
63
            displacement -= 2
64

65
        self.y = self.y + displacement
66

67
        if displacement < 0 or self.y < self.height + 50:
68
            if self.tilt < self.MAX_ROTATION:
69
                self.tilt = self.MAX_ROTATION
70
        else:
71
            if self.tilt > -90:
72
                self.tilt -= self.ROTATION_VELOCITY
73

74
    def draw(self, win):
75
        self.image_count += 1
76

77
        if self.image_count < self.ANIMATION_TIME:
78
            self.image = self.IMAGES[0]
79
        elif self.image_count < self.ANIMATION_TIME * 2:
80
            self.image = self.IMAGES[1]
81
        elif self.image_count < self.ANIMATION_TIME * 3:
82
            self.image = self.IMAGES[2]
83
        elif self.image_count < self.ANIMATION_TIME * 4:
84
            self.image = self.IMAGES[1]
85
        elif self.image_count == self.ANIMATION_TIME * 4 + 1:
86
            self.image = self.IMAGES[0]
87
            self.image_count = 0
88

89
        if self.tilt <= -80:
90
            self.image = self.IMAGES[1]
91
            self.image_count = self.ANIMATION_TIME * 2
92

93
        rotated_image = pygame.transform.rotate(self.image, self.tilt)
94
        new_rect = rotated_image.get_rect(
95
            center=self.image.get_rect(topleft=(self.x, self.y)).center)
96
        win.blit(rotated_image, new_rect.topleft)
97

98
    def get_mask(self):
99
        return pygame.mask.from_surface(self.image)
100

101

102
class Pipe:
103
    GAP = 200
104
    VEL = 5
105

106
    def __init__(self, x):
107
        self.x = x
108
        self.height = 0
109

110
        self.top = 0
111
        self.bottom = 0
112
        self.PIPE_TOP = pygame.transform.flip(PIPE_IMAGE, False, True)
113
        self.PIPE_BOTTOM = PIPE_IMAGE
114

115
        self.passed = False
116
        self.set_height()
117

118
    def set_height(self):
119
        self.height = random.randrange(50, 450)
120
        self.top = self.height - self.PIPE_TOP.get_height()
121
        self.bottom = self.height + self.GAP
122

123
    def move(self):
124
        self.x -= self.VEL
125

126
    def draw(self, win):
127
        win.blit(self.PIPE_TOP, (self.x, self.top))
128
        win.blit(self.PIPE_BOTTOM, (self.x, self.bottom))
129

130
    def collide(self, bird):
131
        bird_mask = bird.get_mask()
132
        top_mask = pygame.mask.from_surface(self.PIPE_TOP)
133
        bottom_mask = pygame.mask.from_surface(self.PIPE_BOTTOM)
134

135
        top_offset = (self.x - bird.x, self.top - round(bird.y))
136
        bottom_offset = (self.x - bird.x, self.bottom - round(bird.y))
137

138
        t_point = bird_mask.overlap(top_mask, top_offset)
139
        b_point = bird_mask.overlap(bottom_mask, bottom_offset)
140

141
        if t_point or b_point:
142
            return True
143

144
        return False
145

146

147
class Base:
148
    VEL = 5
149
    WIDTH = BASE_IMAGE.get_width()
150
    IMG = BASE_IMAGE
151

152
    def __init__(self, y):
153
        self.y = y
154
        self.x1 = 0
155
        self.x2 = self.WIDTH
156

157
    def move(self):
158
        self.x1 -= self.VEL
159
        self.x2 -= self.VEL
160

161
        if self.x1 + self.WIDTH < 0:
162
            self.x1 = self.x2 + self.WIDTH
163

164
        if self.x2 + self.WIDTH < 0:
165
            self.x2 = self.x1 + self.WIDTH
166

167
    def draw(self, win):
168
        win.blit(self.IMG, (self.x1, self.y))
169
        win.blit(self.IMG, (self.x2, self.y))
170

171

172
def draw_window(win, birds, pipes, base, score):
173
    win.blit(BG_IMAGE, (0, 0))
174
    for pipe in pipes:
175
        pipe.draw(win)
176

177
    text = STAT_FONT.render("Score: " + str(score), 1, (255, 255, 255))
178
    win.blit(text, (WINDOW_WIDTH - 10 - text.get_width(), 10))
179

180
    base.draw(win)
181
    for bird in birds:
182
        bird.draw(win)
183
    pygame.display.update()
184

185

186
def main(genomes, config):
187
    nets = []
188
    ge = []
189
    birds = []
190

191
    for _, g in genomes:
192
        net = neat.nn.FeedForwardNetwork.create(g, config)
193
        nets.append(net)
194
        birds.append(Bird(230, 350))
195
        g.fitness = 0
196
        ge.append(g)
197

198
    base = Base(730)
199
    pipes = [Pipe(700)]
200
    win = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
201
    clock = pygame.time.Clock()
202
    score = 0
203

204
    run = True
205
    while run:
206
        clock.tick(30)
207
        for event in pygame.event.get():
208
            if event.type == pygame.QUIT:
209
                run = False
210
                pygame.quit()
211
                quit()
212

213
        pipe_ind = 0
214
        if len(birds) > 0:
215
            if len(pipes) > 1 and birds[0].x > pipes[0].x + pipes[0].PIPE_TOP.get_width():
216
                pipe_ind = 1
217
        else:
218
            run = False
219
            break
220

221
        for x, bird in enumerate(birds):
222
            bird.move()
223
            ge[x].fitness += 0.1
224

225
            output = nets[x].activate((bird.y, abs(
226
                bird.y - pipes[pipe_ind].height), abs(bird.y - pipes[pipe_ind].bottom)))
227

228
            if output[0] > 0.5:
229
                bird.jump()
230

231
        add_pipe = False
232
        remove_pipes = []
233
        for pipe in pipes:
234
            for x, bird in enumerate(birds):
235
                if pipe.collide(bird):
236
                    ge[x].fitness -= 1
237
                    birds.pop(x)
238
                    nets.pop(x)
239
                    ge.pop(x)
240

241
                if not pipe.passed and pipe.x < bird.x:
242
                    pipe.passed = True
243
                    add_pipe = True
244

245
            if pipe.x + pipe.PIPE_TOP.get_width() < 0:
246
                remove_pipes.append(pipe)
247

248
            pipe.move()
249

250
        if add_pipe:
251
            score += 1
252
            for g in ge:
253
                g.fitness += 5
254
            pipes.append(Pipe(700))
255

256
        for pipe in remove_pipes:
257
            pipes.remove(pipe)
258

259
        for x, bird in enumerate(birds):
260
            if bird.y + bird.image.get_height() >= 730 or bird.y < 0:
261
                birds.pop(x)
262
                nets.pop(x)
263
                ge.pop(x)
264

265
        base.move()
266
        draw_window(win, birds, pipes, base, score)
267

268

269
def run_neat():
270
    local_dir = os.path.dirname(__file__)
271
    config_path = os.path.join(local_dir, "config-feedforward.txt")
272
    config = neat.Config(neat.DefaultGenome, neat.DefaultReproduction,
273
                         neat.DefaultSpeciesSet, neat.DefaultStagnation,
274
                         config_path)
275

276
    population = neat.Population(config)
277
    population.add_reporter(neat.StdOutReporter(True))
278
    stats = neat.StatisticsReporter()
279
    population.add_reporter(stats)
280

281
    winner = population.run(main, 50)
282

283
    print("\nBest genome:\n{!s}".format(winner))
284

285

286
if __name__ == "__main__":
287
    pygame.init()
288
    STAT_FONT = pygame.font.SysFont("comicsans", 50)
289
    run_neat()
290

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.