view release on metacpan or search on metacpan
example.conf view on Meta::CPAN
# This config file is in YAML format. If you're not familiar with it, just
# follow the syntax as shown below, and everything will work out just fine.
# NORMAL OPERATING PARAMETERS
# Select the physics engine for this instance. This is "othello", "ttt",
# or any other module under the AI::Evolve::Befunge::Physics:: namespace.
physics: ttt
# The number of CPUs (or cores) this node has. This affects the number of
# worker child processes.
cpus: 1
# The number of critters alive in each generation.
popsize: 40
# NETWORKING
# Hostname or IP address to connect to. See tools/migrationd for more info.
migrationd_host: quack.glines.org
lib/AI/Evolve/Befunge.pm view on Meta::CPAN
Normally, end users can use the "evolve" script as a frontend. If
that's what you're after, please see the documentation contained
within that script. Otherwise, read on.
This particular file (AI/Evolve/Befunge.pm) does not contain any code;
it exists mainly to provide a version number to keep Build.PL happy.
The rest of this file acts as a quick-start guide to the rest of the
codebase.
The important bits from a user's standpoint are the Population object
(which drives the main process of evolving AI), and the Physics plugin
(which implements the rules of the universe those AI live in). There
are sections below containing more detail on what these two things
are, and how they work.
=head1 POPULATIONS
The Population object is the main user interface to this project.
Basically you just keep running it over and over, and presumably, the
result gets better and better.
The important thing to remember here is that the results take time -
it will probably take several weeks of solid processing time before you
begin to see any promising results at all. It takes a lot of random
code generation before it starts to generate code that does what you
want it to do.
If you don't know anything about Befunge, I recommend you read up on
that first, before trying to understand how this works.
The individuals of this population (which we call Critters) may be of
various sizes, and may make heavy or light use of threads and stacks.
Each one is issued a certain number of "tokens" (which you can think
lib/AI/Evolve/Befunge.pm view on Meta::CPAN
number of tokens, depending on the code size. After that, doing things
(like executing a befunge command, pushing a value to the stack,
spawning a thread) all take a certain number of tokens to accomplish.
When the number of tokens drops to 0, the critter dies. So it had
better accomplish its task before that happens.
After a population fights it out for a while, the winners are chosen
(who continue to live) and everyone else dies. Then a new population
is generated from the winners, some random mutation (random
generation of code, as well as potentially resizing the codebase)
occurs, and the whole process starts over for the next generation.
=head1 PHYSICS
At the other end of all of this is the Physics plugin. The Physics
plugin implements the rules of the universe inhabited by these AI
creatures. It provides a scoring mechanism through which multiple
critters may be weighed against eachother. It provides a set of
commands which may be used by the critters to do useful things within
its universe (such as make a move in a board game, do a spellcheck,
lib/AI/Evolve/Befunge.pm view on Meta::CPAN
Rather than describing the entire API in detail, I suggest you read
through the "othello" and "ttt" modules provided along with this
distribution. They are small and simple, and should make good
examples.
=head1 MIGRATION
Further performance may be improved through the use of migration.
Migration is a very simple form of parallel processing. It should scale
nearly linearly, and is a very effective means of increasing performance.
The idea is, you run multiple populations on multiple machines (one per
machine). The only requirement is that each Population has a different
"hostname" setting. And that's not really a requirement, it's just useful
for tracking down which host a critter came from.
When a Population object has finished processing a generation, there is
a chance that one or more (up to 3) of the surviving critters will be
written out to a special directory (which acts as an "outbox").
A separate networking program (implemented by Migrator.pm and spawned
automatically when creating a Population object) may pick up these
critters and broadcast them to some or all of the other nodes in a cluster
(deleting them from the "outbox" folder at the same time). The instances
of this networking program on the other nodes will receive them, and write
them out to another special directory (which acts as an "inbox").
When a Population object has finished processing a generation, another
thing it does is checks the "inbox" directory for any new incoming
critters. If they are detected, they are imported into the population
(and deleted from the "inbox").
Imported critters will compete in the next generation. If they win,
they will be involved in the reproduction process and may contribute to
the local gene pool.
On the server end, a script called "migrationd" is provided to accept
connections and distribute critters between nodes. The config file
specifies which server to connect to. See the CONFIG FILE section,
below.
=head1 PRACTICAL APPLICATION
lib/AI/Evolve/Befunge.pm view on Meta::CPAN
objects and runs them directly, over and over and over to your heart's
content. After you have reached your goal, you need not continue to
evolve or test new critters.
=head1 CONFIG FILE
You can find an example config file ("example.conf") in the source
tarball. It contains all of the variables with their default values,
and descriptions of each. It lets you configure many important
parameters about how the evolutionary process works, so you probably
want to copy and edit it.
This file can be copied to ".ai-evolve-befunge" in your home
directory, or "/etc/ai-evolve-befunge.conf" for sitewide use. If both
files exist, they are both loaded, in such a way that the homedir
settings supercede the ones from /etc. If the "AIEVOLVEBEFUNGE"
environment variable is set, that too is loaded as a config file, and
its settings take priority over the other files (if any).
=cut
lib/AI/Evolve/Befunge/Blueprint.pm view on Meta::CPAN
$_popid = 0 unless defined $_popid;
return $_popid++;
}
=head2 set_popid
set_popid($id);
Initialize the iterator to the given value. This is typically done
when a new process reads a results file, to keep node identifiers
unique across runs.
=cut
sub set_popid :Export(:DEFAULT) {
$_popid = shift;
}
}
new_popid();
lib/AI/Evolve/Befunge/Critter.pm view on Meta::CPAN
last unless scalar @ips;
$ip = shift @ips;
$interp->set_ips([@ips]);
}
unless(defined $$ip{_ai_critter}) {
$$ip{_ai_critter} = $self;
weaken($$ip{_ai_critter});
}
last unless $self->spend($self->itercost);
$interp->set_curip($ip);
$interp->process_ip();
if(defined($$self{move})) {
debug("move made: " . $$self{move} . "\n");
$rv->choice( $$self{move} );
return $rv;
}
}
debug("play timeout\n");
return $rv;
}
lib/AI/Evolve/Befunge/Migrator.pm view on Meta::CPAN
=head1 SYNOPSIS
my $migrator = AI::Evolve::Befunge::Migrator->new(Local => $socket);
$migrator->spin() while $migrator->alive();
=head1 DESCRIPTION
Maintains a connection to the migration server, migrationd. This
module is meant to run in a child process, which will die when the
Local socket is closed.
It provides a non-blocking, fault tolerant adaptation layer between
migrationd (which may be somewhere across the internet) and the
AI::Evolve::Befunge population object (which spends most of its time
evolving critters, and only occasionally polls us).
=head1 CONSTRUCTOR
=head2 new
my $migrator = AI::Evolve::Befunge::Migrator->new(Local => $socket);
Construct a new Migrator object.
The Local parameter is mandatory, it is the socket (typically a UNIX
domain socket) used to pass critters to and from the parent process.
Note that you probably don't want to call this directly... in most
cases you should call spawn_migrator, see below.
=cut
sub new {
my ($package, %args) = @_;
croak("The 'Local' parameter is required!") unless exists $args{Local};
my $host = global_config('migrationd_host', 'quack.glines.org');
lib/AI/Evolve/Befunge/Migrator.pm view on Meta::CPAN
lastc => 0,
};
return bless($self, $package);
}
=head2 spawn_migrator
my $socket = spawn_migrator($config);
Spawn off an external migration child process. This process will live
as long as the returned socket lives; it will die when the socket is
closed. See AI::Evolve::Befunge::Migrator for implementation details.
=cut
sub spawn_migrator :Export(:DEFAULT) {
my ($sock1, $sock2) = IO::Socket->socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
my $pid = fork();
if($pid) {
close($sock2);
lib/AI/Evolve/Befunge/Population.pm view on Meta::CPAN
$population->generation($gen+1);
}
=head1 DESCRIPTION
This manages a population of Befunge AI critters.
This is the main evolution engine for AI::Evolve::Befunge. It has
all of the steps necessary to evolve a population and generate the
next generation. The more times you run this process, the more
progress it will (theoretically) make.
=head1 CONSTRUCTORS
There are two constructors, depending on whether you want to create
a new population, or resume a saved one.
=head2 new
lib/AI/Evolve/Befunge/Population.pm view on Meta::CPAN
}
$self->blueprints([@population]);
}
=head2 breed
$population->breed();
Bring the population count back up to the "popsize" level, by a
process of sexual reproduction. The newly created critters will have
a combination of two previously existing ("winners") genetic makeup,
plus some random mutation. See the L</crossover> and L</mutate>
methods, below. There is also a one of 5 chance a critter will be
resized, see the L</crop> and L</grow> methods, below.
=cut
sub breed {
my $self = shift;
my $popsize = $self->popsize;
lib/AI/Evolve/Befunge/Population.pm view on Meta::CPAN
=head2 pair
my ($c1, $c2) = $population->pair(map { 1 } (@population));
my ($c1, $c2) = $population->pair(map { $_->fitness } (@population));
Randomly select and return two blueprints from the blueprints array.
Some care is taken to ensure that the two blueprints returned are not
actually two copies of the same blueprint.
The @fitness parameter is used to weight the selection process. There
must be one number passed per entry in the blueprints array. If you
pass a list of 1's, you will get an equal probability. If you pass
the critter's fitness scores, the more fit critters have a higher
chance of selection.
=cut
sub pair {
my $self = shift;
my @population = @{$self->blueprints};
t/10migration.t view on Meta::CPAN
# migrate (disconnected from test server)
close($incoming);
lives_ok(sub { $population->migrate() }, 'migrate runs without server connection');
waitpid($serverpid, 0);
lives_ok(sub { $population->migrate() }, 'migrate runs without server connection');
BEGIN { $num_tests += 2 };
# by assigning one side of the socketpair to an external variable, the socket
# will stay open. When the test script exits, the socket will be closed,
# signalling the child process to exit.
sub spawn_test_server {
my $listener = IO::Socket::INET->new(
Listen => 1,
LocalAddr => '127.0.0.1',
Proto => 'tcp',
ReuseAddr => 1,
);
croak("can't create TCP listener socket") unless defined $listener;
my $sock2;
($incoming, $sock2) = IO::Socket->socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
tools/migrationd view on Meta::CPAN
=head1 SYNOPSIS
migrationd [-q|v|d] [-h host/IP] [-p port]
=head1 DESCRIPTION
This script acts as a server to link multiple instances of
AI::Evolve::Befunge together for the purposes of migration. This is
a cheap, easy form of parallel processing - migration allows useful
traits to propagate without requiring any synchronization, and using
minimal bandwidth.
It will run until it is killed.
Please be aware that this protocol is subject to change over time -
the whole migration process will be adapted to use Net::Cluster when
that becomes available. Until this feature has stabilized, no
guarantee is made that newer versions of AI::Evolve::Befunge will be
able to talk to this version of the server.
=head1 COMMAND LINE ARGUMENTS
=head2 -h <host/IP>, --host=<host/ip>
Hostname or IP address to listen on. By default, connections will be