From 23f2fce7ff7ddc7c9299af4a4aec403f8a7ac804 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Thu, 8 Dec 2011 03:14:57 +0100 Subject: [PATCH] Support for breeding One party initiates the breeding; the other party exerts most of the energy if its breeding key matches. It is that party's responsibility to set up the newborn agent's connection (but the father can pass it an arbitrary message). Newborn is spawned immediately but can be renegotiated on first connect. --- README | 14 +++++++++++++- agent.cc | 47 +++++++++++++++++++++++++++++++++++++++++++++++ agent.h | 17 +++++++++++------ connection.cc | 26 ++++++++++++++++++++++++-- connection.h | 1 + world.h | 4 ++++ 6 files changed, 100 insertions(+), 9 deletions(-) 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;