-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
161 lines (133 loc) · 6.54 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import os
import random
import numpy as np
import pyglet
from icecream import ic
from Bird import Bird
from Ecosystem import Ecosystem
from NeuralNet import NeuralNet
from settings import *
pyglet.resource.path = ["resources"]
pyglet.resource.reindex()
main_batch = pyglet.graphics.Batch()
# groups, to make sure the background is drawn before forground
background = pyglet.graphics.OrderedGroup(0)
foreground = pyglet.graphics.OrderedGroup(1)
# TODO: make this game resizable
# TODO: add an icon to the window?
window = pyglet.window.Window(WINDOW_WIDTH, WINDOW_HEIGHT, resizable=False)
updates_per_frame_label = pyglet.text.Label(f"updates per frame: {updates_per_frame}", font_size=20, color=(0, 0, 0, 255))
max_score_label = pyglet.text.Label(font_size=20, x=window.width // 2, y=window.height - 50, color=(0, 0, 0, 255))
background_image = pyglet.resource.image(BACKGROUND_IMG)
# TODO: is it a must to create a sprite for images...?
background_image_sprite = pyglet.sprite.Sprite(img=background_image, batch=main_batch, group=background)
bottom_pipes_image = pyglet.resource.image(PIPE_IMG)
top_pipes_image = pyglet.resource.image(PIPE_IMG, flip_y=True)
bottom_pipes = []
top_pipes = []
for i in range(NUM_PIPES_PER_FRAME + 1):
posx = FIRST_PIPE_OFFSET + (i * (window.width // NUM_PIPES_PER_FRAME))
bottom_pipe = pyglet.sprite.Sprite(img=bottom_pipes_image, x=posx, batch=main_batch, group=foreground)
bottom_pipe.y = random.randint(-bottom_pipe.height, window.height - (bottom_pipe.height + PIPE_VERT_DIST))
bottom_pipe.scale_x = PIPE_IMG_SCALE_X
bottom_pipes.append(bottom_pipe)
for i in range(NUM_PIPES_PER_FRAME + 1):
posx = FIRST_PIPE_OFFSET + (i * (window.width // NUM_PIPES_PER_FRAME))
top_pipe = pyglet.sprite.Sprite(img=top_pipes_image, x=posx, batch=main_batch, group=foreground)
top_pipe.y = bottom_pipes[i].y + PIPE_VERT_DIST + (top_pipe.height * 2)
top_pipe.scale_x = PIPE_IMG_SCALE_X
top_pipes.append(top_pipe)
first_pipe_idx = 0
ecosystem = Ecosystem(
population_size=30,
num_generations=30,
holdout=10,
bird_spawner_func=lambda: Bird(
brain=NeuralNet([4, 16, 16, 1]),
bird_img=pyglet.resource.image(BIRD_IMG),
batch=main_batch,
group=foreground,
x_offset=BIRD_X_OFFSET,
default_y=window.height // 2
)
)
'''
# event handlers ###################################################################################
'''
@window.event
def on_key_press(symbol, modifier):
global updates_per_frame
if symbol == pyglet.window.key.UP:
updates_per_frame += 1
updates_per_frame_label.text = f"updates per frame: {updates_per_frame}"
if symbol == pyglet.window.key.DOWN:
updates_per_frame = updates_per_frame - 1 if updates_per_frame > 1 else 1
updates_per_frame_label.text = f"updates per frame: {updates_per_frame}"
def update(dt):
# TODO: probably, we shouldn't use dt, for neural nets...?
# TODO: update order...? does it matter? rn it's, enemy_update->decision->player_update
global first_pipe_idx
for _ in range(updates_per_frame):
# TODO: do these for bottom and top pipes in one loop?
for pipe in bottom_pipes:
if pipe.x <= -pipe.width:
first_pipe_idx -= 1 # TODO: do this line once only... (fix this, by using 1 loop...)
new_pipe = pipe
new_pipe.x = bottom_pipes[-1].x + window.width // NUM_PIPES_PER_FRAME
bottom_pipes.remove(pipe)
bottom_pipes.append(new_pipe)
pipe.x -= PIPE_MOVEMENT_SPEED
for pipe in top_pipes:
if pipe.x <= -pipe.width:
new_pipe = pipe
new_pipe.x = top_pipes[-1].x + window.width // NUM_PIPES_PER_FRAME
top_pipes.remove(pipe)
top_pipes.append(new_pipe)
pipe.x -= PIPE_MOVEMENT_SPEED
all_birds_died = True
for bird in ecosystem.birds:
if not bird.died:
all_birds_died = False
# first top pipe up corner coord
coord_top_pipe = top_pipes[first_pipe_idx].y - top_pipes[first_pipe_idx].height
coord_bottom_pipe = coord_top_pipe - PIPE_VERT_DIST # same for bottom pipe...
x_dist_to_pipe = bird.get_x_dist(top_pipes[first_pipe_idx].x)
y_dist_to_top_pipe = bird.get_y_dist(coord_top_pipe)
y_dist_to_bottom_pipe = bird.get_y_dist(coord_bottom_pipe)
bird.take_decision(y_dist_to_top_pipe=y_dist_to_top_pipe,
y_dist_to_bottom_pipe=y_dist_to_bottom_pipe,
x_dist_to_pipe=x_dist_to_pipe)
bird.update_movement()
if x_dist_to_pipe <= 0:
if y_dist_to_top_pipe <= 0 or y_dist_to_bottom_pipe >= 0:
bird.die()
if x_dist_to_pipe <= -top_pipes[first_pipe_idx].width:
bird.score += 1
first_pipe_idx = first_pipe_idx + 1 if first_pipe_idx + 1 < len(top_pipes) else 0
elif bird.get_y_dist(window.height) <= 0 or bird.get_y_dist(0) >= 0:
bird.die()
# TODO: this is being calculated twice...(here and in ecosystem class)
max_score_label.text = f"max score: {str(np.max([bird.score for bird in ecosystem.birds]))} | " \
f"gen id: {ecosystem.generation_id}"
if all_birds_died:
ecosystem.new_generation()
# reset the pipes position...
first_pipe_idx = 0
for i in range(NUM_PIPES_PER_FRAME + 1):
posx = FIRST_PIPE_OFFSET + (i * (window.width // NUM_PIPES_PER_FRAME))
bottom_pipes[i].y = random.randint(-bottom_pipe.height, window.height - (bottom_pipe.height + PIPE_VERT_DIST))
bottom_pipes[i].x = posx
for i in range(NUM_PIPES_PER_FRAME + 1):
posx = FIRST_PIPE_OFFSET + (i * (window.width // NUM_PIPES_PER_FRAME))
top_pipes[i].x = posx
top_pipes[i].y = bottom_pipes[i].y + PIPE_VERT_DIST + (top_pipes[i].height * 2)
@window.event
def on_draw():
window.clear()
main_batch.draw()
updates_per_frame_label.draw() # drawing at last, so that it's drawn above everything... (TODO: better way?)
max_score_label.draw() # same reason as above...
if __name__ == "__main__":
# pyglet.clock.schedule_interval(update, 1/(MONITOR_REFRESH_RATE * updates_per_frame))
pyglet.clock.schedule_interval(update, 1 / MONITOR_REFRESH_RATE)
pyglet.app.run()