Perl Weekly Challenge: Week 36

I usually get very busy from December to January. As I don't really observe Christmas beyond shopping sales at the mall and eating a little more, I often volunteer to pick up the slack for those of my colleagues who do celebrate. As a result this weeks challenge attempt was rather mediocre. You will see many of the next several weekly challenges being entered weeks later and out of order.

Challenge 1:

Write a program to validate given Vehicle Identification Number (VIN). For more information, please checkout wikipedia.

It is possible to get a large amount of information out of a VIN but my scripts only do the most basic validation. This is the Perl version:

sub validateVIN {
    my ($vin) = @_;

    if (length $vin != 17) {
        return undef;
    }

    if ($vin !~ /^
        [[:alnum:]]{3} # World Manufacturer Identifier
        [[:alnum:]]{6} # Vehicle Descriptor Section
        [[:alnum:]]{8} # Vehicle Identifier Section
    $/msx) {
        return undef;
    }

    return 1;
}

say (validateVIN(shift // q{}) ? 'VALID' : 'INVALID');

(Full code on Github.)

...and this is Raku.

sub validateVIN(Str $vin) {

    if ($vin.chars != 17) {
        return False;
    }

    if $vin !~~ /^
        <alnum> ** 3 # World Manufacturer Identifier
        <alnum> ** 6 # Vehicle Descriptor Section
        <alnum> ** 8 # Vehicle Identifier Section
    $/ {
        return False;
    }

    return True;
}

multi sub MAIN(Str $vin) {

    say (validateVIN($vin) ?? 'VALID' !! 'INVALID');
}

(Full code on Github.)

Challenge 2:

Write a program to solve Knapsack Problem.

There are 5 color coded boxes with varying weights and amounts in GBP. Which boxes should be choosen to maximize the amount of money while still keeping the overall weight under or equal to 15 kgs?

  R: (weight = 1 kg, amount = £1)
  B: (weight = 1 kg, amount = £2)
  G: (weight = 2 kg, amount = £2)
  Y: (weight = 12 kg, amount = £4)
  P: (weight = 4 kg, amount = £10)

Bonus task, what if you were allowed to pick only 2 boxes or 3 boxes or 4 boxes? Find out which combination of boxes is the most optimal?

I didn't even do this one until March.

First I defined an array containing hashes, each of which describes what is known about a particular box.

my @boxes = (
    { name => 'R', weight => 1,  amount => 1  },
    { name => 'B', weight => 1,  amount => 2  },
    { name => 'G', weight => 2,  amount => 2  },
    { name => 'Y', weight => 12, amount => 4  },
    { name => 'P', weight => 4,  amount => 10 },
);

It makes sense to do the bonus task at the same time so I repeat the attempt for all combinations of 2 to 5 boxes.

for my $i (2 .. 5) {

I defined $max to keep track of the best choice found so far. It is a reference to a hash with the same format as the ones in @boxes.

    my $max = { name => q{}, weight => 0, amount => -1 };

Perl doesn't have a builtin function for combinations so I used a routine I had written for challenge 38 which I had actually completed before this one.

    for my $combo (combinations(\@boxes, $i)) {

For each combination, I added up the values of the constituent boxes storing the total in a reference to a hash of the same format as those used previously.

        my $total = { name => q{}, weight => 0, amount =>  0 };
        for my $box (@{$combo}) {
            $total->{name}   .= $box->{name};
            $total->{weight} += $box->{weight};
            $total->{amount} += $box->{amount};
        }

If the $total meets the weight criterion and the amount is greater than what we have previously seen, it becomes the new $max value.

        if ($total->{weight} <= 15 && $total->{amount} > $max->{amount}) {
            $max = $total;
        }
    }

Once all the combinations have been assessed, we can print the results. We must take into consideration that it might not be possible to meet the criterion with a particular number of boxes.

    if ($max->{amount} > -1) {
        say "For $i boxes, the best combination is $max->{name} which weighs ",
            "$max->{weight} kg and is valued £$max->{amount}.";
    } else {
        say "for $i boxes, it is not possible to meet the criteria.";
    }
}

(Full code on Github.)

This is the Raku version.

multi sub MAIN() {
    my @boxes = (
        { name => 'R', weight => 1,  amount => 1  },
        { name => 'B', weight => 1,  amount => 2  },
        { name => 'G', weight => 2,  amount => 2  },
        { name => 'Y', weight => 12, amount => 4  },
        { name => 'P', weight => 4,  amount => 10 },
    );

    for 2 .. 5 -> $i {
        my $max = { name => q{}, weight => 0,  amount => -1 };

        for @boxes.combinations($i) -> @combo {
            my $total = { name => q{}, weight => 0,  amount => 0 };
            for @combo -> $box {
                $total<name> ~= $box<name>;
                $total<weight> += $box<weight>;
                $total<amount> += $box<amount>;
            }
            if $total<weight> <= 15 && $total<amount> > $max<amount> {
                $max = $total;
            }
        }
        if $max<amount> > -1 {
            say "For $i boxes, the best combination is $max<name> which weighs",
                " $max<weight> kg and is valued £$max<amount>.";
        } else {
            say "for $i boxes, it is not possible to meet the criteria.";
        }
    }
}

(Full code on Github.)