Skip to content

Commit

Permalink
small update and tweaks
Browse files Browse the repository at this point in the history
- reorder the member of some class and their initializer lists.

- renamed some members and parameter names.

- create one random engine instead of multiple ones.
  • Loading branch information
mrdcvlsc committed Oct 26, 2023
1 parent a8c8a8a commit caa99db
Show file tree
Hide file tree
Showing 13 changed files with 46 additions and 52 deletions.
4 changes: 3 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@

A Flappy Bird like game that uses a feedforward neural network to play the game, and a genetic algorithm to learn how to play the game.

In windows you need to install [Microsoft Visual C++ Redistributable](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#visual-studio-2015-2017-2019-and-2022) to make the release versions working.
Anyone can [download the released executables](https://github.com/mrdcvlsc/flappy-ffnn-ga/releases), extract it and run the program, these release executables only supports x86-64 or 64-bit systems: [DOWNLOAD HERE](https://github.com/mrdcvlsc/flappy-ffnn-ga/releases)

In windows, one might need to install [Microsoft Visual C++ Redistributable](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#visual-studio-2015-2017-2019-and-2022) in their system if the release versions of the executables is not working.
19 changes: 7 additions & 12 deletions src/bird.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@

sf::Vector2f Bird::target_gap = {0.f, 0.f};

std::mt19937 Bird::color_engine(std::chrono::system_clock::now().time_since_epoch().count());
std::uniform_int_distribution<size_t> Bird::color_rng(0, 255);
std::uniform_int_distribution<size_t> Bird::rand_color(0, 255);

Bird::Bird()
: sf::RectangleShape({SIZE, SIZE}), time_lived(0.f), speed(JUMP_SPEED * 0.6f), fitness(0.f), dead(false),
neural_net() {
: sf::RectangleShape({SIZE, SIZE}), neural_net(), time_lived(0.f), speed(JUMP_SPEED * 0.6f), fitness(0.f),
dead(false) {
setOrigin(getSize() * 0.5f);
setFillColor(sf::Color(color_rng(color_engine), color_rng(color_engine), color_rng(color_engine)));
setFillColor(sf::Color(rand_color(rand_engine), rand_color(rand_engine), rand_color(rand_engine)));
setOutlineThickness(3.f);
setOutlineColor(sf::Color::Black);
reset();
Expand Down Expand Up @@ -56,15 +55,11 @@ void Bird::apply_random_mutation() {
neural_net.mutate();
}

void Bird::become_offspring(Bird const &parentA, Bird const &parentB) {
sf::Color new_color(
parentA.getFillColor().r | (parentB.getFillColor().r >> 4),
parentB.getFillColor().g | (parentA.getFillColor().g >> 4),
parentA.getFillColor().b | (parentB.getFillColor().b << 4)
);
void Bird::become_offspring(Bird const &b1, Bird const &b2) {
sf::Color new_color(b1.getFillColor().r, b2.getFillColor().g, (b1.getFillColor().b + b2.getFillColor().b) % 256);

setFillColor(new_color);
neural_net.combine(parentA.neural_net, parentB.neural_net);
neural_net.combine(b1.neural_net, b2.neural_net);
}

/////////////////////// Birds ///////////////////////
Expand Down
7 changes: 3 additions & 4 deletions src/bird.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,21 @@ struct Bird : public sf::RectangleShape {
/// The current `Pipe` gap that the bird needs to jump over.
static sf::Vector2f target_gap;

static std::mt19937 color_engine;
static std::uniform_int_distribution<size_t> color_rng;
static std::uniform_int_distribution<size_t> rand_color;

FFNN neural_net;
float time_lived;
float speed;
float fitness;
bool dead;
FFNN neural_net;

Bird();

void jump();
void update(float dt);
void reset();
void apply_random_mutation();
void become_offspring(Bird const &parentA, Bird const &parentB);
void become_offspring(Bird const &b1, Bird const &b2);
};

struct Birds : public sf::Drawable {
Expand Down
5 changes: 5 additions & 0 deletions src/config.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#ifndef MRDCVLSC_FLAPPY_FFNN_GA_HPP
#define MRDCVLSC_FLAPPY_FFNN_GA_HPP

#include <chrono>
#include <random>

#include <cstddef>
#include <cinttypes>

Expand All @@ -9,4 +12,6 @@ static constexpr float VIEW_MOVE_DISTANCE = 2.f;
static constexpr size_t WINDOW_WIDTH = 640;
static constexpr size_t WINDOW_HEIGHT = 360;

static std::mt19937 rand_engine(std::chrono::system_clock::now().time_since_epoch().count());

#endif
19 changes: 8 additions & 11 deletions src/ffnn.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
#include "ffnn.hpp"
#include <chrono>
#include <random>

std::mt19937 FFNN::engine(std::chrono::system_clock::now().time_since_epoch().count());
std::uniform_int_distribution<size_t> FFNN::random_chance(0U, 100U);
std::uniform_real_distribution<float> FFNN::new_random_weight(-0.5f, 0.5f);

Expand All @@ -13,13 +10,13 @@ FFNN::FFNN() : input_layer(), w1(), hidden_layer(), w2(), output_layer() {
void FFNN::randomize_network() {
for (size_t j = 0; j < INPUTS; ++j) {
for (size_t i = 0; i < HIDDEN; ++i) {
w1(i, j) = new_random_weight(engine);
w1(i, j) = new_random_weight(rand_engine);
}
}

for (size_t j = 0; j < HIDDEN; ++j) {
for (size_t i = 0; i < OUTPUT; ++i) {
w2(i, j) = new_random_weight(engine);
w2(i, j) = new_random_weight(rand_engine);
}
}
}
Expand Down Expand Up @@ -53,18 +50,18 @@ void FFNN::mutate() {
// mutate weight 1
for (size_t j = 0; j < INPUTS; ++j) {
for (size_t i = 0; i < HIDDEN; ++i) {
size_t chance = random_chance(engine);
size_t chance = random_chance(rand_engine);
if (chance <= MUTATION_CHANCE_THRESHOLD) {
w1(i, j) = new_random_weight(engine);
w1(i, j) = new_random_weight(rand_engine);
}
}
}

for (size_t j = 0; j < HIDDEN; ++j) {
for (size_t i = 0; i < OUTPUT; ++i) {
size_t chance = random_chance(engine);
size_t chance = random_chance(rand_engine);
if (chance <= MUTATION_CHANCE_THRESHOLD) {
w2(i, j) = new_random_weight(engine);
w2(i, j) = new_random_weight(rand_engine);
}
}
}
Expand All @@ -74,7 +71,7 @@ void FFNN::combine(FFNN const &net1, FFNN const &net2) {
// mutate weight 1
for (size_t j = 0; j < INPUTS; ++j) {
for (size_t i = 0; i < HIDDEN; ++i) {
size_t chance = random_chance(engine);
size_t chance = random_chance(rand_engine);
if (chance <= WEIGHT_SELECTION_CHANCE_THRESHOLD) {
w1(i, j) = net1.w1(i, j);
} else {
Expand All @@ -85,7 +82,7 @@ void FFNN::combine(FFNN const &net1, FFNN const &net2) {

for (size_t j = 0; j < HIDDEN; ++j) {
for (size_t i = 0; i < OUTPUT; ++i) {
size_t chance = random_chance(engine);
size_t chance = random_chance(rand_engine);
if (chance <= WEIGHT_SELECTION_CHANCE_THRESHOLD) {
w2(i, j) = net1.w2(i, j);
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/ffnn.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

#include <Eigen/Core>

#include "config.hpp"

/// \brief Feed-Forward Neural Network (2x5x1).
struct FFNN {
static constexpr size_t INPUTS = 2;
Expand All @@ -22,8 +24,6 @@ struct FFNN {
/// \brief 50% chance to choose from parentA's weight.
static constexpr size_t WEIGHT_SELECTION_CHANCE_THRESHOLD = 50;

static std::mt19937 engine;

/// \brief generates number from 1 to 100.
static std::uniform_int_distribution<size_t> random_chance;

Expand Down
4 changes: 2 additions & 2 deletions src/gamestats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
const sf::Time GameStats::TIME_PER_FRAME = sf::seconds(1.f / static_cast<float>(GameStats::FRAME_LIMIT));

GameStats::GameStats()
: generation(1u), current_population(Birds::INITIAL_POPULATION), total_population(Birds::INITIAL_POPULATION),
fps(0.f), game_clock(), fps_clock(), timeSinceLastUpdate(sf::Time::Zero) {
: game_clock(), fps_clock(), timeSinceLastUpdate(sf::Time::Zero), generation(1u),
current_population(Birds::INITIAL_POPULATION), total_population(Birds::INITIAL_POPULATION), fps(0.f) {
if (!m_font.loadFromFile("calibril.ttf")) {
throw std::runtime_error("Error loading calibril.ttf");
}
Expand Down
9 changes: 5 additions & 4 deletions src/gamestats.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ class GameStats : public sf::Drawable {

static const sf::Time TIME_PER_FRAME;

unsigned int generation, current_population, total_population;
float fps;
sf::Clock game_clock, fps_clock;
sf::Time timeSinceLastUpdate;
sf::Clock game_clock, fps_clock;
sf::Time timeSinceLastUpdate;

size_t generation, current_population, total_population;
float fps;

GameStats();

Expand Down
8 changes: 3 additions & 5 deletions src/genetic_algo.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
#include "bird.hpp"
#include "config.hpp"
#include "ffnn.hpp"
#include "genetic_algo.hpp"
#include <algorithm>
#include <iterator>

#include "genetic_algo.hpp"

void GeneticAlgorithm::get_inputs(Birds &birds, Pipes const &pipes) {
// calculate front/last pipe gap position.
auto top_pipe1 = pipes.pairs[pipes.front_pipe].top;
Expand Down Expand Up @@ -59,7 +57,7 @@ void GeneticAlgorithm::apply_mutations(Birds &birds) {
birds.collection[i].apply_random_mutation();
}

std::shuffle(birds.collection.begin() + fit_size, birds.collection.end(), FFNN::engine);
std::shuffle(birds.collection.begin() + fit_size, birds.collection.end(), rand_engine);

// breed the good birds to fill the missing population.
constexpr size_t unfit_size = (Birds::INITIAL_POPULATION - fit_size) * MUTATION_KEEP_BAD;
Expand Down
1 change: 1 addition & 0 deletions src/genetic_algo.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <vector>
#include <cmath>

#include "config.hpp"
#include "bird.hpp"
#include "pipe.hpp"

Expand Down
2 changes: 1 addition & 1 deletion src/makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ bird.o : bird.cpp bird.hpp config.hpp ffnn.o

######################## independent targets ########################

ffnn.o : ffnn.cpp ffnn.hpp
ffnn.o : ffnn.cpp ffnn.hpp config.hpp
$(strip $(CXX) $(CXX_STANDARD) $(WFLAGS) -c $< -o $@ $(DFLAGS))

pipe.o : pipe.cpp pipe.hpp config.hpp
Expand Down
11 changes: 5 additions & 6 deletions src/pipe.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "pipe.hpp"
#include "config.hpp"

////////////////////////// Pipe //////////////////////////

Expand Down Expand Up @@ -53,11 +54,9 @@ void PipePair::draw(sf::RenderTarget &target, sf::RenderStates states) const {

////////////////////////// Pipes //////////////////////////

Pipes::Pipes()
: engine(std::chrono::system_clock::now().time_since_epoch().count()),
rng(PipePair::MIN_HEIGHT, PipePair::MAX_HEIGHT), front_pipe(0ULL) {
Pipes::Pipes() : rand_height(PipePair::MIN_HEIGHT, PipePair::MAX_HEIGHT), front_pipe(0ULL) {
for (size_t i = 0; i < COUNT; ++i) {
pairs[i] = PipePair(START_X + i * DISTANCE, rng(engine));
pairs[i] = PipePair(START_X + i * DISTANCE, rand_height(rand_engine));
}
}

Expand All @@ -78,7 +77,7 @@ void Pipes::update(float tick) {
pairs[index].set_pos(pairs[back_index].getPosition().x + DISTANCE);

// generate new heights for the pipe that was moved to the back.
float top_height = rng(engine);
float top_height = rand_height(rand_engine);
pairs[index].top.setSize({Pipe::WIDTH, top_height});
pairs[index].btm.setSize({Pipe::WIDTH, static_cast<float>(WINDOW_HEIGHT) - top_height - PipePair::GAP});
pairs[index].btm.setPosition(pairs[index].btm.getPosition().x, top_height + PipePair::GAP);
Expand All @@ -94,6 +93,6 @@ void Pipes::update(float tick) {
void Pipes::new_generation() {
front_pipe = 0ULL;
for (size_t i = 0; i < COUNT; ++i) {
pairs[i] = PipePair(START_X + i * DISTANCE, rng(engine));
pairs[i] = PipePair(START_X + i * DISTANCE, rand_height(rand_engine));
}
}
5 changes: 1 addition & 4 deletions src/pipe.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

#include <iostream>
#include <array>
#include <chrono>
#include <random>

#include <cstddef>
#include <cstdint>
Expand Down Expand Up @@ -83,8 +81,7 @@ struct Pipes : public sf::Drawable {

std::array<PipePair, COUNT> pairs;

std::mt19937 engine;
std::uniform_real_distribution<float> rng;
std::uniform_real_distribution<float> rand_height;

/// Current front pipe index.
size_t front_pipe;
Expand Down

0 comments on commit caa99db

Please sign in to comment.