diff --git a/src/ant.cpp b/src/ant.cpp index 9d6f84b..9b6b5f9 100644 --- a/src/ant.cpp +++ b/src/ant.cpp @@ -3,136 +3,124 @@ #include #include #include +#include -Ant::Ant(Pheromones &pheromones, const Colony &colony, double x, double y) - : x(x), y(y), pheromones(pheromones), colony(colony) { - std::random_device rd; // obtain a random number from hardware - std::mt19937 gen(rd()); // seed the generator +Ant::Ant(Pheromones& pheromones, double x, double y) + : WorldObject(x, y, 2, 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); - - setAppearance(); + std::uniform_real_distribution<> degree_distribution(0, 2 * std::numbers::pi); + direction = degree_distribution(gen); } -Ant::Ant(Pheromones &pheromones, const Colony &colony, unsigned short direction) - : direction(direction), pheromones(pheromones), colony(colony) { - std::random_device device; // obtain a random number from hardware - std::mt19937 generator(device()); // seed the generator +Ant::Ant(Pheromones& pheromones, unsigned short direction) + : WorldObject(0, 0, 2, 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); + 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_int_distribution<> height_distribution(0, HEIGHT); + y = height_distribution(generator); - setAppearance(); + updateAppearance(); } -Ant::Ant(Pheromones &pheromones, const Colony &colony) - : pheromones(pheromones), colony(colony) { - std::random_device device; // obtain a random number from hardware - std::mt19937 generator(device()); // seed the generator +Ant::Ant(Pheromones& pheromones) + : WorldObject(0, 0, 2, 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<> width_distribution(0, WIDTH); + x = width_distribution(generator); - std::uniform_int_distribution<> height_distribution(0, HEIGHT); - y = height_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_real_distribution<> degree_distribution(0, 2 * std::numbers::pi); + direction = degree_distribution(generator); - setAppearance(); + updateAppearance(); } -void Ant::setAppearance() { - appearance = sf::CircleShape(2); - appearance.setFillColor(sf::Color::Black); - appearance.setPosition(x - appearance.getRadius(), y - appearance.getRadius()); +void Ant::addToUmwelt(std::shared_ptr object) { + umwelt.push_back(object); } void Ant::update() { - // TODO: save the generator for each ant - std::random_device device; // obtain a random number from hardware - std::mt19937 generator(device()); // seed the generator + move(); - // Move - std::uniform_real_distribution<> degree_distribution(-std::numbers::pi, - std::numbers::pi); + // Respect borders + if (isOffScreen()) { + direction += std::numbers::pi / 2; - direction += degree_distribution(generator) * (1 / determination); - if (direction > 2 * std::numbers::pi) { - direction -= 2 * std::numbers::pi; - } + x += std::cos(direction) * speed; + y += std::sin(direction) * speed; - x += std::cos(direction) * speed; - y += std::sin(direction) * speed; + return; + } - // Update appearance - appearance.setPosition(x - appearance.getRadius(), y - appearance.getRadius()); - if (was_home) { - appearance.setFillColor(sf::Color::Red); - } - if (has_food) { - appearance.setFillColor(sf::Color::Green); - } + updateAppearance(); + updatePheromones(); + updateUmwelt(); +} - // Respect borders - if (isOffScreen()) { - direction += std::numbers::pi / 2; +void Ant::move() { + // TODO: Leftoff, add ant-vision lol + for (int i = x; i < WIDTH * HEIGHT; ++i) { + if (true) { + + } + } + + 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 / determination); + if (direction > 2 * std::numbers::pi) { + direction -= 2 * std::numbers::pi; + } x += std::cos(direction) * speed; y += std::sin(direction) * speed; - - appearance.setPosition(x - appearance.getRadius(), y - appearance.getRadius()); - - return; - } - - // Visited Home? - if (!was_home && colony.antIsHome(*this)) { - was_home = true; - std::cout << "Ant has come home!" << std::endl; - } - - // Pheromones - if (was_home && next_pheromone_home_drop == 0) { - dropHomePheromone(); - next_pheromone_home_drop = pheromone_home_interval + 1; - } - next_pheromone_home_drop = std::max(0, next_pheromone_home_drop - 1); - - if (has_food && next_pheromone_food_drop == 0) { - dropFoodPheromone(); - next_pheromone_food_drop = pheromone_food_interval + 1; - } - next_pheromone_food_drop = std::max(0, next_pheromone_food_drop - 1); } -bool Ant::isOffScreen() const { - return x < 0 || x > WIDTH || y < 0 || y > HEIGHT; +void Ant::updateAppearance() { + appearance.setPosition(x, y); } -void Ant::dropHomePheromone() { - if (isOffScreen()) { - std::cout << "Ant can't drop Pheromones offscreen!" << std::endl; - return; - } - - // TODO: Replace this with null-safe funtion - pheromones.map[((short)y) * WIDTH + ((short)x)].color = sf::Color::Red; - // pheromones.map[((short)y - 1) * WIDTH + ((short)x)].color = sf::Color::Red; - // pheromones.map[((short)y + 1) * WIDTH + ((short)x)].color = sf::Color::Red; - // pheromones.map[((short)y) * WIDTH + ((short)x - 1)].color = sf::Color::Red; - // pheromones.map[((short)y) * WIDTH + ((short)x + 1)].color = sf::Color::Red; +void Ant::updatePheromones() { + if (next_pheromone_drop == 0) { + dropPheromone(); + next_pheromone_drop = pheromone_interval + 1; + } + next_pheromone_drop = std::max(0, next_pheromone_drop - 1); } -void Ant::dropFoodPheromone() { - if (isOffScreen()) { - std::cout << "Ant can't drop Pheromones offscreen!" << std::endl; - return; - } - - pheromones.map[((short)y) * WIDTH + ((short)x)].color = sf::Color::Green; +sf::Color Ant::getPheromoneType() const { + return sf::Color::Transparent; +} + +void Ant::dropPheromone() { + if (isOffScreen()) { + std::cout << "Ant can't drop Pheromones offscreen!" << std::endl; + return; + } + + pheromones.place(x, y, pheromone_type); +} + +void Ant::updateUmwelt() { + for (std::shared_ptr const& obj: umwelt) { + if (obj->collides(*this)) { + pheromone_type = obj->getPheromoneType(); + } + } } diff --git a/src/ant.hpp b/src/ant.hpp index 8625337..fb044fc 100644 --- a/src/ant.hpp +++ b/src/ant.hpp @@ -2,51 +2,47 @@ #define H_ANT #include -#include +#include +#include -#include "food.hpp" -#include "colony.hpp" +#include "world_object.hpp" #include "pheromones.hpp" - -extern const unsigned short WIDTH; -extern const unsigned short HEIGHT; +#include "colony.hpp" +#include "food.hpp" const double speed = 1; const double determination = 25; // straightness of the path, (0, 1] -const unsigned short pheromone_home_interval = 10; // updates between pheromone-drops -const unsigned short pheromone_food_interval = 5; +const unsigned short pheromone_interval = 5; // updates between drops const unsigned short view_angle = 45; // angle degrees to each side const unsigned short view_distance = 25; -class Ant { - double x, y; - double direction; // in radians +class Ant : public WorldObject +{ + double direction; // in radians - Pheromones &pheromones; - const Colony &colony; - - bool was_home = false; // TODO: Timer-based - bool has_food = false; - unsigned short next_pheromone_home_drop = 0; - unsigned short next_pheromone_food_drop = 0; - - friend class Colony; - friend class Food; + Pheromones& pheromones; + unsigned short next_pheromone_drop = 0; + sf::Color pheromone_type = sf::Color::Transparent; // ANT, HOME, FOOD public: - sf::CircleShape appearance; + std::vector> umwelt; public: - Ant(Pheromones &pheromones, const Colony &colony, double x, double y); - Ant(Pheromones &pheromones, const Colony &colony, unsigned short direction); - Ant(Pheromones &pheromones, const Colony &colony); - void setAppearance(); + Ant(Pheromones& pheromones, double x, double y); + Ant(Pheromones& pheromones, unsigned short direction); + explicit Ant(Pheromones& pheromones); - void update(); - bool isOffScreen() const; - void dropHomePheromone(); // red - void dropFoodPheromone(); // green + void addToUmwelt(std::shared_ptr object); + + void update() override; + sf::Color getPheromoneType() const override; + + void move(); + void updateAppearance(); + void updatePheromones(); + void dropPheromone(); // red + void updateUmwelt(); }; #endif diff --git a/src/colony.cpp b/src/colony.cpp index a8b2efd..2a2fb99 100644 --- a/src/colony.cpp +++ b/src/colony.cpp @@ -1,15 +1,9 @@ #include "colony.hpp" -#include "ant.hpp" -Colony::Colony(double x, double y) - : x(x), y(y) { - appearance = sf::CircleShape(25); - appearance.setFillColor(sf::Color::Red); - appearance.setPosition(x - appearance.getRadius(), y - appearance.getRadius()); -} +Colony::Colony(double x, double y) : WorldObject(x, y, 25, sf::Color::Red) {} -// TODO: don't use appearance for this, add radius field -bool Colony::antIsHome(const Ant& ant) const { - return ant.x > x - appearance.getRadius() && ant.x < x + appearance.getRadius() - && ant.y > y - appearance.getRadius() && ant.y < y + appearance.getRadius(); +void Colony::update() {} + +sf::Color Colony::getPheromoneType() const { + return sf::Color::Red; } diff --git a/src/colony.hpp b/src/colony.hpp index 032535f..9d4b556 100644 --- a/src/colony.hpp +++ b/src/colony.hpp @@ -2,19 +2,17 @@ #define __COLONY_H_ #include +#include "world_object.hpp" -class Ant; // Colony and Ant can't include eachother so forward-declare - -class Colony { - double x, y; +class Colony : public WorldObject +{ public: - sf::CircleShape appearance; + Colony(double x, double y); -public: - Colony(double x, double y); + void update() override; - bool antIsHome(const Ant& ant) const; + sf::Color getPheromoneType() const override; }; #endif // __COLONY_H_ diff --git a/src/food.cpp b/src/food.cpp index 81776b7..8089242 100644 --- a/src/food.cpp +++ b/src/food.cpp @@ -1,14 +1,9 @@ #include "food.hpp" -#include "ant.hpp" -Food::Food(double x, double y) - : x(x), y(y) { - appearance = sf::CircleShape(15); - appearance.setFillColor(sf::Color::Green); - appearance.setPosition(x - appearance.getRadius(), y - appearance.getRadius()); -} +Food::Food(double x, double y) : WorldObject(x, y, 15, sf::Color::Green) {} -bool Food::antHasFood(const Ant &ant) const { - return ant.x > x - appearance.getRadius() && ant.x < x + appearance.getRadius() - && ant.y > y - appearance.getRadius() && ant.y < y + appearance.getRadius(); +void Food::update() {} + +sf::Color Food::getPheromoneType() const { + return sf::Color::Green; } diff --git a/src/food.hpp b/src/food.hpp index 5abfc09..de935df 100644 --- a/src/food.hpp +++ b/src/food.hpp @@ -2,19 +2,17 @@ #define __FOOD_H_ #include +#include "world_object.hpp" -class Ant; - -class Food { - double x, y; +class Food : public WorldObject +{ public: - sf::CircleShape appearance; + Food(double x, double y); -public: - Food(double x, double y); + void update() override; - bool antHasFood(const Ant& ant) const; + sf::Color getPheromoneType() const override; }; #endif // __FOOD_H_ diff --git a/src/main.cpp b/src/main.cpp index e19a365..49b3b37 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,18 +5,21 @@ #include +#include "pheromones.hpp" +#include "world_object.hpp" #include "ant.hpp" #include "colony.hpp" +#include "food.hpp" const unsigned short HEIGHT = 500; const unsigned short WIDTH = 500; const unsigned short FPS = 60; -const unsigned short ANTCOUNT = 50; +const unsigned short ANTCOUNT = 500; -int main(int argc, char *argv[]) { +int main(int argc, char* argv[]) { sf::ContextSettings settings; - settings.antialiasingLevel = 8; +// settings.antialiasingLevel = 8; sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "Ants", sf::Style::Close, settings); window.setFramerateLimit(FPS); // Limit FPS @@ -24,14 +27,19 @@ int main(int argc, char *argv[]) { float t = 0.0; // Verstrichene Zeit in ms float dt = 1.0 / FPS; // Schrittweite in ms - Pheromones pheromones = Pheromones(); - const Colony colony = Colony(WIDTH / 2, HEIGHT / 2); - const Food foodA = Food(50, 50); - - std::vector ants; + Pheromones pheromones; + std::vector> ants; // Use pointer bc we can't instatiate abstract classes ants.reserve(ANTCOUNT); + + 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) { - ants.push_back(Ant(pheromones, colony)); + ants.push_back(std::make_unique(pheromones)); + } + for (std::unique_ptr const& ant : ants) { + ant->addToUmwelt(colony); + ant->addToUmwelt(foodA); } while (window.isOpen()) { @@ -45,18 +53,19 @@ int main(int argc, char *argv[]) { // Update t += dt; - for (unsigned long i = 0; i < ants.size(); ++i) { - ants[i].update(); + for (std::unique_ptr const& obj: ants) { + obj->update(); } pheromones.update(); // Render window.clear(sf::Color::White); window.draw(pheromones.map); - for (Ant const& ant: ants) { - window.draw(ant.appearance); + for (std::unique_ptr const& obj: ants) { + window.draw(obj->appearance); } - window.draw(colony.appearance); + window.draw(colony->appearance); + window.draw(foodA->appearance); window.display(); } diff --git a/src/object.hpp b/src/object.hpp deleted file mode 100644 index 01dd0d2..0000000 --- a/src/object.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __OBJECT_H_ -#define __OBJECT_H_ - -#include - -class Object { -protected: - double x, y; - const unsigned short radius; - - sf::CircleShape appearance; - -protected: - Object(double x, double y, unsigned short radius); - -public: - virtual bool collide(const Object& other); -}; - -#endif // __OBJECT_H_ diff --git a/src/pheromones.cpp b/src/pheromones.cpp index d36f97d..fba2ffd 100644 --- a/src/pheromones.cpp +++ b/src/pheromones.cpp @@ -4,19 +4,31 @@ extern const unsigned short WIDTH; extern const unsigned short HEIGHT; Pheromones::Pheromones() { - for (unsigned short y = 0; y < HEIGHT; ++y) { - for (unsigned short x = 0; x < WIDTH; ++x) { - map[y * WIDTH + x].position.x = x; - map[y * WIDTH + x].position.y = y; - map[y * WIDTH + x].color = sf::Color(0, 0, 0, 0); - } - } + for (unsigned short y = 0; y < HEIGHT; ++y) { + for (unsigned short x = 0; x < WIDTH; ++x) { + map[y * WIDTH + x].position.x = x; + map[y * WIDTH + x].position.y = y; + map[y * WIDTH + x].color = sf::Color(0, 0, 0, 0); + } + } +} + +void Pheromones::place(unsigned short x, unsigned short y, sf::Color col) { + map[y * WIDTH + x].color = col; + map[(y + 1) * WIDTH + x].color = col; + map[(y - 1) * WIDTH + x].color = col; + map[y * WIDTH + (x + 1)].color = col; + map[y * WIDTH + (x - 1)].color = col; } void Pheromones::update() { - for (int i = 0; i < WIDTH * HEIGHT; ++i) { - // if (map[i].color != sf::Color::White) { - map[i].color -= sf::Color(decay, decay, decay, decay); - // } - } + for (int i = 0; i < WIDTH * HEIGHT; ++i) { + map[i].color -= sf::Color(decay, decay, decay, decay); + } + for (int x = 0; x < WIDTH; ++x) { + map[(HEIGHT / 2) * WIDTH + x].color = sf::Color::Black; + } + for (int y = 0; y < HEIGHT; ++y) { + map[y * WIDTH + (WIDTH / 2)].color = sf::Color::Black; + } } diff --git a/src/pheromones.hpp b/src/pheromones.hpp index 4216572..7e33cc8 100644 --- a/src/pheromones.hpp +++ b/src/pheromones.hpp @@ -8,14 +8,17 @@ extern const unsigned short HEIGHT; const unsigned short decay = 1; -class Pheromones { +class Pheromones +{ public: - sf::VertexArray map = sf::VertexArray(sf::PrimitiveType::Points, WIDTH * HEIGHT); + sf::VertexArray map = sf::VertexArray(sf::PrimitiveType::Points, WIDTH * HEIGHT); public: - Pheromones(); + Pheromones(); - void update(); + void place(unsigned short x, unsigned short y, sf::Color col); + + void update(); }; #endif diff --git a/src/umwelt.hpp b/src/umwelt.hpp deleted file mode 100644 index ed371ae..0000000 --- a/src/umwelt.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __UMWELT_H_ -#define __UMWELT_H_ - -#include -#include "object.hpp" - -struct Umwelt { - std::vector objects; -}; - -#endif // __UMWELT_H_ diff --git a/src/world_object.cpp b/src/world_object.cpp new file mode 100644 index 0000000..97a8fe3 --- /dev/null +++ b/src/world_object.cpp @@ -0,0 +1,28 @@ +#include "world_object.hpp" + +WorldObject::WorldObject(double x, double y, unsigned short radius, sf::Color color) + : x(x), y(y), radius(radius) { + appearance = sf::CircleShape(radius); + appearance.setFillColor(color); + appearance.setPosition(x, y); + appearance.setOrigin(radius, radius); +} + +bool WorldObject::isOffScreen() const { + return x < 0 || x > WIDTH || y < 0 || y > HEIGHT; +} + +bool WorldObject::collides(const WorldObject& other) const { + return distance(other) < radius + other.radius; +} + +double WorldObject::distance(const WorldObject& other) const { + const double dx = std::abs(x - other.x); + const double dy = std::abs(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); + return std::asin(dy / distance(other)); +} diff --git a/src/world_object.hpp b/src/world_object.hpp new file mode 100644 index 0000000..0e3ab01 --- /dev/null +++ b/src/world_object.hpp @@ -0,0 +1,34 @@ +#ifndef __OBJECT_H_ +#define __OBJECT_H_ + +#include +#include + +extern const unsigned short WIDTH; +extern const unsigned short HEIGHT; + +class WorldObject +{ +protected: + double x, y; + const unsigned short radius; + +public: + sf::CircleShape appearance; + +protected: + WorldObject(double x, double y, unsigned short radius, sf::Color color); + +public: + bool isOffScreen() const; // virtual: late-binding, no static linkage + bool collides(const WorldObject& other) const; + + double distance(const WorldObject& other) const; + + double angle(const WorldObject& other) const; + + virtual void update() = 0; // pure virtual: has to be overridden + virtual sf::Color getPheromoneType() const = 0; +}; + +#endif // __OBJECT_H_