Saw a challenge from the Pragmatic Bookshelf on Mastodon

Fill each column with the digits 1 through 6 without repeating or omitting digits so the sums in the right column are the total of each row. A digit may appear more than one across rows.

Because putting that matrix into alt-text is weird, here’s a table!

C1 C2 C3 C4 C5 TOTAL
6   5 2   19
1   3     10
1 3   11
4 5       24
3 2   4   18
6   5 4 23

This …

I suppose I have to say it.

This looks like a job for Recursion!

We’re placing a digit into a place in a matrix, proceeding, then testing at the end. We’re returning arrayrefs, and if we have the answer, it’ll be a full one. If not, `[]`.

I expand my habitual use of List::Util, adding `first` and `any` to my bag of tricks. `any` is a boolean, checking an array for anything. Here, `any { /x/ } @flatten` tests if any of the elements in `@flatten` contain the letter `x`. Within my code, I’m using `x` as the placeholder for unfilled elements, so we can tell if the matrix has unfilled spots.

We then fill the spots in the array, and I use `first` to find the indexes we need to fill, with `\$row = first { 'x' eq \$matrix->[\$_][\$col] } 1 .. 6`.

We’re supposed to have each column use the numbers 1 through 6, so I use hashes and `grep` to discern the numbers in each column not already used, and then only use those numbers to fill in the column. Because of this, I don’t have to test if the column are correct, because they can’t not be. I test by sum, which is kinda halfhearted.

I feel I should mention that you don’t need to make a program to solve this. After being hung up a bit, I put the numbers into a spreadsheet, used a few `=SUM()` lines because I didn’t want to do simple addition if I could get the computer to do it, and was able to discern the numbers by hand, then use that as a test set to make sure my recursion was working.

#### Show Me The Code!

``````#!/usr/bin/env perl

use strict;

use warnings;
use experimental qw{ say signatures state fc };

use List::Util qw{ any first sum0 };

my \$array = [

[qw{ 6 x 5 2 x }],
[qw{ 1 x 3 x x }],
[qw{ x x 1 3 x }],
[qw{ 4 5 x x x }],
[qw{ 3 2 x 4 x }],
[qw{ x 6 x 6 4 }],
];

my @totals = qw{19 10 11 24 18 23};
my \$matrix = fill_matrix( \$array, \@totals );
say 'OUTPUT';
say join "\n", map { join ' ', \$_->@* } \$matrix->@*;
exit;

sub fill_matrix ( \$matrix, \$totals, \$col = 0 ) {
my @flat = map { \$_->@* } \$matrix->@*;
if ( any { \$_ eq 'x' } @flat ) {
no warnings;
my @column = map { \$_->[\$col] } \$matrix->@*;
if ( !any { /x/ } @column ) {
return fill_matrix( \$matrix, \$totals, \$col + 1 );
}

my \$row = first { 'x' eq \$matrix->[\$_][\$col] } 1 .. 6;
my %list;
my @list = 1 .. 6;
\$list{\$_}++ for @column;
my @needed = grep { !\$list{\$_} } @list;
my \$copy;
for my \$i ( 0 .. -1 + scalar \$matrix->@* ) {
my @row = \$matrix->[\$i]->@*;
push \$copy->@*, \@row;
}

for my \$n (@needed) {
\$copy->[\$row][\$col] = \$n;
my \$return = fill_matrix( \$copy, \$totals, \$col );
return \$return if scalar \$return->@*;
}
return [];
}
else {
for my \$i ( 0 .. -1 + scalar \$matrix->[0]->@* ) {
my @col = map { \$matrix->[\$_][\$i] } 0 .. 5;
my \$sum = sum0 @col;
return [] if \$sum != 21;
}
for my \$i ( 0 .. 5 ) {
my \$t   = \$totals->[\$i];
my \$sum = sum0 \$matrix->[\$i]->@*;
if ( \$sum ne \$totals->[\$i] ) {
return [];
}
}
return \$matrix;
}
}
``````
``````\$ ./prag_array.pl
OUTPUT
6 1 5 2 5
1 3 3 1 2
2 4 1 3 1
4 5 4 5 6
3 2 6 4 3
5 6 2 6 4
``````