diff options
author | Paul Oliver <contact@pauloliver.dev> | 2024-02-29 19:27:35 +0100 |
---|---|---|
committer | Paul Oliver <contact@pauloliver.dev> | 2024-02-29 19:27:49 +0100 |
commit | 17909d029c6a8872b2fddf4e171d7925bbbe9c5c (patch) | |
tree | cbb08af84cd68d24acc362d593a2048b0fa79689 /Simulation/Guppie.cpp |
Diffstat (limited to 'Simulation/Guppie.cpp')
-rw-r--r-- | Simulation/Guppie.cpp | 584 |
1 files changed, 584 insertions, 0 deletions
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; +} |