initial refactor to pheromone matrix

This commit is contained in:
churl
2022-05-15 17:06:47 +02:00
parent 725c69e54a
commit e1a9eb2beb
14 changed files with 64 additions and 201 deletions

View File

@ -42,7 +42,6 @@ Ant::Ant(PheromoneMap& pheromones)
updateAppearance();
}
// TODO: Unnecessary, only used for food/base
void Ant::addToUmwelt(const std::shared_ptr<WorldObject>& 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<WorldObject> const& obj : umwelt) {
if (obj->collides(*this)) {
pheromone_type = obj->getPheromoneType();
}
}
pheromones.place(x, y);
}

View File

@ -3,6 +3,7 @@
#include "colony.hpp"
#include "food.hpp"
#include "main.hpp"
#include "pheromone_map.hpp"
#include "world_object.hpp"
#include <cmath>
@ -13,43 +14,43 @@
#include <SFML/Graphics.hpp>
#include <string>
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<std::shared_ptr<WorldObject>> 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<WorldObject>& object);
void update() override;
PheroType getPheromoneType() const override;
void move();
void updateAppearance();
void updatePheromones();
void dropPheromone(); // red
// TODO: Regarding umwelt
void updateUmwelt();
};
#endif

View File

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

View File

@ -5,12 +5,10 @@
#include <SFML/Graphics.hpp>
class Colony : public WorldObject {
public:
Colony(double x, double y);
void update() override;
PheroType getPheromoneType() const override;
};
#endif // __COLONY_H_

View File

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

View File

@ -5,12 +5,10 @@
#include <SFML/Graphics.hpp>
class Food : public WorldObject {
public:
Food(double x, double y);
void update() override;
PheroType getPheromoneType() const override;
};
#endif // __FOOD_H_

View File

@ -1,21 +1,13 @@
#include <cmath>
#include <iostream>
#include <memory>
#include <vector>
#include <SFML/Graphics.hpp>
#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 <cmath>
#include <iostream>
#include <memory>
#include <SFML/Graphics.hpp>
#include <vector>
int main(int argc, char* argv[]) {
sf::ContextSettings settings;
@ -59,15 +51,10 @@ int main(int argc, char* argv[]) {
for (std::unique_ptr<Ant> 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<Ant> const& obj : ants) {
window.draw(obj->appearance);
}

9
src/main.hpp Normal file
View File

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

View File

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

View File

@ -1,25 +0,0 @@
//
// Created by christoph on 11.04.21.
//
#ifndef ANTSIMULATOR_PHEROMONE_HPP
#define ANTSIMULATOR_PHEROMONE_HPP
#include "world_object.hpp"
#include <SFML/Graphics.hpp>
// 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

View File

@ -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<Pheromone> PheromoneMap::getInVision(const Ant& ant, PheroType type, unsigned short radius) {
std::vector<Pheromone> 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)
}

View File

@ -1,23 +1,16 @@
#ifndef __PHEROMONES_H_
#define __PHEROMONES_H_
#include "ant.hpp"
#include "pheromone.hpp"
#include <iostream>
#include <SFML/Graphics.hpp>
#include <vector>
class Ant;
#include "main.hpp"
#include <array>
class PheromoneMap {
public:
// TODO: Use fixed size matrix (and only int locations) for this, currently it's just slow
std::vector<Pheromone> pheromones;
std::array<std::array<double, HEIGHT>, WIDTH> pheromones;
// TODO: Move this to ant
std::vector<Pheromone> 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

View File

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

View File

@ -1,26 +1,16 @@
#ifndef __WORLD_OBJECT_H_
#define __WORLD_OBJECT_H_
#include "main.hpp"
#include <cmath>
#include <SFML/Graphics.hpp>
// 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
};