AI-Evolve-Befunge

 view release on metacpan or  search on metacpan

TODO  view on Meta::CPAN

* Go
* OCR
* Speech recognition
* Weather prediction

Portability:
* I know it works on linux
* I doubt it works anywhere else.  (patches and test reports welcome)

Other issues:
* Disable migration by default.
* Figure out why L::B hangs sometimes, and fix that.

example.conf  view on Meta::CPAN

# This is an example config file for AI::Evolve::Befunge.
# It can be installed as ~/.ai-evolve-befunge or /etc/ai-evolve-befunge.conf.
# All of the valid settings are listed below, along with their default values.

# 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

lib/AI/Evolve/Befunge.pm  view on Meta::CPAN

Well, once you get some critters that perform well, you can always
write up a production program which creates the Physics and Critter
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).

lib/AI/Evolve/Befunge/Blueprint.pm  view on Meta::CPAN


Create a new Blueprint object.  Two attributes are mandatory:

    code - a Befunge code string.  This must be exactly the right
           length to fill a hypercube of the given dimensions.
    dimensions - The number of dimensions we will operate in.

Other arguments are optional, and will be determined automatically if
not specified:

    fitness - assign it a fitness score, default is 0.
    id - assign it an id, default is to call new_popid() (see below).
    host - the hostname, default is $ENV{HOST}.

=cut

sub new {
    my $self = bless({}, shift);
    my %args = @_;
    my $usage = 'Usage: AI::Evolve::Befunge::Blueprint->new(code => "whatever", dimensions => 4, [, id => 2, host => "localhost", fitness => 5]);\n';
    croak $usage unless exists $args{code};
    croak $usage unless exists $args{dimensions};
    $$self{code}      = $args{code};

lib/AI/Evolve/Befunge/Blueprint.pm  view on Meta::CPAN

    my $rv =
        "[I$$self{id} D$$self{dims} F$$self{fitness} H$$self{host}]";
    $rv .= $$self{code};
    $rv .= "\n";
    return $rv;
}


=head1 STANDALONE FUNCTIONS

These functions are exported by default.

=cut

{
    my $_popid;

=head2 new_popid

    my $id = new_popid();

lib/AI/Evolve/Befunge/Critter.pm  view on Meta::CPAN


=item CodeCost

This is the number of tokens the critter pays (up front, at birth
time) for the codespace it inhabits.  If the blueprint's CodeSize
is (8,8,8), 8*8*8 = 512 spaces are taken up.  If the CodeCost is 1,
that means the critter pays 512 tokens just to be born.  If CodeCost
is 2, the critter pays 1024 tokens, and so on.

If not specified, this will be pulled from the variable "codecost" in
the config file.  If that can't be found, a default value of 1 is
used.


=item IterCost

This is the number of tokens the critter pays for each command it
runs.  It is a basic operational overhead, decremented for each clock
tick for each running thread.

If not specified, this will be pulled from the variable "itercost" in
the config file.  If that can't be found, a default value of 2 is
used.


=item RepeatCost

This is the number of tokens the critter pays for each time a command
is repeated (with the "k" instruction).  It makes sense for this value
to be lower than the IterCost setting, as it is somewhat more
efficient.

If not specified, this will be pulled from the variable "repeatcost"
in the config file.  If that can't be found, a default value of 1 is
used.


=item StackCost

This is the number of tokens the critter pays for each time a value
is pushed onto the stack.  It also has an effect when the critter
creates a new stack; the number of stack entries to be copied is
multiplied by the StackCost to determine the total cost.

If not specified, this will be pulled from the variable "stackcost"
in the config file.  If that can't be found, a default value of 1 is
used.


=item ThreadCost

This is a fixed number of tokens the critter pays for spawning a new
thread.  When a new thread is created, this cost is incurred, plus the
cost of duplicating all of the thread's stacks (see StackCost, above).
The new threads will begin incurring additional costs from the
IterCost (also above), when it begins executing commands of its own.

If not specified, this will be pulled from the variable "threadcost"
in the config file.  If that can't be found, a default value of 10 is
used.


=item Color

This determines the color of the player, which (for board games)
indicates which type of piece the current player is able to play.  It
has no other effect, and thus, it is not necessary for non-boardgame
physics models.

If not specified, a default value of 1 is used.


=item BoardSize

If specified, a board game of the given size (specified as a Vector
object) is created.

=back

=cut

sub new {
    my $package = shift;
    my %args = (
        # defaults
        Color       => 1,
        @_
    );
    # args
    my $usage = 
      "Usage: $package->new(Blueprint => \$blueprint, Physics => \$physics,\n"
     ."                     Tokens => 2000, BoardSize => \$vector, Config => \$config)";
    croak $usage unless exists  $args{Config};
    $args{Tokens}     = $args{Config}->config('tokens'     , 2000) unless defined $args{Tokens};
    $args{CodeCost}   = $args{Config}->config("code_cost"  , 1   ) unless defined $args{CodeCost};

lib/AI/Evolve/Befunge/Physics/othello.pm  view on Meta::CPAN

Use AI::Evolve::Befunge::Physics->new() to get an othello object;
there is no constructor in this module for you to call directly.


=head1 METHODS

=head2 setup_board

    $othello->setup_board($board);

Initialize the board to its default state.  For othello, this looks
like:

    ........
    ........
    ........
    ...xo...
    ...ox...
    ........
    ........
    ........

lib/AI/Evolve/Befunge/Physics/ttt.pm  view on Meta::CPAN

Use AI::Evolve::Befunge::Physics->new() to get a ttt object;
there is no constructor in this module for you to call directly.


=head1 METHODS

=head2 setup_board

    $ttt->setup_board($board);

Initialize the board to its default state.  For tic tac toe, this
looks like:

    ...
    ...
    ...

=cut

sub setup_board {
    my ($self, $board) = @_;

lib/AI/Evolve/Befunge/Population.pm  view on Meta::CPAN

    $args{Generation} //= 1;
    $args{Blueprints} //= [];

    my $self = bless({
        host       => $args{Host},
        blueprints => [],
        generation => $args{Generation},
        migrate    => spawn_migrator(),
    }, $package);

    $self->reload_defaults();
    my $nd          = $self->dimensions;
    my $config      = $self->config;
    my $code_size   = v(map { 4 } (1..$nd));
    my @population;

    foreach my $code (@{$args{Blueprints}}) {
        my $chromosome = Blueprint->new(code => $code, dimensions => $nd);
        push @population, $chromosome;
    }

lib/AI/Evolve/Befunge/Population.pm  view on Meta::CPAN

        } else {
            confess "unknown savefile line: $line\n";
        }
    }
    my $self = bless({
        host       => $host,
        blueprints => [@population],
        generation => $generation,
        migrate    => spawn_migrator(),
    }, $package);
    $self->reload_defaults();
    return $self;
}


=head1 PUBLIC METHODS

These methods are intended to be the normal user interface for this
module.  Their APIs will not change unless I find a very good reason.


=head2 reload_defaults

    $population->reload_defaults();

Rehashes the config file, pulls various values from there.  This is
common initializer code, shared by new() and load().  It defines the
values for the following items:

=over 4

=item boardsize

=item config

lib/AI/Evolve/Befunge/Population.pm  view on Meta::CPAN

=item physics

=item popsize

=item tokens

=back

=cut

sub reload_defaults {
    my $self = shift;
    my @config_args = (host => $self->host, gen => $self->generation);
    my $config = custom_config(@config_args);
    delete($$self{boardsize});
    my $physics        = $config->config('physics', 'ttt');
    $$self{physics}    = Physics->new($physics);
    $config            = custom_config(@config_args, physics => $self->physics->name);
    $$self{dimensions} = $config->config('dimensions', 3);
    $$self{popsize}    = $config->config('popsize', 40);
    $$self{tokens}     = $config->config('tokens', 2000);

lib/AI/Evolve/Befunge/Population.pm  view on Meta::CPAN


When set, as a side effect, rehashes the config file so that new
generational overrides may take effect.

=cut

sub generation {
    my ($self, $gen) = @_;
    if(defined($gen)) {
        $$self{generation} = $gen;
        $self->reload_defaults();
    }
    return $$self{generation};
}


1;

lib/AI/Evolve/Befunge/Util.pm  view on Meta::CPAN

        }
    }
    $global_config = Config->new({hash => \%global_config});
    $loaded_config_before = 1;
}


=head2 global_config

    my $value = global_config('name');
    my $value = global_config('name', 'default');
    my @list  = global_config('name', 'default');
    my @list  = global_config('name', ['default1', 'default2']);

Fetch some config from the config file.  This queries the global
config database - it will not take local overrides (for host,
generation, or physics plugin) into account.  For more specific
(and flexible) config, see L</custom_config>, below.

=cut

sub global_config :Export(:DEFAULT) {
    setup_configs();
    return $global_config->config(@_);
}


=head2 custom_config

    my $config = custom_config(host => $host, physics => $physics, gen => $gen);
    my $value = $config('name');
    my $value = $config('name', 'default');
    my @list  = $config('name', 'default');
    my @list  = $config('name', ['default1', 'default2']);

Generate a config object from the config file.  This queries the
global config database, but allows for overrides by various criteria -
it allows you to specify overridden values for particular generations
(if the current generation is greater than or equal to the ones in the
config file, with inheritance), for particular physics engines, and
for particular hostnames.

This is more specific than L</global_config> can be.  This is the
interface you should be using in almost all cases.

lib/AI/Evolve/Befunge/Util/Config.pm  view on Meta::CPAN


=head1 NAME

    AI::Evolve::Befunge::Util::Config - config database object


=head2 SYNOPSIS

    use AI::Evolve::Befunge::Util;
    my $config = custom_config(host => 'test', physics => 'ttt', gen => 1024);
    my $value = $config->config('value', 'default');


=head2 DESCRIPTION

This is a config object.  The config file allows overrides based on
hostname, physics engine in use, and AI generation.  Thus, the config
file data needs to be re-assessed every time one of these (usually
just the generation) is changed.  The result of this is a Config
object, which is what this module implements.

lib/AI/Evolve/Befunge/Util/Config.pm  view on Meta::CPAN


This module does not actually implement the constructor - please see
custom_config() in L<AI::Evolve::Befunge::Util> for the details.


=head1 METHODS

=head2 config

    my $value = global_config('name');
    my $value = global_config('name', 'default');
    my @list  = global_config('name', 'default');
    my @list  = global_config('name', ['default1', 'default2']);

Fetch some data from the config object.

=cut

sub config {
    my ($self, $keyword, $value) = @_;
    $value = $$self{hash}{$keyword}
        if exists $$self{hash}{$keyword};

t/01config.t  view on Meta::CPAN

use AI::Evolve::Befunge::Util;


# global_config
is(scalar global_config('basic_value', 'undefined'), 42, 'config(exists)');
is(scalar global_config('nonexistent', 'undefined'), 'undefined', 'config(!exists)');
is_deeply([global_config('nonexistent', 'undefined')], ['undefined'], 'wantarray config(!exists)');
is_deeply([global_config('nonexistent', undef)], [undef], 'wantarray config(!exists)');
is_deeply([global_config('nonexistent')], [], 'wantarray config(!exists)');
is_deeply([global_config('test_list')], [5,8,13], 'wantarray config(array exists)');
is_deeply([global_config('basic_value')], [42], 'wantarray returns value even if no default given');
BEGIN { $num_tests += 7 };


my $global = custom_config();
my $proper = custom_config(host => 'myhost', physics => 'foo', gen => 6);
my $wrong1 = custom_config(host => 'myhost', physics => 'bar', gen => 8);
my $wrong2 = custom_config(host => 'nohost', physics => 'bar', gen => 2);
is($global->config('basic_value'                ), 42, 'global value inherited');
is($proper->config('basic_value'                ), 42, 'global value inherited');
is($wrong1->config('basic_value'                ), 42, 'global value inherited');

t/03blueprint.t  view on Meta::CPAN

use File::Temp qw(tempfile);
use IO::File;

use aliased 'AI::Evolve::Befunge::Blueprint' => 'Blueprint';

# new
my $blueprint = Blueprint->new(code => '0'x16, dimensions => 4);
ok(ref($blueprint) eq "AI::Evolve::Befunge::Blueprint", "create an blueprint object");
is($blueprint->code,    '0000000000000000','code as passed');
is($blueprint->dims,    4,                 '4 dimensions');
is($blueprint->id,      1,                 'default id');
is($blueprint->host,    $ENV{HOST},    'default hostname');
is($blueprint->fitness, 0,                 'default fitness');
dies_ok( sub { Blueprint->new(); }, "Blueprint->new dies without code argument");
like($@, qr/Usage: /, "died with usage message");
dies_ok( sub { Blueprint->new(code => 'abc'); }, "Blueprint->new dies without dimensions argument");
like($@, qr/Usage: /, "died with usage message");
dies_ok( sub { Blueprint->new(code => 'abc', dimensions => 4); }, "Blueprint->new dies without code argument");
like($@, qr/non-orthogonal/, "died with non-orthogonality message");
lives_ok( sub { Blueprint->new(code => 'a'x16, dimensions => 4); }, "Blueprint->new lives");
$blueprint = Blueprint->new(code => ' 'x8, dimensions => 3, fitness => 1, id => 321, host => 'foo');
is($blueprint->code,    '        ','code as passed');
is($blueprint->dims,    3,         'dims as passed');

t/04board.t  view on Meta::CPAN

like($@, qr/must be at least 1/, "died with proper message");
$size = v(2, 2, 2);
dies_ok( sub { Board->new(Size => $size); }, "Board->new dies with dimensional overflow");
like($@, qr/isn't smart enough/, "died with proper message");
$size = v(2, 2, 1);
lives_ok( sub { Board->new(Size => $size); }, "Board->new makes an exception for d(2+) == 1");
BEGIN { $num_tests += 18 };

# set_value
# fetch_value
is($board->fetch_value(v(0,0)), 0, "default value is 0");
$board->set_value(v(2,2),2);
is($board->fetch_value(v(2,2)), 2, "fetch_value returns value set by set_value");
is($board->fetch_value(v(4,4)), 0, "default value is 0");
dies_ok( sub { $board->fetch_value(0)  }, 'fetch_value with no vector');
dies_ok( sub { $board->set_value(0, 1) }, 'set_value with no vector');
dies_ok( sub { $board->fetch_value(v(-1,0)) }, 'fetch_value out of range');
like($@, qr/out of range/, "died with proper message");
dies_ok( sub { $board->fetch_value(v(5,0))  }, 'fetch_value out of range');
like($@, qr/out of range/, "died with proper message");
dies_ok( sub { $board->fetch_value(v(0,-1)) }, 'fetch_value out of range');
like($@, qr/out of range/, "died with proper message");
dies_ok( sub { $board->fetch_value(v(0,5))  }, 'fetch_value out of range');
like($@, qr/out of range/, "died with proper message");

t/06util.t  view on Meta::CPAN

my $num_tests;
BEGIN { $num_tests = 0; };
use Test::More;
use Test::Output;
use Test::Exception;

use AI::Evolve::Befunge::Util;


# quiet
is(get_quiet(), 0, "non-quiet by default");
push_quiet(3);
is(get_quiet(), 3, "quiet now");
stdout_is(sub { quiet("foo") }, "foo", "quiet() writes when quiet value non-zero");
stdout_is(sub { nonquiet("foo") }, "", "nonquiet() writes nothing");
pop_quiet();
pop_quiet();
is(get_quiet(), 0, "now back to non-quiet default");
stdout_is(sub { quiet("foo") }, "", "quiet() writes nothing");
stdout_is(sub { nonquiet("foo") }, "foo", "nonquiet() writes correctly");
BEGIN { $num_tests += 7 };


# verbose
is(get_verbose(), 0, "non-verbose by default");
push_verbose(3);
is(get_verbose(), 3, "verbose now");
stdout_is(sub { verbose("foo") }, "foo", "verbose() writes when verbose value non-zero");
pop_verbose();
pop_verbose();
is(get_verbose(), 0, "now back to non-verbose default");
stdout_is(sub { verbose("foo") }, "", "verbose() writes nothing");
BEGIN { $num_tests += 5 };


# debug
is(get_debug(), 0, "non-debug by default");
push_debug(3);
is(get_debug(), 3, "debug now");
stdout_is(sub { debug("foo") }, "foo", "debug() writes when debug value non-zero");
pop_debug();
pop_debug();
is(get_debug(), 0, "now back to non-debug default");
stdout_is(sub { debug("foo") }, "", "debug() writes nothing");
BEGIN { $num_tests += 5 };


# v
is(v(1, 2, 3), "(1,2,3)", "v returns a vector");
is(ref(v(1, 2, 3)), "Language::Befunge::Vector", "v the right kind of object");
BEGIN { $num_tests += 2 };


t/09population.t  view on Meta::CPAN

push_quiet(1);

my $num_tests;
BEGIN { $num_tests = 0; };
plan tests => $num_tests;


# constructor
$ENV{HOST} = 'test';
my $population;
lives_ok(sub { $population = Population->new() }, 'defaults work');
is($population->physics->name, 'ttt' , 'default physics used');
is($population->popsize , 40         , 'default popsize used');
set_popid(1);
$population = Population->new(Host => 'host');
my $population2 = Population->new(Host => 'phost');
is(ref($population), 'AI::Evolve::Befunge::Population', 'ref to right class');
is($population2->popsize,   8, 'popsize passed through correctly');
is(ref($population->physics),  'AI::Evolve::Befunge::Physics::ttt',
                               'physics created properly');
is(ref($population2->physics), 'AI::Evolve::Befunge::Physics::test',
                               'physics created properly');
is($population->dimensions, 4, 'correct dimensions');
is($population->generation, 1, 'default generation');
BEGIN { $num_tests += 9 };


# default blueprints
my $listref = $population->blueprints;
is(scalar @$listref, 10, 'default blueprints created');
foreach my $i (0..7) {
    my $individual = $$listref[$i];
    my $code = $individual->code;
    is(index($code, "\0"), -1, "new_code_fragment contains no nulls");
    is(length($code), 256, "newly created blueprints have right code size");
}
BEGIN { $num_tests += 17 };


# new_code_fragment

tools/evolve  view on Meta::CPAN

Enable verbose mode.  This will increase the amount of output.


=head2 -d, --debug

Enable debug mode.  This will increase the amount of output.


=head2 -h <hostname>, --hostname=<hostname>

Set the hostname to the specified value.  The default is to use the
output of the "hostname" shell command.


=head2 <savefile>

If specified, previous genetic data is read from this file.  You can
easily "fork" an existing population on a new host by reading its
savefile.


=cut

# default command line options
my $debug    = 0;
my $quiet    = 0;
my $verbose  = 0;
my $help     = 0;
my $hostname = undef;

die("Usage: $0 [-q|v|d] [-h host] [-c num] [savefile]\n") unless GetOptions(
    'debug'      => \$debug,
    'quiet'      => \$quiet,
    'verbose'    => \$verbose,

tools/migrationd  view on Meta::CPAN

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
accepted on all available IPs.


=head2 -p <port>, --port=<port>

TCP port to listen on.  The default port is 29522.


=head2 -q, --quiet

Enable quiet mode.  This will reduce the amount of output.


=head2 -v, --verbose

Enable verbose mode.  This will increase the amount of output.

tools/migrationd  view on Meta::CPAN

=cut


=head2 -d, --debug

Enable debug mode.  This will increase the amount of output.

=cut


# default command line options
my $quiet   = 0;
my $verbose = 0;
my $debug   = 0;
my $help    = 0;
my $host    = '0.0.0.0';
my $port    = 29522;

die("Usage: $0 [-q|v|d] [-h host] [-p port]\n") unless GetOptions(
    'debug'   => \$debug,
    'quiet'   => \$quiet,



( run in 0.663 second using v1.01-cache-2.11-cpan-0a6323c29d9 )