Very literally and verbosely so you can see what it's doing:
#!/usr/bin/perl
#
# Matrix Spiral
#
use strict;
use warnings;
#
# Size of input matrix in columns and rows
#
my $cols = 7;
my $rows = 3;
#
# Decrement rows & columns because we're using them as array indices
# (i.e. counting from 0 not 1)
#
$cols--;
$rows--;
#
# Populate a list of elements to feed the matrix from
#
my @elements;
while (<DATA>) {
#
# Display data to screen so we can compare results to it
#
print;
push @elements, (split(' ', $_));
}
#
# Keep the size of elements for after we've shifted it empty
#
my $count = @elements;
#
# Populate a matrix of the elements
# which pacman can then walk around & eat pill by pill...
#
my @matrix;
for my $row (0 .. $rows ) {
for my $col (0 .. $cols ) {
$matrix[$col][$row] = shift(@elements);
}
}
my @output;
my $x = 0; # X position in matrix
my $y = 0; # Y position in matrix
my $v = 0; # Vertical direction
my $h = 1; # Horizontal direction
my $row_s = 0; # Current Row start
my $row_e = $rows; # Current Row end
my $col_s = 0; # Current Column start
my $col_e = $cols; # Current Column end
for my $place ( 0 .. $count-1 ) {
#
# Show X and Y coordinate in bounds "min<coord<max"
# and the value at that coordinate in [ ] so we can see it work
#
print "$place x[$col_s<$x<$col_e] y[$row_s<$y<$row_e]",
" [$matrix[$x][$y]]\n";
#
# Push the element at that matrix coordinate into the output list
#
$output[$place] = $matrix[$x][$y];
if ( $h == 1 && $v == 0 && $x == $col_e && $y == $row_s ) {
print "> reached last col. go v\n";
$h = 0; # Stop horizontal movement (was right)
$v = 1; # Go down
# Finished highest remaining row
# so remove it from matrix bounds
$row_s++;
}
elsif ( $h == 0 && $v == 1 && $x == $col_e && $y == $row_e ) {
print "v reached last row. go <\n";
$h = -1; # Go left
$v = 0; # Stop vertical movement (was down)
# Finished rightmost remaining column
# so remove it from matrix bounds
$col_e--;
}
elsif ( $h == -1 && $v == 0 && $x == $col_s && $y == $row_e ) {
print "< reached first col. go ^\n";
$h = 0; # Stop horizontal movement (was left)
$v = -1; # Go up
# Finished lowest remaining row
# so remove it from matrix bounds
$row_e--;
}
elsif ( $h == 0 && $v == -1 && $x == $col_s && $y == $row_s ) {
print "^ reached first row. go >\n";
$h = 1; # Go right
$v = 0; # Stop vertical movement (was up)
# Finished leftmost remaining column
# so remove it from matrix bounds
$col_s++;
}
#
# Increment (or decrement) matrix position pointer
#
$x += $h;
$y += $v;
}
print join($/, @output), $/;
__DATA__
a b c d e f g
h i j k l m n
o p q r s t u
Try it with:
my $cols = 26;
my $rows = 26;
at the top and
__DATA__
aa ab ac ad ae af ag ah ai aj ak al am an ao ap aq ar as at au av aw a
+x ay az
ba bb bc bd be bf bg bh bi bj bk bl bm bn bo bp bq br bs bt bu bv bw b
+x by bz
ca cb cc cd ce cf cg ch ci cj ck cl cm cn co cp cq cr cs ct cu cv cw c
+x cy cz
da db dc dd de df dg dh di dj dk dl dm dn do dp dq dr ds dt du dv dw d
+x dy dz
ea eb ec ed ee ef eg eh ei ej ek el em en eo ep eq er es et eu ev ew e
+x ey ez
fa fb fc fd fe ff fg fh fi fj fk fl fm fn fo fp fq fr fs ft fu fv fw f
+x fy fz
ga gb gc gd ge gf gg gh gi gj gk gl gm gn go gp gq gr gs gt gu gv gw g
+x gy gz
ha hb hc hd he hf hg hh hi hj hk hl hm hn ho hp hq hr hs ht hu hv hw h
+x hy hz
ia ib ic id ie if ig ih ii ij ik il im in io ip iq ir is it iu iv iw i
+x iy iz
ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju jv jw j
+x jy jz
ka kb kc kd ke kf kg kh ki kj kk kl km kn ko kp kq kr ks kt ku kv kw k
+x ky kz
la lb lc ld le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw l
+x ly lz
ma mb mc md me mf mg mh mi mj mk ml mm mn mo mp mq mr ms mt mu mv mw m
+x my mz
na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv nw n
+x ny nz
oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ot ou ov ow o
+x oy oz
pa pb pc pd pe pf pg ph pi pj pk pl pm pn po pp pq pr ps pt pu pv pw p
+x py pz
qa qb qc qd qe qf qg qh qi qj qk ql qm qn qo qp qq qr qs qt qu qv qw q
+x qy qz
ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs rt ru rv rw r
+x ry rz
sa sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv sw s
+x sy sz
ta tb tc td te tf tg th ti tj tk tl tm tn to tp tq tr ts tt tu tv tw t
+x ty tz
ua ub uc ud ue uf ug uh ui uj uk ul um un uo up uq ur us ut uu uv uw u
+x uy uz
va vb vc vd ve vf vg vh vi vj vk vl vm vn vo vp vq vr vs vt vu vv vw v
+x vy vz
wa wb wc wd we wf wg wh wi wj wk wl wm wn wo wp wq wr ws wt wu wv ww w
+x wy wz
xa xb xc xd xe xf xg xh xi xj xk xl xm xn xo xp xq xr xs xt xu xv xw x
+x xy xz
ya yb yc yd ye yf yg yh yi yj yk yl ym yn yo yp yq yr ys yt yu yv yw y
+x yy yz
za zb zc zd ze zf zg zh zi zj zk zl zm zn zo zp zq zr zs zt zu zv zw z
+x zy zz
at the bottom, that way it's easier to see what it's doing because the letters correspond to columns and rows.
Also with such a large list you might want to format the output with:
my $place;
for my $row (0 .. $rows) {
for my $col (0 .. $cols) {
print $output[$place++], " ";
}
print $/;
}
instead of:
print join($/, @output), $/;