mirror of
https://github.com/brmlab/brmlife.git
synced 2025-08-02 18:03:37 +02:00
Merge branch 'master' of ssh://github.com/brmlab/brmlife
This commit is contained in:
commit
2b8a07dee6
13 changed files with 427 additions and 60 deletions
2
Makefile
2
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 $@ $^
|
||||
|
|
44
README
44
README
|
@ -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.
|
||||
|
|
88
agent.cc
88
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));
|
||||
}
|
||||
}
|
||||
|
|
33
agent.h
33
agent.h
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
152
connection.cc
152
connection.cc
|
@ -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);
|
||||
|
|
|
@ -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
2
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
|
||||
|
|
3
map.h
3
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) {};
|
||||
|
|
48
pheromone.cc
Normal file
48
pheromone.cc
Normal 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
35
pheromone.h
Normal 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
|
48
viewer.nlogo
48
viewer.nlogo
|
@ -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
13
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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue