Here is a package (Park.pm) that implements the base rules for the Dr Dobbs, Dr Ecco's Omniheurist challenge.

The basic methods are new, build, park, and bump (time bump). This was done in a couple hours of watching TV, so there is plenty of room for optimization. Any attempt to design the agents suggested in the article will have to speed up the bump() routine especially.

I'll try to keep this page up to date with any suggested changes PMers make, as well as the canonical source to be found Here. Here is a basic script to show the solution to the easy part of the challenge

#!/usr/bin/perl use Park; use strict; my $p = Park->new; # accept all defaults $p->spoiler1; # place parks in the correct solution $p->bump_and_run; # time 'bump' until completion print $p->str_status; # print some stats print $p->str_grid; # print the grid visually exit(0);
The code for Park.pm is as follows. The most up-to-date version can be foundHere.
package Park; use strict; use vars qw/$PARK $BUILDING/; =comments This is an implementation of the November Dr Dobbs puzzle [Dr Ecco's Omniheurist Corner] It implements the park grid, and performs the spreading of development every bump =cut $PARK = 'P'; $BUILDING = 'B'; my $GRID_HEIGHT = 12; my $GRID_WIDTH = 12; sub new { my $class = shift; my %args = (height => $GRID_HEIGHT, width => $GRID_WIDTH, setup => 'default', # default, empty @_); my $self = bless \%args, $class; if ($self->{height} <= 0 || $self->{width} <= 0) { die "Illegal size for height or width"; } # initialize our variables $self->{bumps} = 0; # our count of time $self->{houses} = 0; # count of houses $self->{parks} = 0; # count of parks $self->{parcels} = $self->{height} * $self->{width}; my $grid = []; for (my $i = 0; $i < $self->{height}; $i++) { my $row = []; for (my $j = 0; $j < $self->{width}; $j++) { push @$row, ' '; } push @$grid, $row; } $self->{grid} = $grid; my $setup = delete $self->{setup}; if ($setup eq 'default') { $self->build(3,3); $self->build(3,4); $self->build(9,9); $self->build(8,9); } elsif ($setup ne 'empty') { die "Illegal parameter for setup of '$setup'"; } # else empty return $self; } sub free_parcels { # how many parcels are neither park nor built? my $self = shift; return ($self->{parcels} - $self->{houses} - $self->{parks}); } sub build { # put a building on a lot my $self = shift; my ($x, $y) = @_; $self->{houses}++; # we don't check the previous state return $self->{grid}->[$x]->[$y] = $BUILDING; } sub park { # put a park on a lot my $self = shift; my ($x, $y) = @_; $self->{parks}++; # we don't check the previous state return $self->{grid}->[$x]->[$y] = $PARK; } sub grid { # get the value of a particular spot my $self = shift; my ($x, $y) = @_; if ($x < 0 || $x > ($self->{height} -1) || $y < 0 || $y > ($self-> +{width} -1)) { return undef; } return $self->{grid}->[$x]->[$y]; } sub str_status { # string of current stats my $self = shift; return sprintf("%d turns, %d parks, %d/%d houses built", $self->{bumps}, $self->{parks}, $self->{houses}, $self->{parcels}, ); } sub str_grid { # string of ASCII grid my $self = shift; my $str = ''; my $bar; for ((-1..$self->{width})) { $bar .= '-'; } $bar .= "\n"; $str .= $bar; for (my $i = 0; $i < $self->{height}; $i++) { $str .= '|'; for (my $j = 0; $j < $self->{width}; $j++) { $str .= $self->grid($i, $j); } $str .= "|\n"; } $str .= $bar; return $str; } sub bump { # increase the time by one, my $self = shift; my @coords; for (my $i = 0; $i < $self->{height}; $i++) { for (my $j = 0; $j < $self->{width}; $j++) { if ($self->needs_building($i, $j)) { push @coords, ($i, $j); } } } while (@coords) { my ($x, $y) = splice(@coords, 0, 2); $self->build($x, $y); } $self->{bumps}++; } sub bump_and_run { # bump until completion my $self = shift; my $last = -1; while ($self->free_parcels != 0 && $self->free_parcels != $last) { $last = $self->free_parcels; $self->bump; } } # This routine could use lots of optimization sub needs_building { # has two neighbors that are built my $self = shift; my ($x, $y) = @_; my $current = $self->grid($x, $y); return 0 if ($current eq $PARK || $current eq $BUILDING); my @coords = ($x , $y + 1, $x - 1, $y + 1, $x + 1, $y + 1, $x + 1, $y, $x - 1, $y, $x , $y - 1, $x - 1, $y - 1, $x + 1, $y - 1, ); my $neighbor_cnt = 0; while (@coords) { my ($i, $j) = splice(@coords, 0, 2); $neighbor_cnt += ($self->grid($i, $j) eq $BUILDING && 1); } return ($neighbor_cnt >= 2 && 1); } sub spoiler1 { # solution to the first (simple) puzzle my $p = shift; $p->park(11, 6); $p->park(10, 5); $p->park(9, 4); $p->park(9, 3); $p->park(9, 2); } 1;

In reply to Dr Dobbs November by jackdied

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.