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();
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
41
src/ant.hpp
41
src/ant.hpp
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_
|
||||
|
27
src/main.cpp
27
src/main.cpp
@ -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
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"
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user