diff --git a/Makefile b/Makefile index df473ef..3a51ce3 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ CFLAGS=-Wall -g -pthread LDFLAGS=-pthread -OBJS=main.o map.o agent.o connection.o +OBJS=main.o map.o agent.o connection.o pheromone.o brmlife: $(OBJS) $(CXX) $(LDFLAGS) -o $@ $^ diff --git a/agent.cc b/agent.cc index 61fcef8..fefe291 100644 --- a/agent.cc +++ b/agent.cc @@ -91,6 +91,15 @@ agent::attack_dir(int dir_x, int dir_y) return attack_roll >= defense_roll; } +bool +agent::secrete(int id, double intensity) +{ + pheromone p(id, intensity); + pheromones.secrete(p); + chenergy(intensity * world::pheromone_cost); + return true; +} + void agent::chenergy(int delta) { @@ -129,9 +138,13 @@ agent::on_action_takes(void) void agent::on_tick(void) { + pheromones.decay(world::phdecay_gamma, world::phdecay_delta); + if (!tile) return; + pheromones.seep(tile->pheromones, world::phseep_alpha, world::phseep_beta); + if (!dead) { chenergy(world::sun_energy); chenergy(attr.move * world::move_idle_cost); diff --git a/agent.h b/agent.h index 911a46e..5687d1f 100644 --- a/agent.h +++ b/agent.h @@ -2,6 +2,7 @@ #define BRMLIFE__AGENT_H #include "map.h" +#include "pheromone.h" #include "world.h" class connection; @@ -23,6 +24,8 @@ public: int energy; bool dead; + class pheromones pheromones; + agent(int id_, class connection *conn_, class map &map_) : id (id_), conn (conn_), map (map_), tile (NULL) { @@ -37,6 +40,7 @@ public: bool move_dir(int dir_x, int dir_y); bool attack_dir(int dir_x, int dir_y); + bool secrete(int id, double intensity); void chenergy(int delta); void die(void); diff --git a/connection.cc b/connection.cc index 95e3a43..c283e36 100644 --- a/connection.cc +++ b/connection.cc @@ -37,6 +37,19 @@ connection::senses(int tick_id, class agent &a) } bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), "\r\n"); + bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), "pheromones"); + for (int i = 0; i < dir_n; i++) { + bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), " ("); + class pheromones &ps = a.tile->tile_in_dir(dirs[i][0], dirs[i][1]).pheromones; + for (std::list::iterator pi = ps.spectrum.begin(); pi != ps.spectrum.end(); pi++) { + if (pi != ps.spectrum.begin()) + bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), ","); + bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), "%d:%f", pi->id, pi->val); + } + bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), ")"); + } + bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), "\r\n"); + bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), "\r\n"); pthread_mutex_lock(&buf_lock); out_buf.append(buf); @@ -102,6 +115,14 @@ connection::actions(class agent &agent) if (!agent.attack_dir(x, y)) bump(); mask |= 2; + } else if (!negotiation && !cmd.compare("secrete")) { + int id = 0; double v = 0; + sscanf(line.c_str(), "%d %lf", &id, &v); + if (id < 0) id = 0; if (id >= PH_COUNT) id = PH_COUNT - 1; + if (v < 0) v = 0; + if (!agent.secrete(id, v)) + bump(); + /* We deliberately allow multiple secrete commands. */ } else { std::cout << "unknown line " << cmd << " " << line << " ...\n"; diff --git a/map.cc b/map.cc index ddfe787..43bfe43 100644 --- a/map.cc +++ b/map.cc @@ -39,6 +39,8 @@ tile::on_tick(void) if (tile_in_dir(-1, 0).herb_here()) { tile_in_dir(-1, 0).agent->chenergy(world::soil_energy / herbs); } if (tile_in_dir(0, -1).herb_here()) { tile_in_dir(0, -1).agent->chenergy(world::soil_energy / herbs); } } + + pheromones.decay(world::phdecay_gamma, world::phdecay_delta); } bool diff --git a/map.h b/map.h index 9f7027e..b527f2f 100644 --- a/map.h +++ b/map.h @@ -1,6 +1,8 @@ #ifndef BRMLIFE__MAP_H #define BRMLIFE__MAP_H +#include "pheromone.h" + class agent; class map; @@ -10,6 +12,7 @@ public: class map ↦ class agent *agent; + class pheromones pheromones; tile(int x_, int y_, class map &map_) : x(x_), y(y_), map(map_), agent(NULL) {}; diff --git a/pheromone.cc b/pheromone.cc new file mode 100644 index 0000000..c5c4894 --- /dev/null +++ b/pheromone.cc @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +#include "pheromone.h" +#include "main.h" + + +void +pheromones::secrete(class pheromone &p) +{ + for (std::list::iterator pi = spectrum.begin(); pi != spectrum.end(); pi++) { + if (pi->id < p.id) + continue; + if (pi->id == p.id) { + pi->val += p.val; + return; + } + spectrum.insert(pi, p); + return; + } + spectrum.push_back(p); +} + +void +pheromones::seep(class pheromones &to, double alpha, double beta) +{ + for (std::list::iterator pi = spectrum.begin(); pi != spectrum.end(); pi++) { + class pheromone p(pi->id, pi->val * (alpha * PH_COUNT / pi->id + beta)); + to.secrete(p); + } +} + +void +pheromones::decay(double gamma, double delta) +{ + for (std::list::iterator pi = spectrum.begin(); pi != spectrum.end(); pi++) { +next_p: + pi->val *= gamma; + pi->val -= delta; + if (pi->val < 0.001) { + pi = spectrum.erase(pi); + if (pi != spectrum.end()) + goto next_p; + } + } +} diff --git a/pheromone.h b/pheromone.h new file mode 100644 index 0000000..0783a82 --- /dev/null +++ b/pheromone.h @@ -0,0 +1,35 @@ +#ifndef BRMLIFE__PHEROMONE_H +#define BRMLIFE__PHEROMONE_H + +/* Pheromone spectrum in a particular state. */ + +#include + +#define PH_COUNT 65536 + +class pheromone { +public: + int id; + double val; + + pheromone(int id_ = 0, double val_ = 0) + : id (id_), val (val_) {}; +}; + +class pheromones { +public: + std::list spectrum; + + /* Add a pheromone to the spectrum. */ + void secrete(class pheromone &p); + /* Merge slight trail of spectrum to another spectrum (transfer + * by touching). Pheromones with lower index are transmitted + * better than those with higher index: + * v' = v * (alpha / i + beta) */ + void seep(class pheromones &to, double alpha, double beta); + /* Decay the spectrum, multiplying each value by gamma and + * then reducing it by delta. */ + void decay(double gamma, double delta); +}; + +#endif diff --git a/world.h b/world.h index 47a3b81..a70ea18 100644 --- a/world.h +++ b/world.h @@ -9,6 +9,7 @@ struct world { const static int move_cost = -50; const static int attack_cost = -400; const static int defense_cost = -200; + const static int pheromone_cost = -10; const static int move_idle_cost = -15; /* ... * attr.move */ const static int attack_idle_cost = -15; /* ... * attr.attack */ @@ -21,6 +22,11 @@ struct world { const static int dead_decay = -50; const static int herb_rate = 15; /* initially, one herb per herb_rate tiles */ + + const static double phseep_alpha = 0.1; + const static double phseep_beta = 0.05; + const static double phdecay_gamma = 0.95; + const static double phdecay_delta = 0.01; }; #endif