Skip to content

Instantly share code, notes, and snippets.

@kuniyoshi
Created September 11, 2016 15:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kuniyoshi/9c9138dde89d11bc42efe44262343f50 to your computer and use it in GitHub Desktop.
Save kuniyoshi/9c9138dde89d11bc42efe44262343f50 to your computer and use it in GitHub Desktop.
my trial script to use Behaviour Tree
#!/usr/bin/env perl
use 5.10.0;
use utf8;
use strict;
use warnings;
use open qw( :utf8 :std );
use feature "refaliasing";
use Data::Dumper;
use Readonly;
use Time::HiRes qw( gettimeofday tv_interval sleep );
no warnings "experimental::refaliasing";
Readonly my $Epsilon => 0.3;
package Status;
use Readonly;
Readonly our %Id => (
ready => 0,
success => 1,
failure => 2,
running => 3,
);
package Node;
sub new {
return bless {
( splice @_, 1 ),
status_id => $Status::Id{ready},
}, $_[0];
}
sub child {
my $self = shift;
if ( @_ ) {
$self->{child} = shift;
}
else {
return $self->{child};
}
}
sub clear {
my $self = shift;
if ( $self->{status_id} == $Status::Id{running} ) {
return;
}
$self->{status_id} = $Status::Id{ready};
}
sub process {
my $self = shift;
my $blackboard = shift;
my $child = $self->{child}
or return $Status::Id{failure};
return $self->status_id( $child->process( $blackboard ) );
}
sub status_id {
my $self = shift;
if ( @_ ) {
$self->{status_id} = shift;
}
return $self->{status_id};
}
package Node::Action;
use base "Node";
sub process {
my $self = shift;
my $blackboard = shift;
say "i am node";
return $Status::Id{success};
}
package Node::Decorator;
use base "Node";
sub process {
my $self = shift;
my $blackboard = shift;
if ( $self->test( $blackboard ) ) {
return $self->status_id( $self->{child}->process( $blackboard ) );
}
return $self->status_id( $Status::Id{failure} );
}
sub test {
my $self = shift;
my $blackboard = shift;
$self->{test_sub}
and return $self->{test_sub}( $self, $blackboard );
return 1;
}
package Node::Selector;
use base "Node";
sub children {
my $self = shift;
if ( @_ ) {
$self->{children} = shift;
}
return $self->{children};
}
sub process {
my $self = shift;
my $blackboard = shift;
for my $child ( @{ $self->{children} } ) {
if ( $child->process( $blackboard ) == $Status::Id{success} ) {
return $self->status_id( $Status::Id{success} );
}
}
return $self->status_id( $Status::Id{failure} );
}
package Node::Sequence;
use base "Node";
sub children {
my $self = shift;
if ( @_ ) {
$self->{children} = shift;
}
return $self->{children};
}
sub process {
my $self = shift;
my $blackboard = shift;
if ( $self->{status_id} != $Status::Id{running} ) {
$self->{current_index} = 0;
return $self->status_id( $Status::Id{running} );
}
if ( $self->{current_index} < 0 || $self->{current_index} >= @{ $self->{children} } ) {
die "current_index goes out of range.";
}
my $child = $self->{children}[ $self->{current_index} ];
$self->{current_index}++;
if ( $child->process( $blackboard ) == $Status::Id{failure} ) {
return $self->status_id( $Status::Id{failure} );
}
elsif ( $self->{current_index} == @{ $self->{children} } ) {
return $self->status_id( $Status::Id{success} );
}
else {
return $self->status_id( $Status::Id{running} );
}
}
package Node::Action::Say;
use base "Node::Action";
sub process {
my $self = shift;
my $blackboard = shift;
system( "say", $blackboard->{next_count} ) == 0
or warn;
return $Status::Id{success};
}
package Node::Action::Sleep;
use base "Node::Action";
sub process {
my $self = shift;
sleep $self->{epsilon};
return $Status::Id{success};
}
package Blackboard;
sub new { bless { splice @_, 1 }, $_[0] }
package main;
my $count_down = Node::Decorator->new(
count => 3,
last_count => undef,
test_sub => sub {
my $self = shift;
my $blackboard = shift;
return
if defined $blackboard->{next_count} && !$blackboard->{next_count};
my $elapsed = tv_interval( $blackboard->{started_at} );
my $current_count = $self->{count} - int $elapsed;
if ( !$self->{last_count} || $current_count != $self->{last_count} ) {
$blackboard->{next_count} = $current_count;
$self->{last_count} = $current_count;
return 1;
}
else {
return;
}
},
);
$count_down->child( Node::Action::Say->new );
my $eden = Node::Decorator->new(
test_sub => sub {
my $self = shift;
my $blackboard = shift;
if ( $blackboard->{next_count} <= 0 ) {
return;
}
return 1;
},
);
$eden->child( Node::Action::Sleep->new( epsilon => $Epsilon ) );
my $selector = Node::Selector->new(
children => [
$count_down,
$eden,
],
);
my $root = Node->new;
$root->child( $selector );
my $blackboard = Blackboard->new(
started_at => [ gettimeofday ],
);
while ( $root->status_id != $Status::Id{failure} ) {
$root->clear;
$root->process( $blackboard );
}
exit;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment