Algorithm-Gutter

 view release on metacpan or  search on metacpan

lib/Algorithm/Gutter.pm  view on Meta::CPAN

# -*- Perl -*-
#
# Algorithm::Gutter - cellular automata to simulate rain in a gutter,
# or, "the hundred and forty-second worst drum machine in the West".

package Algorithm::Gutter;
use 5.26.0;
use Object::Pad 0.66;
our $VERSION = '0.02';

class Algorithm::Gutter::Cell {
    field $amount :mutator :param = 0;
    field $context :mutator;
    field $enabled :mutator :param   = 0;
    field $id :reader :param         = 0;
    field $threshold :mutator :param = ~0;
    field $update :mutator :param    = undef;

    method drain ( $index, $all = 1, $stash = undef ) {
        if ( $enabled and $amount >= $threshold ) {
            my $drained;
            if ($all) {
                $drained = $amount;
                $amount  = 0;
            } else {
                $drained = $threshold;
                $amount -= $threshold;
            }
            die "no update callback" unless defined $update;
            return $update->( $self, $index, $drained, $stash );
        }
        return;
    }
}

class Algorithm::Gutter {
    field $gutter :reader :param = [];
    field $rain :writer :param   = undef;

    # Try to drain cells, possibly triggering cell update functions.
    method drain ( $all = 1, $stash = undef ) {
        my $index = 0;
        map { $_->drain( $index++, $all, $stash ) } @$gutter;
    }

    # Adding water to the cells left as an exercise to the caller.
    method rain ( $stash = undef ) {
        die "no rain callback supplied" unless defined $rain;
        $rain->( $gutter, $stash );
    }

    # Redistribute imbalances in the water level between adjacent cells.
    # There are doubtless more complicated or more efficient ways to do
    # this, but those would take time to figure out.
    method slosh ( $max = ~0, $dmax = 1 ) {
        return if @$gutter < 2;
        my $iterations = 0;
        my $end        = @$gutter - 1;
        while ( $max-- > 0 ) {
            $iterations++;
            my $done = 1;
            for my $i ( 0 .. $end - 1 ) {
                my $delta = $gutter->[$i]->amount - $gutter->[ $i + 1 ]->amount;
                if ( $delta > $dmax ) {



( run in 0.535 second using v1.01-cache-2.11-cpan-39bf76dae61 )