Merge branch 'master' of ssh://github.com/brmlab/brmlife

This commit is contained in:
Petr Baudis 2011-12-09 01:37:55 +01:00
commit 2b8a07dee6
13 changed files with 427 additions and 60 deletions

View file

@ -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 $@ $^

44
README
View file

@ -20,10 +20,17 @@ CRLF ("\r\n"), not just LF ("\n")!
The following inputs (in no particular order) are supported:
agent_id <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 <ticknum>
BUMP
if received, the agent's move failed
(or attack of non-existent agent, etc.)
BRED <id> <father_info>
a new agent has been spawned, connect using agent_id <id>;
<father_info> is arbitrary string passed from the father,
can be used for genetic recombination
DEAD
if received, the agent is dead!
energy <points>
@ -38,6 +45,13 @@ The following inputs (in no particular order) are supported:
a dead agent
A alive agent
x herp
pheromones (<ph>,<ph>,<ph>,...) (<ph>...)...
(<ph>,...) 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
<ph> format is <id>:<intensity>
<id>: pheromone id 0..65535
<intensity>: floating-point number
The following outputs are supported:
@ -47,11 +61,21 @@ The following outputs are supported:
attack_dir <x> <y>
<x> and <y> are integer offsets relative
to the current position; may be just {-1,0,1}
breed_dir <x> <y> <info>
<info> is arbitrary string passed to the "mother"
(to be passed to the child)
secrete <phid> <phintensity>
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 <rate>
<rate> between 0 and 1, describing probability
@ -60,6 +84,20 @@ by empty line), but with these commands instead:
<rate> between 0 and 1.
defense <rate>
<rate> between 0 and 1.
breeding_key <value>
<value> 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 <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.

View file

@ -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));
}
}

33
agent.h
View file

@ -1,11 +1,29 @@
#ifndef BRMLIFE__AGENT_H
#define BRMLIFE__AGENT_H
#include <string>
#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

View file

@ -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";

View file

@ -2,6 +2,7 @@
#include <cstdio>
#include <cstring>
#include <iostream>
#include <sstream>
#include <sys/select.h>
#include <pthread.h>
@ -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<class pheromone>::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<class pheromone>::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<class agent *>::iterator ai = agents.begin(); ai != agents.end(); ai++) {
if ((*ai)->id == id) {
a2 = *ai;
break;
}
}
if (!a2 || a2->conn || !a2->tile || (dynamic_cast<herb *> (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);

View file

@ -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);

2
map.cc
View file

@ -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

3
map.h
View file

@ -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 &map;
class agent *agent;
class pheromones pheromones;
tile(int x_, int y_, class map &map_)
: x(x_), y(y_), map(map_), agent(NULL) {};

48
pheromone.cc Normal file
View file

@ -0,0 +1,48 @@
#include <cassert>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include "pheromone.h"
#include "main.h"
void
pheromones::secrete(class pheromone &p)
{
for (std::list<class pheromone>::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<class pheromone>::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<class pheromone>::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;
}
}
}

35
pheromone.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef BRMLIFE__PHEROMONE_H
#define BRMLIFE__PHEROMONE_H
/* Pheromone spectrum in a particular state. */
#include <list>
#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<class pheromone> 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

View file

@ -1,10 +1,11 @@
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
@ -14,7 +15,7 @@ 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
@#$#@#$#@
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?
-----------

13
world.h
View file

@ -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