update, implement polymorphism

This commit is contained in:
churl
2021-04-09 02:03:04 +02:00
parent e00b4f28cb
commit 083d8cdc0c
13 changed files with 252 additions and 228 deletions

View File

@ -3,136 +3,124 @@
#include <cmath>
#include <iostream>
#include <numbers>
#include <random>
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<WorldObject> 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<WorldObject> const& obj: umwelt) {
if (obj->collides(*this)) {
pheromone_type = obj->getPheromoneType();
}
}
}

View File

@ -2,51 +2,47 @@
#define H_ANT
#include <SFML/Graphics.hpp>
#include <random>
#include <string>
#include <memory>
#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<std::shared_ptr<WorldObject>> 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<WorldObject> object);
void update() override;
sf::Color getPheromoneType() const override;
void move();
void updateAppearance();
void updatePheromones();
void dropPheromone(); // red
void updateUmwelt();
};
#endif

View File

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

View File

@ -2,19 +2,17 @@
#define __COLONY_H_
#include <SFML/Graphics.hpp>
#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_

View File

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

View File

@ -2,19 +2,17 @@
#define __FOOD_H_
#include <SFML/Graphics.hpp>
#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_

View File

@ -5,18 +5,21 @@
#include <SFML/Graphics.hpp>
#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<Ant> ants;
Pheromones pheromones;
std::vector<std::unique_ptr<Ant>> ants; // Use pointer bc we can't instatiate abstract classes
ants.reserve(ANTCOUNT);
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) {
ants.push_back(Ant(pheromones, colony));
ants.push_back(std::make_unique<Ant>(pheromones));
}
for (std::unique_ptr<Ant> 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<Ant> 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<Ant> const& obj: ants) {
window.draw(obj->appearance);
}
window.draw(colony.appearance);
window.draw(colony->appearance);
window.draw(foodA->appearance);
window.display();
}

View File

@ -1,20 +0,0 @@
#ifndef __OBJECT_H_
#define __OBJECT_H_
#include <SFML/Graphics.hpp>
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_

View File

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

View File

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

View File

@ -1,11 +0,0 @@
#ifndef __UMWELT_H_
#define __UMWELT_H_
#include <vector>
#include "object.hpp"
struct Umwelt {
std::vector<Object> objects;
};
#endif // __UMWELT_H_

28
src/world_object.cpp Normal file
View File

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

34
src/world_object.hpp Normal file
View File

@ -0,0 +1,34 @@
#ifndef __OBJECT_H_
#define __OBJECT_H_
#include <cmath>
#include <SFML/Graphics.hpp>
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_