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/README b/README index 6fad823..d29cfe1 100644 --- a/README +++ b/README @@ -20,10 +20,17 @@ CRLF ("\r\n"), not just LF ("\n")! The following inputs (in no particular order) are supported: + agent_id + unique id of agent; may be sent only once at the beginning + (you can use it to reconnect to the same agent later) tick 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 @@ -38,6 +45,13 @@ The following inputs (in no particular order) are supported: a dead agent A alive agent x herp + pheromones (,,,...) (...)... + (,...) describes set of pheromones on a tile, + in the same order as visual; if agent is on a tile, + its pheromones are merged with tile pheromones + format is : + : pheromone id 0..65535 + : floating-point number The following outputs are supported: @@ -47,11 +61,21 @@ 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 + visited by the agent; pheromones with lower id + transfer from agent to tile faster + energy required is proportional to phintensity -After connecting, the client specifies its desired attributes, -in the same format as in normal output (line-based, terminated -by empty line), but with these commands instead: +When new agent connects, the client first enters the "negotiation" +phase, specifying its desired attributes, in the same format as in +normal output (line-based, terminated by empty line), but with +these commands instead: move between 0 and 1, describing probability @@ -60,6 +84,20 @@ by empty line), but with 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. + +Alternately, the client may send a single command in the negotiation +phase: + + agent_id + +If the id corresponds to a disconnected agent, the connection +is immediately attached to that agent. Combining this with other +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 61fcef8..277d81a 100644 --- a/agent.cc +++ b/agent.cc @@ -91,6 +91,62 @@ 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) +{ + pheromone p(id, intensity); + pheromones.secrete(p); + chenergy(intensity * world::pheromone_cost); + return true; +} + void agent::chenergy(int delta) { @@ -117,21 +173,24 @@ agent::on_action_takes(void) return; if (conn->error) { - if (!dead) die(); conn->cancel(); conn = NULL; return; } - conn->actions(*this); + conn->actions(this); } 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); @@ -168,6 +227,14 @@ agent::~agent() } +void +herb::die(void) +{ + /* No corpse, just clean up tile. */ + tile->on_agent_leave(*this); + tile = NULL; +} + static void spawn_herb(class tile &t) { @@ -178,6 +245,15 @@ spawn_herb(class tile &t) h->spawn_at(t); } +void +herb::smell_herb(class tile &t) +{ + /* Herb pheromone ("smell") #32768. */ + class pheromone p(32768, world::herb_phintensity); + t.pheromones.secrete(p); + chenergy(p.val * world::pheromone_cost); +} + void herb::on_tick(void) { @@ -189,7 +265,11 @@ herb::on_tick(void) spawn_herb(tile->tile_in_dir(0, 1)); spawn_herb(tile->tile_in_dir(-1, 0)); spawn_herb(tile->tile_in_dir(0, -1)); - tile->on_agent_leave(*this); - tile = NULL; + die(); + } else { + smell_herb(tile->tile_in_dir(1, 0)); + smell_herb(tile->tile_in_dir(0, 1)); + smell_herb(tile->tile_in_dir(-1, 0)); + smell_herb(tile->tile_in_dir(0, -1)); } } diff --git a/agent.h b/agent.h index 911a46e..5a33611 100644 --- a/agent.h +++ b/agent.h @@ -1,11 +1,29 @@ #ifndef BRMLIFE__AGENT_H #define BRMLIFE__AGENT_H +#include + #include "map.h" +#include "pheromone.h" #include "world.h" class connection; +/* Agent object lifetime is slightly complicated, since an agent + * may be tied to a tile or a connection or both: + * + * - tile set, connection set: active client or connected corpse + * - tile set, connection NULL: herb or disconnected corpse + * - 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 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. */ + class agent { public: int id; @@ -18,17 +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; }; @@ -37,9 +59,11 @@ 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); - void die(void); + virtual void die(void); void on_action_takes(void); virtual void on_tick(void); @@ -59,7 +83,10 @@ public: energy = world::herb_energy; } + void die(void); void on_tick(void); +private: + void smell_herb(class tile &); }; #endif diff --git a/client/example.pl b/client/example.pl index 3176483..ea92b58 100755 --- a/client/example.pl +++ b/client/example.pl @@ -120,6 +120,7 @@ sub take_action($$) { } else { print $socket "move_dir $max->[0] $max->[1]\r\n"; } + print $socket "secrete 65536 1\r\n"; print $socket "\r\n"; } @@ -139,9 +140,14 @@ $socket = IO::Socket::INET->new( print "[ii] connected\r\n"; # negotiate attributs -print $socket "move 1.0\r\n"; -print $socket "attack 1.0\r\n"; -print $socket "defense 1.0\r\n"; +if ($ARGV[1]) { + print "[ii] recovering agent $ARGV[1]\r\n"; + print $socket "agent_id $ARGV[1]\r\n"; +} else { + print $socket "move 1.0\r\n"; + print $socket "attack 1.0\r\n"; + print $socket "defense 1.0\r\n"; +} print $socket "\r\n"; print "[ii] agent created\r\n"; diff --git a/connection.cc b/connection.cc index 9e99822..9df4ac0 100644 --- a/connection.cc +++ b/connection.cc @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -9,6 +10,7 @@ #include "agent.h" #include "connection.h" +#include "main.h" #define buf_incomplete(buf) \ (buf.find("\r\n") == std::string::npos || (buf.find("\r\n") > 0 && buf.find("\r\n\r\n") == std::string::npos)) @@ -18,46 +20,75 @@ connection::senses(int tick_id, class agent &a) { assert(!negotiation); + int dirs[][2] = { + {0,-1}, {1,-1}, {1,0}, {1,1}, {0,1}, {-1,1}, {-1,0}, {-1,-1}, + {0,-2}, {1,-2}, {2,-2}, {2,-1}, {2,0}, {2,1}, {2,2}, {1,2}, {0,2}, {-1,2}, {-2,2}, {-2,1}, {-2,0}, {-2,-1}, {-2,-2}, {-1,-2}, + }; + int dir_n = sizeof(dirs) / sizeof(dirs[0]); + char buf[1024]; char *bufp = buf; bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), "tick %d\r\n", tick_id); if (a.dead) bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), "DEAD\r\n"); bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), "energy %d\r\n", a.energy); - bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), "visual %s %s %s %s %s %s %s %s" " %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s" "\r\n", - a.tile->tile_in_dir(0, -1).str(), - a.tile->tile_in_dir(1, -1).str(), - a.tile->tile_in_dir(1, 0).str(), - a.tile->tile_in_dir(1, 1).str(), - a.tile->tile_in_dir(0, 1).str(), - a.tile->tile_in_dir(-1, 1).str(), - a.tile->tile_in_dir(-1, 0).str(), - a.tile->tile_in_dir(-1, -1).str(), - - a.tile->tile_in_dir(0, -2).str(), - a.tile->tile_in_dir(1, -2).str(), - a.tile->tile_in_dir(2, -2).str(), - a.tile->tile_in_dir(2, -1).str(), - a.tile->tile_in_dir(2, 0).str(), - a.tile->tile_in_dir(2, 1).str(), - a.tile->tile_in_dir(2, 2).str(), - a.tile->tile_in_dir(1, 2).str(), - a.tile->tile_in_dir(0, 2).str(), - a.tile->tile_in_dir(-1, 2).str(), - a.tile->tile_in_dir(-2, 2).str(), - a.tile->tile_in_dir(-2, 1).str(), - a.tile->tile_in_dir(-2, 0).str(), - a.tile->tile_in_dir(-2, -1).str(), - a.tile->tile_in_dir(-2, -2).str(), - a.tile->tile_in_dir(-1, -2).str()); + bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), "visual"); + for (int i = 0; i < dir_n; i++) { + bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), " %s", a.tile->tile_in_dir(dirs[i][0], dirs[i][1]).str()); + } 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; + class agent *ai = a.tile->tile_in_dir(dirs[i][0], dirs[i][1]).agent; + if (ai) { + /* We need to merge pheromones. */ + class pheromones &pt = ai->pheromones; + std::list::iterator pi, pj; + for (pi = ps.spectrum.begin(), pj = pt.spectrum.begin(); + pi != ps.spectrum.end() || pj != pt.spectrum.end(); ) { + if (pi != ps.spectrum.begin() || pj != pt.spectrum.begin()) + bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), ","); + int i; double v; + if (pj == pt.spectrum.end() || (pi != ps.spectrum.end() && pi->id < pj->id)) { + i = pi->id; v = pi->val; ++pi; + } else if (pi == ps.spectrum.end() || pj->id < pi->id) { + i = pj->id; v = pj->val; ++pj; + } else { + i = pi->id; v = pi->val + pj->val; ++pi, ++pj; + } + bufp += snprintf(bufp, sizeof(buf) - (bufp - buf), "%d:%f", i, v); + } + } else { + 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); 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) { @@ -66,7 +97,7 @@ connection::bump(void) } void -connection::actions(class agent &agent) +connection::actions(class agent *agent) { pthread_mutex_lock(&buf_lock); if (buf_incomplete(in_buf)) { @@ -86,27 +117,52 @@ connection::actions(class agent &agent) line.erase(0, spofs + 1); if (negotiation && !cmd.compare("move")) { - double rate = agent.attr.move; + double rate = agent->attr.move; sscanf(line.c_str(), "%lf", &rate); if (rate >= 0 && rate <= 1) - agent.attr.move = rate; + agent->attr.move = rate; } else if (negotiation && !cmd.compare("attack")) { - double rate = agent.attr.attack; + double rate = agent->attr.attack; sscanf(line.c_str(), "%lf", &rate); if (rate >= 0 && rate <= 1) - agent.attr.attack = rate; + agent->attr.attack = rate; } else if (negotiation && !cmd.compare("defense")) { - double rate = agent.attr.defense; + double rate = agent->attr.defense; sscanf(line.c_str(), "%lf", &rate); if (rate >= 0 && rate <= 1) - agent.attr.defense = rate; + 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; + sscanf(line.c_str(), "%d", &id); + if (id < 0) { +bump_negot: + bump(); out_buf.append("\r\n"); + } else { + class agent *a2 = NULL; + for (std::list::iterator ai = agents.begin(); ai != agents.end(); ai++) { + if ((*ai)->id == id) { + a2 = *ai; + break; + } + } + if (!a2 || a2->conn || !a2->tile || (dynamic_cast (a2))) + goto bump_negot; + /* Round and round she goes, where she stops, nobody knows. */ + a2->conn = this; + agent->conn = NULL; + agent = a2; + negotiation = agent->newborn; + } } else if (!negotiation && !cmd.compare("move_dir") && !(mask & 1)) { int x = 0, y = 0; sscanf(line.c_str(), "%d %d", &x, &y); if (x < -1) x = -1; if (x > 1) x = 1; if (y < -1) y = -1; if (y > 1) y = 1; - if (!agent.move_dir(x, y)) + if (!agent->move_dir(x, y)) bump(); mask |= 1; } else if (!negotiation && !cmd.compare("attack_dir") && !(mask & 2)) { @@ -114,9 +170,26 @@ connection::actions(class agent &agent) sscanf(line.c_str(), "%d %d", &x, &y); if (x < -1) x = -1; if (x > 1) x = 1; if (y < -1) y = -1; if (y > 1) y = 1; - if (!agent.attack_dir(x, y)) + 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); + 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"; @@ -125,8 +198,13 @@ connection::actions(class agent &agent) 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"; + out_buf.append(s.str()); } pthread_mutex_unlock(&buf_lock); diff --git a/connection.h b/connection.h index 2102cda..7c4722f 100644 --- a/connection.h +++ b/connection.h @@ -28,7 +28,8 @@ public: } void senses(int tick_id, class agent &); - void actions(class agent &); + void bred(int agent_id, std::string &info); + void actions(class agent *); void cancel(void); 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/viewer.nlogo b/viewer.nlogo index 2c7af9d..2299f5b 100644 --- a/viewer.nlogo +++ b/viewer.nlogo @@ -1,20 +1,21 @@ -globals [w h] +globals [w h symbol herbs] to init clear-all; file-close-all; set w 0; set h 0; - + set herbs 0; + set symbol ""; file-open "rawio_cfg"; resize-world 0 ((read-from-string file-read-line) - 1) (1 - (read-from-string file-read-line)) 0; file-close-all end - + to go file-open "rawio_map"; while [file-at-end? = false] [ - let symbol file-read-characters 1; + set symbol file-read-characters 1; if (symbol = "\n" or symbol = "\r") and file-at-end? = false [ set symbol file-read-characters 1; @@ -24,20 +25,29 @@ to go if symbol = "x" [ ask patch w h [set pcolor green ]; + set herbs herbs + 1 ] if symbol = "." [ - ask patch w h [set pcolor black ] + ask patch w h [set pcolor black ]; + ] + if member? symbol ["0" "1" "2" "3" "4" "5" "6" "7" "8" "9"] + [ + ask patch w h [set pcolor red ]; ] set w w + 1; ] file-close-all; + + set-current-plot "herbs"; + set-current-plot-pen "herbs"; + plot count patches with [pcolor = green]; + tick; set w 0; set h 0; wait 1; -end - +end @#$#@#$#@ GRAPHICS-WINDOW 111 @@ -97,6 +107,34 @@ NIL NIL NIL +PLOT +91 +332 +291 +482 +herbs +NIL +NIL +0.0 +10.0 +0.0 +10.0 +true +false +PENS +"herbs" 1.0 0 -16777216 true + +MONITOR +17 +411 +74 +456 +herbs +count patches with [pcolor = green] +17 +1 +11 + @#$#@#$#@ WHAT IS IT? ----------- diff --git a/world.h b/world.h index 47a3b81..ac21459 100644 --- a/world.h +++ b/world.h @@ -9,18 +9,29 @@ 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 */ const static int attack_idle_cost = -15; /* ... * attr.attack */ const static int defense_idle_cost = -15; /* ... * attr.defense */ const static int sun_energy = 10; - const static int soil_energy = 10; /* ... times five for lone herbs, times one for dense forests */ + const static int soil_energy = 20; /* ... times five for lone herbs, times one for dense forests */ const static int dead_body_energy = 2000; const static double dead_body_energy_carryover = 0.5; const static int dead_decay = -50; 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; + const static double phdecay_delta = 0.01; }; #endif