diff options
Diffstat (limited to 'Simulation')
30 files changed, 2747 insertions, 0 deletions
diff --git a/Simulation/Camera.cpp b/Simulation/Camera.cpp new file mode 100644 index 0000000..84b98a8 --- /dev/null +++ b/Simulation/Camera.cpp @@ -0,0 +1,241 @@ +#include "Camera.hpp" +#include "SimBase.hpp" + +void Camera::create(SimBase *sim) +{ +    hSim = sim; + +    defaultZoom = hSim->prms.worldRad / 200.f; +    zoom = defaultZoom; +    prevZoom = zoom; +    trgtZoom = zoom; + +    step = 1; +    update(); +} + + +void Camera::onResize() +{ +    sf::View view = hSim->window.getView(); +    sf::Vector2u winSize = hSim->window.getSize(); +    view.setSize(winSize.x * zoom, winSize.y * zoom); +    view.setCenter(currentCrds); +    hSim->window.setView(view); +} + + +void Camera::onClick() +{ +    prevZoom = zoom; +    trgtZoom = zoom; + +    prevCrds = currentCrds; + +    sf::Vector2f mousePos = hSim->window.mapPixelToCoords(sf::Mouse::getPosition(hSim->window)); +    b2Vec2 b2MousePos(mousePos.x, mousePos.y); +    b2Vec2 additive(0.001f, 0.001f); +    b2AABB aabb; +    aabb.upperBound = b2MousePos + additive; +    aabb.lowerBound = b2MousePos - additive; + +    QueryCallback callback; +    callback.m_point = b2MousePos; +    hSim->tank.world.QueryAABB(&callback, aabb); + +    if (callback.m_fixture) +    { +        trgtBody = callback.m_fixture->GetBody(); +    } +    else +    { +        trgtCrds = mousePos; +        trgtBody = nullptr; +    } + +    isOnFollow = false; +    step = Params::CAM_STEPS; +} + + +void Camera::shift(const sf::Vector2f &newTrgtCrds, int zoomInOut, bool release) +{ +    if (!release && trgtBody) +    { +        prevCrds = sf::Vector2f(trgtBody->GetPosition().x, trgtBody->GetPosition().y); +        currentCrds =  sf::Vector2f(trgtBody->GetPosition().x, trgtBody->GetPosition().y); +        trgtCrds = sf::Vector2f(trgtBody->GetPosition().x, trgtBody->GetPosition().y); +        isOnFollow = true; +    } +    else +    { +        trgtBody = nullptr; +        prevCrds = currentCrds; +        trgtCrds = newTrgtCrds; +        isOnFollow = false; +    } + +    prevZoom = zoom; +    if (zoomInOut) +    { +        if (zoomInOut == 1) +        { +            trgtZoom = zoom - 0.2f * zoom; +        } +        else if (zoomInOut == -1) +        { +            trgtZoom = zoom + 0.2f * zoom; +        } +        else if (zoomInOut == -2) +        { +            trgtZoom = defaultZoom; +        } +    } +    else +    { +        trgtZoom = zoom; +    } + +    step = Params::CAM_STEPS; +} + +void Camera::update() +{ +    if (trgtBody || step) +    { +        if (!step) +        { +            currentCrds = sf::Vector2f(trgtBody->GetPosition().x, trgtBody->GetPosition().y); +        } +        else +        { +            if (trgtBody) +            { +                trgtCrds = sf::Vector2f(trgtBody->GetPosition().x, trgtBody->GetPosition().y); +                if (isOnFollow) +                { +                    prevCrds = trgtCrds; +                } +            } + +            float f1 = (step - 1.f) / (float)Params::CAM_STEPS; +            float f2 = 1.f - f1; + +            currentCrds = prevCrds * f1 + trgtCrds * f2; +            zoom = prevZoom * f1 + trgtZoom * f2; + +            if (currentCrds.x * currentCrds.x + currentCrds.y * currentCrds.y > hSim->prms.worldRad * hSim->prms.worldRad) +            { +                float angle = atanf(currentCrds.y / currentCrds.x); +                currentCrds.y = sinf(angle) * hSim->prms.worldRad * (currentCrds.x > 1.f ? 1.f : -1.f); +                currentCrds.x = cosf(angle) * hSim->prms.worldRad * (currentCrds.x > 1.f ? 1.f : -1.f); +            } + +            if (zoom > defaultZoom) +            { +                zoom = defaultZoom; +            } +            if (zoom < Params::MAX_ZOOM) +            { +                zoom = Params::MAX_ZOOM; +            } + +            --step; +        } + +        sf::View view = hSim->window.getView(); +        view.setCenter(currentCrds); +        view.setSize(hSim->window.getSize().x * zoom, hSim->window.getSize().y * zoom); +        hSim->window.setView(view); +    } + +    // Prepare low zoom graphics +    hSim->tank.worldEdges.setOutlineThickness(zoom * 2.f); + +    if (zoom > Params::ZAPPER_RAD / 2.f) +    { +        if (zoom > Params::ZAPPER_RAD * 2.f) +        { +            setCenterRad(hSim->zapperPoint, zoom); +            hSim->zapperPoint.setFillColor(hSim->prms.zapperColor); +        } +        else +        { +            setCenterRad(hSim->zapperPoint, Params::ZAPPER_RAD); +            hSim->zapperPoint.setFillColor(hSim->prms.zapperColor); +        } +    } +    else +    { +        setCenterRad(hSim->zapperShell, hSim->prms.ZAPPER_RAD - zoom * 2.f); +        hSim->zapperShell.setOutlineThickness(zoom * 2.f); +    } + +    if (zoom > Params::PELLET_RAD / 2.f) +    { +        if (zoom > Params::PELLET_RAD * 2.f) +        { +            setCenterRad(hSim->pelletPoint, zoom); +            hSim->pelletPoint.setFillColor(hSim->prms.pelletColor); +        } +        else +        { +            setCenterRad(hSim->pelletPoint, Params::PELLET_RAD); +            hSim->pelletPoint.setFillColor(hSim->prms.pelletColor); +        } +    } +    else +    { +        setCenterRad(hSim->pelletShell, hSim->prms.PELLET_RAD - zoom * 2.f); +        hSim->pelletShell.setOutlineThickness(zoom * 2.f); +    } + +    if (zoom > Params::CORPSE_RAD / 2.f) +    { +        if (zoom > Params::CORPSE_RAD * 2.f) +        { +            setCenterRad(hSim->corpsePoint, zoom); +            hSim->corpsePoint.setFillColor(hSim->prms.corpseColor); +        } +        else +        { +            setCenterRad(hSim->corpsePoint, Params::CORPSE_RAD); +            hSim->corpsePoint.setFillColor(hSim->prms.corpseColor); +        } +    } +    else +    { +        setCenterRad(hSim->corpseShell, hSim->prms.CORPSE_RAD - zoom * 2.f); +        hSim->corpseShell.setOutlineThickness(zoom * 2.f); +    } + +    if (zoom > Params::EGG_RAD / 2.f) +    { +        if (zoom > Params::EGG_RAD * 2.f) +        { +            setCenterRad(hSim->eggPoint, zoom); +            hSim->eggPoint.setFillColor(hSim->guppN); +        } +        else +        { +            setCenterRad(hSim->eggPoint, Params::EGG_RAD); +            hSim->eggPoint.setFillColor(hSim->guppN); +        } +    } +    else +    { +        setCenterRad(hSim->guppieEgg, hSim->prms.EGG_RAD - zoom * 2.f); +        hSim->guppieEgg.setOutlineThickness(zoom * 2.f); +    } + +    if (zoom > Params::GUPPIE_RAD) +    { +        setCenterRad(hSim->guppiePoint, zoom); +    } + +    else if (zoom > 0.02f) +    { +        setCenterRad(hSim->guppieShell, Params::GUPPIE_RAD - zoom); +        hSim->guppieShell.setOutlineThickness(zoom); +    } +} diff --git a/Simulation/Camera.hpp b/Simulation/Camera.hpp new file mode 100644 index 0000000..b6ee8a9 --- /dev/null +++ b/Simulation/Camera.hpp @@ -0,0 +1,42 @@ +#ifndef __CAMERA_HPP__ +#define __CAMERA_HPP__ + +#include <SFML/Graphics.hpp> +#include <Box2D.h> + +class SimBase; + +class Camera +{ +public: +    void create(SimBase *sim); +    void onResize(); +    void onClick(); +    void shift(const sf::Vector2f &newTrgtCrds, int zoomInOut = 0, bool release = true); +    void update(); + +    sf::Vector2f getCrds() const { return currentCrds; } +    float getZoom() const { return zoom; } + +private: +    SimBase *hSim = nullptr; + +    float defaultZoom = 0.f; +    float zoom = 0.f; +    float prevZoom = 0.f; +    float trgtZoom = 0.f; +    sf::Vector2f currentCrds = { 0.f, 0.f }; +    sf::Vector2f prevCrds = { 0.f, 0.f }; +    sf::Vector2f trgtCrds = { 0.f, 0.f }; +    unsigned step = 0; +    bool isOnFollow = false; +    b2Body *trgtBody = nullptr; + +    friend class ContactListener; +    friend class Zapper; +    friend class Pellet; +    friend class Guppie; +    friend class Corpse; +}; + +#endif // __CAMERA_HPP__ diff --git a/Simulation/ContactListener.cpp b/Simulation/ContactListener.cpp new file mode 100644 index 0000000..6c42365 --- /dev/null +++ b/Simulation/ContactListener.cpp @@ -0,0 +1,168 @@ +#include "ContactListener.hpp" +#include "SimBase.hpp" + +void ContactListener::PreSolve(b2Contact *contact, const b2Manifold *oldManifold) +{ +    b2Body *bodyA = contact->GetFixtureA()->GetBody(); +    b2Body *bodyB = contact->GetFixtureB()->GetBody(); + +    Guppie *gp1 = nullptr; +    Guppie *gp2 = nullptr; + +    if (bodyA->GetType() == b2_dynamicBody) +    { +        if (((Entity *)bodyA->GetUserData())->radius == Params::GUPPIE_RAD) +        { +            if (((Guppie *)bodyA->GetUserData())->isActive) +            { +                gp1 = (Guppie *)bodyA->GetUserData(); +            } +        } +    } +    if (bodyB->GetType() == b2_dynamicBody) +    { +        if (((Entity *)bodyB->GetUserData())->radius == Params::GUPPIE_RAD) +        { +            if (((Guppie *)bodyB->GetUserData())->isActive) +            { +                gp2 = (Guppie *)bodyB->GetUserData(); +            } +        } +    } + +    // If only one body is a Guppie +    if ((gp1 && !gp2) || (!gp1 && gp2)) +    { +        Guppie *gp = gp1 ? gp1 : gp2; +        b2Body *bd = gp1 ? bodyB : bodyA; + +        if (bd->GetType() == b2_staticBody) +        { +            gp->lastContactStep = hSim->prms.contactSteps; +            gp->lastContact = Guppie::ZAPPER; +            gp->energy += hSim->prms.etFromZapper; +        } +        else +        { +            if (((Entity *)bd->GetUserData())->radius == Params::ZAPPER_RAD) +            { +                gp->lastContactStep = hSim->prms.contactSteps; +                gp->lastContact = Guppie::ZAPPER; +                gp->energy += hSim->prms.etFromZapper; +            } +            else +            { +                sf::Transform trans; +                trans.translate(gp->body->GetPosition().x, gp->body->GetPosition().y); +                trans.rotate(gp->body->GetAngle() * Params::RAD_DGRS); +                sf::Vector2f beak1 = trans.transformPoint(hSim->beak1.getPoint(1)); +                sf::Vector2f beak2 = trans.transformPoint(hSim->beak2.getPoint(1)); +                float cateteX1 = beak1.x - bd->GetPosition().x; +                float cateteY1 = beak1.y - bd->GetPosition().y; +                float cateteX2 = beak2.x - bd->GetPosition().x; +                float cateteY2 = beak2.y - bd->GetPosition().y; +                if (((Entity *)bd->GetUserData())->radius == Params::PELLET_RAD) +                { +                    if (cateteX1 * cateteX1 + cateteY1 * cateteY1 < hSim->prms.PELLET_RAD * hSim->prms.PELLET_RAD || +                        cateteX2 * cateteX2 + cateteY2 * cateteY2 < hSim->prms.PELLET_RAD * hSim->prms.PELLET_RAD) +                    { +                        gp->lastContactStep = hSim->prms.contactSteps; +                        gp->lastContact = Guppie::PELLET; +                        gp->energy += hSim->prms.etFromPellet; +                        gp->fitness += hSim->prms.forPellet; +                        ((Pellet *)bd->GetUserData())->toBeDestroyed = true; +                        if (hSim->camera.trgtBody == bd) +                        { +                            hSim->camera.prevCrds = hSim->camera.currentCrds; +                            hSim->camera.step = Params::CAM_STEPS; +                            hSim->camera.trgtBody = gp->body; +                        } +                    } +                } +                else if (((Entity *)bd->GetUserData())->radius == Params::CORPSE_RAD) +                { +                    if (cateteX1 * cateteX1 + cateteY1 * cateteY1 < hSim->prms.CORPSE_RAD * hSim->prms.CORPSE_RAD || +                        cateteX2 * cateteX2 + cateteY2 * cateteY2 < hSim->prms.CORPSE_RAD * hSim->prms.CORPSE_RAD) +                    { +                        gp->lastContactStep = hSim->prms.contactSteps; +                        gp->lastContact = Guppie::CORPSE; +                        gp->energy += ((Corpse *)bd->GetUserData())->energy; +                        gp->fitness += hSim->prms.forCorpse * (((Corpse *)bd->GetUserData())->energy / hSim->prms.etFromCorpse); +                        ((Corpse *)bd->GetUserData())->toBeDestroyed = true; +                        if (hSim->camera.trgtBody == bd) +                        { +                            hSim->camera.prevCrds = hSim->camera.currentCrds; +                            hSim->camera.step = Params::CAM_STEPS; +                            hSim->camera.trgtBody = gp->body; +                        } +                    } +                } +            } +        } + +        if (gp->energy > hSim->prms.maxEnergy) +        { +            gp->energy = hSim->prms.maxEnergy; +        } +    } +    // If both bodies are guppies +    else if (gp1 && gp2) +    { +        sf::Transform trans1; +        sf::Transform trans2; +        trans1.translate(gp1->body->GetPosition().x, gp1->body->GetPosition().y); +        trans1.rotate(gp1->body->GetAngle() * Params::RAD_DGRS); +        trans2.translate(gp2->body->GetPosition().x, gp2->body->GetPosition().y); +        trans2.rotate(gp2->body->GetAngle() * Params::RAD_DGRS); + +        sf::Vector2f beak1_1 = trans1.transformPoint(hSim->beak1.getPoint(1)); +        sf::Vector2f beak1_2 = trans1.transformPoint(hSim->beak2.getPoint(1)); +        sf::Vector2f beak2_1 = trans2.transformPoint(hSim->beak1.getPoint(1)); +        sf::Vector2f beak2_2 = trans2.transformPoint(hSim->beak2.getPoint(1)); + +        float cateteX1_1 = beak1_1.x - gp2->body->GetPosition().x; +        float cateteY1_1 = beak1_1.y - gp2->body->GetPosition().y; +        float cateteX2_1 = beak2_1.x - gp2->body->GetPosition().x; +        float cateteY2_1 = beak2_1.y - gp2->body->GetPosition().y; +        float cateteX1_2 = beak1_2.x - gp1->body->GetPosition().x; +        float cateteY1_2 = beak1_2.y - gp1->body->GetPosition().y; +        float cateteX2_2 = beak2_2.x - gp1->body->GetPosition().x; +        float cateteY2_2 = beak2_2.y - gp1->body->GetPosition().y; + +        if (cateteX1_1 * cateteX1_1 + cateteY1_1 * cateteY1_1 < hSim->prms.GUPPIE_RAD * hSim->prms.GUPPIE_RAD || +            cateteX2_1 * cateteX2_1 + cateteY2_1 * cateteY2_1 < hSim->prms.GUPPIE_RAD * hSim->prms.GUPPIE_RAD) +        { +            gp1->lastContactStep = hSim->prms.contactSteps; +            gp1->lastContact = Guppie::GUPPIE; +            gp1->energy += hSim->prms.etFromGuppie; +            gp1->fitness += hSim->prms.forGuppie; + +            gp2->lastContactStep = hSim->prms.contactSteps; +            gp2->lastContact = Guppie::GUPPIE; +            gp2->energy -= hSim->prms.etFromGuppie; +        } + +        if (cateteX1_2 * cateteX1_2 + cateteY1_2 * cateteY1_2 < hSim->prms.GUPPIE_RAD * hSim->prms.GUPPIE_RAD || +            cateteX2_2 * cateteX2_2 + cateteY2_2 * cateteY2_2 < hSim->prms.GUPPIE_RAD * hSim->prms.GUPPIE_RAD) +        { +            gp1->lastContactStep = hSim->prms.contactSteps; +            gp1->lastContact = Guppie::GUPPIE; +            gp1->energy -= hSim->prms.etFromGuppie; + +            gp2->lastContactStep = hSim->prms.contactSteps; +            gp2->lastContact = Guppie::GUPPIE; +            gp2->energy += hSim->prms.etFromGuppie; +            gp2->fitness += hSim->prms.forGuppie; +        } + +        if (gp1->energy > hSim->prms.maxEnergy) +        { +            gp1->energy = hSim->prms.maxEnergy; +        } + +        if (gp2->energy > hSim->prms.maxEnergy) +        { +            gp2->energy = hSim->prms.maxEnergy; +        } +    } +} diff --git a/Simulation/ContactListener.hpp b/Simulation/ContactListener.hpp new file mode 100644 index 0000000..4b4d472 --- /dev/null +++ b/Simulation/ContactListener.hpp @@ -0,0 +1,16 @@ +#ifndef __CONTACTLISTENER_HPP__ +#define __CONTACTLISTENER_HPP__ + +#include <Box2D.h> + +class SimBase; + +class ContactListener : public b2ContactListener +{ +public: +    void PreSolve(b2Contact *contact, const b2Manifold *oldManifold); + +    SimBase *hSim = nullptr; +}; + +#endif // __CONTACTLISTENER_HPP__ diff --git a/Simulation/Corpse.cpp b/Simulation/Corpse.cpp new file mode 100644 index 0000000..82879ed --- /dev/null +++ b/Simulation/Corpse.cpp @@ -0,0 +1,99 @@ +#include "Corpse.hpp" +#include "SimBase.hpp" + +void Corpse::startup(SimBase *sim) +{ +    hSim = sim; +    radius = Params::CORPSE_RAD; +} + +void Corpse::setup(b2Vec2 pos, b2Vec2 lVel, float angle, float aVel) +{ +    if (!body) +    { +        return; +    } + +    lifetime = hSim->prms.corpseDecayTime; +    energy = hSim->prms.etFromCorpse; + +    body->SetTransform(pos, angle); +    body->SetLinearVelocity(lVel); +    body->SetAngularVelocity(aVel); +} + + +void Corpse::update() +{ +    if (!body) +    { +        return; +    } + +    if (hSim->prms.corpseDecay) +    { +        --lifetime; +        energy = hSim->prms.etFromCorpse * ((float)lifetime / (float)hSim->prms.corpseDecayTime); +    } + +    if (!lifetime || toBeDestroyed) +    { +        destroy(); +    } +} + + +void Corpse::destroy() +{ +    if (hSim->camera.trgtBody == body) +    { +        hSim->camera.prevCrds = hSim->camera.currentCrds; +        hSim->camera.trgtCrds = hSim->camera.currentCrds; +        hSim->camera.step = 0; +        hSim->camera.trgtBody = nullptr; +    } + +    toBeDestroyed = false; +    hSim->tank.world.DestroyBody(body); +    body = nullptr; + +    --hSim->corpseCount; +    hSim->text.corpseCnt.setString("Corpse count: " + nts(hSim->corpseCount)); +} + + +void Corpse::draw() +{ +    if (!body) +    { +        return; +    } + +    sf::Vector2f vSize = hSim->window.getView().getSize(); +    sf::Vector2f vCent = hSim->window.getView().getCenter(); +    if ( body->GetPosition().x + radius < vCent.x - vSize.x / 2.f || +         body->GetPosition().y + radius < vCent.y - vSize.y / 2.f || +         body->GetPosition().x - radius > vCent.x + vSize.x / 2.f || +         body->GetPosition().y - radius > vCent.y + vSize.y / 2.f  ) +    { +        return; +    } + +    sf::Color corpseColor = mix(hSim->prms.corpseColor, hSim->prms.worldColor, ((float)lifetime / (float)hSim->prms.corpseDecayTime)); +    if (hSim->camera.zoom > Params::CORPSE_RAD / 2.f) +    { +        hSim->corpsePoint.setFillColor(corpseColor); +        hSim->corpsePoint.setPosition(body->GetPosition().x, body->GetPosition().y); +        hSim->window.draw(hSim->corpsePoint); +    } +    else +    { +        hSim->corpseShell.setOutlineColor(corpseColor); +        hSim->corpseShell.setPosition(body->GetPosition().x, body->GetPosition().y); +        hSim->corpseNucleus.setFillColor(corpseColor); +        hSim->corpseNucleus.setPosition(body->GetPosition().x, body->GetPosition().y); +        hSim->corpseNucleus.setRotation(body->GetAngle() * Params::RAD_DGRS); +        hSim->window.draw(hSim->corpseShell); +        hSim->window.draw(hSim->corpseNucleus); +    } +} diff --git a/Simulation/Corpse.hpp b/Simulation/Corpse.hpp new file mode 100644 index 0000000..0153c24 --- /dev/null +++ b/Simulation/Corpse.hpp @@ -0,0 +1,28 @@ +#ifndef __CORPSE_HPP__ +#define __CORPSE_HPP__ + +#include "Entity.hpp" + +class Corpse : public Entity +{ +public: +    void startup(SimBase *sim); +    void update(); +    void draw(); + +    bool isCreated() const { return body ? true : false; } +    bool toBeDestroyed = false; + +    void setup(b2Vec2 pos, b2Vec2 lVel, float angle, float aVel); + +private: +    void destroy(); + +    unsigned lifetime = 0; +    float energy = 0.f; + +    friend class ContactListener; +    friend class Guppie; +}; + +#endif // __CORPSE_HPP__ diff --git a/Simulation/Entity.cpp b/Simulation/Entity.cpp new file mode 100644 index 0000000..d5ccb75 --- /dev/null +++ b/Simulation/Entity.cpp @@ -0,0 +1,59 @@ +#include "Entity.hpp" +#include "SimBase.hpp" + +void Entity::create() +{ +    if (!hSim || body || !radius) +    { +        return; +    } + +    b2BodyDef bodyDef; +    bodyDef.type = b2_dynamicBody; +    bodyDef.position = { hSim->prms.worldRad, hSim->prms.worldRad }; +    float hypotenuse = hSim->prms.worldRad - radius; +    while (bodyDef.position.x * bodyDef.position.x + bodyDef.position.y * bodyDef.position.y > hypotenuse * hypotenuse) +    { +        bodyDef.position.x = realRand(-hSim->prms.worldRad, hSim->prms.worldRad); +        bodyDef.position.y = realRand(-hSim->prms.worldRad, hSim->prms.worldRad); +    } +    bodyDef.angle = realRand(0.f, 360.f / Params::RAD_DGRS); +    bodyDef.linearDamping = 0.1f; +    bodyDef.angularDamping = 0.1f; +    bodyDef.allowSleep = true; +    bodyDef.userData = this; + +    body = hSim->tank.world.CreateBody(&bodyDef); + +    b2CircleShape shape; +    shape.m_radius = radius; + +    b2FixtureDef fixture; +    fixture.shape = &shape; +    fixture.friction = 1.f; +    fixture.restitution = 1.f; +    fixture.density = 1.f; + +    body->CreateFixture(&fixture); + +    if (radius == Params::PELLET_RAD) +    { +        ++hSim->pelletCount; +        hSim->text.pelletCnt.setString("Pellet count: " + nts(hSim->pelletCount) + " / " + nts(hSim->prms.pelletQtty)); +    } +    else if (radius == Params::CORPSE_RAD) +    { +        ++hSim->corpseCount; +        hSim->text.corpseCnt.setString("Corpse count: " + nts(hSim->corpseCount)); +    } +    else if (radius == Params::GUPPIE_RAD) +    { +        ++hSim->guppieCount; +        hSim->text.guppieCnt.setString("Guppie count: " + nts(hSim->guppieCount)); +        ((Guppie *)this)->energy = hSim->prms.initEnergy; +        body->SetLinearDamping(2.f); +        body->SetAngularDamping(2.f); + +        ((b2CircleShape *)body->GetFixtureList()->GetShape())->m_radius = hSim->prms.EGG_RAD; +    } +} diff --git a/Simulation/Entity.hpp b/Simulation/Entity.hpp new file mode 100644 index 0000000..0374611 --- /dev/null +++ b/Simulation/Entity.hpp @@ -0,0 +1,34 @@ +#ifndef __ENTITY_HPP__ +#define __ENTITY_HPP__ + +#include <SFML/Graphics.hpp> +#include <Box2D.h> + +class SimBase; + +class Entity +{ +public: +    virtual void startup(SimBase *sim) = 0; + +    void create(); +    void destroy(); + +    virtual void update() = 0; +    virtual void draw() = 0; + +    float getRadius() const { return radius; } + +protected: +    SimBase *hSim = nullptr; + +    b2Body *body = nullptr; +    float radius = 0.f; +    float resistance = 0.f; + +    friend class ContactListener; +    friend class Guppie; +    friend class Camera; +}; + +#endif // __ENTITY_HPP__ diff --git a/Simulation/GraphicObjs.hpp b/Simulation/GraphicObjs.hpp new file mode 100644 index 0000000..8fab10e --- /dev/null +++ b/Simulation/GraphicObjs.hpp @@ -0,0 +1,56 @@ +#ifndef __GRAPHICOBJS_HPP__ +#define __GRAPHICOBJS_HPP__ + +#include <SFML/Graphics.hpp> + +class GraphicObjs +{ +protected: +    sf::CircleShape zapperPoint = sf::CircleShape(0, 12); +    sf::CircleShape zapperShell; +    sf::CircleShape zapperNucleus = sf::CircleShape(0, 5); + +    sf::CircleShape pelletPoint = sf::CircleShape(0, 6); +	sf::CircleShape pelletShell; +	sf::CircleShape pelletNucleus = sf::CircleShape(0, 3); + +	sf::CircleShape guppiePoint = sf::CircleShape(0, 6); +	sf::CircleShape guppieShell = sf::CircleShape(0, 30); +	/////////////// +	sf::CircleShape eggPoint = sf::CircleShape(0, 6); +	sf::CircleShape guppieEgg = sf::CircleShape(0, 60); +	/////////////// +	sf::Color guppN; +	sf::Vector2f visionCone[31]; +	sf::Vector2f skinRadius[180]; +	sf::Vector2f senseRadius[180]; +	///////////////////////////// +	sf::CircleShape gOuterSkin; +	/////////////////////////// +	sf::ConvexShape beak1; +	sf::ConvexShape beak2; +	/////////////////////////// +	sf::ConvexShape touchCells[30]; +	sf::ConvexShape smellCells[30]; +	sf::ConvexShape tailMask; +	////////////////////////////// +	sf::CircleShape innerBodyMask; +	///////////////////////////// +	sf::ConvexShape thrusters[4]; +	sf::ConvexShape thrusterMask; +	sf::ConvexShape heart; +	sf::ConvexShape heartMask; +	sf::ConvexShape heartTop; +	sf::ConvexShape heartTri; +	sf::ConvexShape glandMask; +	sf::ConvexShape gland; +	sf::ConvexShape eyeMask; +	sf::ConvexShape eyeCells[15]; +	sf::ConvexShape eyeCavity; + +    sf::CircleShape corpsePoint = sf::CircleShape(0, 6); +	sf::CircleShape corpseShell; +	sf::CircleShape corpseNucleus = sf::CircleShape(0, 4); +}; + +#endif // __GRAPHICOBJS_HPP__ diff --git a/Simulation/Guppie.cpp b/Simulation/Guppie.cpp new file mode 100644 index 0000000..91788c2 --- /dev/null +++ b/Simulation/Guppie.cpp @@ -0,0 +1,584 @@ +#include "Guppie.hpp" +#include "SimFitness.hpp" + +void Guppie::startup(SimBase *sim) +{ +    hSim = sim; +    radius = Params::GUPPIE_RAD; +} + +void Guppie::update() +{ +    if (!body) +    { +        return; +    } + +    // Update states +    ++lifetime; + +    if (lifetime < hSim->prms.activationDelay) +    { +        return; +    } + +    if (lifetime == hSim->prms.activationDelay) +    { +        isActive = true; +        skinColor = hSim->guppN; +        glandState = ALPHA; + +        ((b2CircleShape *)body->GetFixtureList()->GetShape())->m_radius = hSim->prms.GUPPIE_RAD; +    } + +    fitness += energy; + +    energy -= (1.f / 60.f) + (float)lifetime / (hSim->prms.agingRate * 3600.f); + +    if (energy < 0.f) +    { +        destroy(); +        return; +    } + +    if (lastContactStep) +    { +        --lastContactStep; +    } + +    if (skinColor.r) +    { +        skinColor.r -= 1; +    } +    if (skinColor.g) +    { +        skinColor.g -= 1; +    } +    if (skinColor.b) +    { +        skinColor.b -= 1; +    } + +    // Update sensors +    sf::Transform trans; +    trans.translate(body->GetPosition().x, body->GetPosition().y); +    trans.rotate(body->GetAngle() * Params::RAD_DGRS); +    // Eye sensors +    sf::Vector2f eyePoint = trans.transformPoint(hSim->visionCone[30]); +    for (int i = 0; i < 15; ++i) +    { +        RayCastCallback points[2]; +        sf::Color colors[2]; +        for (int j = 0; j < 2; ++j) +        { +            colors[j] = hSim->prms.worldColor; +            sf::Vector2f eyeLine = trans.transformPoint(hSim->visionCone[i * 2 + j]); +            hSim->tank.world.RayCast(&points[j], b2Vec2(eyePoint.x, eyePoint.y), b2Vec2(eyeLine.x, eyeLine.y)); +            if (points[j].m_fixture) +            { +                if (points[j].m_fixture->GetBody()->GetType() == b2_staticBody) +                { +                    colors[j] = mix(hSim->prms.worldColor, hSim->prms.zapperColor, points[j].m_fraction); +                } +                else +                { +                    Entity *entity = (Entity *)points[j].m_fixture->GetBody()->GetUserData(); +                    if (entity->radius == Params::ZAPPER_RAD) +                    { +                        colors[j] = mix(hSim->prms.worldColor, hSim->prms.zapperColor, points[j].m_fraction); +                    } +                    else if (entity->radius == Params::EGG_RAD) +                    { +                        colors[j] = mix(hSim->prms.worldColor, hSim->guppN, points[j].m_fraction); +                    } +                    else if (entity->radius == Params::PELLET_RAD) +                    { +                        colors[j] = mix(hSim->prms.worldColor, hSim->prms.pelletColor, points[j].m_fraction); +                    } +                    else if (entity->radius == Params::CORPSE_RAD) +                    { +                        sf::Color corpseColor = mix(hSim->prms.corpseColor, hSim->prms.worldColor, ((float)((Corpse *)entity)->lifetime / (float)hSim->prms.corpseDecayTime)); +                        colors[j] = mix(hSim->prms.worldColor, corpseColor, points[j].m_fraction); +                    } +                    else if (entity->radius == Params::GUPPIE_RAD) +                    { +                        if (!((Guppie *)entity)->isActive) +                        { +                            colors[j] = mix(hSim->prms.worldColor, hSim->guppN, points[j].m_fraction); +                        } +                        else +                        { +                            colors[j] = mix(hSim->prms.worldColor, ((Guppie *)entity)->skinColor, points[j].m_fraction); +                        } +                    } +                } +            } +        } + +        sf::Color sum = mix(colors[0], colors[1], 0.5f); +        eyeR[i] = (float)sum.r / 256.f; +        eyeG[i] = (float)sum.g / 256.f; +        eyeB[i] = (float)sum.b / 256.f; +    } +    // Skin sensors +    for (int i = 0; i < 30; ++i) +    { +        RayCastCallback points[6]; +        float valueTouch = 0.f; +//        float valueSmell = 0.f; +        for (int j = 0; j < 6; ++j) +        { +            sf::Vector2f skinPoint = trans.transformPoint(hSim->skinRadius[i * 6 + j]); +            sf::Vector2f senseLine = trans.transformPoint(hSim->senseRadius[i * 6 + j]); +            hSim->tank.world.RayCast(&points[j], b2Vec2(skinPoint.x, skinPoint.y), b2Vec2(senseLine.x, senseLine.y)); +            if (points[j].m_fixture) +            { +                valueTouch += 1.f - points[j].m_fraction; +                // FOR SWARM SIMULATION STYLE +//                if (points[j].m_fixture->GetBody()->GetType() == b2_dynamicBody) +//                { +//                    Entity *entity = (Entity *)points[j].m_fixture->GetBody()->GetUserData(); +//                    if (entity->radius == Params::GUPPIE_RAD) +//                    { +//                        if (((Guppie *)entity)->isActive) +//                        { +//                            if (((Guppie *)entity)->glandState == glandState) +//                            { +//                                valueSmell += 1.f - points[j].m_fraction; +//                            } +//                        } +//                    } +//                } +            } +        } + +        touch[i] = valueTouch / 6.f; +//        smell[i] = valueSmell / 6.f; +    } +    // Current color sensor +    cColorR = (float)skinColor.r / 255.f; +    cColorG = (float)skinColor.g / 255.f; +    cColorB = (float)skinColor.b / 255.f; +    // Current gland color sensor +//    cGland1 = 0.f; +//    cGland2 = 0.f; +//    cGland3 = 0.f; +//    if (glandState == ALPHA) +//    { +//        cGland1 = 1.f; +//    } +//    else if (glandState == BETTA) +//    { +//        cGland2 = 1.f; +//    } +//    else +//    { +//        cGland3 = 1.f; +//    } +    // Speed sensors +    b2Vec2 axisSpeed = body->GetLinearVelocity(); +    sf::Transform rotat; +    rotat.rotate(body->GetAngle() * Params::RAD_DGRS); +    sf::Vector2f fwdDirect = rotat.transformPoint(sf::Vector2f(0.f, -1.f)); +    sf::Vector2f sdeDirect = rotat.transformPoint(sf::Vector2f(1.f, 0.f)); +    fwdSpeed = sigmoid(axisSpeed.x * fwdDirect.x + axisSpeed.y * fwdDirect.y); +    sideSpeed = sigmoid(axisSpeed.x * sdeDirect.x + axisSpeed.y * sdeDirect.y); +    // Rotation sensor +    rotation = sigmoid(body->GetAngularVelocity()); +    // Low energy sensor +    lowEnergy = sigmoid(energy / 10.f) * 2.f - 1.f; + +    // Merge inputs and get response from neural net +    std::vector<float> inputs; +    inputs.insert(inputs.end(), eyeR.begin(), eyeR.end()); +    inputs.insert(inputs.end(), eyeG.begin(), eyeG.end()); +    inputs.insert(inputs.end(), eyeB.begin(), eyeB.end()); +    inputs.insert(inputs.end(), touch.begin(), touch.end()); +//    inputs.insert(inputs.end(), smell.begin(), smell.end()); +    inputs.push_back(cColorR); +    inputs.push_back(cColorG); +    inputs.push_back(cColorB); +//    inputs.push_back(cGland1); +//    inputs.push_back(cGland2); +//    inputs.push_back(cGland3); +    inputs.push_back(fwdSpeed); +    inputs.push_back(sideSpeed); +    inputs.push_back(rotation); +    inputs.push_back(lowEnergy); + +    std::vector<float> outputs = neuralNet->io(inputs); + +    // Apply outputs +    thruster1 = outputs[0]; +    thruster2 = outputs[1]; +    addClrR = outputs[2]; +    addClrG = outputs[3]; +    addClrB = outputs[4]; +//    addGln1 = outputs[5]; +//    addGln2 = outputs[6]; +//    addGln3 = outputs[7]; + +    // Handle outputs +    // Guppies gain fitness by going straight forward +    if (thruster1 + thruster2 > 1.f) +    { +        float forwardness = thruster1 + thruster2 - 1.f; +        fitness += forwardness * hSim->prms.forGoingStraight; +    } + +    // Apply thrust +    sf::Vector2f leftThrustPoint = trans.transformPoint(-hSim->prms.thrustRadius, 0.f); +    sf::Vector2f rightThrustPoint = trans.transformPoint(hSim->prms.thrustRadius, 0.f); + +    thruster1 = thruster1 * 2.f - 1.f; +    sf::Vector2f thrLeft = rotat.transformPoint(-hSim->prms.thrustRadius, thruster1 * hSim->prms.thrustForce); +    body->ApplyForce(b2Vec2(thrLeft.x, thrLeft.y), b2Vec2(leftThrustPoint.x, leftThrustPoint.y)); + +    thruster2 = thruster2 * 2.f - 1.f; +    sf::Vector2f thrRight = rotat.transformPoint(hSim->prms.thrustRadius, thruster2 * hSim->prms.thrustForce); +    body->ApplyForce(b2Vec2(thrRight.x, thrRight.y), b2Vec2(rightThrustPoint.x, rightThrustPoint.y)); +    // Substract energy +//    energy -= fabsf(thruster1 / 60.f); +//    energy -= fabsf(thruster2 / 60.f); + +    // Apply color change +    float temp = 0.f; +    if ((temp = skinColor.r + addClrR * 8.f) < 255) +    { +        skinColor.r += temp; +    } +    else +    { +        skinColor.r = 255; +    } +    if ((temp = skinColor.g + addClrG * 8.f) < 255) +    { +        skinColor.g += temp; +    } +    else +    { +        skinColor.g = 255; +    } +    if ((temp = skinColor.b + addClrB * 8.f) < 255) +    { +        skinColor.b += temp; +    } +    else +    { +        skinColor.b = 255; +    } +    // Substract energy +//    energy -= addClrR / 60.f; +//    energy -= addClrG / 60.f; +//    energy -= addClrB / 60.f; +    // Apply gland change +//    nGland1 += addGln1; +//    nGland2 += addGln2; +//    nGland3 += addGln3; +//    bool changeGln = false; +//    if (nGland1 > 30.f) +//    { +//        glandState = ALPHA; +//        changeGln = true; +//    } +//    else if (nGland2 > 30.f) +//    { +//        glandState = BETTA; +//        changeGln = true; +//    } +//    else if (nGland3 > 30.f) +//    { +//        glandState = GAMMA; +//        changeGln = true; +//    } +//    if (changeGln) +//    { +//        nGland1 = 0.f; +//        nGland2 = 0.f; +//        nGland3 = 0.f; +//    } +//    // Substract energy +//    energy -= addGln1 / 60.f; +//    energy -= addGln2 / 60.f; +//    energy -= addGln3 / 60.f; + +    if (hSim->prms.simStyle == SELECTION_BY_FITNESS) +    { +        if (fitness > ((SimFitness *)hSim)->fitnessRecord) +        { +            ((SimFitness *)hSim)->fitnessRecord = fitness; +            hSim->text.longestLife.setString("Fitness record: " + nts(((SimFitness *)hSim)->fitnessRecord)); +        } +    } +} + + +void Guppie::destroy() +{ +    if (!body) +    { +        return; +    } + +    --hSim->guppieCount; +    hSim->text.guppieCnt.setString("Guppie count: " + nts(hSim->guppieCount)); + +    // Place a corpse +    if (hSim->prms.leaveCorpse) +    { +        bool vacant = false; +        for (auto &i : hSim->corpses) +        { +            if (!i.isCreated()) +            { +                i.create(); +                i.setup(body->GetPosition(), body->GetLinearVelocity(), body->GetAngle(), body->GetAngularVelocity()); +                if (hSim->camera.trgtBody == body) +                { +                    hSim->camera.trgtBody = i.body; +                } +                vacant = true; +                break; +            } +        } +        if (!vacant) +        { +            hSim->corpses.push_back(Corpse()); +            hSim->corpses.back().startup(hSim); +            hSim->corpses.back().create(); +            hSim->corpses.back().setup(body->GetPosition(), body->GetLinearVelocity(), body->GetAngle(), body->GetAngularVelocity()); +            if (hSim->camera.trgtBody == body) +            { +                hSim->camera.trgtBody = hSim->corpses.back().body; +            } +        } +    } +    else if (hSim->camera.trgtBody == body) +    { +        hSim->camera.trgtBody = nullptr; +    } + +    hSim->tank.world.DestroyBody(body); +    body = nullptr; +} + + +void Guppie::draw() +{ +    if (!body) +    { +        return; +    } + +    sf::Vector2f vSize = hSim->window.getView().getSize(); +    sf::Vector2f vCent = hSim->window.getView().getCenter(); +    if ( body->GetPosition().x + radius < vCent.x - vSize.x / 2.f || +         body->GetPosition().y + radius < vCent.y - vSize.y / 2.f || +         body->GetPosition().x - radius > vCent.x + vSize.x / 2.f || +         body->GetPosition().y - radius > vCent.y + vSize.y / 2.f  ) +    { +        return; +    } + +    if (hSim->camera.zoom > Params::GUPPIE_RAD) +    { +        float blend = sigmoid((hSim->camera.zoom - Params::GUPPIE_RAD) * 8.f); +        if (isActive) +        { +            hSim->guppiePoint.setFillColor(mix(hSim->prms.worldColor, skinColor, blend)); +        } +        else +        { +            hSim->guppiePoint.setFillColor(mix(hSim->prms.worldColor, hSim->guppN, blend)); +        } + +        hSim->guppiePoint.setPosition(body->GetPosition().x, body->GetPosition().y); +        hSim->window.draw(hSim->guppiePoint); +        return; +    } + +    // Position and rotate +    float posX = body->GetPosition().x; +    float posY = body->GetPosition().y; +    float rot = body->GetAngle() * Params::RAD_DGRS; +    sf::ConvexShape *shape = nullptr; + +    hSim->gOuterSkin.setPosition(posX, posY); +    for (shape = &hSim->beak1; shape <= &hSim->tailMask; ++shape) +    { +        shape->setPosition(posX, posY); +        shape->setRotation(rot); +    } +    hSim->innerBodyMask.setPosition(posX, posY); +    hSim->innerBodyMask.setRotation(rot); +    for (shape = hSim->thrusters; shape <= &hSim->eyeCavity; ++shape) +    { +        shape->setPosition(posX, posY); +        shape->setRotation(rot); +    } + +    // Apply colors +    // When inactive +    if (!isActive) +    { +        // Skin +        hSim->beak1.setFillColor(hSim->guppN); +        hSim->beak2.setFillColor(hSim->guppN); +        hSim->gOuterSkin.setFillColor(hSim->guppN); +        for (int i = 0; i < 15; ++i) +        { +            hSim->eyeCells[i].setFillColor(hSim->prms.worldColor); +        } +        for (int i = 0; i < 30; ++i) +        { +            hSim->touchCells[i].setFillColor(hSim->guppN); +            hSim->smellCells[i].setFillColor(hSim->guppN); +        } +        // Thrusters +        for (int i = 0; i < 4; ++i) +        { +            hSim->thrusters[i].setFillColor(hSim->guppN); +        } +        // Heart +        hSim->heart.setFillColor(hSim->guppN); +        hSim->heartTop.setFillColor(hSim->guppN); +        hSim->heartTri.setFillColor(hSim->guppN); +        hSim->heartMask.setFillColor(hSim->guppN); +        // Gland +        hSim->gland.setFillColor(hSim->guppN); +    } +    // When active +    else +    { +        // Outer skin +        hSim->beak1.setFillColor(skinColor); +        hSim->beak2.setFillColor(skinColor); +        hSim->gOuterSkin.setFillColor(skinColor); +        // Eye cells +        for (int i = 0; i < 15; ++i) +        { +            sf::Color cellColor; +            cellColor.r = eyeR[i] * 255.f; +            cellColor.g = eyeG[i] * 255.f; +            cellColor.b = eyeB[i] * 255.f; +            hSim->eyeCells[i].setFillColor(cellColor); +        } +        // Skin cells +        for (int i = 0; i < 30; ++i) +        { +            hSim->touchCells[i].setFillColor(mix(hSim->prms.guppieColorI, hSim->guppN, touch[i])); +//            hSim->smellCells[i].setFillColor(mix(hSim->prms.guppieColorI, hSim->guppN, smell[i])); +        } +        // Thrusters +        if (thruster1 > 0.f) +        { +            hSim->thrusters[3].setFillColor(mix(hSim->prms.guppieColorI, hSim->guppN, thruster1)); +            hSim->thrusters[1].setFillColor(hSim->guppN); +        } +        else +        { +            hSim->thrusters[1].setFillColor(mix(hSim->prms.guppieColorI, hSim->guppN, -thruster1)); +            hSim->thrusters[3].setFillColor(hSim->guppN); +        } + +        if (thruster2 > 0.f) +        { +            hSim->thrusters[2].setFillColor(mix(hSim->prms.guppieColorI, hSim->guppN, thruster2)); +            hSim->thrusters[0].setFillColor(hSim->guppN); +        } +        else +        { +            hSim->thrusters[0].setFillColor(mix(hSim->prms.guppieColorI, hSim->guppN, -thruster2)); +            hSim->thrusters[2].setFillColor(hSim->guppN); +        } +        // Heart beat +        sf::Color beat = mix(hSim->guppN, hSim->prms.guppieColorI, sinf(((float)lifetime * (1.f + (1.f - lowEnergy))) / 15.f) / 2.f + 0.5f); +        hSim->heart.setFillColor(beat); +        hSim->heartTop.setFillColor(beat); +        hSim->heartTri.setFillColor(beat); +        // Stomach +        if (lastContactStep) +        { +            if (lastContact == ZAPPER) +            { +                hSim->heartMask.setFillColor(mix(hSim->prms.zapperColor, hSim->guppN, (float)lastContactStep / (float)hSim->prms.contactSteps)); +            } +            else if (lastContact == PELLET) +            { +                hSim->heartMask.setFillColor(mix(hSim->prms.pelletColor, hSim->guppN, (float)lastContactStep / (float)hSim->prms.contactSteps)); +            } +            else if (lastContact == CORPSE) +            { +                hSim->heartMask.setFillColor(mix(hSim->prms.corpseColor, hSim->guppN, (float)lastContactStep / (float)hSim->prms.contactSteps)); +            } +            else +            { +                hSim->heartMask.setFillColor(mix(hSim->prms.guppieColorI, hSim->guppN, (float)lastContactStep / (float)hSim->prms.contactSteps)); +            } +        } +        else +        { +            hSim->heartMask.setFillColor(hSim->guppN); +        } +        // Gland +        if (glandState == ALPHA) +        { +            hSim->gland.setFillColor(hSim->prms.glandColor1); +        } +        else if (glandState == BETTA) +        { +            hSim->gland.setFillColor(hSim->prms.glandColor2); +        } +        else +        { +            hSim->gland.setFillColor(hSim->prms.glandColor3); +        } +    } + +    // Draw +    hSim->window.draw(hSim->gOuterSkin); +    for (shape = &hSim->beak1; shape <= &hSim->tailMask; ++shape) +    { +        hSim->window.draw(*shape); +    } +    hSim->window.draw(hSim->innerBodyMask); +    for (shape = hSim->thrusters; shape <= &hSim->eyeCavity; ++shape) +    { +        hSim->window.draw(*shape); +    } + +    if (hSim->camera.zoom > 0.02f) +    { +        if (isActive) +        { +            hSim->guppieShell.setOutlineColor(skinColor); +        } +        else +        { +            hSim->guppieShell.setOutlineColor(hSim->guppN); +        } + +        hSim->guppieShell.setPosition(body->GetPosition().x, body->GetPosition().y); +        hSim->window.draw(hSim->guppieShell); +    } + +    if (!isActive) +    { +        hSim->guppieEgg.setPosition(body->GetPosition().x, body->GetPosition().y); +        hSim->window.draw(hSim->guppieEgg); +    } +} + + +void Guppie::clean() +{ +    isActive = false; +    lifetime = 0; +    fitness = 0.; +    energy = 0.f; +    skinColor = hSim->guppN; +//    nGland1 = 0.f; +//    nGland2 = 0.f; +//    nGland3 = 0.f; +    lastContactStep = 0; +} diff --git a/Simulation/Guppie.hpp b/Simulation/Guppie.hpp new file mode 100644 index 0000000..14770ff --- /dev/null +++ b/Simulation/Guppie.hpp @@ -0,0 +1,58 @@ +#ifndef __GUPPIE_HPP__ +#define __GUPPIE_HPP__ + +#include <NeuralNet.hpp> +#include "Entity.hpp" + +class Guppie : public Entity +{ +public: +    void startup(SimBase *sim); +    void update(); +    void draw(); + +private: +    void destroy(); +    void clean(); + +    // State variables +    bool isActive = false; +    unsigned lifetime = 0; +    double fitness = 0; +    float energy = 0.f; +    sf::Color skinColor; +    enum gs { ALPHA = 0, BETTA = 1, GAMMA = 2 } glandState; +//    float nGland1 = 0.f; +//    float nGland2 = 0.f; +//    float nGland3 = 0.f; +    enum lc { ZAPPER, PELLET, CORPSE, GUPPIE } lastContact; +    unsigned lastContactStep = 0; + +    // Sensors +    std::vector<float> eyeR = std::vector<float>(15, 0.f); +    std::vector<float> eyeG = std::vector<float>(15, 0.f); +    std::vector<float> eyeB = std::vector<float>(15, 0.f); +    std::vector<float> touch = std::vector<float>(30, 0.f); +//    std::vector<float> smell = std::vector<float>(30, 0.f); +    float cColorR = 0.f, cColorG = 0.f, cColorB = 0.f; +//    float cGland1 = 0.f, cGland2 = 0.f, cGland3 = 0.f; +    float fwdSpeed = 0.f; +    float sideSpeed = 0.f; +    float rotation = 0.f; +    float lowEnergy = 0.f; + +    // Neural net +    std::shared_ptr<NeuralNet> neuralNet; + +    // Outputs +    float thruster1 = 0.f, thruster2 = 0.f; +    float addClrR = 0.f, addClrG = 0.f, addClrB = 0.f; +//    float addGln1 = 0.f, addGln2 = 0.f, addGln3 = 0.f; + +    friend class ContactListener; +    friend class SimFitness; +    friend class Entity; +    friend class Camera; +}; + +#endif // __GUPPIE_HPP__ diff --git a/Simulation/GuppiesInclude.hpp b/Simulation/GuppiesInclude.hpp new file mode 100644 index 0000000..4a7bf94 --- /dev/null +++ b/Simulation/GuppiesInclude.hpp @@ -0,0 +1,6 @@ +#ifndef __GUPPIESINCLUDE_HPP__ +#define __GUPPIESINCLUDE_HPP__ + +#include "SimFitness.hpp" + +#endif // __GUPPIESINCLUDE_HPP__ diff --git a/Simulation/Params.hpp b/Simulation/Params.hpp new file mode 100644 index 0000000..979cd4e --- /dev/null +++ b/Simulation/Params.hpp @@ -0,0 +1,172 @@ +#ifndef __PARAMS_HPP__ +#define __PARAMS_HPP__ + +#include <sstream> +#include <cmath> + +#include <SFML/Graphics.hpp> +#include <Box2D.h> + +#include <NeuralNetworks.hpp> + + +enum SimStyle +{ +	SELECTION_BY_FITNESS +}; + +struct Params +{ +	// Variable parameters must be defined before simulation starts + +	// Simulation params +	SimStyle simStyle = SELECTION_BY_FITNESS; +	// If selection by fitness +	NeuralNetClass netClass = SIMPLE_RN; +	NodeClass nodeClass = MEMORY_CELL; +	unsigned npHiddenLayer = 80; +	unsigned popSize = 30; +	unsigned popQtty = 10; +	unsigned elites  = 30; + +	// World params +	float worldRad = 40.f; + +	// Entity params +	float zapperForce = 50.f;//100 +	float zapperTorque = 150.f;//300 +	unsigned zapperQtty = 20; +	unsigned pelletQtty = 400; +	unsigned pelletCreationDelay = 150; +	bool startScarce = false; +	bool corpseDecay = true; +	float corpseDecayTime = 3600.f; + +	// Goopy params +	unsigned activationDelay = 120; +	unsigned contactSteps = 15; +	float thrustForce = 5.f; +	float thrustRadius = 0.05f; +	float initEnergy = 60.f; +	float maxEnergy = 180.f; +	float agingRate = 600.f; +	bool leaveCorpse = true; + +	// Energy transfers +	float etFromZapper = -20.f; +	float etFromPellet = 15.f; +	float etFromGuppie = 60.f; +	float etFromCorpse = 60.f; + +	// Fitness bonus +	float forPellet = 9000.f; +	float forGuppie = 36000.f; +	float forCorpse = 36000.f; +	float forGoingStraight = 12.f; + +	// Colors +	sf::Color clearColor    = {   0,   0,  10 }; +	sf::Color worldColor    = {   0,   0,   0 }; +	sf::Color textColor     = { 191, 191, 191 }; +	sf::Color zapperColor   = {   0,   0, 255 }; +	sf::Color pelletColor   = {   0, 255,   0 }; +	sf::Color corpseColor   = {   0, 255,   0 }; +	sf::Color guppieColorI  = { 255,   0,   0 }; +	sf::Color glandColor1   = { 255, 127,   0 }; +	sf::Color glandColor2   = {   0, 255, 127 }; +	sf::Color glandColor3   = { 127,   0, 255 }; + + +	// Constant values cant be changed +	constexpr static unsigned WIN_WIDTH = 800; +	constexpr static unsigned WIN_HEIGHT = 600; +	constexpr static unsigned ANTIALIAS = 8; +	constexpr static unsigned CAM_STEPS = 8; +	constexpr static float ZAPPER_RAD = 5.f; +	constexpr static float GUPPIE_RAD = 0.5f; +	constexpr static float EGG_RAD    = 1.5f; +	constexpr static float CORPSE_RAD = 0.25f; +	constexpr static float PELLET_RAD = 0.1f; +	constexpr static float TXT_SIZE = 12.f; +	constexpr static float MAX_ZOOM = 0.002f; +	constexpr static float RAD_DGRS = 57.2957795f; +}; + + +// Utility functions +template <class T> +inline std::string nts(T num) +{ +    std::ostringstream ss; +    ss << num; +    return ss.str(); +} + + +inline sf::Vector2f vecMult(const sf::Vector2f &v1, const sf::Vector2f &v2) +{ +    return sf::Vector2f(v1.x * v2.x, v1.y * v2.y); +} + + +inline void setCenterRad(sf::CircleShape &circle, float radius) +{ +    circle.setRadius(radius); +    circle.setOrigin(radius, radius); +} + + +inline sf::Color mix(const sf::Color &clr1, const sf::Color &clr2, float f1 = 0.2f) +{ +    sf::Color result; +    float f2 = 1.f - f1; + +    result.r = clr1.r * f1 + clr2.r * f2; +    result.g = clr1.g * f1 + clr2.g * f2; +    result.b = clr1.b * f1 + clr2.b * f2; + +    return result; +} + + +// Utility classes +class QueryCallback : public b2QueryCallback +{ +public: +    bool ReportFixture(b2Fixture* fixture) +    { +		b2Body* body = fixture->GetBody(); +		if (body->GetType() != b2_staticBody) +		{ +			bool inside = fixture->TestPoint(m_point); +			if (inside) +			{ +				m_fixture = fixture; +				return false; +			} +		} + +		return true; +	} + +	b2Vec2 m_point; +	b2Fixture *m_fixture = nullptr; +}; + + +class RayCastCallback : public b2RayCastCallback +{ +public: +    float32 ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float32 fraction) +    { +        m_fixture = fixture; +        m_fraction = fraction; + +        return fraction; +    } + +    float m_fraction = 0.f; +    b2Fixture *m_fixture = nullptr; +}; + +#endif // __PARAMS_HPP__ diff --git a/Simulation/Pellet.cpp b/Simulation/Pellet.cpp new file mode 100644 index 0000000..1a911ff --- /dev/null +++ b/Simulation/Pellet.cpp @@ -0,0 +1,66 @@ +#include "Pellet.hpp" +#include "SimBase.hpp" + +void Pellet::startup(SimBase *sim) +{ +    hSim = sim; +    radius = Params::PELLET_RAD; +} + + +void Pellet::update() +{ +    if (!body) +    { +        return; +    } + +    if (toBeDestroyed) +    { +        destroy(); +    } +} + + +void Pellet::destroy() +{ +    toBeDestroyed = false; +    hSim->tank.world.DestroyBody(body); +    body = nullptr; + +    --hSim->pelletCount; +    hSim->text.pelletCnt.setString("Pellet count: " + nts(hSim->pelletCount) + " / " + nts(hSim->prms.pelletQtty)); +} + + +void Pellet::draw() +{ +    if (!body) +    { +        return; +    } + +    sf::Vector2f vSize = hSim->window.getView().getSize(); +    sf::Vector2f vCent = hSim->window.getView().getCenter(); +    if ( body->GetPosition().x + radius < vCent.x - vSize.x / 2.f || +         body->GetPosition().y + radius < vCent.y - vSize.y / 2.f || +         body->GetPosition().x - radius > vCent.x + vSize.x / 2.f || +         body->GetPosition().y - radius > vCent.y + vSize.y / 2.f  ) +    { +        return; +    } + +    if (hSim->camera.zoom > Params::PELLET_RAD / 2.f) +    { +        hSim->pelletPoint.setPosition(body->GetPosition().x, body->GetPosition().y); +        hSim->window.draw(hSim->pelletPoint); +    } +    else +    { +        hSim->pelletShell.setPosition(body->GetPosition().x, body->GetPosition().y); +        hSim->pelletNucleus.setPosition(body->GetPosition().x, body->GetPosition().y); +        hSim->pelletNucleus.setRotation(body->GetAngle() * hSim->prms.RAD_DGRS); +        hSim->window.draw(hSim->pelletShell); +        hSim->window.draw(hSim->pelletNucleus); +    } +} diff --git a/Simulation/Pellet.hpp b/Simulation/Pellet.hpp new file mode 100644 index 0000000..f3c31f8 --- /dev/null +++ b/Simulation/Pellet.hpp @@ -0,0 +1,20 @@ +#ifndef __PELLET_HPP__ +#define __PELLET_HPP__ + +#include "Entity.hpp" + +class Pellet : public Entity +{ +public: +    void startup(SimBase *sim); +    void update(); +    void draw(); + +    bool isCreated() const { return body ? true : false; } +    bool toBeDestroyed = false; + +private: +    void destroy(); +}; + +#endif // __PELLET_HPP__ diff --git a/Simulation/SimBase.hpp b/Simulation/SimBase.hpp new file mode 100644 index 0000000..83e7f4c --- /dev/null +++ b/Simulation/SimBase.hpp @@ -0,0 +1,70 @@ +#ifndef __SIMBASE_HPP__ +#define __SIMBASE_HPP__ + +//#include <fstream> +#include <list> + +#include "Params.hpp" +#include "GraphicObjs.hpp" + +#include "Camera.hpp" +#include "Tank.hpp" +#include "TextDisplay.hpp" +#include "Zapper.hpp" +#include "Pellet.hpp" +#include "Guppie.hpp" +#include "Corpse.hpp" + +class SimBase : public GraphicObjs +{ +public: +    bool createNew(const Params &usrPrms); +    //bool load(const std::ifstream &file); +    void execute(); + +protected: +    void prepareGraphics(); +    void update(); +    void draw(); + +    virtual bool startSpecs() = 0; +    virtual void updateSpecs() = 0; +    //void onClose(); + +    Params prms; +    sf::RenderWindow window; +    Camera camera; +    Tank tank; +    TextDisplay text; + +    sf::Clock timer; +    unsigned frameCounter = 0; + +    bool paused = false; +    bool gfx = true; +    bool displayText = true; +    bool vSync = true; +    bool fullscreen = false; +    unsigned step = 0; + +    unsigned pelletCount = 0; +    unsigned guppieCount = 0; +    unsigned corpseCount = 0; + +    std::list<Zapper> zappers; +    std::list<Pellet> pellets; +    std::list<Guppie> guppies; +    std::list<Corpse> corpses; + +    friend class Camera; +    friend class Tank; +    friend class ContactListener; +    friend class TextDisplay; +    friend class Entity; +    friend class Zapper; +    friend class Pellet; +    friend class Corpse; +    friend class Guppie; +}; + +#endif // __SIMBASE_HPP__ diff --git a/Simulation/SimBase_CreateNew.cpp b/Simulation/SimBase_CreateNew.cpp new file mode 100644 index 0000000..de752aa --- /dev/null +++ b/Simulation/SimBase_CreateNew.cpp @@ -0,0 +1,49 @@ +#include "SimBase.hpp" + +bool SimBase::createNew(const Params &usrPrms) +{ +    prms = usrPrms; + +    seedRand(); + +    sf::VideoMode vmd = sf::VideoMode(prms.WIN_WIDTH, prms.WIN_HEIGHT); +    sf::ContextSettings ctx = sf::ContextSettings(0, 0, prms.ANTIALIAS, 2, 0); +    window.create(vmd, "Neural Guppies - 0.1 beta", sf::Style::Default, ctx); +    window.setVerticalSyncEnabled(vSync); + +    sf::Image icon; +    icon.loadFromFile("gfx/icon.png"); +    window.setIcon(32, 32, icon.getPixelsPtr()); + +    prepareGraphics(); + +    camera.create(this); +    tank.create(this); + +    zappers.resize(prms.zapperQtty); +    for (auto &i : zappers) +    { +        i.startup(this); +        i.create(); +    } + +    pellets.resize(prms.pelletQtty); +    for (auto &i : pellets) +    { +        i.startup(this); +    } +    if (!prms.startScarce) +    { +        for (auto &i : pellets) +        { +            i.create(); +        } +    } + +    if (!startSpecs()) +    { +        return false; +    } + +    return true; +} diff --git a/Simulation/SimBase_Draw.cpp b/Simulation/SimBase_Draw.cpp new file mode 100644 index 0000000..0bfe915 --- /dev/null +++ b/Simulation/SimBase_Draw.cpp @@ -0,0 +1,40 @@ +#include "SimBase.hpp" + +void SimBase::draw() +{ +    window.clear(prms.clearColor); + +    camera.update(); + +    if (gfx) +    { +        tank.draw(); + +        for (auto &i : zappers) +        { +            i.draw(); +        } + +        for (auto &i : pellets) +        { +            i.draw(); +        } + +        for (auto &i : guppies) +        { +            i.draw(); +        } + +        for (auto &i : corpses) +        { +            i.draw(); +        } +    } + +    if (displayText) +    { +        text.print(); +    } + +    window.display(); +} diff --git a/Simulation/SimBase_Execute.cpp b/Simulation/SimBase_Execute.cpp new file mode 100644 index 0000000..eb3166a --- /dev/null +++ b/Simulation/SimBase_Execute.cpp @@ -0,0 +1,152 @@ +#include "SimBase.hpp" + +void SimBase::execute() +{ +    while (window.isOpen()) +    { +        update(); + +        sf::Event event; +        while (window.pollEvent(event)) +        { +            if (event.type == sf::Event::Closed) +            { +                window.close(); +            } + +            if (event.type == sf::Event::Resized) +            { +                camera.onResize(); +            } + +            if (event.type == sf::Event::MouseButtonPressed) +            { +                if (event.mouseButton.button == sf::Mouse::Button::Left) +                { +                    camera.onClick(); +                } +            } + +            if (event.type == sf::Event::MouseWheelMoved) +            { +                sf::Vector2f mousePos = window.mapPixelToCoords(sf::Mouse::getPosition(window)); +                float f1 = 2.f / 10.f; +                float f2 = 1.f - f1; + +                if (event.mouseWheel.delta > 0) +                { +                    sf::Vector2f target = camera.getCrds() * f2 + mousePos * f1; +                    camera.shift(target, 1); +                } +                else +                { +                    mousePos = camera.getCrds() * 2.f - mousePos; +                    sf::Vector2f target = camera.getCrds() * f2 + mousePos * f1; +                    camera.shift(target, -1); +                } +            } + +            if (event.type == sf::Event::KeyPressed) +            { +                if (event.key.code == sf::Keyboard::Escape) +                { +                    window.close(); +                } + +                if (event.key.code == sf::Keyboard::Space) +                { +                    paused = !paused; +                    text.simState.setString("Sim. state: " + std::string(paused ? "PAUSED" : "RUNNING")); +                } + +                if (event.key.code == sf::Keyboard::G) +                { +                    gfx = !gfx; +                    text.gfx.setString("Graphics: " + std::string(gfx ? "ON" : "OFF")); +                } + +                if (event.key.code == sf::Keyboard::T) +                { +                    displayText = !displayText; +                } + +                if (event.key.code == sf::Keyboard::V) +                { +                    vSync = !vSync; +                    window.setVerticalSyncEnabled(vSync); +                    text.vSync.setString("V. Sync: " + std::string(vSync ? "ON" : "OFF")); +                } + +                if (event.key.code == sf::Keyboard::Z) +                { +                    camera.shift(sf::Vector2f(), -2); +                } + +                if (event.key.code == sf::Keyboard::F11) +                { +                    sf::ContextSettings ctx = window.getSettings(); +                    std::string title = "Neural Guppies - 0.1 beta"; + +                    if (fullscreen) +                    { +                        fullscreen = false; +                        window.create(sf::VideoMode(prms.WIN_WIDTH, prms.WIN_HEIGHT), title, sf::Style::Default, ctx); +                        sf::Image icon; +                        icon.loadFromFile("gfx/icon.png"); +                        window.setIcon(32, 32, icon.getPixelsPtr()); +                    } +                    else +                    { +                        fullscreen = true; +                        window.create(sf::VideoMode::getDesktopMode(), title, sf::Style::Fullscreen, ctx); +                    } + +                    window.setVerticalSyncEnabled(vSync); +                    camera.onResize(); +                } + +                if (event.key.code == sf::Keyboard::I) +                { +                    camera.shift(camera.getCrds(), 1, false); +                } + +                if (event.key.code == sf::Keyboard::O) +                { +                    camera.shift(camera.getCrds(), -1, false); +                } + +                if ( event.key.code == sf::Keyboard::W || +                     event.key.code == sf::Keyboard::A || +                     event.key.code == sf::Keyboard::S || +                     event.key.code == sf::Keyboard::D ) +                { +                    float shift = 30.f * camera.getZoom(); +                    sf::Vector2f shiftVec; + +                    if (event.key.code == sf::Keyboard::W) +                    { +                        shiftVec.y = -shift; +                    } +                    else if (event.key.code == sf::Keyboard::A) +                    { +                        shiftVec.x = -shift; +                    } +                    else if (event.key.code == sf::Keyboard::S) +                    { +                        shiftVec.y = shift; +                    } +                    else if (event.key.code == sf::Keyboard::D) +                    { +                        shiftVec.x = shift; +                    } + +                    camera.shift(camera.getCrds() + shiftVec); +                } +            } +        } + +        draw(); +    } + +    //onClose(); +} diff --git a/Simulation/SimBase_PrepareGraphics.cpp b/Simulation/SimBase_PrepareGraphics.cpp new file mode 100644 index 0000000..ba7b56d --- /dev/null +++ b/Simulation/SimBase_PrepareGraphics.cpp @@ -0,0 +1,269 @@ +#include "SimBase.hpp" + +void SimBase::prepareGraphics() +{ +    // Prepare zappers +    setCenterRad(zapperShell, prms.ZAPPER_RAD); +    zapperShell.setPointCount(180); +    zapperShell.setFillColor(mix(prms.zapperColor, prms.worldColor)); +    zapperShell.setOutlineColor(prms.zapperColor); +    setCenterRad(zapperNucleus, prms.ZAPPER_RAD); +    zapperNucleus.setFillColor(prms.zapperColor); + +    // Prepare pellets +    setCenterRad(pelletShell, prms.PELLET_RAD); +    pelletShell.setPointCount(60); +    pelletShell.setFillColor(mix(prms.pelletColor, prms.worldColor)); +    pelletShell.setOutlineColor(prms.pelletColor); +    setCenterRad(pelletNucleus, prms.PELLET_RAD); +    pelletNucleus.setFillColor(prms.pelletColor); + +    // Prepare corpses +    setCenterRad(corpseShell, prms.CORPSE_RAD); +    corpseShell.setPointCount(60); +    corpseShell.setFillColor(mix(prms.corpseColor, prms.worldColor)); +    corpseShell.setOutlineColor(prms.corpseColor); +    setCenterRad(corpseNucleus, prms.CORPSE_RAD); +    corpseNucleus.setFillColor(prms.corpseColor); + +    // Prepare guppies +    guppieShell.setFillColor(sf::Color::Transparent); +    guppN = mix(prms.guppieColorI, prms.worldColor, 0.1f); +    // Prepare egg +    guppieEgg.setFillColor(sf::Color::Transparent); +    guppieEgg.setOutlineColor(guppN); +    guppieEgg.setRadius(prms.EGG_RAD); +    // Prepare vision cone shape +    sf::CircleShape fovTempA = sf::CircleShape(10.f, 360); +    sf::ConvexShape fovTemp; +    fovTemp.setPointCount(360); +    for (int i = 0; i < 360; ++i) +    { +        fovTemp.setPoint(i, fovTempA.getPoint(i) - sf::Vector2f(10.f, 10.f + prms.GUPPIE_RAD)); +    } +    for (int i = 0; i < 15; ++i) +    { +        visionCone[i] = fovTemp.getPoint(i * 2 + 331); +    } +    for (int i = 0; i < 15; ++i) +    { +        visionCone[i + 15] = fovTemp.getPoint(i * 2 + 1); +    } +    visionCone[30] = sf::Vector2f(0.f, -prms.GUPPIE_RAD); +    // Prepare beaks +    beak1.setPointCount(3); +    beak1.setPoint(0, sf::Vector2f(0.f, -0.5f)); +    beak1.setPoint(1, sf::Vector2f(0.0116f, -0.5201f)); +    beak1.setPoint(2, sf::Vector2f(0.0236f, -0.4994f)); +    beak2.setPointCount(3); +    beak2.setPoint(0, sf::Vector2f(0.f, -0.5f)); +    beak2.setPoint(1, sf::Vector2f(-0.0116f, -0.5201f)); +    beak2.setPoint(2, sf::Vector2f(-0.0236f, -0.4994f)); +    // Prepare skin radius shape +    sf::CircleShape sknTempA = sf::CircleShape(prms.GUPPIE_RAD, 360); +    sf::ConvexShape sknTemp; +    sknTemp.setPointCount(360); +    for (int i = 0; i < 360; ++i) +    { +        sknTemp.setPoint(i, sknTempA.getPoint(i) - sf::Vector2f(prms.GUPPIE_RAD, prms.GUPPIE_RAD)); +    } +    for (int i = 0; i < 180; ++i) +    { +        skinRadius[i] = sknTemp.getPoint(i * 2 + 1); +    } +    // Prepare sense radius shape +    sf::CircleShape snsTempA = sf::CircleShape(10.f, 360); +    sf::ConvexShape snsTemp; +    snsTemp.setPointCount(360); +    for (int i = 0; i < 360; ++i) +    { +        snsTemp.setPoint(i, snsTempA.getPoint(i) - sf::Vector2f(10.f, 10.f)); +    } +    for (int i = 0; i < 180; ++i) +    { +        senseRadius[i] = snsTemp.getPoint(i * 2 + 1); +    } +    // Draw skin +    gOuterSkin.setPointCount(180); +    setCenterRad(gOuterSkin, prms.GUPPIE_RAD); +    // Draw touch and smell cells +    sf::CircleShape tcTemp = sf::CircleShape(prms.GUPPIE_RAD - 0.04f, 180); +    tcTemp.setOrigin(prms.GUPPIE_RAD - 0.04f, prms.GUPPIE_RAD - 0.04f); +    sf::CircleShape scTemp = sf::CircleShape(prms.GUPPIE_RAD - 0.06f, 180); +    scTemp.setOrigin(prms.GUPPIE_RAD - 0.06f, prms.GUPPIE_RAD - 0.06f); +    for (int i = 0; i < 30; ++i) +    { +        touchCells[i].setPointCount(8); +        smellCells[i].setPointCount(8); +        for (int j = 0; j < 7; ++j) +        { +            touchCells[i].setPoint(j, tcTemp.getPoint((i * 6 + j) % 180)); +            smellCells[i].setPoint(j, scTemp.getPoint((i * 6 + j) % 180)); +        } +        touchCells[i].setPoint(7, tcTemp.getOrigin()); +        touchCells[i].setOrigin(tcTemp.getOrigin()); +        smellCells[i].setPoint(7, scTemp.getOrigin()); +        smellCells[i].setOrigin(scTemp.getOrigin()); +        smellCells[i].setFillColor(guppN); +    } +    // Draw inner body masks +    tailMask.setFillColor(guppN); +    tailMask.setPointCount(3); +    tailMask.setPoint(0, sf::Vector2f(0.f, 0.5f)); +    tailMask.setPoint(1, sf::Vector2f(-0.0478f, 0.4173f)); +    tailMask.setPoint(2, sf::Vector2f( 0.0478f, 0.4173f)); +    innerBodyMask.setFillColor(guppN); +    innerBodyMask.setPointCount(180); +    setCenterRad(innerBodyMask, prms.GUPPIE_RAD - 0.08f); +    // Draw thrusters +    sf::CircleShape thTemp = sf::CircleShape(prms.GUPPIE_RAD - 0.1f, 180); +    for (int i = 0; i < 4; ++i) +    { +        thrusters[i].setPointCount(41); +        thrusters[i].setOrigin(prms.GUPPIE_RAD - 0.1f, prms.GUPPIE_RAD - 0.1f); +    } +    thrusters[0].setPoint(0, sf::Vector2f(0.4863f, 0.0094f)); +    for (int i = 0; i < 38; ++i) +    { +        thrusters[0].setPoint(i + 1, thTemp.getPoint(i + 7)); +    } +    thrusters[0].setPoint(39, sf::Vector2f(0.7999f, 0.39f)); +    thrusters[0].setPoint(40, sf::Vector2f(prms.GUPPIE_RAD - 0.1f, 0.39f)); +    for (int i = 0; i < 41; ++i) +    { +        sf::Vector2f p1 = vecMult(thrusters[0].getPoint(i), sf::Vector2f(-1.f, 1.f)) + sf::Vector2f((prms.GUPPIE_RAD - 0.1f) * 2.f, 0.f); +        sf::Vector2f p2 = vecMult(thrusters[0].getPoint(i), sf::Vector2f(1.f, -1.f)) + sf::Vector2f(0.f, (prms.GUPPIE_RAD - 0.1f) * 2.f); +        sf::Vector2f p3 = vecMult(thrusters[0].getPoint(i), sf::Vector2f(-1.f, -1.f)) + sf::Vector2f((prms.GUPPIE_RAD - 0.1f) * 2.f, (prms.GUPPIE_RAD - 0.1f) * 2.f); +        thrusters[1].setPoint(i, p1); +        thrusters[2].setPoint(i, p2); +        thrusters[3].setPoint(i, p3); +    } +    // Draw thruster mask +    thrusterMask.setFillColor(guppN); +    thrusterMask.setPointCount(48); +    thrusterMask.setPoint(0, sf::Vector2f(0.0863f, -0.3906f)); +    thrusterMask.setPoint(1, sf::Vector2f(0.214f, -0.1694f)); +    thrusterMask.setPoint(2, sf::Vector2f(0.2148f, -0.1679f)); +    thrusterMask.setPoint(3, sf::Vector2f(0.2237f, -0.1509f)); +    thrusterMask.setPoint(4, sf::Vector2f(0.2313f, -0.1335f)); +    thrusterMask.setPoint(5, sf::Vector2f(0.2377f, -0.1159f)); +    thrusterMask.setPoint(6, sf::Vector2f(0.2429f, -0.0981f)); +    thrusterMask.setPoint(7, sf::Vector2f(0.2471f, -0.0803f)); +    thrusterMask.setPoint(8, sf::Vector2f(0.2504f, -0.0624f)); +    thrusterMask.setPoint(9, sf::Vector2f(0.2527f, -0.0446f)); +    thrusterMask.setPoint(10, sf::Vector2f(0.2542f, -0.0267f)); +    thrusterMask.setPoint(11, sf::Vector2f(0.2549f, -0.01f)); +    for (int i = 0; i < 12; ++i) +    { +        thrusterMask.setPoint(i + 12, vecMult(thrusterMask.getPoint(11 - i), sf::Vector2f(1.f, -1.f))); +    } +    for (int i = 0; i < 24; ++i) +    { +        thrusterMask.setPoint(i + 24, vecMult(thrusterMask.getPoint(23 - i), sf::Vector2f(-1.f, 1.f))); +    } +    // Draw heart +    heart.setPointCount(43); +    heart.setPoint(0, sf::Vector2f(0.1883f, -0.1321f)); +    heart.setPoint(1, sf::Vector2f(0.1901f, -0.1282f)); +    heart.setPoint(2, sf::Vector2f(0.1962f, -0.1133f)); +    heart.setPoint(3, sf::Vector2f(0.2013f, -0.0982f)); +    heart.setPoint(4, sf::Vector2f(0.2055f, -0.0830f)); +    heart.setPoint(5, sf::Vector2f(0.2088f, -0.0679f)); +    heart.setPoint(6, sf::Vector2f(0.2114f, -0.0527f)); +    heart.setPoint(7, sf::Vector2f(0.2132f, -0.0376f)); +    heart.setPoint(8, sf::Vector2f(0.2144f, -0.0225f)); +    heart.setPoint(9, sf::Vector2f(0.2149f, -0.0075f)); +    for (int i = 0; i < 9; ++i) +    { +        heart.setPoint(i + 10, vecMult(heart.getPoint(9 - i), sf::Vector2f(1.f, -1.f))); +    } +    heart.setPoint(19, sf::Vector2f(0.1829f, 0.1429f)); +    heart.setPoint(20, sf::Vector2f(0.1793f, 0.1494f)); +    heart.setPoint(21, sf::Vector2f(0.f, 0.46f)); +    for (int i = 0; i < 21; ++i) +    { +        heart.setPoint(i + 22, vecMult(heart.getPoint(20 - i), sf::Vector2f(-1.f, 1.f))); +    } +    // Draw heart mask +    heartMask.setFillColor(guppN); +    heartMask.setPointCount(37); +    heartMask.setPoint(0, sf::Vector2f(0.1655f, -0.078f)); +    heartMask.setPoint(1, sf::Vector2f(0.168f, -0.0679f)); +    heartMask.setPoint(2, sf::Vector2f(0.1705f, -0.0554f)); +    heartMask.setPoint(3, sf::Vector2f(0.1723f, -0.043f)); +    heartMask.setPoint(4, sf::Vector2f(0.1737f, -0.0306f)); +    heartMask.setPoint(5, sf::Vector2f(0.1745f, -0.0183f)); +    heartMask.setPoint(6, sf::Vector2f(0.1749f, -0.0061f)); +    for (int i = 0; i < 6; ++i) +    { +        heartMask.setPoint(i + 7, vecMult(heartMask.getPoint(6 - i), sf::Vector2f(1.f, -1.f))); +    } +    heartMask.setPoint(13, sf::Vector2f(0.1648f, 0.0804f)); +    heartMask.setPoint(14, sf::Vector2f(0.1610f, 0.093f)); +    heartMask.setPoint(15, sf::Vector2f(0.1563f, 0.1055f)); +    heartMask.setPoint(16, sf::Vector2f(0.1508f, 0.1178f)); +    heartMask.setPoint(17, sf::Vector2f(0.1447f, 0.1294f)); +    heartMask.setPoint(18, sf::Vector2f(0.f, 0.38f)); +    for (int i = 0; i < 18; ++i) +    { +        heartMask.setPoint(i + 19, vecMult(heartMask.getPoint(17 - i), sf::Vector2f(-1.f, 1.f))); +    } +    // Draw heart top +    heartTop.setPointCount(8); +    heartTop.setPoint(0, sf::Vector2f(-0.1655f, -0.0780f)); +    heartTop.setPoint(1, sf::Vector2f(-0.1401f, -0.0689f)); +    heartTop.setPoint(2, sf::Vector2f(-0.1097f, -0.0601f)); +    heartTop.setPoint(3, sf::Vector2f(-0.1025f, -0.0584f)); +    for (int i = 0; i < 4; ++i) +    { +        heartTop.setPoint(i + 4, vecMult(heartTop.getPoint(3 - i), sf::Vector2f(-1.f, 1.f))); +    } +    // Draw heart triangle +    heartTri.setPointCount(3); +    heartTri.setPoint(0, sf::Vector2f(-0.1025f, -0.0584f)); +    heartTri.setPoint(1, sf::Vector2f(0.f, 0.0935f)); +    heartTri.setPoint(2, sf::Vector2f(0.1025f, -0.0584f)); +    // Draw gland mask +    glandMask.setFillColor(guppN); +    glandMask.setPointCount(3); +    glandMask.setPoint(0, sf::Vector2f(-0.0783f, -0.0942f)); +    glandMask.setPoint(1, sf::Vector2f(0.f, 0.022f)); +    glandMask.setPoint(2, sf::Vector2f(0.0783f, -0.0942f)); +    // Draw gland +    gland.setPointCount(3); +    gland.setPoint(0, sf::Vector2f(-0.0513f, -0.0899f)); +    gland.setPoint(1, sf::Vector2f(0.f, -0.0138f)); +    gland.setPoint(2, sf::Vector2f(0.0513f, -0.0899f)); +    // Draw eye mask +    sf::CircleShape emTemp = sf::CircleShape(0.4133f, 180); +    eyeMask.setFillColor(guppN); +    eyeMask.setOrigin(0.4133f, 0.9133f); +    eyeMask.setPointCount(32); +    for (int i = 0; i < 31; ++i) +    { +        eyeMask.setPoint(i, emTemp.getPoint(i + 75)); +    } +    eyeMask.setPoint(31, sf::Vector2f(0.4133f, 0.4133f)); +    // Draw eye cells +    sf::CircleShape ecTemp = sf::CircleShape(0.3933f, 180); +    for (int i = 0; i < 15; ++i) +    { +        eyeCells[i].setPointCount(4); +        eyeCells[i].setOrigin(0.3933f, 0.8933f); +        for (int j = 0; j < 3; ++j) +        { +            eyeCells[i].setPoint(j, ecTemp.getPoint(i * 2 + j + 75)); +        } +        eyeCells[i].setPoint(3, sf::Vector2f(0.3933f, 0.3933f)); +    } +    // Draw eye cavity +    sf::CircleShape ekTemp = sf::CircleShape(0.3333f, 180); +    eyeCavity.setPointCount(32); +    eyeCavity.setFillColor(prms.worldColor); +    eyeCavity.setOrigin(0.3333f, 0.8333f); +    for (int i = 0; i < 31; ++i) +    { +        eyeCavity.setPoint(i, ekTemp.getPoint(i + 75)); +    } +    eyeCavity.setPoint(31, sf::Vector2f(0.3333f, 0.3333f)); +} diff --git a/Simulation/SimBase_Update.cpp b/Simulation/SimBase_Update.cpp new file mode 100644 index 0000000..fc50c58 --- /dev/null +++ b/Simulation/SimBase_Update.cpp @@ -0,0 +1,55 @@ +#include "SimBase.hpp" + +void SimBase::update() +{ +    if (paused) +    { +        camera.update(); +        return; +    } + +    ++step; +    text.steps.setString("Steps: " + nts(step)); + +    if (timer.getElapsedTime().asSeconds() > 1.f) +    { +        timer.restart(); +        text.fps.setString("FPS: " + nts(frameCounter)); +        frameCounter = 0; +    } +    else +    { +        ++frameCounter; +    } + +    for (auto &i : zappers) +    { +        i.update(); +    } + +    // Create new pellet if needed +    if (pelletCount < prms.pelletQtty && !(step % prms.pelletCreationDelay)) +    { +        for (auto &i : pellets) +        { +            if (!i.isCreated()) +            { +                i.create(); +                break; +            } +        } +    } +    for (auto &i : pellets) +    { +        i.update(); +    } + +    for (auto &i : corpses) +    { +        i.update(); +    } + +    updateSpecs(); + +    tank.updateAllPhysics(); +} diff --git a/Simulation/SimFitness.hpp b/Simulation/SimFitness.hpp new file mode 100644 index 0000000..7eb7974 --- /dev/null +++ b/Simulation/SimFitness.hpp @@ -0,0 +1,26 @@ +#ifndef __SIMFITNESS_HPP__ +#define __SIMFITNESS_HPP__ + +#include <Population.hpp> +#include <SingleMLP.hpp> +#include <DualMLP.hpp> +#include <SimpleRN.hpp> +#include <FullyRN.hpp> + +#include "SimBase.hpp" + +class SimFitness : public SimBase +{ +    bool startSpecs(); +    void updateSpecs(); + +    std::unique_ptr<Population> population; + +    unsigned currentPopulation = 0; +    unsigned currentGeneration = 0; +    unsigned fitnessRecord = 0; + +    friend class Guppie; +}; + +#endif // __SIMFITNESS_HPP__ diff --git a/Simulation/SimFitness_StartSpecs.cpp b/Simulation/SimFitness_StartSpecs.cpp new file mode 100644 index 0000000..8fe1a41 --- /dev/null +++ b/Simulation/SimFitness_StartSpecs.cpp @@ -0,0 +1,60 @@ +#include "SimFitness.hpp" + +bool SimFitness::startSpecs() +{ +    if (!text.startup(this, SELECTION_BY_FITNESS)) +    { +        return false; +    } + +    std::unique_ptr<NeuralNet> dummy; +    if (prms.netClass == SINGLE_MLP) +    { +        dummy = std::unique_ptr<NeuralNet>(new SingleMLP(82, prms.npHiddenLayer, 5, prms.nodeClass, true)); +    } +    else if (prms.netClass == DUAL_MLP) +    { +        dummy = std::unique_ptr<NeuralNet>(new DualMLP(82, prms.npHiddenLayer, 5, prms.nodeClass, true)); +    } +    else if (prms.netClass == SIMPLE_RN) +    { +        dummy = std::unique_ptr<NeuralNet>(new SimpleRN(82, prms.npHiddenLayer, 5, prms.nodeClass, true)); +    } +    else if (prms.netClass == FULLY_RN) +    { +        dummy = std::unique_ptr<NeuralNet>(new FullyRN(82, prms.npHiddenLayer, 5, prms.nodeClass, true)); +    } +    unsigned chromosomeSize = dummy->getChromosomeSize(); +    population = std::unique_ptr<Population>(new Population(prms.popQtty * prms.popSize, prms.elites, chromosomeSize)); + +    // Set first population +    guppies.resize(prms.popSize); +    unsigned index = 0; +    for (auto &i : guppies) +    { +        i.startup(this); +        if (prms.netClass == SINGLE_MLP) +        { +            i.neuralNet = std::shared_ptr<NeuralNet>(new SingleMLP(82, prms.npHiddenLayer, 5, prms.nodeClass, true)); +        } +        else if (prms.netClass == DUAL_MLP) +        { +            i.neuralNet = std::shared_ptr<NeuralNet>(new DualMLP(82, prms.npHiddenLayer, 5, prms.nodeClass, true)); +        } +        else if (prms.netClass == SIMPLE_RN) +        { +            i.neuralNet = std::shared_ptr<SimpleRN>(new SimpleRN(82, prms.npHiddenLayer, 5, prms.nodeClass, true)); +        } +        else if (prms.netClass == FULLY_RN) +        { +            i.neuralNet = std::shared_ptr<FullyRN>(new FullyRN(82, prms.npHiddenLayer, 5, prms.nodeClass, true)); +        } +        i.create(); +        // Set neural net initial random weights +        i.neuralNet->setChromosome(population->getChromosome(index)); + +        ++index; +    } + +    return true; +} diff --git a/Simulation/SimFitness_UpdateSpecs.cpp b/Simulation/SimFitness_UpdateSpecs.cpp new file mode 100644 index 0000000..3919914 --- /dev/null +++ b/Simulation/SimFitness_UpdateSpecs.cpp @@ -0,0 +1,40 @@ +#include "SimFitness.hpp" + +void SimFitness::updateSpecs() +{ +    for (auto &i : guppies) +    { +        i.update(); +    } + +    if (!guppieCount) +    { +        unsigned soul = 0; +        for (auto &i : guppies) +        { +            population->setFitness(currentPopulation * prms.popSize + soul, (unsigned)i.fitness); +            ++soul; +        } + +        ++currentPopulation; +        if (currentPopulation == prms.popQtty) +        { +            population->roulleteWheel(); + +            currentPopulation = 0; +            ++currentGeneration; +        } + +        unsigned index = 0; +        for (auto &i : guppies) +        { +            i.clean(); +            i.create(); +            i.neuralNet->setChromosome(population->getChromosome(currentPopulation * prms.popSize + index)); +            ++index; +        } + +        text.currentPop.setString("Current population: " + nts(currentPopulation + 1) + " / " + nts(prms.popQtty)); +        text.currentGen.setString("Current generation: " + nts(currentGeneration + 1)); +    } +} diff --git a/Simulation/Tank.cpp b/Simulation/Tank.cpp new file mode 100644 index 0000000..7d0c6d1 --- /dev/null +++ b/Simulation/Tank.cpp @@ -0,0 +1,52 @@ +#include "Tank.hpp" +#include "SimBase.hpp" + +void Tank::create(SimBase *sim) +{ +    hSim = sim; + +    // Prepare tank graphics +    setCenterRad(worldEdges, hSim->prms.worldRad); +    worldEdges.setPointCount(360); +    worldEdges.setFillColor(hSim->prms.worldColor); +    worldEdges.setOutlineColor(hSim->prms.zapperColor); +    worldEdges.setOutlineThickness(hSim->prms.worldRad / 100.f); + +    // Prepare tank physics +    world.SetAllowSleeping(true); +    contactListener.hSim = hSim; +    world.SetContactListener(&contactListener); + +    // Create edge of the world +    b2BodyDef worldDef; +    worldDef.type = b2_staticBody; + +    tankEdge = world.CreateBody(&worldDef); + +    b2ChainShape shape; +    b2Vec2 vrtx[360]; +    for (int dgr = 0; dgr < 360; ++dgr) +    { +        vrtx[dgr].Set(cosf((float)dgr / Params::RAD_DGRS), sinf((float)dgr / Params::RAD_DGRS)), +        vrtx[dgr] *= hSim->prms.worldRad; +    } +    shape.CreateLoop(vrtx, 360); + +    b2FixtureDef worldFix; +    worldFix.shape = &shape; +    worldFix.friction = 1.f; + +    tankEdge->CreateFixture(&worldFix); +} + + +void Tank::updateAllPhysics() +{ +    world.Step(1.f / 60.f, 8, 3); +} + + +void Tank::draw() +{ +    hSim->window.draw(worldEdges); +} diff --git a/Simulation/Tank.hpp b/Simulation/Tank.hpp new file mode 100644 index 0000000..f11d42b --- /dev/null +++ b/Simulation/Tank.hpp @@ -0,0 +1,34 @@ +#ifndef __TANK_HPP__ +#define __TANK_HPP__ + +#include <SFML/Graphics.hpp> +#include <Box2d.h> + +#include "ContactListener.hpp" + +class SimBase; + +class Tank +{ +public: +    void create(SimBase *sim); +    void updateAllPhysics(); +    void draw(); + +private: +    SimBase *hSim = nullptr; + +    b2World world = b2World(b2Vec2(0.f, 0.f)); +    sf::CircleShape worldEdges; +    b2Body *tankEdge = nullptr; + +    ContactListener contactListener; + +    friend class Camera; +    friend class Entity; +    friend class Pellet; +    friend class Guppie; +    friend class Corpse; +}; + +#endif // __TANK_HPP__ diff --git a/Simulation/TextDisplay.cpp b/Simulation/TextDisplay.cpp new file mode 100644 index 0000000..6cdd1ad --- /dev/null +++ b/Simulation/TextDisplay.cpp @@ -0,0 +1,136 @@ +#include "TextDisplay.hpp" +#include "SimBase.hpp" + +#include <sstream> + +bool TextDisplay::startup(SimBase *sim, SimStyle style) +{ +    hSim = sim; +    sStyle = style; + +    if (!font.loadFromFile("gfx\\font.ttf")) +    { +        return false; +    } + +    const float TXT_MARGIN = Params::TXT_SIZE / 2.f; + +    // RIGHT PANEL +    // Prepare left panel text +    simState  = sf::Text("Sim. state: RUNNING", font, Params::TXT_SIZE); +    steps     = sf::Text("Steps: 0", font, Params::TXT_SIZE); +    // Separator +    fps       = sf::Text("FPS: 0", font, Params::TXT_SIZE); +    gfx       = sf::Text("Graphics: ON", font, Params::TXT_SIZE); +    vSync     = sf::Text("V. Sync: ON", font, Params::TXT_SIZE); +    // Separator +    worldRad  = sf::Text("World radius: " + nts(hSim->prms.worldRad), font, Params::TXT_SIZE); +    // Separator +    zapperCnt = sf::Text("Zapper count: " + nts(hSim->prms.zapperQtty), font, Params::TXT_SIZE); +    pelletCnt = sf::Text("Pellet count: " + nts(hSim->pelletCount) + " / " + nts(hSim->prms.pelletQtty), font, Params::TXT_SIZE); +    guppieCnt = sf::Text("Guppie count: 0", font, Params::TXT_SIZE); +    corpseCnt = sf::Text("Corpse count: 0", font, Params::TXT_SIZE); +    if (sStyle == SELECTION_BY_FITNESS) +    { +        simStyle    = sf::Text("Sim. style: FITNESS SELECTION", font, Params::TXT_SIZE); +        currentPop  = sf::Text("Current population: 1 / " + nts(hSim->prms.popQtty), font, Params::TXT_SIZE); +        currentGen  = sf::Text("Current generation: 1", font, Params::TXT_SIZE); +        // Separator +        longestLife = sf::Text("Fitness record: 0", font, Params::TXT_SIZE); +    } +    // Position and color text items +    sf::Text *line = nullptr; +    float posX = TXT_MARGIN * 2.f; +    float posY = TXT_MARGIN * 2.f - Params::TXT_SIZE / 4.f; +    for (line = &simState; line <= &corpseCnt; ++line) +    { +        line->setPosition(posX, posY); +        line->setColor(hSim->prms.textColor); +        posY += Params::TXT_SIZE; +        if (line == &steps || line == &vSync || line == &worldRad || line == &corpseCnt) +        { +            posY += Params::TXT_SIZE; +        } +    } +    if (sStyle == SELECTION_BY_FITNESS) +    { +        for (line = &simStyle; line <= &longestLife; ++line) +        { +            line->setPosition(posX, posY); +            line->setColor(hSim->prms.textColor); +            posY += Params::TXT_SIZE; +            if (line == ¤tGen) +            { +                posY += Params::TXT_SIZE; +            } +        } +    } +    // Prepare left panel +    float lpTxtWidth = 0.f; +    float lpTxtHeight = 0.f; +    for (line = &simState; line <= &corpseCnt; ++line) +    { +        line->getGlobalBounds().width > lpTxtWidth ? lpTxtWidth = line->getGlobalBounds().width : lpTxtWidth = lpTxtWidth; +    } +    if (sStyle == SELECTION_BY_FITNESS) +    { +        for (line = &simStyle; line <= &longestLife; ++line) +        { +            line->getGlobalBounds().width > lpTxtWidth ? lpTxtWidth = line->getGlobalBounds().width : lpTxtWidth = lpTxtWidth; +        } +        lpTxtHeight = (longestLife.getGlobalBounds().height + longestLife.getPosition().y) - simState.getPosition().y; +    } +    leftPanel.setSize(sf::Vector2f(lpTxtWidth + TXT_MARGIN * 2.f, lpTxtHeight + TXT_MARGIN * 2.f)); +    leftPanel.setPosition(TXT_MARGIN, TXT_MARGIN); +    leftPanel.setOutlineThickness(1.f); +    leftPanel.setFillColor(sf::Color(0, 0, 0, 191)); +    leftPanel.setOutlineColor(hSim->prms.textColor); +    // Append left panel separators +    for (int i = 0; i < 5; ++i) +    { +        sepLP[i].setSize(sf::Vector2f(lpTxtWidth, 1.f)); +        sepLP[i].setFillColor(hSim->prms.textColor); +    } +    sepLP[0].setPosition(posX, fps.getPosition().y - TXT_MARGIN * 2.f / 3.f); +    sepLP[1].setPosition(posX, worldRad.getPosition().y - TXT_MARGIN * 2.f / 3.f); +    sepLP[2].setPosition(posX, zapperCnt.getPosition().y - TXT_MARGIN * 2.f / 3.f); +    if (sStyle == SELECTION_BY_FITNESS) +    { +        sepLP[3].setPosition(posX, simStyle.getPosition().y - TXT_MARGIN * 2.f / 3.f); +        sepLP[4].setPosition(posX, longestLife.getPosition().y - TXT_MARGIN * 2.f / 3.f); +    } + +    return true; +} + + +void TextDisplay::print() +{ +    sf::View textView = sf::View(sf::Vector2f(hSim->window.getSize().x / 2.f, hSim->window.getSize().y / 2.f), sf::Vector2f(hSim->window.getSize().x, hSim->window.getSize().y)); +    sf::View crntView = hSim->window.getView(); + +    hSim->window.setView(textView); + +    // Print left panel +    hSim->window.draw(leftPanel); +    sf::Text *line = nullptr; +    for (line = &simState; line <= &simStyle; ++line) +    { +        hSim->window.draw(*line); +    } +    if (sStyle == SELECTION_BY_FITNESS) +    { +        for (line = ¤tPop; line <= &longestLife; ++line) +        { +            hSim->window.draw(*line); +        } +    } +    for (int i = 0; i < 5; ++i) +    { +        hSim->window.draw(sepLP[i]); +    } + +    // Print right panel + +    hSim->window.setView(crntView); +} diff --git a/Simulation/TextDisplay.hpp b/Simulation/TextDisplay.hpp new file mode 100644 index 0000000..b46cefb --- /dev/null +++ b/Simulation/TextDisplay.hpp @@ -0,0 +1,55 @@ +#ifndef __TEXTDISPLAY_HPP__ +#define __TEXTDISPLAY_HPP__ + +#include <SFML/Graphics.hpp> + +#include "Params.hpp" + +class SimBase; + +class TextDisplay +{ +public: +    bool startup(SimBase *sim, SimStyle style); +    void print(); + +private: +    SimBase *hSim = nullptr; +    SimStyle sStyle; + +    sf::Font font; +    sf::RectangleShape leftPanel; + +    // ON LEFT PANEL +    sf::Text simState; +    sf::Text steps; +    // Separator +    sf::Text fps; +    sf::Text gfx; +    sf::Text vSync; +    // Separator +    sf::Text worldRad; +    // Separator +    sf::Text zapperCnt; +    sf::Text pelletCnt; +    sf::Text guppieCnt; +    sf::Text corpseCnt; +    // Separator +    sf::Text simStyle; +    // If sim. style == sel. by fitness +        sf::Text currentPop; +        sf::Text currentGen; +        // Separator +        sf::Text longestLife; +    // Left panel separators +    sf::RectangleShape sepLP[5]; + +    friend class SimBase; +    friend class SimFitness; +    friend class Entity; +    friend class Pellet; +    friend class Guppie; +    friend class Corpse; +}; + +#endif // __TEXTDISPLAY_HPP__ diff --git a/Simulation/Zapper.cpp b/Simulation/Zapper.cpp new file mode 100644 index 0000000..f31b996 --- /dev/null +++ b/Simulation/Zapper.cpp @@ -0,0 +1,46 @@ +#include "Zapper.hpp" +#include "SimBase.hpp" + +void Zapper::startup(SimBase *sim) +{ +    hSim = sim; +    radius = Params::ZAPPER_RAD; +} + + +void Zapper::update() +{ +    float forceX = realRand(-hSim->prms.zapperForce, hSim->prms.zapperForce); +    float forceY = realRand(-hSim->prms.zapperForce, hSim->prms.zapperForce); +    body->ApplyForceToCenter(b2Vec2(forceX, forceY)); +    float torque = realRand(-hSim->prms.zapperTorque, hSim->prms.zapperTorque); +    body->ApplyTorque(torque); +} + + +void Zapper::draw() +{ +    sf::Vector2f vSize = hSim->window.getView().getSize(); +    sf::Vector2f vCent = hSim->window.getView().getCenter(); +    if ( body->GetPosition().x + radius < vCent.x - vSize.x / 2.f || +         body->GetPosition().y + radius < vCent.y - vSize.y / 2.f || +         body->GetPosition().x - radius > vCent.x + vSize.x / 2.f || +         body->GetPosition().y - radius > vCent.y + vSize.y / 2.f  ) +    { +        return; +    } + +    if (hSim->camera.zoom > Params::ZAPPER_RAD / 2.f) +    { +        hSim->zapperPoint.setPosition(body->GetPosition().x, body->GetPosition().y); +        hSim->window.draw(hSim->zapperPoint); +    } +    else +    { +        hSim->zapperShell.setPosition(body->GetPosition().x, body->GetPosition().y); +        hSim->zapperNucleus.setPosition(body->GetPosition().x, body->GetPosition().y); +        hSim->zapperNucleus.setRotation(body->GetAngle() * hSim->prms.RAD_DGRS); +        hSim->window.draw(hSim->zapperShell); +        hSim->window.draw(hSim->zapperNucleus); +    } +} diff --git a/Simulation/Zapper.hpp b/Simulation/Zapper.hpp new file mode 100644 index 0000000..e71ff82 --- /dev/null +++ b/Simulation/Zapper.hpp @@ -0,0 +1,14 @@ +#ifndef __ZAPPER_HPP__ +#define __ZAPPER_HPP__ + +#include "Entity.hpp" + +class Zapper : public Entity +{ +public: +    void startup(SimBase *sim); +    void update(); +    void draw(); +}; + +#endif // __ZAPPER_HPP__  | 
