From 1e6378e17c81ed993e1799410c2f0c936079bf3d Mon Sep 17 00:00:00 2001 From: churl Date: Sun, 15 May 2022 18:38:33 +0200 Subject: [PATCH] print pheros + ants drop pheros + return to base --- src/ant.cpp | 107 ++++++++++++++++++++++++------------------ src/ant.hpp | 22 ++++----- src/colony.hpp | 1 + src/food.hpp | 1 + src/main.cpp | 7 +-- src/pheromone_map.cpp | 22 ++++++--- src/pheromone_map.hpp | 5 +- src/world_object.hpp | 1 + 8 files changed, 97 insertions(+), 69 deletions(-) diff --git a/src/ant.cpp b/src/ant.cpp index 43889c3..1546e22 100644 --- a/src/ant.cpp +++ b/src/ant.cpp @@ -1,43 +1,32 @@ #include "ant.hpp" +std::tuple direction_to_offset(direction dir) { + switch (dir) { + case direction::NORTH: + return std::make_tuple(0, 1); + case direction::EAST: + return std::make_tuple(1, 0); + case direction::SOUTH: + return std::make_tuple(0, -1); + case direction::WEST: + return std::make_tuple(-1, 0); + } +} + // TODO: Should every ant have its own random generators? Why not initialize with coordinates and put the random numbers in there (from main) -Ant::Ant(PheromoneMap& pheromones, double x, double y) - : WorldObject(x, y, 3, sf::Color::Black), pheromones(pheromones) { +Ant::Ant(PheromoneMap& pheromones) + : WorldObject(0, 0, 3, sf::Color::Black), pheromones(pheromones) { std::random_device rd; // obtain a random number from hardware std::mt19937 gen(rd()); // seed the generator - std::uniform_real_distribution<> degree_distribution(0, 2 * std::numbers::pi); - direction = degree_distribution(gen); -} - -Ant::Ant(PheromoneMap& pheromones, unsigned int direction) - : WorldObject(0, 0, 3, sf::Color::Black), direction(direction), - pheromones(pheromones) { - std::random_device device; // obtain a random number from hardware - std::mt19937 generator(device()); // seed the generator - std::uniform_int_distribution<> width_distribution(0, WIDTH); - x = width_distribution(generator); + x = width_distribution(gen); std::uniform_int_distribution<> height_distribution(0, HEIGHT); - y = height_distribution(generator); + y = height_distribution(gen); - updateAppearance(); -} - -Ant::Ant(PheromoneMap& pheromones) - : WorldObject(0, 0, 3, sf::Color::Black), pheromones(pheromones) { - std::random_device device; // obtain a random number from hardware - std::mt19937 generator(device()); // seed the generator - - std::uniform_int_distribution<> width_distribution(0, WIDTH); - x = width_distribution(generator); - - std::uniform_int_distribution<> height_distribution(0, HEIGHT); - y = height_distribution(generator); - - std::uniform_real_distribution<> degree_distribution(0, 2 * std::numbers::pi); - direction = degree_distribution(generator); + std::uniform_int_distribution<> dir_distribution(0, 3); + dir = (direction)dir_distribution(gen); updateAppearance(); } @@ -51,32 +40,60 @@ void Ant::update() { // Respect borders if (isOffScreen()) { - direction += std::numbers::pi / 2; + dir = (direction)((dir + 2) % 4); - x += std::cos(direction) * speed; - y += std::sin(direction) * speed; + x += std::get<0>(direction_to_offset(dir)) * speed; + y += std::get<1>(direction_to_offset(dir)) * speed; return; } updateAppearance(); + if (has_food) { + dropPheromone(); + } } void Ant::move() { - // 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); - - direction += degree_distribution(generator) * (1.0 / determination); // Normalize with determination to smooth movement - if (direction > 2 * std::numbers::pi) { - direction -= 2 * std::numbers::pi; + for (const auto& world_obj : umwelt) { + if (world_obj->has_pheromones() && collides(*world_obj)) { + has_food = true; + } else if (collides(*world_obj)) { + has_food = false; + } } - x += std::cos(direction) * speed; - y += std::sin(direction) * speed; + // TODO: Should this random generator be created on every move or should the ant carry it always? + std::random_device device; // obtain a random number from hardware + std::mt19937 gen(device()); // seed the generator + + if (has_food) { + move_to_base(); + } else { + // Change direction randomly + std::uniform_int_distribution<> dir_distribution(-1, 1); + if (dir_distribution(gen) == 0) { + // Change direction less often (1/3 of the time) + dir = (direction)(((unsigned int)dir + (dir_distribution(gen) + 4)) % 4); // + 4 so the value won't become negative + } + } + + // Move + x += std::get<0>(direction_to_offset(dir)) * speed; + y += std::get<1>(direction_to_offset(dir)) * speed; +} + +// TODO: Replace umwelt with direct references to base/food and update this +void Ant::move_to_base() { + if (x < WIDTH / 2) { + dir = direction::EAST; + } else if (y < HEIGHT / 2) { + dir = direction::NORTH; + } else if (x > WIDTH / 2) { + dir = direction::WEST; + } else if (y > HEIGHT / 2) { + dir = direction::SOUTH; + } } void Ant::updateAppearance() { diff --git a/src/ant.hpp b/src/ant.hpp index 5e6c9e7..bfbd1c1 100644 --- a/src/ant.hpp +++ b/src/ant.hpp @@ -6,13 +6,12 @@ #include "main.hpp" #include "pheromone_map.hpp" #include "world_object.hpp" -#include #include #include -#include #include #include #include +#include constexpr unsigned int speed = 1; @@ -26,31 +25,30 @@ typedef enum { WEST } direction; +std::tuple direction_to_offset(direction dir); + class Ant : public WorldObject { private: - // TODO: Switch direction to only N, E, S, W - direction dir; // in radians + direction dir; + bool has_food = false; // TODO: Should this be here or just global? PheromoneMap& pheromones; - // TODO: Leave continuous trail and remove this - unsigned int next_pheromone_drop = 0; - public: - // Contains the WorldObjects (other ants, base, food) - // TODO: Is this even needed? + // Contains the WorldObjects (other ants?, base, food) + // TODO: Replace this with direct refereces to base/food std::vector> umwelt; - 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 + Ant(PheromoneMap& pheromones); void addToUmwelt(const std::shared_ptr& object); void update() override; void move(); + void move_to_base(); void updateAppearance(); void dropPheromone(); // red + bool has_pheromones() override { return false; } }; #endif diff --git a/src/colony.hpp b/src/colony.hpp index 13e286a..e5428ff 100644 --- a/src/colony.hpp +++ b/src/colony.hpp @@ -9,6 +9,7 @@ public: Colony(double x, double y); void update() override; + bool has_pheromones() override { return false; } }; #endif // __COLONY_H_ diff --git a/src/food.hpp b/src/food.hpp index 5a24981..0ff2b0e 100644 --- a/src/food.hpp +++ b/src/food.hpp @@ -9,6 +9,7 @@ public: Food(double x, double y); void update() override; + bool has_pheromones() override { return true; } }; #endif // __FOOD_H_ diff --git a/src/main.cpp b/src/main.cpp index 1454f95..2f16d2a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,7 +9,7 @@ #include #include -int main(int argc, char* argv[]) { +int main() { sf::ContextSettings settings; settings.antialiasingLevel = 8; @@ -26,7 +26,8 @@ int main(int argc, char* argv[]) { std::shared_ptr colony = std::make_shared(WIDTH / 2, HEIGHT / 2); std::shared_ptr foodA = std::make_shared(50, 50); - for (int i = 0; i < ANTCOUNT; ++i) { + // Init ants + for (auto i = 0U; i < ANTCOUNT; ++i) { ants.push_back(std::make_unique(pheromones)); } for (std::unique_ptr const& ant : ants) { @@ -54,7 +55,7 @@ int main(int argc, char* argv[]) { // Render window.clear(sf::Color::White); - pheromones.draw(); + window.draw(pheromones.vertex_array()); for (std::unique_ptr const& obj : ants) { window.draw(obj->appearance); } diff --git a/src/pheromone_map.cpp b/src/pheromone_map.cpp index e649c2b..08f2051 100644 --- a/src/pheromone_map.cpp +++ b/src/pheromone_map.cpp @@ -1,19 +1,27 @@ #include "pheromone_map.hpp" -void PheromoneMap::place(double x, double y) { +void PheromoneMap::place(unsigned int x, unsigned int y) { pheromones[x][y] = 1.0; } // Decays the pheromone intensity void PheromoneMap::update() { - 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); + for (auto x = 0U; x < WIDTH; ++x) { + for (auto y = 0U; y < HEIGHT; ++y) { + // TODO: Use defined pheromone decay rate instead of magic number + pheromones[x][y] = std::max(0.0, pheromones[x][y] - 0.001); } } } -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) +// TODO: Represent pheromones as VertexArray directly to skip conversion every frame +sf::VertexArray PheromoneMap::vertex_array() const { + sf::VertexArray arr = sf::VertexArray(sf::Points, WIDTH * HEIGHT); + for (auto x = 0U; x < WIDTH; ++x) { + for (auto y = 0U; y < HEIGHT; ++y) { + arr[x + y * WIDTH].position = sf::Vector2f(x, y); + arr[x + y * WIDTH].color = sf::Color(0, 0, 255, 255 * pheromones[x][y]); + } + } + return arr; } diff --git a/src/pheromone_map.hpp b/src/pheromone_map.hpp index b7d90fa..04f7c21 100644 --- a/src/pheromone_map.hpp +++ b/src/pheromone_map.hpp @@ -3,14 +3,15 @@ #include "main.hpp" #include +#include class PheromoneMap { public: std::array, WIDTH> pheromones; - void place(double x, double y); + void place(unsigned int x, unsigned int y); void update(); - void draw(); + sf::VertexArray vertex_array() const; }; #endif diff --git a/src/world_object.hpp b/src/world_object.hpp index 594764f..02724e9 100644 --- a/src/world_object.hpp +++ b/src/world_object.hpp @@ -21,6 +21,7 @@ public: double angle(const WorldObject& other) const; virtual void update() = 0; // pure virtual: has to be overridden + virtual bool has_pheromones() = 0; }; #endif // __OBJECT_H_