From 8f47088a5621b03d562975e437b1452e6fca5557 Mon Sep 17 00:00:00 2001 From: Joscha Date: Tue, 25 Apr 2017 06:32:13 +0000 Subject: [PATCH] Implement and test Chromosome --- src/Chromosome.cpp | 228 ++++++++++++++++++++++++++++++++++++--------- src/Chromosome.hpp | 32 +++++-- src/main.cpp | 101 ++++++++------------ 3 files changed, 246 insertions(+), 115 deletions(-) diff --git a/src/Chromosome.cpp b/src/Chromosome.cpp index 34d36a4..a57aed2 100644 --- a/src/Chromosome.cpp +++ b/src/Chromosome.cpp @@ -1,57 +1,195 @@ #include "Chromosome.hpp" - - -/* -class Chromosome : public sf::Drawable -{ -public: - Chromosome(); // create empty chromosome - Chromosome(Chromosome& father, Chromosome& mother); // cross over two chromosomes - - void mutate(); // randomly mutate chromosome's genes - - virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const; - -private: - struct Gene +#include +#if __GNUC__ < 7 // gcc 7 will support clamp +namespace std { + template + T clamp(T v, T lo, T hi) { - sf::Vector2f position; - float size; - sf::Color color; - }; - - sf::CircleShape circle; // drawing the chromosome, one draw call at a time - std::vector genes; -}; -*/ + return std::min(hi, std::max(lo, v)); + } +} +#endif + +#include +#include + + + +// #include +sf::Vector2f Chromosome::size(0, 0); +float Chromosome::stddev_position = .1; +float Chromosome::stddev_radius = .1; +float Chromosome::stddev_color = 20; +std::mt19937_64* Chromosome::re; + Chromosome::Chromosome() { // this->genes is already empty -}; + this->circle.setPointCount(100); +} -Chromosome::Chromosome(Chromosome& father, Chromosome& mother) { - // randomly swap father and mother - if (/*TODO: random bool*/ false) { - Chromosome& tmp = father; - father = mother; - mother = tmp; + +Chromosome::Chromosome(Chromosome& father, Chromosome& mother) : + Chromosome() +{ + std::uniform_int_distribution<> booldist(0, 1); + + auto fpair = this->selectSegment(father.genes); + auto mpair = this->selectSegment(mother.genes); + +// auto cur_it = this->genes.begin(); +// std::cout << std::distance(cur_it, father.genes.begin()) << std::endl; +// std::cout << std::distance(cur_it, mother.genes.begin()) << std::endl; +// cur_it = this->genes.insert(cur_it, fpair.second, father.genes.end()); +// cur_it = this->genes.insert(cur_it, mpair.first, mpair.second); +// cur_it = this->genes.insert(cur_it, father.genes.begin(), fpair.first); + + if (booldist(*Chromosome::re)) { + this->genes.insert(this->genes.begin(), fpair.second, father.genes.end()); + this->genes.insert(this->genes.begin(), mpair.first, mpair.second); + this->genes.insert(this->genes.begin(), father.genes.begin(), fpair.first); + } else { + this->genes.insert(this->genes.begin(), mpair.second, mother.genes.end()); + this->genes.insert(this->genes.begin(), fpair.first, fpair.second); + this->genes.insert(this->genes.begin(), mother.genes.begin(), mpair.first); + } +} + + +void Chromosome::mutate() +{ + std::uniform_int_distribution<> booldist(0, 1); + std::uniform_int_distribution<> choicedist(0, 12); + while (booldist(*Chromosome::re)) { + int choice = choicedist(*Chromosome::re); + + if (choice < 1) { // add +// std::cout << "Added circle" << std::endl; + this->genes.push_back(this->randomGene()); + + } else if (choice < 2) { // remove +// std::cout << "Removed circle" << std::endl; + auto it = this->selectGene(this->genes); + if (it != this->genes.end()) { + this->genes.erase(it); + } + + } else if (choice < 4) { // swap +// std::cout << "Swapped circles" << std::endl; + auto it_one = this->selectGene(this->genes); + auto it_two = this->selectGene(this->genes); + if (it_one != this->genes.end() && it_two != this->genes.end() && it_one != it_two) { + auto tmp = *it_one; + *it_one = *it_two; + *it_two = tmp; + } + + } else { // mutate +// std::cout << "Mutated circle" << std::endl; + auto it = this->selectGene(this->genes); + if (it != this->genes.end()) { + this->mutateGene(*it); + } + } + } +} + + +void Chromosome::draw(sf::RenderTarget& target, sf::RenderStates states) const +{ + for (auto gene : this->genes) { + this->circle.setPosition(gene.position); + this->circle.setRadius(gene.radius); + this->circle.setOrigin(sf::Vector2f(gene.radius, gene.radius)); + this->circle.setFillColor(gene.color); + target.draw(this->circle, states); + } +} + + +Chromosome::Gene Chromosome::randomGene() +{ + float max_radius = std::min(Chromosome::size.x, Chromosome::size.y)/2; + std::uniform_real_distribution<> xdist(-max_radius, Chromosome::size.x + max_radius); + std::uniform_real_distribution<> ydist(-max_radius, Chromosome::size.y + max_radius); + std::uniform_real_distribution<> rdist(0, sqrt(max_radius)); + std::uniform_int_distribution<> colordist(0, 255); + + sf::Vector2f position(xdist(*Chromosome::re), ydist(*Chromosome::re)); + float radius = (pow(rdist(*Chromosome::re), 2)); + sf::Color color(colordist(*Chromosome::re), colordist(*Chromosome::re), colordist(*Chromosome::re)); + + Chromosome::Gene gene; + gene.position = position; + gene.radius = radius; + gene.color = color; + + return gene; +} + + +void Chromosome::mutateGene(Gene& gene) +{ + std::uniform_int_distribution<> booldist(0, 1); + float max_radius = std::min(Chromosome::size.x, Chromosome::size.y)/2; + + if (booldist(*Chromosome::re)) { // position + std::normal_distribution<> posdist(0, Chromosome::stddev_position); + gene.position.x = std::clamp( + gene.position.x + posdist(*Chromosome::re)*max_radius, + -max_radius, + Chromosome::size.x + max_radius + ); + gene.position.y = std::clamp( + gene.position.y + posdist(*Chromosome::re)*max_radius, + -max_radius, + Chromosome::size.y + max_radius + ); } - // replace random segment of mother with random segment of father + if (booldist(*Chromosome::re)) { // radius + std::normal_distribution<> raddist(0, Chromosome::stddev_radius); + gene.radius = std::clamp( + gene.radius + raddist(*Chromosome::re)*max_radius, + 0, + max_radius + ); + } - // TODO: in seperate function: - // finding random segment: - // randomly find starting position, then length - // find starting iterator, then end iterator - - // using function from above: - // find father segment: f_start, f_stop (iterators) - // find mother segment: m_start, m_stop (iterators) - - // RIGHT: - // append mother until m_start - // append father from f_start to f_end - // append mother from m_end + if (booldist(*Chromosome::re)) { // color (all three values at the same time) + std::normal_distribution<> coldist(0, Chromosome::stddev_color); + gene.color.r = std::clamp(gene.color.r + coldist(*Chromosome::re), 0, 255); + gene.color.g = std::clamp(gene.color.g + coldist(*Chromosome::re), 0, 255); + gene.color.b = std::clamp(gene.color.b + coldist(*Chromosome::re), 0, 255); + } +} + + +std::pair::iterator, std::vector::iterator> +Chromosome::selectSegment(std::vector& genes) +{ + std::uniform_int_distribution<> randdist(0, genes.size()); + auto first = genes.begin() + randdist(*Chromosome::re); + auto second = genes.begin() + randdist(*Chromosome::re); + + if (first > second) { + std::swap(first, second); + } + + return std::pair::iterator, + std::vector::iterator>(first, second); +} + + +std::vector::iterator +Chromosome::selectGene(std::vector& genes) +{ + if (genes.empty()) { + return genes.end(); + } else { + std::uniform_int_distribution<> posdist(0, genes.size()); + return genes.begin() + posdist(*Chromosome::re); + } } diff --git a/src/Chromosome.hpp b/src/Chromosome.hpp index 822ed9e..8d40275 100644 --- a/src/Chromosome.hpp +++ b/src/Chromosome.hpp @@ -1,33 +1,45 @@ #pragma once +#include +#include +#include #include +#include class Chromosome : public sf::Drawable { public: + static sf::Vector2f size; + static float stddev_position; // percent of max_radius + static float stddev_radius; // percent of max_radius + static float stddev_color; + static std::mt19937_64* re; + Chromosome(); // create empty chromosome - Chromosome(Chromosome& father, Chromosome& mother); // cross over two chromosomes + Chromosome(Chromosome& father, Chromosome& mother); // crossover void mutate(); // randomly mutate chromosome's genes virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const; -private: - // TODO: random numbers - // pass as reference to Chromosome and Mutate? - // pass as reference on object creation? - +protected: struct Gene { sf::Vector2f position; - float size; + float radius; sf::Color color; }; - sf::CircleShape circle; // drawing the chromosome, one draw call at a time + Gene randomGene(); + void mutateGene(Gene& gene); - size_t genesize = 0; - std::forward_list genes; + std::pair::iterator, std::vector::iterator> + selectSegment(std::vector& genes); + + std::vector::iterator selectGene(std::vector& genes); + + mutable sf::CircleShape circle; // drawing the chromosome, one draw call at a time + std::vector genes; }; diff --git a/src/main.cpp b/src/main.cpp index 845585e..2b3cada 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,7 @@ -#include #include +#include +#include +#include "Chromosome.hpp" /* * UI Modes: @@ -106,10 +108,25 @@ int main() { const float winW = 480; const float winH = 480; + std::mt19937_64 randomEngine; + randomEngine.seed(1); sf::RenderWindow window(sf::VideoMode(winW, winH), "gross"); window.setMouseCursorVisible(false); // hide the cursor + Chromosome::size = sf::Vector2f(winW/2, winH/2); + Chromosome::re = &randomEngine; + Chromosome father, mother, child, monster; + sf::View vfather(sf::FloatRect(0, 0, winW/2, winH/2)); + sf::View vmother(sf::FloatRect(0, 0, winW/2, winH/2)); + sf::View vchild(sf::FloatRect(0, 0, winW/2, winH/2)); + sf::View vmonster(sf::FloatRect(0, 0, winW/2, winH/2)); + vfather.setViewport(sf::FloatRect(0, 0, .5, .5)); + vmother.setViewport(sf::FloatRect(.5, 0, .5, .5)); + vchild.setViewport(sf::FloatRect(0, .5, .5, .5)); + vmonster.setViewport(sf::FloatRect(.5, .5, .5, .5)); + + /* // load images sf::Texture base; sf::Texture comp; @@ -137,8 +154,8 @@ int main() { std::cout << "The medshader is not available\n"; return 1; } - medcompshdr.setUniform("base", comp); - medcompshdr.setUniform("curr", base); + medcompshdr.setUniform("base", base); + medcompshdr.setUniform("curr", comp); medcompshdr.setUniform("size", sf::Vector2f(240, 240)); medcompshdr.setUniform("offs", sf::Vector2f(240, 0)); @@ -148,6 +165,9 @@ int main() { // Use a timer to obtain the time elapsed // sf::Clock clk; // clk.restart(); // start the timer + */ + + while (window.isOpen()) { // Event handling @@ -155,8 +175,17 @@ int main() { while (window.pollEvent(event)) { // Exit the app when a key is pressed - if (event.type == sf::Event::KeyPressed) - window.close(); + if (event.type == sf::Event::KeyPressed) { + father = Chromosome(); + mother = Chromosome(); + for (int i=0; i<100; ++i) { + father.mutate(); + mother.mutate(); + } + child = Chromosome(father, mother); + monster = child; + monster.mutate(); + } } // Set the others parameters who need to be updated every frames @@ -167,7 +196,13 @@ int main() { // Draw the sprites window.clear(); + window.setView(vfather); window.draw(father); + window.setView(vmother); window.draw(mother); + window.setView(vchild); window.draw(child); + window.setView(vmonster); window.draw(monster); + window.display(); + /* window.draw(sprbase); sprcomp.setPosition(240, 0); @@ -178,63 +213,9 @@ int main() { sprcomp.setPosition(240, 240); window.draw(sprcomp, &medcompshdr); + */ - window.display(); } return 0; } - -// old main - keep for testing purposes -/* -int main() { - const float winW = 800; - const float winH = 600; - - sf::RenderWindow window(sf::VideoMode(winW, winH), "gross"); - window.setMouseCursorVisible(false); // hide the cursor - - // Create a texture and a sprite for the shader - sf::Texture tex; - tex.create(winW, winH); - sf::Sprite spr(tex); - - sf::Shader shader; - shader.loadFromFile("fire.glsl", sf::Shader::Fragment); // load the shader - - if (!shader.isAvailable()) { - std::cout << "The shader is not available\n"; - } - - // Set the resolution parameter (the resoltion is divided to make the fire smaller) - shader.setParameter("resolution", sf::Vector2f(winW / 2, winH / 2)); - - // Use a timer to obtain the time elapsed - sf::Clock clk; - clk.restart(); // start the timer - - while (window.isOpen()) { - // Event handling - sf::Event event; - - while (window.pollEvent(event)) { - // Exit the app when a key is pressed - if (event.type == sf::Event::KeyPressed) - window.close(); - } - - // Set the others parameters who need to be updated every frames - shader.setParameter("time", clk.getElapsedTime().asSeconds()); - - sf::Vector2i mousePos = sf::Mouse::getPosition(window); - shader.setParameter("mouse", sf::Vector2f(mousePos.x, mousePos.y - winH)); - - // Draw the sprite with the shader on it - window.clear(); - window.draw(spr, &shader); - window.display(); - } - - return 0; -} -*/