diff --git a/header/Agent.hpp b/header/Agent.hpp new file mode 100644 index 0000000..0834bcd --- /dev/null +++ b/header/Agent.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "Field.hpp" + +class Agent +{ +public: + Agent(float fl_x, float fl_y, float fl_w, float fl_h, float direction, float fl_pheromone_strength, float fl_random_influence, float fl_lookahead_distance); + ~Agent(); + + void tick(Field* field); + +private: + float fl_x, fl_y, fl_w, fl_h, fl_direction, fl_pheromone_strength, fl_random_influence, fl_lookahead_distance; +}; \ No newline at end of file diff --git a/header/Field.hpp b/header/Field.hpp new file mode 100644 index 0000000..e54c5e5 --- /dev/null +++ b/header/Field.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +class Field +{ +public: + Field(int i_width, int i_height, float fl_pheromone_max); + ~Field(); + + void tick(); + + void add_pheromone(float fl_x, float fl_y, float fl_strength); + + float get_pheromone_strength(float fl_x, float fl_y); + float get_pheromone_strength(int i_x, int i_y); + + float get_pheromone_max(); + +private: + int i_width, i_height; + + float** fl_ground; + + float fl_pheromone_max; +}; \ No newline at end of file diff --git a/src/Agent.cpp b/src/Agent.cpp new file mode 100644 index 0000000..6655496 --- /dev/null +++ b/src/Agent.cpp @@ -0,0 +1,89 @@ +#include "Agent.hpp" + +#include + +Agent::Agent(float fl_x, float fl_y, float fl_w, float fl_h, float fl_direction, float fl_pheromone_strength, float fl_random_influence, float fl_lookahead_distance) +{ + this->fl_x = fl_x; + this->fl_y = fl_y; + this->fl_w = fl_w; + this->fl_h = fl_h; + this->fl_direction = fl_direction; + this->fl_pheromone_strength = fl_pheromone_strength; + this->fl_random_influence = 100.0f*fl_random_influence; + this->fl_lookahead_distance = fl_lookahead_distance; +} + +Agent::~Agent() +{ + +} + +void Agent::tick(Field* field) +{ + ///Update direction + float fl_xd, fl_yd; + + if ((float)(rand()%100) < fl_random_influence) + { //Move randomly + switch (rand() % 3) + { + case 0: + fl_direction += M_PI_4; + break; + case 1: + break; + case 2: + fl_direction -= M_PI_4; + break; + } + } + else + { //Move based on environment + + //Gather information + float fl_l_pheromone, fl_s_pheromone, fl_r_pheromone, fl_test_dir = fl_direction; + fl_xd = fl_lookahead_distance*cos(fl_test_dir); + fl_yd = fl_lookahead_distance*sin(fl_test_dir); + + fl_s_pheromone = field->get_pheromone_strength(fl_x+fl_xd, fl_y+fl_yd); + + fl_test_dir += M_PI/6.0F; + fl_xd = fl_lookahead_distance*cos(fl_test_dir); + fl_yd = fl_lookahead_distance*sin(fl_test_dir); + fl_l_pheromone = field->get_pheromone_strength(fl_x+fl_xd, fl_y+fl_yd); + + fl_test_dir -= M_PI/3.0F; + fl_xd = fl_lookahead_distance*cos(fl_test_dir); + fl_yd = fl_lookahead_distance*sin(fl_test_dir); + fl_r_pheromone = field->get_pheromone_strength(fl_x+fl_xd, fl_y+fl_yd); + + if (fl_r_pheromone >= fl_s_pheromone && fl_r_pheromone >= fl_l_pheromone) + { + fl_direction -= M_PI/6.0; + } + else if (fl_l_pheromone >= fl_s_pheromone && fl_l_pheromone >= fl_r_pheromone) + { + fl_direction += M_PI/6.0; + } + } + + //Move + fl_xd = cos(fl_direction); + fl_yd = sin(fl_direction); + + if (fl_x + fl_xd < 0 || fl_x + fl_xd >= fl_w || fl_y + fl_yd < 0 || fl_y + fl_yd >= fl_h) + { + fl_x = fmin(fl_w,fmax(0.0f, fl_x+fl_xd)); + fl_y = fmin(fl_h,fmax(0.0f, fl_y+fl_yd)); + fl_direction = 2.0f*M_PI*((float)(rand()%100)/100.0f); + } + else + { + fl_x += fl_xd; + fl_y += fl_yd; + } + + //Add pheromone to new position + field->add_pheromone(fl_x, fl_y, fl_pheromone_strength); +} \ No newline at end of file diff --git a/src/Field.cpp b/src/Field.cpp new file mode 100644 index 0000000..735d173 --- /dev/null +++ b/src/Field.cpp @@ -0,0 +1,118 @@ +#include "Field.hpp" + +#include + +Field::Field(int i_width, int i_height, float fl_pheromone_max) +{ + this->i_width = i_width; + this->i_height = i_height; + + fl_ground = new float*[i_width]; + + for (int i = 0; i < i_width; i++) + { + fl_ground[i] = new float[i_height]; + + for(int j = 0; j < i_height; j++) + { + fl_ground[i][j] = 0.0f; + } + } + + this->fl_pheromone_max = fl_pheromone_max; +} + +Field::~Field() +{ + for(int i = 0; i < i_width; i++) + { + delete[] fl_ground[i]; + } + + delete[] fl_ground; +} + +void Field::tick() +{ + float** fl_temp_ground; + fl_temp_ground = new float*[i_width]; + + for (int i = 0; i < i_width; i++) + { + fl_temp_ground[i] = new float[i_height]; + } + + + for(int x = 0; x < i_width; x++) + { + for(int y = 0; y < i_height; y++) + { + + float fl_sum = 0.0f; + float fl_count = 0.0f; + + for (int i = x-1; i <= x+1; i++) + { + for (int j = y-1; j <= y+1; j++) + { + if (i < 0 || i >= i_width || j < 0 || j >= i_height) + { + continue; + } + + fl_sum += fl_ground[i][j]; + fl_count += 1.0f; + } + } + + fl_temp_ground[x][y] = fmax(0.0f, fl_sum/fl_count-0.005); + } + } + + for(int i = 0; i < i_width; i++) + { + memcpy(&fl_ground[i][0], &fl_temp_ground[i][0], sizeof(float)*i_height); + delete[] fl_temp_ground[i]; + } + + delete[] fl_temp_ground; +} + +void Field::add_pheromone(float fl_x, float fl_y, float fl_strength) +{ + int i_x = (int)fl_x; + int i_y = (int)fl_y; + + if (i_x < 0 || i_x >= i_width || i_y < 0 || i_y >= i_height) + { + return; + } + + fl_ground[i_x][i_y] = fmin(fl_ground[i_x][i_y]+fl_strength, fl_pheromone_max); +} + +float Field::get_pheromone_strength(float fl_x, float fl_y) +{ + int i_x = (int)fl_x, i_y = (int)fl_y; + if (i_x < 0 || i_x >= i_width || i_y < 0 || i_y >= i_height) + { + return 0.0f; + } + + return fl_ground[i_x][i_y]; +} + +float Field::get_pheromone_strength(int i_x, int i_y) +{ + if (i_x < 0 || i_x >= i_width || i_y < 0 || i_y >= i_height) + { + return 0.0f; + } + + return fl_ground[i_x][i_y]; +} + +float Field::get_pheromone_max() +{ + return fl_pheromone_max; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 8130be6..4b38ce9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,9 +1,23 @@ #include #include +#include +#include +#include +#include +#include + +#include "Field.hpp" +#include "Agent.hpp" + +unsigned long long SYS_TIME_US() +{ + return (unsigned long long)(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); +} int main(int argc, char* argv[]) { + srand(time(0)); int width, height; if (argc == 1) @@ -23,25 +37,129 @@ int main(int argc, char* argv[]) SDL_Window* window = SDL_CreateWindow("Slime Mold Simulator", SDL_WINDOWPOS_CENTERED-width/2, SDL_WINDOWPOS_CENTERED-height/2, width, height, SDL_WINDOW_SHOWN); - SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); - SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00); - SDL_RenderClear(renderer); + SDL_Texture* field_texture = SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), SDL_TEXTUREACCESS_TARGET, width, height); - SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); + Field field(width, height, 1.0f); - SDL_Rect half_screen = (SDL_Rect){width/4, height/4, width/2, height/2}; + std::vector agents; - SDL_RenderFillRect(renderer, &half_screen); + bool* start_positions = new bool[width*height]; + for(int i = 0; i < width*height; i++) + { + start_positions[i] = false; + } - SDL_RenderPresent(renderer); + int created = 0; + int index = 0; + int max_count = (int)sqrt(width*height)*10; + while(created < max_count) + { + int rng_cur = rand()%(width*height/max_count); + if (rng_cur == 0 && !start_positions[index]) + { + start_positions[index] = true; + created++; + } - SDL_Delay(2000); + index = (index+1)%(width*height); + } + for(int i = 0; i < width*height; i++) + { + if (start_positions[i]) + { + Agent* next_agent = new Agent((float)(i%width), (float)(i/width), (float)width, (float)height, 2.0f*M_PI*((float)(rand()%100)/100.0f), 0.8f, 0.1f,3.0f); + agents.push_back(next_agent); + + field.add_pheromone((float)(i%width),(float)(i/width), 0.8f); + } + } + + delete[] start_positions; + + SDL_Rect screen_rect = {0,0,width,height}; + + unsigned long long time_old, time_now; + time_now = time_old = SYS_TIME_US(); + + int iteration_duration = 16666/10; + int max_loops = 10000000/iteration_duration; + bool pause = true; + + while(true) + { + SDL_Event event; + if (1 == SDL_PollEvent(&event)) + { + if (event.type == SDL_KEYDOWN) + { + if (event.key.keysym.scancode == SDL_SCANCODE_Q) + { + break; + } + else if (event.key.keysym.scancode == SDL_SCANCODE_P) + { + pause = !pause; + } + } + } + + time_now = SYS_TIME_US(); + if (time_now >= time_old + iteration_duration) + { + time_old = time_now; + if (pause) + { + continue; + } + + field.tick(); + for(unsigned int i = 0; i < agents.size(); i++) + { + agents[i]->tick(&field); + } + + SDL_SetRenderTarget(renderer, field_texture); + SDL_SetRenderDrawColor(renderer, 0,0,0,0); + SDL_RenderClear(renderer); + + float max_strength = field.get_pheromone_max(); + + for (int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + float cur_strength = field.get_pheromone_strength(x, y); + + int color_val = std::max(0,std::min(255,(int)(255.0f * cur_strength / max_strength))); + SDL_SetRenderDrawColor(renderer, color_val, color_val, color_val, 0xFF); + + SDL_RenderDrawPoint(renderer, x, y); + } + } + + SDL_SetRenderTarget(renderer, NULL); + SDL_SetRenderDrawColor(renderer, 0,0,0,0); + SDL_RenderClear(renderer); + + SDL_RenderCopy(renderer, field_texture, &screen_rect, &screen_rect); + + SDL_RenderPresent(renderer); + } + } + + SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); + for (unsigned int i = 0; i < agents.size(); i++) + { + delete agents[i]; + } + return 0; } \ No newline at end of file