Spiral matrix
Object-oriented Solution
Suppose we set up a Turtle class like this:
class Turtle {
my @dv = [0,-1], [1,-1], [1,0], [1,1], [0,1], [-1,1], [-1,0], [-1,-1];
my $points = 8; # 'compass' points of neighbors on grid: north=0, northeast=1, east=2, etc.
has @.loc = 0,0;
has $.dir = 0;
has %.world;
has $.maxegg;
has $.range-x;
has $.range-y;
method turn-left ($angle = 90) { $!dir -= $angle / 45; $!dir %= $points; }
method turn-right($angle = 90) { $!dir += $angle / 45; $!dir %= $points; }
method lay-egg($egg) {
%!world{~@!loc} = $egg;
$!maxegg max= $egg;
$!range-x minmax= @!loc[0];
$!range-y minmax= @!loc[1];
}
method look($ahead = 1) {
my $there = @!loc »+« @dv[$!dir] »*» $ahead;
%!world{~$there};
}
method forward($ahead = 1) {
my $there = @!loc »+« @dv[$!dir] »*» $ahead;
@!loc = @($there);
}
method showmap() {
my $form = "%{$!maxegg.chars}s";
my $endx = $!range-x.max;
for $!range-y.list X $!range-x.list -> ($y, $x) {
print (%!world{"$x $y"} // '').fmt($form);
print $x == $endx ?? "\n" !! ' ';
}
}
}
# Now we can build the spiral in the normal way from outside-in like this:
sub MAIN(Int $size = 5) {
my $t = Turtle.new(dir => 2);
my $counter = 0;
$t.forward(-1);
for 0..^ $size -> $ {
$t.forward;
$t.lay-egg($counter++);
}
for $size-1 ... 1 -> $run {
$t.turn-right;
$t.forward, $t.lay-egg($counter++) for 0..^$run;
$t.turn-right;
$t.forward, $t.lay-egg($counter++) for 0..^$run;
}
$t.showmap;
}
Or we can build the spiral from inside-out like this:
sub MAIN(Int $size = 5) {
my $t = Turtle.new(dir => ($size %% 2 ?? 4 !! 0));
my $counter = $size * $size;
while $counter {
$t.lay-egg(--$counter);
$t.turn-left;
$t.turn-right if $t.look;
$t.forward;
}
$t.showmap;
}
Note that with these "turtle graphics" we don't actually have to care about the coordinate system, since the showmap
method can show whatever rectangle was modified by the turtle. So unlike the standard inside-out algorithm, we don't have to find the center of the matrix first.
Procedural Solution
sub spiral_matrix ( $n ) {
my @sm;
my $len = $n;
my $pos = 0;
for ^($n/2).ceiling -> $i {
my $j = $i + 1;
my $e = $n - $j;
@sm[$i ][$i + $_] = $pos++ for ^( $len); # Top
@sm[$j + $_][$e ] = $pos++ for ^(--$len); # Right
@sm[$e ][$i + $_] = $pos++ for reverse ^( $len); # Bottom
@sm[$j + $_][$i ] = $pos++ for reverse ^(--$len); # Left
}
return @sm;
}
say .fmt('%3d') for spiral_matrix(5);
Output:
0 1 2 3 4
15 16 17 18 5
14 23 24 19 6
13 22 21 20 7
12 11 10 9 8