Perl Weekly Challenge: Week 293

Sal Mubarak! Happy Gujarati New Year for VS 2081.

Challenge 1:

Similar Dominos

You are given a list of dominos, @dominos.

Write a script to return the number of dominoes that are similar to any other domino.

$dominos[i] = [a, b] and $dominos[j] = [c, d] are same if either (a = c and b = d) or (a = d and b = c).

Example 1
Input: @dominos = ([1, 3], [3, 1], [2, 4], [6, 8])
Output: 2

Similar Dominos: $dominos[0], $dominos[1]
Example 2
Input: @dominos = ([1, 2], [2, 1], [1, 1], [1, 2], [2, 2])
Output: 3

Similar Dominos: $dominos[0], $dominos[1], $dominos[3]

We have gone a long time without a one-liner. This solution stretches the definition a bit but it's close enough. There are three separate statements so I shall describe them separately.

We are going to need to store data in a hash.

my %a;

The domino data is taken from the command-line as a series of arguments where each one is a string consisting of two numbers separated by spaces. So for example 2, the arguments '1 2' '2 1' '1 1' '1 2' '2 2' This line splits them up, sorts the two numbers in ascending order and then joins them up again. This is so the joined numbers can be used as keys for the hash. The values of the hash will be the number of times each key occurs.

 @*ARGS.map({[.split(q{ })].sort.join}).map({%a{$_}++});

Finally, we just need to find the biggest value in the hash and print it.

 %a.values.max.say

(Full code on Github.)

The Perl version is also pretty concise.

my %a; for (map{join q{}, sort split q{ }} @ARGV){$a{$_}++}; say [sort values %a]->[-1]

(Full code on Github.)

Challenge 2:

Boomerang

You are given an array of points, (x, y).

Write a script to find out if the given points are a boomerang.

A boomerang is a set of three points that are all distinct and not in a straight line.

Example 1
Input: @points = ( [1, 1], [2, 3], [3,2] )
Output: true
Example 2
Input: @points = ( [1, 1], [2, 2], [3, 3] )
Output: false
Example 3
Input: @points = ( [1, 1], [1, 2], [2, 3] )
Output: true
Example 4
Input: @points = ( [1, 1], [1, 2], [1, 3] )
Output: false
Example 5
Input: @points = ( [1, 1], [2, 1], [3, 1] )
Output: false
Example 6
Input: @points = ( [0, 0], [2, 3], [4, 5] )
Output: true

The MAIN() function is simple.

First we transform the command-line arguments (in the same format as in challenge 1) in to a list of two-element arrays.

    my @points = @args.map({ .split(q{ }) });

Then we feed that list to the isBoomerang() function which does all the work and returns true or false which is then printed out.

    say isBoomerang(@points);

This is the isBoomerang() function.

sub isBoomerang(@points) {

First we split the points back up into individual x and y co-ordinates.

    my ($x1, $y1) = @points[0];
    my ($x2, $y2) = @points[1];
    my ($x3, $y3) = @points[2];

We caa use these values to check if any of the points are in the same location because then obviously they wouldn't be a boomerang.

    if ($x1 == $x2 && $y1 == $y2) ||
    ($x1 == $x3 && $y1 == $y3) ||
    ($x2 == $x3 && $y2 == $y3) {
        return False ;
    }

What is a boomerang actually? Really, it's just a plain old triangle. We can use the co-ordinates to try and find the area of the triangle. If this is 0, the points must be collinear ("in a straight lines" as we non-mathematicians would say.) It is not a triangle and therefore not a boomerang.

    my $area = $x1 * ($y2 - $y3) + $x2 * ($y3 - $y1) + $x3 * ($y1 - $y2);
    return $area != 0;
}

(Full code on Github.)

The Perl version works exactly the same way.

sub isBoomerang(@points) {
    my ($x1, $y1) = @{$points[0]};
    my ($x2, $y2) = @{$points[1]};
    my ($x3, $y3) = @{$points[2]};

    if (
        ($x1 == $x2 && $y1 == $y2) ||
        ($x1 == $x3 && $y1 == $y3) ||
        ($x2 == $x3 && $y2 == $y3)
    ) {
        return false;
    }

    my $area = $x1 * ($y2 - $y3) + $x2 * ($y3 - $y1) + $x3 * ($y1 - $y2);
    return $area != 0;
}

my @points = map { [split q{ }] } @ARGV;
say isBoomerang(@points) ? 'true' : 'false';

(Full code on Github.)