# Perl Weekly Challenge 152: St. Valentine’s sums and triangles

It is sad that, after more than two years of me doing Raku, I still don’t have any production code project to work on. Therefore, in order to keep my coding and Raku-ing (is that a term?) knowdledge, I try to solve every Perl Weekly Challenge tasks.

In the following, the assigned tasks for Challenge 152.

## PWC 152 - Task 1

The first task was very easy: given a nested array of integers, where each sub-array is a level of a triangle, compute the less expensive path from top to bottom.

``````sub MAIN() {
my @triangle = [ [1], [5,3], [2,3,4], [7,1,0,2], [6,4,5,2,8] ];
my \$sum += \$_.min for @triangle;
\$sum.say;
}
``````

Essentially, do make the sum of all the mins for every level of the triangle.

## PWC 152 - Task 2

Gosh, overlapping rectangles! It took me more time to understand the problem than to implement it. You are given two rectangles (by their opposite corners), and must compute the total area covered by the two, meaning that you need to compute the sum of the areas without double summing the overlapping one.
I decided to start it easy: design a `Point` and a `Rectangle` class:

``````class Point {
has Int \$.x;
has Int \$.y;
}

class Rectangle {
has Point \$.corner-left;
has Point \$.corner-right;

method area() {
abs( \$!corner-right.x - \$!corner-left.x )
* abs( \$!corner-right.y - \$!corner-left.y );
}

method overlapping-area( Rectangle \$r ) {
my \$left = Point.new( x => max( \$!corner-left.x, \$r.corner-left.x ),
y => max( \$!corner-left.y, \$r.corner-left.y )
);
my \$right = Point.new( x => min( \$!corner-right.x, \$r.corner-right.x ),
y => min( \$!corner-right.y, \$r.corner-right.y )
);

my Rectangle \$overlapping = Rectangle.new:
corner-left =>  \$left,
corner-right => \$right;
return \$overlapping.area;

}
}

``````

The `Rectangle` class has two interesting methods:
• `area` computes the current are of the rectangle;
• `overlapping-area` computes the overlapped area between the current rectangle and an overlapping one.
The last method considres the coordinates of the overlapping rectangles, and computes the area of such rectangle.
Therefore, the `MAIN` results in:

``````sub MAIN(  *@points where { @points.elems == 8 && @points.grep( * ~~ Int ) == @points.elems } ) {
my @corners;
@corners.push: Point.new( x => \$_[ 0 ].Int, y => \$_[ 1 ].Int ) for @points.rotor( 2 );
my Rectangle \$r1 = Rectangle.new: corner-left => @corners[ 0 ], corner-right => @corners[ 1 ];
my Rectangle \$r2 = Rectangle.new: corner-left => @corners[ 2 ], corner-right => @corners[ 3 ];

"{ \$r1.area + \$r2.area - \$r1.overlapping-area( \$r2 ) }".say;
}

``````

First of all, I build up the list of `Point`s out of the incoming list of arguments, then I build the two `Rectangle`s and sum the areas removing the `overlapping-area`.

The above program does not control that the two rectangles are effectively overlapping, thus in the case of non overlapping rectangles, the resulting area is totally wrong because it is subtracted by a piace of area that is not covered at all.
Fear not: it is easy to improve the situaion. First of all, I created an `is-overlapping` private method that checks if the two rectangles are overlapping. Then, the `overlapping-area` method is changed to ignore non overlapping rectangles:

``````class Rectangle {
has Point \$.corner-left;
has Point \$.corner-right;

method area() {
abs( \$!corner-right.x - \$!corner-left.x )
* abs( \$!corner-right.y - \$!corner-left.y );
}

method !is-overlapping( Rectangle \$r ) {
( \$!corner-left.x <= \$r.corner-left.x <= \$!corner-right.x
||  \$!corner-left.x <= \$r.corner-righ.x <= \$!corner-right.x )
&&
( \$!corner-left.y <= \$r.corner-left.y <= \$!corner-right.y
||  \$!corner-left.y <= \$r.corner-right.y <= \$!corner-right.y );

}

method overlapping-area( Rectangle \$r ) {
return 0 if ! self!is-overlapping( \$r );

my \$left = Point.new( x => max( \$!corner-left.x, \$r.corner-left.x ),
y => max( \$!corner-left.y, \$r.corner-left.y )
);
my \$right = Point.new( x => min( \$!corner-right.x, \$r.corner-right.x ),
y => min( \$!corner-right.y, \$r.corner-right.y )
);

my Rectangle \$overlapping = Rectangle.new:
corner-left =>  \$left,
corner-right => \$right;
return \$overlapping.area;

}
}

``````

### Using `map` instead of `push`

Is there room for another little improvement: using `map` to build up the list of corners.

``````sub MAIN(  *@points where { @points.elems == 8 && @points.grep( * ~~ Int ) == @points.elems } ) {
my @corners = @points.rotor( 2 ).map: { Point.new( x => \$_[ 0 ].Int, y => \$_[ 1 ].Int ) };
my Rectangle \$r1 = Rectangle.new: corner-left => @corners[ 0 ], corner-right => @corners[ 1 ];
my Rectangle \$r2 = Rectangle.new: corner-left => @corners[ 2 ], corner-right => @corners[ 3 ];

"{ \$r1.area + \$r2.area - \$r1.overlapping-area( \$r2 ) }".say;
}

``````

The `@corners` array is built on the fly using `map` on every couple of coordinates, and creating the respective `Point`.
The same trick can be applied to the building of `Rectangle`s, therefore the final `MAIN` results in:

``````sub MAIN(  *@points where { @points.elems == 8 && @points.grep( * ~~ Int ) == @points.elems } ) {
my @corners = @points.rotor( 2 ).map: { Point.new( x => \$_[ 0 ].Int, y => \$_[ 1 ].Int ) };
my Rectangle ( \$r1, \$r2 ) = @corners.rotor( 2 ).map: { Rectangle.new(
corner-left => \$_[ 0 ],
corner-right => \$_[ 1 ] ) };
"{ \$r1.area + \$r2.area - \$r1.overlapping-area( \$r2 ) }".say;
}

``````

The article Perl Weekly Challenge 152: St. Valentine's sums and triangles has been posted by Luca Ferrari on February 14, 2022