From af7e23ab119eba7c0579796abd288c027edabfa9 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Thu, 29 Feb 2024 19:20:22 +0100 Subject: Initial commit --- src/Agent.cpp | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 src/Agent.cpp (limited to 'src/Agent.cpp') diff --git a/src/Agent.cpp b/src/Agent.cpp new file mode 100644 index 0000000..c082b96 --- /dev/null +++ b/src/Agent.cpp @@ -0,0 +1,256 @@ +#include + +#include + +#include +#include + +using namespace hn; +using namespace sf; +using namespace std; + +size_t +Agent::_currentLifetime = 1500; + +void +Agent::create(Stage& stage) +{ + _eyeSight.resize(5); + + _currCheckPoint = &stage._checkPoints.back(); + _nextCheckPoint = &stage._checkPoints.front(); + + _shape.setRadius(AGENT_RAD); + _shape.setOrigin(AGENT_RAD, AGENT_RAD); + _shape.setFillColor(AGENT_COLOR); + + b2CircleShape agentShape; + agentShape.m_radius = AGENT_RAD; + + b2FixtureDef agentFixture; + agentFixture.shape = &agentShape; + agentFixture.density = 1.0f; + agentFixture.friction = 0.3f; + agentFixture.filter.groupIndex = -1; + agentFixture.userData = this; + + b2BodyDef agentDef; + agentDef.type = b2_dynamicBody; + + _body = stage._world.CreateBody(&agentDef); + _body->CreateFixture(&agentFixture); + + recreate(stage, false); +} + +void +Agent::recreate(const Stage& stage, bool createNNet) +{ + _currCheckPoint = &stage._checkPoints.back(); + _nextCheckPoint = &stage._checkPoints.front(); + + _completedCircuits = 0; + _lifetime = 0; + _distance = 0.0; + _isOnTrap = false; + _isOld = false; + + auto factoryPos = stage._factory.getPosition(); + double factoryRot = stage._factory.getRotation() / IN_RADIANS; + + _body->SetTransform({factoryPos.x, factoryPos.y}, factoryRot); + _body->SetLinearVelocity( { 0.0f, 0.0f }); + _body->SetAngularVelocity(0.0f); + + float pX = _body->GetPosition().x; + float pY = _body->GetPosition().y; + float angle = _body->GetAngle() / IN_DEGREES; + + Transform tr; + tr.translate(pX, pY); + tr.rotate(angle); + + float sight = AGENT_SIGHT; + float iSight = AGENT_SIGHT * 0.7071f; + + _eyeSight[0] = tr.transformPoint({ -sight, 0.0f}); + _eyeSight[1] = tr.transformPoint({-iSight, iSight}); + _eyeSight[2] = tr.transformPoint({ 0.0f, sight}); + _eyeSight[3] = tr.transformPoint({ iSight, iSight}); + _eyeSight[4] = tr.transformPoint({ sight, 0.0f}); + + if (createNNet) { + _organism->createNeuralNet(); + } +} + +void +Agent::setPoints(const Stage& stage, bool passToOrg) +{ + if (!_organism->isOld()) { + return; + } + + // Calculate fitness + double dist = distanceTravelled(stage); + + if (passToOrg) { + _organism->_fitness = (dist / static_cast(max(_lifetime, (size_t)1))); + } +} + +void +Agent::setBehavior(const Stage& stage) +{ + _organism->getBehavior().at(0) = _body->GetPosition().x; + _organism->getBehavior().at(1) = _body->GetPosition().y; + _organism->getBehavior().at(2) = (distanceTravelled(stage) / static_cast(max(_lifetime, (size_t)1))); +} + +void +Agent::update(const Stage& stage, bool withNS) +{ + if (withNS) { + if (_organism->getLifetime() >= (_currentLifetime - 1)) { + if (!_isOld) { + _isOld = true; + } + } else if (_organism->getLifetime() < (_currentLifetime - 1)) { + if (_isOld) { + _isOld = false; + } + } + } + + ++_lifetime; + + if (_isOnTrap) { + return; + } + + float pX = _body->GetPosition().x; + float pY = _body->GetPosition().y; + float angle = _body->GetAngle() / IN_DEGREES; + + Transform tr, rot; + tr.translate(pX, pY); + tr.rotate(angle); + rot.rotate(angle); + + float sight = AGENT_SIGHT; + float iSight = AGENT_SIGHT * 0.7071f; + + _eyeSight[0] = tr.transformPoint({ -sight, 0.0f}); + _eyeSight[1] = tr.transformPoint({-iSight, iSight}); + _eyeSight[2] = tr.transformPoint({ 0.0f, sight}); + _eyeSight[3] = tr.transformPoint({ iSight, iSight}); + _eyeSight[4] = tr.transformPoint({ sight, 0.0f}); + + for (size_t i = 0; i < 5; ++i) { + RayCastCallback callback; + b2Vec2 b2EyeSight = { _eyeSight[i].x, _eyeSight[i].y }; + stage._world.RayCast(&callback, _body->GetPosition(), b2EyeSight); + _organism->_neuralNet->inputAt(i) = callback._fraction; + } + + _organism->_neuralNet->cycle(); + + Vector2f velocity + = rot.transformPoint({ 0.0f, static_cast(_organism->_neuralNet->outputAt(1)) * AGENT_SPEED }); + float rotation + = (((_organism->_neuralNet->outputAt(0) + _organism->_neuralNet->outputAt(2)) * 2.0f - 1.0f) / IN_RADIANS) * + AGENT_SPEED; + + _body->SetLinearVelocity({velocity.x, velocity.y}); + _body->SetAngularVelocity(rotation); +} + +void +Agent::drawOn(sf::RenderWindow& surface) +{ + float pX = _body->GetPosition().x; + float pY = _body->GetPosition().y; + + _shape.setPosition(pX, pY); + + VertexArray eyeSightLines(Lines); + + for (auto & i : _eyeSight) { + eyeSightLines.append({{pX, pY}, SIGHT_COLOR}); + eyeSightLines.append({ i, SIGHT_COLOR}); + } + + surface.draw(_shape); + surface.draw(eyeSightLines); +} + +void +Agent::incrementCheckPoint(Stage& stage) +{ + _currCheckPoint = _nextCheckPoint; + + if (_currCheckPoint == &stage._checkPoints.back()) { + _nextCheckPoint = &stage._checkPoints.front(); + } else { + ++_nextCheckPoint; + } + + if (_currCheckPoint == &stage._checkPoints.back()) { + ++_completedCircuits; + } +} + +double +Agent::distanceTravelled(const Stage& stage) +{ + double pCurr = _currCheckPoint->_value; + double pLoop = stage._checkPoints.back()._value; + + if (_currCheckPoint == &stage._checkPoints.back()) { + pCurr -= pLoop; + } + + double dNext = distanceToCheckpoint(*_nextCheckPoint); + double sNext = _nextCheckPoint->_segment; + + if (dNext > sNext) { + _distance = pCurr + (pLoop * _completedCircuits); + } else { + double pNext = _nextCheckPoint->_value; + double f1 = dNext / sNext; + double f2 = 1.0 - f1; + _distance = (pCurr * f1) + (pNext * f2) + (pLoop * _completedCircuits); + } + + if (_distance < 1.0) { + _distance = 1.0; + } + + return _distance; +} + +float +Agent::distanceToCheckpoint(const Stage::CheckPoint& cp) const +{ + b2Vec2 p = _body->GetPosition(); + b2Vec2 v = {cp._line[0].position.x, cp._line[0].position.y}; + b2Vec2 w = {cp._line[1].position.x, cp._line[1].position.y}; + + float l2 = (w - v).LengthSquared(); + + if (l2 == 0.0f) { + return dist(p, v); + } + + float t = dotP(p - v, w - v) / l2; + + if (t < 0.0f) { + return dist(p, v); + } else if (t > 1.0f) { + return dist(p, w); + } + + b2Vec2 projection = v + t * (w - v); + + return dist(p, projection); +} -- cgit v1.2.1