initial refactor to pheromone matrix
This commit is contained in:
44
src/ant.cpp
44
src/ant.cpp
@ -42,7 +42,6 @@ Ant::Ant(PheromoneMap& pheromones)
|
|||||||
updateAppearance();
|
updateAppearance();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Unnecessary, only used for food/base
|
|
||||||
void Ant::addToUmwelt(const std::shared_ptr<WorldObject>& object) {
|
void Ant::addToUmwelt(const std::shared_ptr<WorldObject>& object) {
|
||||||
umwelt.push_back(object);
|
umwelt.push_back(object);
|
||||||
}
|
}
|
||||||
@ -61,33 +60,17 @@ void Ant::update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateAppearance();
|
updateAppearance();
|
||||||
updatePheromones();
|
|
||||||
updateUmwelt();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ant::move() {
|
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?
|
// TODO: Should this random generator be created on every move?
|
||||||
std::random_device device; // obtain a random number from hardware
|
std::random_device device; // obtain a random number from hardware
|
||||||
std::mt19937 generator(device()); // seed the generator
|
std::mt19937 generator(device()); // seed the generator
|
||||||
|
|
||||||
// Move
|
// Move
|
||||||
std::uniform_real_distribution<> degree_distribution(-std::numbers::pi,
|
std::uniform_real_distribution<> degree_distribution(-std::numbers::pi, std::numbers::pi);
|
||||||
std::numbers::pi);
|
|
||||||
|
|
||||||
direction += degree_distribution(generator) * (1 / determination); // Normalize with determination to smooth movement
|
direction += degree_distribution(generator) * (1.0 / determination); // Normalize with determination to smooth movement
|
||||||
// TODO: Use modulo
|
|
||||||
if (direction > 2 * std::numbers::pi) {
|
if (direction > 2 * std::numbers::pi) {
|
||||||
direction -= 2 * std::numbers::pi;
|
direction -= 2 * std::numbers::pi;
|
||||||
}
|
}
|
||||||
@ -100,32 +83,11 @@ void Ant::updateAppearance() {
|
|||||||
appearance.setPosition(x, y);
|
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() {
|
void Ant::dropPheromone() {
|
||||||
if (isOffScreen()) {
|
if (isOffScreen()) {
|
||||||
std::cout << "Ant can't drop PheromoneMap offscreen!" << std::endl;
|
std::cout << "Ant can't drop PheromoneMap offscreen!" << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pheromones.place(x, y, pheromone_type);
|
pheromones.place(x, y);
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This is weird
|
|
||||||
void Ant::updateUmwelt() {
|
|
||||||
for (std::shared_ptr<WorldObject> const& obj : umwelt) {
|
|
||||||
if (obj->collides(*this)) {
|
|
||||||
pheromone_type = obj->getPheromoneType();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
41
src/ant.hpp
41
src/ant.hpp
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "colony.hpp"
|
#include "colony.hpp"
|
||||||
#include "food.hpp"
|
#include "food.hpp"
|
||||||
|
#include "main.hpp"
|
||||||
#include "pheromone_map.hpp"
|
#include "pheromone_map.hpp"
|
||||||
#include "world_object.hpp"
|
#include "world_object.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@ -13,43 +14,43 @@
|
|||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
const double speed = 1;
|
constexpr unsigned int speed = 1;
|
||||||
const double determination = 25; // straightness of the path
|
|
||||||
const unsigned int pheromone_interval = 5; // how many updates between drops
|
|
||||||
|
|
||||||
const unsigned int view_angle = 45; // angle degrees to each side
|
constexpr unsigned int view_angle = 45; // angle degrees to each side
|
||||||
const unsigned int view_distance = 25; // more like smell distance
|
constexpr unsigned int view_distance = 25; // more like smell distance
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NORTH,
|
||||||
|
EAST,
|
||||||
|
SOUTH,
|
||||||
|
WEST
|
||||||
|
} direction;
|
||||||
|
|
||||||
class Ant : public WorldObject {
|
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?
|
// TODO: Should this be here or just global?
|
||||||
PheromoneMap& pheromones;
|
PheromoneMap& pheromones;
|
||||||
|
|
||||||
|
// TODO: Leave continuous trail and remove this
|
||||||
unsigned int next_pheromone_drop = 0;
|
unsigned int next_pheromone_drop = 0;
|
||||||
PheroType pheromone_type = NONE; // FOOD, HOME, NONE
|
|
||||||
|
|
||||||
public:
|
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;
|
std::vector<std::shared_ptr<WorldObject>> umwelt;
|
||||||
|
|
||||||
public:
|
Ant(PheromoneMap& pheromones, double x, double y); // Just random direction
|
||||||
Ant(PheromoneMap& pheromones, double x, double y);
|
Ant(PheromoneMap& pheromones, unsigned int direction); // Just random position
|
||||||
Ant(PheromoneMap& pheromones, unsigned int direction);
|
explicit Ant(PheromoneMap& pheromones); // All random
|
||||||
explicit Ant(PheromoneMap& pheromones);
|
|
||||||
|
|
||||||
// TODO: Regarding umwelt
|
|
||||||
void addToUmwelt(const std::shared_ptr<WorldObject>& object);
|
void addToUmwelt(const std::shared_ptr<WorldObject>& object);
|
||||||
|
|
||||||
void update() override;
|
void update() override;
|
||||||
PheroType getPheromoneType() const override;
|
|
||||||
|
|
||||||
void move();
|
void move();
|
||||||
void updateAppearance();
|
void updateAppearance();
|
||||||
void updatePheromones();
|
|
||||||
void dropPheromone(); // red
|
void dropPheromone(); // red
|
||||||
|
|
||||||
// TODO: Regarding umwelt
|
|
||||||
void updateUmwelt();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -3,7 +3,3 @@
|
|||||||
Colony::Colony(double x, double y) : WorldObject(x, y, 25, sf::Color::Red) {}
|
Colony::Colony(double x, double y) : WorldObject(x, y, 25, sf::Color::Red) {}
|
||||||
|
|
||||||
void Colony::update() {}
|
void Colony::update() {}
|
||||||
|
|
||||||
PheroType Colony::getPheromoneType() const {
|
|
||||||
return HOME;
|
|
||||||
}
|
|
||||||
|
@ -5,12 +5,10 @@
|
|||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
class Colony : public WorldObject {
|
class Colony : public WorldObject {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Colony(double x, double y);
|
Colony(double x, double y);
|
||||||
|
|
||||||
void update() override;
|
void update() override;
|
||||||
PheroType getPheromoneType() const override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __COLONY_H_
|
#endif // __COLONY_H_
|
||||||
|
@ -3,7 +3,3 @@
|
|||||||
Food::Food(double x, double y) : WorldObject(x, y, 15, sf::Color::Green) {}
|
Food::Food(double x, double y) : WorldObject(x, y, 15, sf::Color::Green) {}
|
||||||
|
|
||||||
void Food::update() {}
|
void Food::update() {}
|
||||||
|
|
||||||
PheroType Food::getPheromoneType() const {
|
|
||||||
return FOOD;
|
|
||||||
}
|
|
||||||
|
@ -5,12 +5,10 @@
|
|||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
class Food : public WorldObject {
|
class Food : public WorldObject {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Food(double x, double y);
|
Food(double x, double y);
|
||||||
|
|
||||||
void update() override;
|
void update() override;
|
||||||
PheroType getPheromoneType() const override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __FOOD_H_
|
#endif // __FOOD_H_
|
||||||
|
27
src/main.cpp
27
src/main.cpp
@ -1,21 +1,13 @@
|
|||||||
#include <cmath>
|
#include "main.hpp"
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <SFML/Graphics.hpp>
|
|
||||||
|
|
||||||
#include "ant.hpp"
|
#include "ant.hpp"
|
||||||
#include "colony.hpp"
|
#include "colony.hpp"
|
||||||
#include "food.hpp"
|
#include "food.hpp"
|
||||||
#include "pheromone.hpp"
|
|
||||||
#include "pheromone_map.hpp"
|
#include "pheromone_map.hpp"
|
||||||
|
#include <cmath>
|
||||||
const unsigned int HEIGHT = 500;
|
#include <iostream>
|
||||||
const unsigned int WIDTH = 500;
|
#include <memory>
|
||||||
const unsigned int FPS = 60;
|
#include <SFML/Graphics.hpp>
|
||||||
|
#include <vector>
|
||||||
const unsigned int ANTCOUNT = 100;
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
sf::ContextSettings settings;
|
sf::ContextSettings settings;
|
||||||
@ -59,15 +51,10 @@ int main(int argc, char* argv[]) {
|
|||||||
for (std::unique_ptr<Ant> const& obj : ants) {
|
for (std::unique_ptr<Ant> const& obj : ants) {
|
||||||
obj->update();
|
obj->update();
|
||||||
}
|
}
|
||||||
for (Pheromone& pheromone : pheromones.pheromones) {
|
|
||||||
pheromone.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
window.clear(sf::Color::White);
|
window.clear(sf::Color::White);
|
||||||
for (Pheromone& pheromone : pheromones.pheromones) {
|
pheromones.draw();
|
||||||
window.draw(pheromone.appearance);
|
|
||||||
}
|
|
||||||
for (std::unique_ptr<Ant> const& obj : ants) {
|
for (std::unique_ptr<Ant> const& obj : ants) {
|
||||||
window.draw(obj->appearance);
|
window.draw(obj->appearance);
|
||||||
}
|
}
|
||||||
|
9
src/main.hpp
Normal file
9
src/main.hpp
Normal 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_
|
@ -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));
|
|
||||||
}
|
|
@ -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
|
|
@ -1,25 +1,19 @@
|
|||||||
#include "pheromone_map.hpp"
|
#include "pheromone_map.hpp"
|
||||||
|
|
||||||
void PheromoneMap::place(double x, double y, PheroType type) {
|
void PheromoneMap::place(double x, double y) {
|
||||||
pheromones.emplace_back(x, y, type);
|
pheromones[x][y] = 1.0;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decays the pheromone intensity
|
||||||
void PheromoneMap::update() {
|
void PheromoneMap::update() {
|
||||||
for (size_t i = 0; i < pheromones.size(); ++i) {
|
for (auto x = 0; x < WIDTH; ++x) {
|
||||||
if (pheromones[i].appearance.getFillColor().a == 0) {
|
for (auto y = 0; y < HEIGHT; ++y) {
|
||||||
// pheromones.erase(pheromones.begin() + i);
|
// 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)
|
||||||
|
}
|
||||||
|
@ -1,23 +1,16 @@
|
|||||||
#ifndef __PHEROMONES_H_
|
#ifndef __PHEROMONES_H_
|
||||||
#define __PHEROMONES_H_
|
#define __PHEROMONES_H_
|
||||||
|
|
||||||
#include "ant.hpp"
|
#include "main.hpp"
|
||||||
#include "pheromone.hpp"
|
#include <array>
|
||||||
#include <iostream>
|
|
||||||
#include <SFML/Graphics.hpp>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
class Ant;
|
|
||||||
|
|
||||||
class PheromoneMap {
|
class PheromoneMap {
|
||||||
public:
|
public:
|
||||||
// TODO: Use fixed size matrix (and only int locations) for this, currently it's just slow
|
std::array<std::array<double, HEIGHT>, WIDTH> pheromones;
|
||||||
std::vector<Pheromone> pheromones;
|
|
||||||
|
|
||||||
// TODO: Move this to ant
|
void place(double x, double y);
|
||||||
std::vector<Pheromone> getInVision(const Ant& ant, PheroType type, unsigned short radius);
|
|
||||||
void place(double x, double y, PheroType type);
|
|
||||||
void update();
|
void update();
|
||||||
|
void draw();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "world_object.hpp"
|
#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) {
|
: x(x), y(y), radius(radius) {
|
||||||
appearance = sf::CircleShape(radius);
|
appearance = sf::CircleShape(radius);
|
||||||
appearance.setFillColor(color);
|
appearance.setFillColor(color);
|
||||||
@ -17,12 +17,13 @@ bool WorldObject::collides(const WorldObject& other) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
double WorldObject::distance(const WorldObject& other) const {
|
double WorldObject::distance(const WorldObject& other) const {
|
||||||
const double dx = std::abs(x - other.x);
|
// No need to use abs here as it gets converted to int
|
||||||
const double dy = std::abs(y - other.y);
|
const int dx = x - other.x;
|
||||||
|
const int dy = y - other.y;
|
||||||
return std::sqrt(dx * dx + dy * dy);
|
return std::sqrt(dx * dx + dy * dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
double WorldObject::angle(const WorldObject& other) const {
|
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));
|
return std::asin(dy / distance(other));
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,16 @@
|
|||||||
#ifndef __WORLD_OBJECT_H_
|
#ifndef __WORLD_OBJECT_H_
|
||||||
#define __WORLD_OBJECT_H_
|
#define __WORLD_OBJECT_H_
|
||||||
|
|
||||||
|
#include "main.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <SFML/Graphics.hpp>
|
#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 {
|
class WorldObject {
|
||||||
protected:
|
protected:
|
||||||
double x, y;
|
unsigned int x, y;
|
||||||
const unsigned int radius;
|
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:
|
public:
|
||||||
sf::CircleShape appearance;
|
sf::CircleShape appearance;
|
||||||
@ -30,8 +20,6 @@ public:
|
|||||||
double distance(const WorldObject& other) const;
|
double distance(const WorldObject& other) const;
|
||||||
double angle(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
|
virtual void update() = 0; // pure virtual: has to be overridden
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user