Peter’s solutions: week 101 — 22 February 2021

THE WEEKLY CHALLENGE
Spiral triangle

The Perl Camel

Task 1

Pack a spiral

You are given an array @A of items (integers say, but they can be anything). Your task is to pack that array into an MxN matrix spirally counterclockwise, as tightly as possible.

‘Tightly’ means the absolute value |M-N| of the difference has to be as small as possible.

Examples


Input: @A = (1,2,3,4)
Output:
    4 3
    1 2

Since the given array is already a 1x4 matrix on its own, but that's not as tight as possible. Instead, you'd spiral it counterclockwise. 4 3 1 2 Example 2: Input: @A = (1..6) Output: 6 5 4 1 2 3 or 5 4 6 3 1 2
Either will do as an answer, because they're equally tight. Example 3: Input: @A = (1..12) Output: 9 8 7 6 10 11 12 5 1 2 3 4 or 8 7 6 9 12 5 10 11 4 1 2 3

Analysis

This is a challenge which is easy to explain but not so easy to translate into Perl.

My best attempt goes like this:

First search the factors of @A to find the closest together $factor and (scalar @A) / $factor; these will be the height and width of the matrix. If scalar @A is prime, those will be 1 and scalar @A.

First, as instructed, put $A[0] in the bottom left corner of the matrix.

Proceed straight ahead ('east') filling cells until you hit either the edge of the matrix, or a filled cell, in which case turn left and continue until all the cells are filled.

That sounds easy, but both 'straight ahead' and 'turn left' imply keeping track of the current and previous filled cells to know which direction is straight ahead. which in turn calls for a choice among 8 cases. In production code I might try to optimise that, but for even a 10,000 member array my solution completes in milliseconds.

Try it 

Try running the script with any input:



example: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12

Script


#!/usr/bin/perl

# Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge

use v5.26;    # The Weekly Challenge - 2021-02-22
use utf8;     # Week 101 - task 1 - Pack a spiral
use warnings; # Peter Campbell Smith
binmode STDOUT, ':utf8';
use Encode;

pack_a_spiral(1..6);
pack_a_spiral(1..13);
pack_a_spiral(1..50);
pack_a_spiral(1..99);
pack_a_spiral(1..169);

sub pack_a_spiral {
    
    my (@z, $w, $h, $count, $width, $height, @factors, $diff, $x, $y, @matrix, $j, $cw, $mov);
    
    # initialise
    @z = @_;
    $count = @z;
    
    # deduce closest height and width
    for $j (1 .. $count) {
        push @factors, $j if $count / $j == int($count / $j);
    }
    $diff = 1e6;
    for $h (@factors) {
        $w = $count / $h;
        last if $h > $w;
        if (abs($w - $h) < $diff) {
            $width = $w;
            $height = $h;
            $diff = abs($w - $h);
        }
    }
    
    # insert values in spiral
    $x = 0;
    $y = $height - 1;
    $cw = 0;
    $mov = 'E';
    B: for $j (0 .. $width * $height - 1) {
        $matrix[$x][$y] = $z[$j];
        $cw = length($z[$j]) if length($z[$j]) > $cw;
        last unless @z;
        
        # cases where spiral continues in same direction
        if ($mov eq 'E' and $x != $width - 1 and not defined $matrix[$x + 1][$y]) {
            $x ++;  # keep moving east
        } elsif ($mov eq 'N' and $y != 0 and not defined $matrix[$x][$y - 1]) {
            $y --;  # keep moving north
        } elsif ($mov eq 'W' and $x != 0 and not defined $matrix[$x - 1][$y]) {
            $x --;  # keep moving west
        } elsif ($mov eq 'S' and $y != $height - 1 and not defined $matrix[$x][$y + 1]) {
            $y ++;  # keep moving south
            
        # cases where it turns a corner
        } elsif ($x != $width - 1 and not defined $matrix[$x + 1][$y]) {
            $x ++; $mov = 'E';  # move east
        } elsif ($y != 0 and not defined $matrix[$x][$y - 1]) {
            $y --; $mov = 'N';  # move north
        } elsif ($x != 0 and not defined $matrix[$x - 1][$y]) {
            $x --; $mov = 'W';  # move west
        } else {
            $y ++; $mov = 'S';  # move south
        }
    }
    # display
    say qq[\nInput: $count];
    say qq[Output: dimensions $width × $height];
    for $y (0 .. $height - 1) {
        for $x (0 .. $width - 1) {
            printf (qq[%${cw}s ], $matrix[$x][$y]);
        }
        say '';
    }
}

44 lines of code
Completed after the closing date and not submitted to GitHub

Output


Input: 6
Output: dimensions 3 × 2
6 5 4
1 2 3

Input: 13
Output: dimensions 13 × 1
 1  2  3  4  5  6  7  8  9 10 11 12 13

Input: 50
Output: dimensions 10 × 5
23 22 21 20 19 18 17 16 15 14
24 43 42 41 40 39 38 37 36 13
25 44 45 46 47 48 49 50 35 12
26 27 28 29 30 31 32 33 34 11
 1  2  3  4  5  6  7  8  9 10

Input: 99
Output: dimensions 11 × 9
29 28 27 26 25 24 23 22 21 20 19
30 59 58 57 56 55 54 53 52 51 18
31 60 81 80 79 78 77 76 75 50 17
32 61 82 95 94 93 92 91 74 49 16
33 62 83 96 97 98 99 90 73 48 15
34 63 84 85 86 87 88 89 72 47 14
35 64 65 66 67 68 69 70 71 46 13
36 37 38 39 40 41 42 43 44 45 12
 1  2  3  4  5  6  7  8  9 10 11

Input: 169
Output: dimensions 13 × 13
 37  36  35  34  33  32  31  30  29  28  27  26  25
 38  79  78  77  76  75  74  73  72  71  70  69  24
 39  80 113 112 111 110 109 108 107 106 105  68  23
 40  81 114 139 138 137 136 135 134 133 104  67  22
 41  82 115 140 157 156 155 154 153 132 103  66  21
 42  83 116 141 158 167 166 165 152 131 102  65  20
 43  84 117 142 159 168 169 164 151 130 101  64  19
 44  85 118 143 160 161 162 163 150 129 100  63  18
 45  86 119 144 145 146 147 148 149 128  99  62  17
 46  87 120 121 122 123 124 125 126 127  98  61  16
 47  88  89  90  91  92  93  94  95  96  97  60  15
 48  49  50  51  52  53  54  55  56  57  58  59  14
  1   2   3   4   5   6   7   8   9  10  11  12  13

 

Any content of this website which has been created by Peter Campbell Smith is in the public domain