Perl Weekly Challenge 293.

My solutions (task 1 and task 2 ) to the The Weekly Challenge - 293.

Task 1: Similar Dominos

Submitted by: Mohammad Sajid Anwar
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]

I first sort each domino so that it is represented in some canonical way, with the lowest number first and the largest number second. Then I sort the set of dominoes on the first and then the second number. Finally I increase a counter whenever I find two indentical consecutive dominoes. I increment it additionally when I enter a group of two or more equivalent dominoes. I use PDL to simplify the input and the comparisons. The result fits a two-liner.

Examples:

perl -MPDL -MPDL::NiceSlice -E '
for(@ARGV){@d=sort{$a((0))<=>$b((0))||$a((1))<=>$b((1))}pdl($_)->qsort->dog;$n=0;$c=0;
for(0..@d-2){if(($d[$_]==$d[$_+1])->all){++$n if !$c;++$c;++$n;}else{$c=0;}}say "$_ -> $n";}
' "[ [1 3][3 1][2 4][6 8] ]" "[ [1 2][2 1][1 1][1 2][2 2] ]"

Results:

[ [1 3][3 1][2 4][6 8] ] -> 2
[ [1 2][2 1][1 1][1 2][2 2] ] -> 3

The full code is:

 1  # Perl weekly challenge 293
 2  # Task 1:  Similar Dominos
 3  #
 4  # See https://wlmb.github.io/2024/10/28/PWC293/#task-1-similar-dominos
 5  use v5.36;
 6  use PDL;
 7  use PDL::NiceSlice;
 8  die <<~"FIN" unless @ARGV;
 9      Usage: $0 D1 D2...
10      to find how many similar dominoes are in the set D1 D2...
11      Di is a string of the form [[a1 b1][a2 b2][a3 b3]...] where
12      each pair [an bn] represent a single domino.
13      FIN
14  for(@ARGV){
15      my @dominoes = sort {$a((0)) <=> $b((0)) || $a((1))<=>$b((1))} pdl($_)->qsort->dog;
16      my $similar = 0;
17      my $in_group = 0;
18      for(0..@dominoes-2){
19          if(($dominoes[$_]==$dominoes[$_+1])->all){
20              ++$similar if !$in_group;
21              ++$in_group;
22              ++$similar;
23          } else {
24              $in_group=0;
25          }
26      }
27      say "$_ -> $similar";
28  }

Example:

./ch-1.pl "[ [1 3][3 1][2 4][6 8] ]" "[ [1 2][2 1][1 1][1 2][2 2] ]"

Results:

[ [1 3][3 1][2 4][6 8] ] -> 2
[ [1 2][2 1][1 1][1 2][2 2] ] -> 3

Task 2: Boomerang

Submitted by: Mohammad Sajid Anwar
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

Three points p, q and r are in a straight line if the area of the triangle pqr is zero, i.e., if the cross product (q-p) x (r-p) is zero. As the cross product is defined in 3D, I can instead use the determinant of the matrix with rows q-p and r-p. If the points are not all distinct, they would be in a straight line, so there is no need for an additional test. I use PDL to manipulate the vectors, yielding a one-liner.

Examples:

perl -MPDL -MPDL::NiceSlice -E '
for(@ARGV){$m=pdl($_);$m(:,1:2)-=$m(:,0);say "$_ -> ",det($m(:,1:2))?"True":"False";}
' "[ [1 1][2 3][3 2] ]" "[ [1 1][2 2][3 3] ]" "[ [1 1][1 2][2 3] ]" \
  "[ [1 1][1 2][1 3] ]" "[ [1 1][2 1][3 1] ]" "[ [0 0][2 3][4 5] ]"

Results:

[ [1 1][2 3][3 2] ] -> True
[ [1 1][2 2][3 3] ] -> False
[ [1 1][1 2][2 3] ] -> True
[ [1 1][1 2][1 3] ] -> False
[ [1 1][2 1][3 1] ] -> False
[ [0 0][2 3][4 5] ] -> True

The full code is:

 1  # Perl weekly challenge 293
 2  # Task 2:  Boomerang
 3  #
 4  # See https://wlmb.github.io/2024/10/28/PWC293/#task-2-boomerang
 5  use v5.36;
 6  use PDL;
 7  use PDL::NiceSlice;
 8  
 9  die <<~"FIN" unless @ARGV;
10      Usage: $0 M1 M2...
11      to check if the three 2D row vectors forming each matrix M1 M2...
12      form a boomerang. The matrices should be strings in the format "[[x0 y0][x1 y1][x2 y2]]"
13      with xi, yi numbers.
14      FIN
15  
16  for(@ARGV){
17      my $matrix=pdl($_);
18      say("Wrong format $m->info: $_"), next unless $m->dim(0)==2 && $->dim(1)==3;
19      my $first=$matrix(:,(0));
20      my $rest=$matrix(:,1:-1);
21      $rest -= $first; # Translate first vector to origin
22      my $determinant = $rest->det;
23      my $result = $determinant != 0? "True" : "False";
24      say "$_ -> $result";
25  }

Example:

./ch-2.pl "[ [1 1][2 3][3 2] ]" "[ [1 1][2 2][3 3] ]" "[ [1 1][1 2][2 3] ]" \
          "[ [1 1][1 2][1 3] ]" "[ [1 1][2 1][3 1] ]" "[ [0 0][2 3][4 5] ]"

Results:

[ [1 1][2 3][3 2] ] -> True
[ [1 1][2 2][3 3] ] -> False
[ [1 1][1 2][2 3] ] -> True
[ [1 1][1 2][1 3] ] -> False
[ [1 1][2 1][3 1] ] -> False
[ [0 0][2 3][4 5] ] -> True

/;

Written on October 28, 2024