Skip to content

Commit

Permalink
Merge pull request #1 from NickJordan289/FB-NEAT
Browse files Browse the repository at this point in the history
NEAT implemented
  • Loading branch information
NickJordan289 committed Aug 20, 2018
2 parents ce1ca80 + cfc7f1e commit d666bd4
Show file tree
Hide file tree
Showing 6 changed files with 647 additions and 64 deletions.
55 changes: 50 additions & 5 deletions FlappyBird/Bird.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include <SFML/Graphics.hpp>

#include "NeuralNetwork.h"

#include "Pipe.h"
#include "ExtraFuncs.h"

Expand All @@ -12,31 +14,68 @@ class Bird {
sf::RectangleShape r;

sf::Vector2f velocity = sf::Vector2f(0.f, 0.f);
sf::Vector2f acc = sf::Vector2f(0.f, 0.f);
float minVelocity = -30.f;
float maxVelocity = 30.f;

bool crashed = false;
bool shouldDraw = true;

sf::RenderWindow& rWindRef;
std::deque<std::shared_ptr<Pipe>>& pipesRef;
int& lifetimeCounterRef;

std::vector<double> vision = { 0.0, 0.0, 0.0, 0.0, 0.0 };
NeuralNetwork nn = NeuralNetwork(vision.size(), 4, 1);
double fitness = 0;

Bird(NeuralNetwork nn, sf::RenderWindow& rWind, std::deque<std::shared_ptr<Pipe>>& pipes, int& lifetime) : Bird(rWind, pipes, lifetime) {
this->nn = nn;
}

Bird(sf::RenderWindow& rWind, std::deque<std::shared_ptr<Pipe>>& pipes) : rWindRef(rWind), pipesRef(pipes) {
Bird(sf::RenderWindow& rWind, std::deque<std::shared_ptr<Pipe>>& pipes, int& lifetime) : rWindRef(rWind), pipesRef(pipes), lifetimeCounterRef(lifetime) {
r.setSize(sf::Vector2f(32.f, 32.f));
r.setOrigin(r.getGlobalBounds().width / 2.f, r.getGlobalBounds().height / 2.f);
r.setPosition(sf::Vector2f(64.f, rWindRef.getSize().y / 2.f));
r.setFillColor(sf::Color(255, 255, 255, 100));
r.setOutlineColor(sf::Color::Black);
r.setOutlineColor(sf::Color(255, 255, 255, 200));
r.setOutlineThickness(0.5f);
}

void jump() {
velocity += sf::Vector2f(0.f, -15.f);
velocity.y = constrain(velocity.y, minVelocity, maxVelocity);
}

// inputs
// 0 : normalised y position
// 1 : normalised y velocity
// 2 : normalised height of top pipe
// 3 : normalised height of bottom pipe
// 4 : normalised distance to end of pipe
void think() {
vision[0] = map(r.getPosition().y, 0.0, rWindRef.getSize().y, 1.0, 0.0);
vision[1] = map(velocity.y, minVelocity, maxVelocity, 0.f, 1.f);
vision[2] = map(pipesRef[0]->top.getGlobalBounds().top+pipesRef[0]->top.getGlobalBounds().height, 0.0, rWindRef.getSize().y, 1.0, 0.0);
vision[3] = map(pipesRef[0]->bot.getGlobalBounds().top, 0.0, rWindRef.getSize().y, 1.0, 0.0);

double distance = (pipesRef[0]->top.getGlobalBounds().left + pipesRef[0]->top.getGlobalBounds().width) - r.getGlobalBounds().left;
if (distance < 0.0)
distance = (pipesRef[1]->top.getGlobalBounds().left + pipesRef[1]->top.getGlobalBounds().width) - r.getGlobalBounds().left;
vision[4] = map(distance, 0.0, rWindRef.getSize().x - r.getGlobalBounds().left, 0.0, 1.0);

std::vector<double> output = nn.predict(vision).map(round).toVector();
if (output[0] == 1) {
jump();
}
}

void update(float speed) {

think();

if(!crashed) {
velocity += sf::Vector2f(0.f, 0.6f);
velocity *= 0.9f;
velocity += sf::Vector2f(0.f, 0.6f); // gravity
velocity *= 0.9f; // air resistance
r.move(velocity);

for (auto p : pipesRef) {
Expand All @@ -51,6 +90,12 @@ class Bird {
if (r.getPosition().y > rWindRef.getSize().y || r.getPosition().y < 0) {
crashed = true;
}

if (crashed) {
fitness = lifetimeCounterRef;
fitness = pow(fitness, 2);
}

} else {
if (shouldDraw) {
r.move(sf::Vector2f(-speed, 0)); // stick and move with the wall we crashed into
Expand Down
50 changes: 26 additions & 24 deletions FlappyBird/ExtraFuncs.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,80 +21,84 @@
typedef long long int64;
typedef unsigned long long uint64;

std::string GetEnvironmentBit() {
inline std::string GetEnvironmentBit() {
return ENVIRONMENTBIT;
}

// inclusive min, inclusive max
template <typename T>
T rNum(T Min, T Max) { return ((float(rand()) / float(RAND_MAX)) * (Max - Min)) + Min; }
inline T rNum(T Min, T Max) { return ((float(rand()) / float(RAND_MAX)) * (Max - Min)) + Min; }
// inclusive max
template <typename T>
T rNum(T Max) { return T((float(rand()) / float(RAND_MAX)) * (Max - 0)) + 0; }
inline T rNum(T Max) { return T((float(rand()) / float(RAND_MAX)) * (Max - 0)) + 0; }

template <typename T>
inline T randomItem(std::vector<T> v) {
return v[rand() % v.size()];
}

template <typename T>
inline T lerp(T v0, T v1, T t) {
return (1 - t)*v0 + t * v1;
}

double map(double n, double start1, double stop1, double start2, double stop2) {
inline double map(double n, double start1, double stop1, double start2, double stop2) {
return ((n - start1) / (stop1 - start1))*(stop2 - start2) + start2;
};

float dist(sf::Vector2f a, sf::Vector2f b) {
inline float dist(sf::Vector2f a, sf::Vector2f b) {
return std::sqrt(std::pow(b.x - a.x, 2) + std::pow(b.y - a.y, 2));
}

float magnitude(sf::Vector2f a) {
inline float magnitude(sf::Vector2f a) {
return std::sqrt(std::pow(a.x, 2) + std::pow(a.y, 2));
}

float magnitude(sf::Vector3f a) {
inline float magnitude(sf::Vector3f a) {
return std::sqrt(std::pow(a.x, 2) + std::pow(a.y, 2) + std::pow(a.z, 2));
}

float sqrMagnitude(sf::Vector3f a) {
inline float sqrMagnitude(sf::Vector3f a) {
return a.x* a.x + a.y* a.y + a.z* a.z;
}

double randomDouble(double a, double b) {
inline double randomDouble(double a, double b) {
double random = ((double)rand()) / (double)RAND_MAX;
double diff = b - a;
double r = random * diff;
return a + r;
}

float dot(sf::Vector3f a, sf::Vector3f b) {
inline float dot(sf::Vector3f a, sf::Vector3f b) {
float aMag = magnitude(a);
float bMag = magnitude(b);
return a.x * b.x + a.y * b.y + a.z * b.z;
}

sf::Vector2f normalize(sf::Vector2f a) {
inline sf::Vector2f normalize(sf::Vector2f a) {
float len = magnitude(a);
if (len != 0.f)
return sf::Vector2f(a.x / len, a.y / len);
return a;
}

sf::Vector3f normalize(sf::Vector3f a) {
inline sf::Vector3f normalize(sf::Vector3f a) {
float len = magnitude(a);
if (len != 0.f)
return sf::Vector3f(a.x / len, a.y / len, a.z / len);
return a;
}

template <typename T>
void RemoveAt(std::vector<T> V, int index) {
inline void RemoveAt(std::vector<T> V, int index) {
V.erase(V.begin() + index);
}

/*
Concatenates Two Vectors
*/
template <typename T>
std::vector<T> AddRange(std::vector<T> A, std::vector<T> B)
{
inline std::vector<T> AddRange(std::vector<T> A, std::vector<T> B) {
std::vector<T> AB;
AB.reserve(A.size() + B.size()); // preallocate memory
AB.insert(AB.end(), A.begin(), A.end());
Expand All @@ -103,17 +107,17 @@ std::vector<T> AddRange(std::vector<T> A, std::vector<T> B)
}

template <typename A, typename B>
A constrain(A n, B low, B high) {
inline A constrain(A n, B low, B high) {
return std::max(std::min(n, high), low);
};

template <typename T>
void delete_pointed_to(T* const ptr) {
inline void delete_pointed_to(T* const ptr) {
delete ptr;
}

template <typename T>
void free_pointer_vector_memory(std::vector<T*> ptr_vector) {
inline void free_pointer_vector_memory(std::vector<T*> ptr_vector) {
std::for_each(ptr_vector.begin(), ptr_vector.end(), delete_pointed_to<T>);
}

Expand All @@ -122,21 +126,19 @@ inline T Interpolate(T x0, T x1, T alpha) {
return x0 * (1 - alpha) + alpha * x1;
}

float TruncateRGB(float n) {
inline float TruncateRGB(float n) {
return std::max(std::min(n, 255.f), 0.f);
}

sf::Color FloatToColour(float value) {
inline sf::Color FloatToColour(float value) {
return sf::Color(sf::Uint8(value*255.f), sf::Uint8(value*255.f), sf::Uint8(value*255.f));
}

int RGBToDec(sf::Color c)
{
inline int RGBToDec(sf::Color c) {
return (c.r << 16) | (c.g << 8) | c.b;
}

sf::Color RGBFromDec(int d)
{
inline sf::Color RGBFromDec(int d) {
sf::Color temp;
temp.r = (d >> 16) & 255;
temp.g = (d >> 8) & 255;
Expand Down
79 changes: 44 additions & 35 deletions FlappyBird/FlappyBirdNEAT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
#include "Bird.h"
#include "Pipe.h"
#include "ExtraFuncs.h"
#include "Population.h"

int main() {
srand(static_cast<unsigned int>(time(NULL)));

const unsigned int SCREEN_WIDTH = 400;
const unsigned int SCREEN_HEIGHT = 600;
const unsigned int FPS = 60; //The desired FPS. (The number of updates each second) or 0 for uncapped.
const unsigned int FPS = 0; //The desired FPS. (The number of updates each second) or 0 for uncapped.

sf::Font ArialFont;
ArialFont.loadFromFile("C:/Windows/Fonts/arial.ttf");
Expand All @@ -30,24 +31,38 @@ int main() {

std::deque<std::shared_ptr<Pipe>> pipes;
pipes.push_back(std::make_shared<Pipe>(window));
std::shared_ptr<Bird> b = std::make_shared<Bird>(window, pipes);
int lifetime = 0;
float speed = 1.f;
int genCount = 1;
float speed = 1.f; // doesn't work
int popSize = 100;
float weightMutationChance = 0.1f;

Population pop = Population(popSize, weightMutationChance, window, pipes, lifetime, speed);
std::cout << "Press 1 for a viewable speed\npress 2 for fast training" << std::endl;

#pragma region Graphics
sf::Text lifetimeCounter;
lifetimeCounter.setFont(ArialFont);
lifetimeCounter.setString("0000");
lifetimeCounter.setPosition(0, 0);
lifetimeCounter.setPosition(2, -4);
lifetimeCounter.setOutlineColor(sf::Color::Black);
lifetimeCounter.setOutlineThickness(1.f);

sf::Text generationCounter;
generationCounter.setFont(ArialFont);
generationCounter.setString("1");
generationCounter.setPosition(2, generationCounter.getCharacterSize()-4);
generationCounter.setOutlineColor(sf::Color::Black);
generationCounter.setOutlineThickness(1.f);

sf::Text fpsCounter;
fpsCounter.setFont(ArialFont);
fpsCounter.setCharacterSize(20);
fpsCounter.setString("000");
fpsCounter.setPosition(window.getSize().x - fpsCounter.getGlobalBounds().width, 0);
fpsCounter.setString("0000");
fpsCounter.setPosition(window.getSize().x - fpsCounter.getGlobalBounds().width-6, 0);
fpsCounter.setOutlineColor(sf::Color::Black);
fpsCounter.setOutlineThickness(1.f);
#pragma endregion

bool paused = false;
sf::Event ev;
Expand All @@ -69,51 +84,45 @@ int main() {
if (sf::Keyboard::isKeyPressed(sf::Keyboard::P)) {
paused = !paused;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space)) {
b->jump();
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Num1))
{
window.setFramerateLimit(60); // unlimited framerate for faster generations
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Num2))
{
window.setFramerateLimit(0); // unlimited framerate for faster generations
}
}
}

// Update
if (!paused) {
if (b->crashed) {
std::cout << "Score: " << lifetime << std::endl;
pipes.clear();
pipes.push_back(std::make_shared<Pipe>(window));
b = std::make_shared<Bird>(window, pipes); // Replace bird with new one
lifetime = 0;
}

lifetime++;
lifetimeCounter.setString(std::to_string(lifetime));

if (lifetime % 200 == 0) {
pipes.push_back(std::make_shared<Pipe>(window));
}
if (!pop.done()) {
lifetime++;
lifetimeCounter.setString(std::to_string(lifetime));

// Move pipes and cull offscreen pipes
for (auto p : pipes) {
p->update(speed);
if (p->top.getGlobalBounds().left + p->top.getGlobalBounds().width < 0.f) {
pipes.pop_front();
if (lifetime % 200 == 0) {
pipes.push_back(std::make_shared<Pipe>(window));
}
}

// Update our bird
b->update(speed);
pop.update();
} else {
pop.naturalSelection();
pop.selection();
pop.reset();

genCount++;
generationCounter.setString(std::to_string(genCount));
}
}

// Draw
window.clear();

for (auto p : pipes) {
p->draw();
}

b->draw();
pop.draw();

window.draw(fpsCounter);
window.draw(generationCounter);
window.draw(lifetimeCounter);

window.display();
Expand Down
Loading

0 comments on commit d666bd4

Please sign in to comment.