print pheros + ants drop pheros + return to base

This commit is contained in:
churl
2022-05-15 18:38:33 +02:00
parent e1a9eb2beb
commit 1e6378e17c
8 changed files with 97 additions and 69 deletions

View File

@ -1,43 +1,32 @@
#include "ant.hpp"
std::tuple<int, int> direction_to_offset(direction dir) {
switch (dir) {
case direction::NORTH:
return std::make_tuple<int, int>(0, 1);
case direction::EAST:
return std::make_tuple<int, int>(1, 0);
case direction::SOUTH:
return std::make_tuple<int, int>(0, -1);
case direction::WEST:
return std::make_tuple<int, int>(-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() {

View File

@ -6,13 +6,12 @@
#include "main.hpp"
#include "pheromone_map.hpp"
#include "world_object.hpp"
#include <cmath>
#include <iostream>
#include <memory>
#include <numbers>
#include <random>
#include <SFML/Graphics.hpp>
#include <string>
#include <tuple>
constexpr unsigned int speed = 1;
@ -26,31 +25,30 @@ typedef enum {
WEST
} direction;
std::tuple<int, int> 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<std::shared_ptr<WorldObject>> 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<WorldObject>& object);
void update() override;
void move();
void move_to_base();
void updateAppearance();
void dropPheromone(); // red
bool has_pheromones() override { return false; }
};
#endif

View File

@ -9,6 +9,7 @@ public:
Colony(double x, double y);
void update() override;
bool has_pheromones() override { return false; }
};
#endif // __COLONY_H_

View File

@ -9,6 +9,7 @@ public:
Food(double x, double y);
void update() override;
bool has_pheromones() override { return true; }
};
#endif // __FOOD_H_

View File

@ -9,7 +9,7 @@
#include <SFML/Graphics.hpp>
#include <vector>
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> colony = std::make_shared<Colony>(WIDTH / 2, HEIGHT / 2);
std::shared_ptr<Food> foodA = std::make_shared<Food>(50, 50);
for (int i = 0; i < ANTCOUNT; ++i) {
// Init ants
for (auto i = 0U; i < ANTCOUNT; ++i) {
ants.push_back(std::make_unique<Ant>(pheromones));
}
for (std::unique_ptr<Ant> 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<Ant> const& obj : ants) {
window.draw(obj->appearance);
}

View File

@ -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;
}

View File

@ -3,14 +3,15 @@
#include "main.hpp"
#include <array>
#include <SFML/Graphics.hpp>
class PheromoneMap {
public:
std::array<std::array<double, HEIGHT>, 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

View File

@ -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_