diff --git a/README b/README index d637647..d29cfe1 100644 --- a/README +++ b/README @@ -27,6 +27,10 @@ The following inputs (in no particular order) are supported: BUMP if received, the agent's move failed (or attack of non-existent agent, etc.) + BRED + a new agent has been spawned, connect using agent_id ; + is arbitrary string passed from the father, + can be used for genetic recombination DEAD if received, the agent is dead! energy @@ -57,6 +61,9 @@ The following outputs are supported: attack_dir and are integer offsets relative to the current position; may be just {-1,0,1} + breed_dir + is arbitrary string passed to the "mother" + (to be passed to the child) secrete produce a pheromone; pheromones are initially associated with an agent and trailed at tiles @@ -77,6 +84,10 @@ these commands instead: between 0 and 1. defense between 0 and 1. + breeding_key + is arbitrary integer number; default is 0; + breeding succeeds only between individuals with key + that is near enough (abs(key1-key2) < kappa). In general, higher rate means higher energy maintenance of the appropriate actuators. @@ -88,4 +99,5 @@ phase: If the id corresponds to a disconnected agent, the connection is immediately attached to that agent. Combining this with other -negotiation commands is undefined. +negotiation commands is undefined, except for newborns - in that case, +negotiation commands must follow after agent_id. diff --git a/agent.cc b/agent.cc index 6c1fa3b..277d81a 100644 --- a/agent.cc +++ b/agent.cc @@ -91,6 +91,53 @@ agent::attack_dir(int dir_x, int dir_y) return attack_roll >= defense_roll; } +bool +agent::breed_dir(int dir_x, int dir_y, std::string info) +{ + if (dead || !tile) + return false; + + class tile *t2 = &tile->tile_in_dir(dir_x, dir_y); + class agent *a = t2->agent; + if (!a || a->dead || !a->conn) + return false; + + /* Self-breeding may not be a bad thing, but there is just + * a technical problem with in/out buf locking. */ + assert(a != this); + + if (abs(a->attr.breeding_key - attr.breeding_key) > world::breeding_kappa) + return false; + + chenergy(world::breed_out_cost); + a->chenergy(world::breed_in_cost); + if (a->dead) + return false; + + /* Grab a tile. */ + int spawn_dirs[][2] = { + {0,-1}, {1,-1}, {1,0}, {1,1}, {0,1}, {-1,1}, {-1,0}, {-1,-1}, + }; + int spawn_dir_n = sizeof(spawn_dirs) / sizeof(spawn_dirs[0]); + class tile *tb = NULL; + for (int i = 0; i < spawn_dir_n; i++) { + class tile *t = &tile->tile_in_dir(spawn_dirs[i][0], spawn_dirs[i][1]); + if (!t->agent) { + tb = t; + break; + } + } + if (!tb) + return false; // still-born + + /* New agent, yay. */ + class agent *ab = new class agent(agent_id++, NULL, map); + agents.push_back(ab); + ab->spawn_at(*tb); + a->conn->bred(ab->id, info); + return true; +} + bool agent::secrete(int id, double intensity) { diff --git a/agent.h b/agent.h index 3648dbf..5a33611 100644 --- a/agent.h +++ b/agent.h @@ -1,6 +1,8 @@ #ifndef BRMLIFE__AGENT_H #define BRMLIFE__AGENT_H +#include + #include "map.h" #include "pheromone.h" #include "world.h" @@ -12,12 +14,12 @@ class connection; * * - tile set, connection set: active client or connected corpse * - tile set, connection NULL: herb or disconnected corpse - * - tile NULL, connection set: negotiation or zombie connection with no corpse anymore + * - tile NULL, connection set: negotiating client or zombie connection with no corpse anymore * * Agents are created (we may not keep this list up to date) on incoming - * connection, by blooming herb or when converting corpse to herb. Herbs - * are immediately spawned (assigned a tile); clients are spawned only - * after negotiation. + * connection, by breeding, by blooming herb or when converting corpse + * to herb. Herbs and bred clients are immediately spawned (assigned a tile); + * connecting clients are spawned only after negotiation. * * Agents are destroyed in the main loop when they are completely abandoned, * i.e. both their tile and connection become NULL. */ @@ -34,19 +36,21 @@ public: double move; double attack; double defense; + long breeding_key; } attr; int energy; - bool dead; + bool newborn, dead; class pheromones pheromones; agent(int id_, class connection *conn_, class map &map_) - : id (id_), conn (conn_), map (map_), tile (NULL) + : id (id_), conn (conn_), map (map_), tile (NULL), newborn (true) { attr.move = 1.0; attr.attack = 0.5; attr.defense = 0.5; + attr.breeding_key = 0; energy = world::newborn_energy; dead = false; }; @@ -55,6 +59,7 @@ public: bool move_dir(int dir_x, int dir_y); bool attack_dir(int dir_x, int dir_y); + bool breed_dir(int dir_x, int dir_y, std::string info); bool secrete(int id, double intensity); void chenergy(int delta); diff --git a/connection.cc b/connection.cc index ead217b..9df4ac0 100644 --- a/connection.cc +++ b/connection.cc @@ -79,6 +79,16 @@ connection::senses(int tick_id, class agent &a) pthread_mutex_unlock(&buf_lock); } +void +connection::bred(int agent_id, std::string &info) +{ + pthread_mutex_lock(&buf_lock); + std::stringstream s; + s << "BRED " << agent_id << " " << info << "\r\n"; + out_buf.append(s.str()); + pthread_mutex_unlock(&buf_lock); +} + void connection::bump(void) { @@ -121,6 +131,8 @@ connection::actions(class agent *agent) sscanf(line.c_str(), "%lf", &rate); if (rate >= 0 && rate <= 1) agent->attr.defense = rate; + } else if (negotiation && !cmd.compare("breeding_key")) { + sscanf(line.c_str(), "%ld", &agent->attr.breeding_key); } else if (negotiation && !cmd.compare("agent_id")) { int id = -1; @@ -161,6 +173,15 @@ bump_negot: if (!agent->attack_dir(x, y)) bump(); mask |= 2; + } else if (!negotiation && !cmd.compare("breed_dir") && !(mask & 4)) { + int x = 0, y = 0, len = 0; + sscanf(line.c_str(), "%d %d %n", &x, &y, &len); + line.erase(0, len); + if (x < -1) x = -1; if (x > 1) x = 1; + if (y < -1) y = -1; if (y > 1) y = 1; + if (!agent->breed_dir(x, y, line)) + bump(); + mask |= 4; } else if (!negotiation && !cmd.compare("secrete")) { int id = 0; double v = 0; sscanf(line.c_str(), "%d %lf", &id, &v); @@ -177,8 +198,9 @@ bump_negot: in_buf.erase(0, 2); if (negotiation) { - negotiation = false; - agent->spawn(); + agent->newborn = negotiation = false; + if (!agent->tile) + agent->spawn(); std::stringstream s; s << "agent_id " << agent->id << "\r\n"; diff --git a/connection.h b/connection.h index 2164b43..7c4722f 100644 --- a/connection.h +++ b/connection.h @@ -28,6 +28,7 @@ public: } void senses(int tick_id, class agent &); + void bred(int agent_id, std::string &info); void actions(class agent *); void cancel(void); diff --git a/world.h b/world.h index 7b8334e..ac21459 100644 --- a/world.h +++ b/world.h @@ -9,6 +9,8 @@ struct world { const static int move_cost = -50; const static int attack_cost = -400; const static int defense_cost = -200; + const static int breed_out_cost = -newborn_energy/4; + const static int breed_in_cost = -newborn_energy*3/4; const static int pheromone_cost = -10; const static int move_idle_cost = -15; /* ... * attr.move */ @@ -24,6 +26,8 @@ struct world { const static int herb_rate = 15; /* initially, one herb per herb_rate tiles */ const static double herb_phintensity = 1.0; + const static long breeding_kappa = 10000; + const static double phseep_alpha = 0.1; const static double phseep_beta = 0.05; const static double phdecay_gamma = 0.95;