From e1a9eb2beb2e46f4bccfd4f2288e9ae55842fa4d Mon Sep 17 00:00:00 2001 From: churl Date: Sun, 15 May 2022 17:06:47 +0200 Subject: [PATCH] initial refactor to pheromone matrix --- src/ant.cpp | 44 +++---------------------------------------- src/ant.hpp | 41 ++++++++++++++++++++-------------------- src/colony.cpp | 4 ---- src/colony.hpp | 2 -- src/food.cpp | 4 ---- src/food.hpp | 2 -- src/main.cpp | 27 +++++++------------------- src/main.hpp | 9 +++++++++ src/pheromone.cpp | 35 ---------------------------------- src/pheromone.hpp | 25 ------------------------ src/pheromone_map.cpp | 28 +++++++++++---------------- src/pheromone_map.hpp | 17 +++++------------ src/world_object.cpp | 9 +++++---- src/world_object.hpp | 18 +++--------------- 14 files changed, 64 insertions(+), 201 deletions(-) create mode 100644 src/main.hpp delete mode 100644 src/pheromone.cpp delete mode 100644 src/pheromone.hpp diff --git a/src/ant.cpp b/src/ant.cpp index c44e258..43889c3 100644 --- a/src/ant.cpp +++ b/src/ant.cpp @@ -42,7 +42,6 @@ Ant::Ant(PheromoneMap& pheromones) updateAppearance(); } -// TODO: Unnecessary, only used for food/base void Ant::addToUmwelt(const std::shared_ptr& object) { umwelt.push_back(object); } @@ -61,33 +60,17 @@ void Ant::update() { } updateAppearance(); - updatePheromones(); - updateUmwelt(); } void Ant::move() { - PheroType attractor; - if (pheromone_type == HOME) { - attractor = FOOD; - } else if (pheromone_type == FOOD) { - attractor = HOME; - } else { - attractor = NONE; - } - for (const Pheromone& pheromone : pheromones.getInVision(*this, attractor, view_distance)) { - // TODO: What is this supposed to do? - } - // TODO: Should this random generator be created on every move? std::random_device device; // obtain a random number from hardware std::mt19937 generator(device()); // seed the generator // Move - std::uniform_real_distribution<> degree_distribution(-std::numbers::pi, - std::numbers::pi); + std::uniform_real_distribution<> degree_distribution(-std::numbers::pi, std::numbers::pi); - direction += degree_distribution(generator) * (1 / determination); // Normalize with determination to smooth movement - // TODO: Use modulo + direction += degree_distribution(generator) * (1.0 / determination); // Normalize with determination to smooth movement if (direction > 2 * std::numbers::pi) { direction -= 2 * std::numbers::pi; } @@ -100,32 +83,11 @@ void Ant::updateAppearance() { appearance.setPosition(x, y); } -void Ant::updatePheromones() { - if (next_pheromone_drop == 0) { - dropPheromone(); - next_pheromone_drop = pheromone_interval + 1; - } - next_pheromone_drop = std::max(0U, next_pheromone_drop - 1); -} - -PheroType Ant::getPheromoneType() const { - return NONE; -} - void Ant::dropPheromone() { if (isOffScreen()) { std::cout << "Ant can't drop PheromoneMap offscreen!" << std::endl; return; } - pheromones.place(x, y, pheromone_type); -} - -// TODO: This is weird -void Ant::updateUmwelt() { - for (std::shared_ptr const& obj : umwelt) { - if (obj->collides(*this)) { - pheromone_type = obj->getPheromoneType(); - } - } + pheromones.place(x, y); } diff --git a/src/ant.hpp b/src/ant.hpp index dd444df..5e6c9e7 100644 --- a/src/ant.hpp +++ b/src/ant.hpp @@ -3,6 +3,7 @@ #include "colony.hpp" #include "food.hpp" +#include "main.hpp" #include "pheromone_map.hpp" #include "world_object.hpp" #include @@ -13,43 +14,43 @@ #include #include -const double speed = 1; -const double determination = 25; // straightness of the path -const unsigned int pheromone_interval = 5; // how many updates between drops +constexpr unsigned int speed = 1; -const unsigned int view_angle = 45; // angle degrees to each side -const unsigned int view_distance = 25; // more like smell distance +constexpr unsigned int view_angle = 45; // angle degrees to each side +constexpr unsigned int view_distance = 25; // more like smell distance + +typedef enum { + NORTH, + EAST, + SOUTH, + WEST +} direction; class Ant : public WorldObject { - double direction; // in radians +private: + // TODO: Switch direction to only N, E, S, W + direction dir; // in radians // TODO: Should this be here or just global? PheromoneMap& pheromones; + + // TODO: Leave continuous trail and remove this unsigned int next_pheromone_drop = 0; - PheroType pheromone_type = NONE; // FOOD, HOME, NONE public: - // TODO: Why is this stored here? I guess it can be removed + // Contains the WorldObjects (other ants, base, food) + // TODO: Is this even needed? std::vector> umwelt; -public: - Ant(PheromoneMap& pheromones, double x, double y); - Ant(PheromoneMap& pheromones, unsigned int direction); - explicit Ant(PheromoneMap& pheromones); + Ant(PheromoneMap& pheromones, double x, double y); // Just random direction + Ant(PheromoneMap& pheromones, unsigned int direction); // Just random position + explicit Ant(PheromoneMap& pheromones); // All random - // TODO: Regarding umwelt void addToUmwelt(const std::shared_ptr& object); - void update() override; - PheroType getPheromoneType() const override; - void move(); void updateAppearance(); - void updatePheromones(); void dropPheromone(); // red - - // TODO: Regarding umwelt - void updateUmwelt(); }; #endif diff --git a/src/colony.cpp b/src/colony.cpp index b8435c0..a016213 100644 --- a/src/colony.cpp +++ b/src/colony.cpp @@ -3,7 +3,3 @@ Colony::Colony(double x, double y) : WorldObject(x, y, 25, sf::Color::Red) {} void Colony::update() {} - -PheroType Colony::getPheromoneType() const { - return HOME; -} diff --git a/src/colony.hpp b/src/colony.hpp index 54010d2..13e286a 100644 --- a/src/colony.hpp +++ b/src/colony.hpp @@ -5,12 +5,10 @@ #include class Colony : public WorldObject { - public: Colony(double x, double y); void update() override; - PheroType getPheromoneType() const override; }; #endif // __COLONY_H_ diff --git a/src/food.cpp b/src/food.cpp index aa5eb3c..29c71a1 100644 --- a/src/food.cpp +++ b/src/food.cpp @@ -3,7 +3,3 @@ Food::Food(double x, double y) : WorldObject(x, y, 15, sf::Color::Green) {} void Food::update() {} - -PheroType Food::getPheromoneType() const { - return FOOD; -} diff --git a/src/food.hpp b/src/food.hpp index a1d182e..5a24981 100644 --- a/src/food.hpp +++ b/src/food.hpp @@ -5,12 +5,10 @@ #include class Food : public WorldObject { - public: Food(double x, double y); void update() override; - PheroType getPheromoneType() const override; }; #endif // __FOOD_H_ diff --git a/src/main.cpp b/src/main.cpp index 7b3e0b8..1454f95 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,21 +1,13 @@ -#include -#include -#include -#include - -#include - +#include "main.hpp" #include "ant.hpp" #include "colony.hpp" #include "food.hpp" -#include "pheromone.hpp" #include "pheromone_map.hpp" - -const unsigned int HEIGHT = 500; -const unsigned int WIDTH = 500; -const unsigned int FPS = 60; - -const unsigned int ANTCOUNT = 100; +#include +#include +#include +#include +#include int main(int argc, char* argv[]) { sf::ContextSettings settings; @@ -59,15 +51,10 @@ int main(int argc, char* argv[]) { for (std::unique_ptr const& obj : ants) { obj->update(); } - for (Pheromone& pheromone : pheromones.pheromones) { - pheromone.update(); - } // Render window.clear(sf::Color::White); - for (Pheromone& pheromone : pheromones.pheromones) { - window.draw(pheromone.appearance); - } + pheromones.draw(); for (std::unique_ptr const& obj : ants) { window.draw(obj->appearance); } diff --git a/src/main.hpp b/src/main.hpp new file mode 100644 index 0000000..da9fce7 --- /dev/null +++ b/src/main.hpp @@ -0,0 +1,9 @@ +#ifndef MAIN_H_ +#define MAIN_H_ + +constexpr unsigned int WIDTH = 500; +constexpr unsigned int HEIGHT = 500; +constexpr unsigned int FPS = 60; +constexpr unsigned int ANTCOUNT = 100; + +#endif // MAIN_H_ diff --git a/src/pheromone.cpp b/src/pheromone.cpp deleted file mode 100644 index 648cda2..0000000 --- a/src/pheromone.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// -// Created by christoph on 11.04.21. -// - -#include "pheromone.hpp" - -Pheromone::Pheromone(double x, double y, PheroType type) - : WorldObject(x, y, 2, sf::Color::Transparent) { - if (type == HOME) { - appearance.setFillColor(sf::Color::Red); - } else if (type == FOOD) { - appearance.setFillColor(sf::Color::Green); - } else { - appearance.setFillColor(sf::Color::Transparent); - } -} - -PheroType Pheromone::getPheromoneType() const { - if (appearance.getFillColor() == sf::Color::Red) { - return HOME; - } - if (appearance.getFillColor() == sf::Color::Green) { - return FOOD; - } - return NONE; -} - -void Pheromone::update() { - intensity = std::max(0U, intensity - decay); - appearance.setFillColor(sf::Color( - appearance.getFillColor().r, - appearance.getFillColor().g, - appearance.getFillColor().b, - intensity)); -} diff --git a/src/pheromone.hpp b/src/pheromone.hpp deleted file mode 100644 index bd0eadb..0000000 --- a/src/pheromone.hpp +++ /dev/null @@ -1,25 +0,0 @@ -// -// Created by christoph on 11.04.21. -// - -#ifndef ANTSIMULATOR_PHEROMONE_HPP -#define ANTSIMULATOR_PHEROMONE_HPP - -#include "world_object.hpp" -#include - -// TODO: Use percentage -const unsigned int decay = 1; - -class Pheromone : public WorldObject { -public: - // TODO: Use 1.0 to 0.0 double - unsigned int intensity = 255; - - Pheromone(double x, double y, PheroType type); - - PheroType getPheromoneType() const override; - void update() override; -}; - -#endif //ANTSIMULATOR_PHEROMONE_HPP diff --git a/src/pheromone_map.cpp b/src/pheromone_map.cpp index 4a0ccaa..e649c2b 100644 --- a/src/pheromone_map.cpp +++ b/src/pheromone_map.cpp @@ -1,25 +1,19 @@ #include "pheromone_map.hpp" -void PheromoneMap::place(double x, double y, PheroType type) { - pheromones.emplace_back(x, y, type); -} - -std::vector PheromoneMap::getInVision(const Ant& ant, PheroType type, unsigned short radius) { - std::vector umwelt; - - for (const Pheromone& pheromone : pheromones) { - if (pheromone.getPheromoneType() == type && pheromone.distance(ant) <= radius) { - umwelt.push_back(pheromone); - } - } - - return umwelt; +void PheromoneMap::place(double x, double y) { + pheromones[x][y] = 1.0; } +// Decays the pheromone intensity void PheromoneMap::update() { - for (size_t i = 0; i < pheromones.size(); ++i) { - if (pheromones[i].appearance.getFillColor().a == 0) { - // pheromones.erase(pheromones.begin() + i); + for (auto x = 0; x < WIDTH; ++x) { + for (auto y = 0; y < HEIGHT; ++y) { + // TODO: Use defined pheromone decay rate instead of magic 0.01 + pheromones[x][y] = std::max(0.0, pheromones[x][y] - 0.01); } } } + +void PheromoneMap::draw() { + // TODO: This should convert the 2d pheromone map to a sfml VertexArray to be drawn to the window (below the ants/base/food) +} diff --git a/src/pheromone_map.hpp b/src/pheromone_map.hpp index f519af4..b7d90fa 100644 --- a/src/pheromone_map.hpp +++ b/src/pheromone_map.hpp @@ -1,23 +1,16 @@ #ifndef __PHEROMONES_H_ #define __PHEROMONES_H_ -#include "ant.hpp" -#include "pheromone.hpp" -#include -#include -#include - -class Ant; +#include "main.hpp" +#include class PheromoneMap { public: - // TODO: Use fixed size matrix (and only int locations) for this, currently it's just slow - std::vector pheromones; + std::array, WIDTH> pheromones; - // TODO: Move this to ant - std::vector getInVision(const Ant& ant, PheroType type, unsigned short radius); - void place(double x, double y, PheroType type); + void place(double x, double y); void update(); + void draw(); }; #endif diff --git a/src/world_object.cpp b/src/world_object.cpp index b5f0962..4486a61 100644 --- a/src/world_object.cpp +++ b/src/world_object.cpp @@ -1,6 +1,6 @@ #include "world_object.hpp" -WorldObject::WorldObject(double x, double y, unsigned int radius, sf::Color color) +WorldObject::WorldObject(unsigned int x, unsigned int y, unsigned int radius, sf::Color color) : x(x), y(y), radius(radius) { appearance = sf::CircleShape(radius); appearance.setFillColor(color); @@ -17,12 +17,13 @@ bool WorldObject::collides(const WorldObject& other) const { } double WorldObject::distance(const WorldObject& other) const { - const double dx = std::abs(x - other.x); - const double dy = std::abs(y - other.y); + // No need to use abs here as it gets converted to int + const int dx = x - other.x; + const int dy = y - other.y; return std::sqrt(dx * dx + dy * dy); } double WorldObject::angle(const WorldObject& other) const { - const double dy = std::abs(y - other.y); + const int dy = y - other.y; return std::asin(dy / distance(other)); } diff --git a/src/world_object.hpp b/src/world_object.hpp index 3b11806..594764f 100644 --- a/src/world_object.hpp +++ b/src/world_object.hpp @@ -1,26 +1,16 @@ #ifndef __WORLD_OBJECT_H_ #define __WORLD_OBJECT_H_ +#include "main.hpp" #include #include -// TODO: Move definitions to header and use constexpr/macro -extern const unsigned int WIDTH; -extern const unsigned int HEIGHT; - -// TODO: Just use FOOD -enum PheroType { - FOOD, - HOME, - NONE -}; - class WorldObject { protected: - double x, y; + unsigned int x, y; const unsigned int radius; - WorldObject(double x, double y, unsigned int radius, sf::Color color); + WorldObject(unsigned int x, unsigned int y, unsigned int radius, sf::Color color); public: sf::CircleShape appearance; @@ -30,8 +20,6 @@ public: double distance(const WorldObject& other) const; double angle(const WorldObject& other) const; - // TODO: Switch to only food pheromones - virtual PheroType getPheromoneType() const = 0; virtual void update() = 0; // pure virtual: has to be overridden };