1+ #include < string>
2+ #include < sstream>
3+ #include < iomanip>
4+
5+ #include < cmath>
6+
7+ #include < SFML/Graphics/Rect.hpp>
8+ #include < SFML/Graphics/View.hpp>
9+ #include < SFML/System/Clock.hpp>
10+ #include < SFML/System/Time.hpp>
11+ #include < SFML/Window/Event.hpp>
12+
13+ #include " game.hpp"
14+
15+ std::string lpad (std::string const &raw, char fillchar, size_t width)
16+ {
17+ static std::ostringstream ss;
18+ ss << std::right << std::setfill (fillchar) << std::setw (width) << raw;
19+ std::string padded = ss.str ();
20+ ss.str (std::string ());
21+ ss.clear ();
22+ return padded;
23+ }
24+
25+ Game::Game (std::vector<std::string> const &args, size_t width, size_t height, std::string const &title, size_t fps) :
26+ window(sf::VideoMode(width, height), title),
27+ game_area({static_cast <float >(width), static_cast <float >(height)}),
28+ background(19 , 235 , 220 ),
29+ timePerFrame(sf::seconds(1 .f / static_cast <float >(fps))),
30+ captured_frames(0 ),
31+ capture_frames(false ),
32+ game_statistics(std::make_shared<GameStats>()),
33+ player_bird(),
34+ birds(),
35+ pipes(),
36+ genetic_algorithm()
37+ {
38+ // check CLI argument if there is a capture flag
39+ if (args.size () == 2 ) {
40+ if (std::string (args[1 ]) == " capture" ) {
41+ std::cerr << " frame capture flag enabled\n " ;
42+ capture_frames = true ;
43+ capture_texture.create (width, height);
44+ }
45+ }
46+
47+ // initial window and view configuration.
48+ sf::View view (sf::FloatRect (0 .f , 0 .f , width, height));
49+ window.setView (view);
50+ window.setFramerateLimit (fps);
51+
52+ // initialize game area.
53+ game_area.setFillColor (sf::Color (0 , 0 , 0 , 0 ));
54+ game_area.setOutlineColor (sf::Color::Black);
55+ game_area.setOutlineThickness (2 .f );
56+
57+ // initialize player bird game color.
58+ player_bird.setOutlineColor (sf::Color::Red);
59+ player_bird.setFillColor (sf::Color::Yellow);
60+
61+ // load the previous fittest network.
62+ if (!birds.collection [0 ].neural_net .load_network (" fittest.nn" )) {
63+ std::cerr << " error loading the fittest network\n " ;
64+ } else {
65+ std::cerr << " the previous fittests network is loaded\n " ;
66+ }
67+ }
68+
69+ void Game::process_events ()
70+ {
71+ sf::Event event;
72+ while (window.pollEvent (event)) {
73+ if (event.type == sf::Event::Closed) {
74+ genetic_algorithm.rank_fitness (birds);
75+
76+ if (birds.collection [0 ].neural_net .save_network (" fittest.nn" )) {
77+ std::cerr << " fittest network saved\n " ;
78+ } else {
79+ std::cerr << " failed to save the fittest network\n " ;
80+ }
81+
82+ window.close ();
83+ }
84+
85+ if (event.type == sf::Event::KeyPressed) {
86+ auto game_view = window.getView ();
87+
88+ // cam up
89+ if (event.key .scancode == sf::Keyboard::Scan::Up) {
90+ game_view.move (0 .f , -VIEW_MOVE_DISTANCE);
91+ window.setView (game_view);
92+ }
93+
94+ // cam down
95+ if (event.key .scancode == sf::Keyboard::Scan::Down) {
96+ game_view.move (0 .f , VIEW_MOVE_DISTANCE);
97+ window.setView (game_view);
98+ }
99+
100+ // cam left
101+ if (event.key .scancode == sf::Keyboard::Scan::Left) {
102+ game_view.move (-VIEW_MOVE_DISTANCE, 0 .f );
103+ window.setView (game_view);
104+ }
105+
106+ // cam right
107+ if (event.key .scancode == sf::Keyboard::Scan::Right) {
108+ game_view.move (VIEW_MOVE_DISTANCE, 0 .f );
109+ window.setView (game_view);
110+ }
111+
112+ // zoom in cam
113+ if (event.key .scancode == sf::Keyboard::Scan::Equal) {
114+ game_view.zoom (0 .99f );
115+ window.setView (game_view);
116+ }
117+
118+ // zoom out cam
119+ if (event.key .scancode == sf::Keyboard::Scan::Hyphen) {
120+ game_view.zoom (1 .01f );
121+ window.setView (game_view);
122+ }
123+
124+ // bird jumps
125+ if (event.key .scancode == sf::Keyboard::Scan::Space) {
126+ player_bird.jump ();
127+ }
128+ }
129+ }
130+
131+ genetic_algorithm.get_inputs (birds, pipes);
132+ }
133+
134+ void Game::run ()
135+ {
136+ sf::Clock clock;
137+ sf::Time timeSinceLastUpdate = sf::Time::Zero;
138+ while (window.isOpen ()) {
139+ process_events ();
140+ timeSinceLastUpdate += clock.restart ();
141+ while (timeSinceLastUpdate > timePerFrame) {
142+ timeSinceLastUpdate -= timePerFrame;
143+
144+ // in the sfml book the also put another `process_events` call here, IDK if removing it
145+ // will have an effect in the game.
146+ // process_events()
147+
148+ update (timePerFrame);
149+ }
150+ render ();
151+ end_of_frame ();
152+ }
153+ }
154+
155+ void Game::update (sf::Time const &dt)
156+ {
157+ pipes.update (dt.asSeconds ());
158+ birds.update (dt.asSeconds ());
159+ player_bird.update (dt.asSeconds ());
160+
161+ game_statistics->record_deaths (birds_collisions (birds, pipes));
162+ bird_collision (player_bird, pipes);
163+ }
164+
165+ void Game::render ()
166+ {
167+ window.clear (background);
168+
169+ window.draw (game_area);
170+ window.draw (birds);
171+
172+ if (!player_bird.dead ) {
173+ window.draw (player_bird);
174+ }
175+
176+ window.draw (pipes);
177+ window.draw (*game_statistics);
178+
179+ window.display ();
180+ }
181+
182+ void Game::end_of_frame ()
183+ {
184+ if (capture_frames) {
185+ capture_texture.update (window);
186+ capture_texture.copyToImage ().saveToFile (
187+ " flappy-ffnn-ga-frame-" + lpad (std::to_string (captured_frames++), ' 0' , 6 ) + " .jpg"
188+ );
189+ }
190+
191+ if (birds.population == 0ULL && player_bird.dead ) {
192+ genetic_algorithm.rank_fitness (birds);
193+
194+ if (birds.collection [0 ].neural_net .save_network (" fittest.nn" )) {
195+ std::cerr << " fittest network saved\n " ;
196+ } else {
197+ std::cerr << " failed to save the fittest network\n " ;
198+ }
199+
200+ genetic_algorithm.apply_mutations (birds);
201+
202+ game_statistics->new_generation ();
203+ pipes.reset ();
204+ birds.reset ();
205+ player_bird.reset ();
206+ }
207+
208+ game_statistics->update ();
209+ }
0 commit comments