package AI::Evolve::Befunge::Util;
use strict;
use warnings;
use Carp;
use IO::Socket;
use Language::Befunge::Vector;
use Perl6::Export::Attrs;
use Socket qw(AF_UNIX SOCK_STREAM PF_UNSPEC);
use YAML qw(LoadFile Load Dump);
use aliased 'AI::Evolve::Befunge::Util::Config' => 'Config';
$ENV{HOST} = global_config("hostname", `hostname`);
$ENV{HOST} = "unknown-host-$$-" . int rand 65536 unless defined $ENV{HOST};
chomp $ENV{HOST};
my @quiet = 0;
my @verbose = 0;
my @debug = 0;
=head1 NAME
AI::Evolve::Befunge::Util - common utility functions
=head1 DESCRIPTION
This is a place for miscellaneous stuff that is used elsewhere
throughout the AI::Evolve::Befunge codespace.
=head1 FUNCTIONS
=head2 push_quiet
push_quiet(1);
Add a new value to the "quiet" stack.
=cut
sub push_quiet :Export(:DEFAULT) {
my $new = shift;
push(@quiet, $new);
}
=head2 pop_quiet
pop_quiet();
Remove the topmost entry from the "quiet" stack, if more than one
item exists on the stack.
=cut
sub pop_quiet :Export(:DEFAULT) {
my $new = shift;
pop(@quiet) if @quiet > 1;
}
=head2 get_quiet
$quiet = get_quiet();
Returns the topmost entry on the "quiet" stack.
=cut
sub get_quiet :Export(:DEFAULT) {
return $quiet[-1];
}
=head2 push_verbose
push_verbose(1);
Add a new value to the "verbose" stack.
=cut
sub push_verbose :Export(:DEFAULT) {
my $new = shift;
push(@verbose, $new);
}
=head2 pop_verbose
pop_verbose();
Remove the topmost entry from the "verbose" stack, if more than one
item exists on the stack.
=cut
sub pop_verbose :Export(:DEFAULT) {
my $new = shift;
pop(@verbose) if @verbose > 1;
}
=head2 get_verbose
$quiet = get_verbose();
Returns the topmost entry on the "verbose" stack.
=cut
sub get_verbose :Export(:DEFAULT) {
return $verbose[-1];
}
=head2 push_debug
push_debug(1);
Add a new value to the "debug" stack.
=cut
sub push_debug :Export(:DEFAULT) {
my $new = shift;
push(@debug, $new);
}
=head2 pop_debug
pop_debug();
Remove the topmost entry from the "debug" stack, if more than one
item exists on the stack.
=cut
sub pop_debug :Export(:DEFAULT) {
my $new = shift;
pop(@debug) if @debug > 1;
}
=head2 get_debug
$quiet = get_debug();
Returns the topmost entry on the "debug" stack.
=cut
sub get_debug :Export(:DEFAULT) {
return $debug[-1];
}
=head2 verbose
verbose("Hi! I'm in verbose mode!\n");
Output a message if get_verbose() is true.
=cut
sub verbose :Export(:DEFAULT) {
print(@_) if $verbose[-1];
}
=head2 debug
verbose("Hi! I'm in debug mode!\n");
Output a message if get_debug() is true.
=cut
sub debug :Export(:DEFAULT) {
print(@_) if $debug[-1];
}
=head2 quiet
quiet("Hi! I'm in quiet mode!\n");
Output a message if get_quiet() is true. Note that this probably
isn't very useful.
=cut
sub quiet :Export(:DEFAULT) {
print(@_) if $quiet[-1];
}
=head2 nonquiet
verbose("Hi! I'm not in quiet mode!\n");
Output a message if get_quiet() is false.
=cut
sub nonquiet :Export(:DEFAULT) {
print(@_) unless $quiet[-1];
}
=head2 v
my $vector = v(1,2);
Shorthand for creating a Language::Befunge::Vector object.
=cut
sub v :Export(:DEFAULT) {
return Language::Befunge::Vector->new(@_);
}
=head2 code_print
code_print($code, $x_size, $y_size);
Pretty-print a chunk of code to stdout.
=cut
sub code_print :Export(:DEFAULT) {
my ($code, $sizex, $sizey) = @_;
my $usage = 'Usage: code_print($code, $sizex, $sizey)';
croak($usage) unless defined $code;
croak($usage) unless defined $sizex;
croak($usage) unless defined $sizey;
my $charlen = 1;
my $hex = 0;
foreach my $char (split("",$code)) {
if($char ne "\n") {
if($char !~ /[[:print:]]/) {
$hex = 1;
}
my $len = length(sprintf("%x",ord($char))) + 1;
$charlen = $len if $charlen < $len;
}
}
$code =~ s/\n//g unless $hex;
$charlen = 1 unless $hex;
my $space = " " x ($charlen);
if($sizex > 9) {
print(" ");
for my $x (0..$sizex-1) {
unless(!$x || ($x % 10)) {
printf("%${charlen}i",$x / 10);
} else {
print($space);
}
}
print("\n");
}
print(" ");
for my $x (0..$sizex-1) {
printf("%${charlen}i",$x % 10);
}
print("\n");
foreach my $y (0..$sizey-1) {
printf("%2i ", $y);
if($hex) {
foreach my $x (0..$sizex-1) {
my $val;
$val = substr($code,$y*$sizex+$x,1)
if length($code) >= $y*$sizex+$x;
if(defined($val)) {
$val = ord($val);
} else {
$val = 0;
}
$val = sprintf("%${charlen}x",$val);
print($val);
}
} else {
print(substr($code,$y*$sizex,$sizex));
}
printf("\n");
}
}
=head2 setup_configs
setup_configs();
Load the config files from disk, set up the various data structures
to allow fetching global and overrideable configs. This is called
internally by L</global_config> and L</custom_config>, so you never
have to call it directly.
=cut
my $loaded_config_before = 0;
my @all_configs = {};
my $global_config;
sub setup_configs {
return if $loaded_config_before;
my %global_config;
my @config_files = (
"/etc/ai-evolve-befunge.conf",
$ENV{HOME}."/.ai-evolve-befunge",
);
push(@config_files, $ENV{AIEVOLVEBEFUNGE}) if exists $ENV{AIEVOLVEBEFUNGE};
foreach my $config_file (@config_files) {
next unless -r $config_file;
push(@all_configs, LoadFile($config_file));
}
foreach my $config (@all_configs) {
my %skiplist = (byhost => 1, bygen => 1, byphysics => 1);
foreach my $keyword (keys %$config) {
next if exists $skiplist{$keyword};
$global_config{$keyword} = $$config{$keyword};
}
}
$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.
If you don't specify a particular attribute, overrides by that
attribute will not show up in the resulting config. This is so you
can (for instance) specify a host-specific override for the physics
engine, and query that successfully before knowing which physics
engine you will be using.
Note that you can recurse these, but if you have two paths to the same
value, you should not rely on which one takes precedence. In other
words, if you have a "byhost" clause within a "bygen" section, and you
also have a "bygen" clause within a "byhost" section, either one may
eventually be used. When in doubt, simplify your config file.
=cut
sub custom_config :Export(:DEFAULT) {
my %args = @_;
setup_configs();
# deep copy
my @configs = Load(Dump(@all_configs));
my $redo = 1;
while($redo) {
$redo = 0;
foreach my $config (@configs) {
if(exists($args{host})) {
my $host = $args{host};
if(exists($$config{byhost}) && exists($$config{byhost}{$host})) {
push(@configs, $$config{byhost}{$host});
$redo = 1;
}
}
delete($$config{byhost});
if(exists($args{physics})) {
my $physics = $args{physics};
if(exists($$config{byphysics}) && exists($$config{byphysics}{$physics})) {
push(@configs, $$config{byphysics}{$physics});
$redo = 1;
}
}
delete($$config{byphysics});
if(exists($args{gen})) {
my $mygen = $args{gen};
if(exists($$config{bygen})) {
# sorted, so that later gens override earlier ones.
foreach my $gen (sort {$a <=> $b} keys %{$$config{bygen}}) {
if($mygen >= $gen) {
push(@configs, $$config{bygen}{$gen});
$redo = 1;
}
}
}
}
delete($$config{bygen});
}
}
# tally up the values
my %config = ();
foreach my $config (@configs) {
foreach my $keyword (keys %$config) {
$config{$keyword} = $$config{$keyword};
}
}
return Config->new({ %args, hash => \%config });
}
1;