This is my response to The Weekly Challenge #334.
Input: @ints = (-2, 0, 3, -5, 2, -1), $x = 0, $y = 2
Output: 1
Elements between indices (0, 2) => (-2, 0, 3)
Range Sum: (-2) + 0 + 3 => 1
Example 2:
Input: @ints = (1, -2, 3, -4, 5), $x = 1, $y = 3
Output: -3
Elements between indices (1, 3) => (-2, 3, -4)
Range Sum: (-2) + 3 + (-4) => -3
Example 3:
Input: @ints = (1, 0, 2, -1, 3), $x = 3, $y = 4
Output: 2
Elements between indices (3, 4) => (-1, 3)
Range Sum: (-1) + 3 => 2
Example 4:
Input: @ints = (-5, 4, -3, 2, -1, 0), $x = 0, $y = 3
Output: -2
Elements between indices (0, 3) => (-5, 4, -3, 2)
Range Sum: (-5) + 4 + (-3) + 2 => -2
Example 5:
Input: @ints = (-1, 0, 2, -3, -2, 1), $x = 0, $y = 2
Output: 1
Elements between indices (0, 2) => (-1, 0, 2)
Range Sum: (-1) + 0 + 2 => 1
#! /usr/bin/env raku
unit sub MAIN (*@ints where @ints.elems > 0 && all(@ints) ~~ Int, # [1]
UInt :$x, # [2]
UInt :$y where $x <= $y <= @ints.end, # [3]
:v(:$verbose));
say ": ({ @ints[$x .. $y].join(", ") }).sum" if $verbose;
say @ints[$x .. $y].sum; # [4]
[1] Ensure at least one input value, all of which must be integers.
[2] The x
value as a mandatory named argument, of the
Unsigned Int UInt
type.
See docs.raku.org/type/UInt for more information about the UInt
type.
[3] The y
value, as [2], but additionally larger or
equal to x
, and smaller or equal to the upper limit (the last
index in the array, obtained with end
).
See docs.raku.org/routine/end for more information about end
.
[4] Get the values with an array slice (given the start and
end indices), and apply sum
to get the sum.
See docs.raku.org/routine/sum for more information about sum
.
Running it (and note the --
to mark the end of the options in
three of the examples, so that the next item is taken as a negative
value instead of a command line option:
$ ./range-sum -x=0 -y=2 -- -2 0 3 -5 2 -1
1
$ ./range-sum -x=1 -y=3 1 -2 3 -4 5
-3
$ ./range-sum -x=3 -y=4 1 0 2 -1 3
2
$ ./range-sum -x=0 -y=3 -- -5 4 -3 2 -1 0
-2
$ ./range-sum -x=0 -y=2 -- -1 0 2 -3 -2 1
1
Looking good.
With verbose mode:
$ ./range-sum -v -x=0 -y=2 -- -2 0 3 -5 2 -1
: (-2, 0, 3).sum
1
$ ./range-sum -v -x=1 -y=3 1 -2 3 -4 5
: (-2, 3, -4).sum
-3
$ ./range-sum -v -x=3 -y=4 1 0 2 -1 3
: (-1, 3).sum
2
$ ./range-sum -v -x=0 -y=3 -- -5 4 -3 2 -1 0
: (-5, 4, -3, 2).sum
-2
$ ./range-sum -v -x=0 -y=2 -- -1 0 2 -3 -2 1
: (-1, 0, 2).sum
1
x
and y
. You are
also given a list of points on the grid.
x-coordinate
or the same y-coordinate
as the current location.
Manhattan distance
to the current location. If multiple valid points
are tied for the smallest distance, return the one with the lowest
index. If no valid points exist, return -1
.
Manhattan distance
between two points (x1, y1)
and
(x2, y2)
is calculated as: |x1 - x2| + |y1 - y2|
Input: $x = 3, $y = 4, @points ([1, 2], [3, 1], [2, 4], [2, 3])
Output: 2
Valid points: [3, 1] (same x), [2, 4] (same y)
Manhattan distances:
[3, 1] => |3-3| + |4-1| = 3
[2, 4] => |3-2| + |4-4| = 1
Closest valid point is [2, 4] at index 2.
Example 2:
Input: $x = 2, $y = 5, @points ([3, 4], [2, 3], [1, 5], [2, 5])
Output: 3
Valid points: [2, 3], [1, 5], [2, 5]
Manhattan distances:
[2, 3] => 2
[1, 5] => 1
[2, 5] => 0
Closest valid point is [2, 5] at index 3.
Example 3:
Input: $x = 1, $y = 1, @points ([2, 2], [3, 3], [4, 4])
Output: -1
No point shares x or y with (1, 1).
Example 4:
Input: $x = 0, $y = 0, @points ([0, 1], [1, 0], [0, 2], [2, 0])
Output: 0
Valid points: all of them
Manhattan distances:
[0, 1] => 1
[1, 0] => 1
[0, 2] => 2
[2, 0] => 2
Tie between index 0 and 1, pick the smaller index: 0
Example 5:
Input: $x = 5, $y = 5, @points ([5, 6], [6, 5], [5, 4], [4, 5])
Output: 0
Valid points: all of them
[5, 6] => 1
[6, 5] => 1
[5, 4] => 1
[4, 5] => 1
All tie, return the one with the lowest index: 0
File: nearest-valid-point
unit sub MAIN (*@points where @points.elems > 0
&& all(@points) ~~ /^\d+\,\d+$/, # [1]
Int :$x, # [2]
Int :$y, # [2a]
:v(:$verbose));
my $smallest = Inf; # [3]
my $smallest-idx = -1; # [4]
for ^@points.elems -> $index # [5]
{
my ($x1, $y1) = @points[$index].split(",")>>.Int; # [6]
next unless $x == $x1 || $y == $y1; # [7]
my $distance = ($x - $x1).abs + ($y - $y1).abs; # [8]
print ": index $index: [$x1, $y1] => $distance" if $verbose;
if $distance < $smallest # [9]
{
$smallest = $distance; # [9a]
$smallest-idx = $index; # [9b]
say " - New smallest Manhattan Distance" if $verbose;
}
elsif $verbose
{
say "";
}
}
say $smallest-idx; # [10]
[1] The points, as comma separated pairs of unsigned integers, with at least one.
[2] The x
and y
values as named arguments.
[3] We are looking for the smallest distance (value), so start with something big to bootstrap the loop.
[4] The index of the (first) smallest distance. If the loop does not find a distance at all, this value will be printed - as specified in the challenge text.
[5] Iterate over the indices of the points, as we are looking for the index of the shortest distance.
[6] Get the x
and y
parts of the point with the current
index.
[7] Skip points that do not have the same x
or y
value
as the current location.
[8] Calculate the distance. Note the use of abs
to get
rid of the sign, if negative.
See docs.raku.org/routine/abs for more information about abs
.
[9] Do we have a smaller distance than what we had before? (The
before value is Inf
the very first time we get here,
and the condition will be met.) Register the distance [9a] and
index [9b].
[10] Print the index.
Running it:
$ ./nearest-valid-point -x=3 -y=4 "1,2" "3,1" "2,4" "2,3"
2
$ ./nearest-valid-point -x=2 -y=5 "3,4" "2,3" "1,5" "2,5"
3
$ ./nearest-valid-point -x=1 -y=1 "2,2" "3,3" "4,4"
-1
$ ./nearest-valid-point -x=0 -y=0 "0,1" "1,0" "0,2" "2,0"
0
$ ./nearest-valid-point -x=5 -y=5 "5,6" "6,5" "5,4" "4,5"
0
Looking good.
With verbose mode:
$ ./nearest-valid-point -v -x=3 -y=4 "1,2" "3,1" "2,4" "2,3"
: index 1: [3, 1] => 3 - New smallest Manhattan Distance
: index 2: [2, 4] => 1 - New smallest Manhattan Distance
2
$ ./nearest-valid-point -v -x=2 -y=5 "3,4" "2,3" "1,5" "2,5"
: index 1: [2, 3] => 2 - New smallest Manhattan Distance
: index 2: [1, 5] => 1 - New smallest Manhattan Distance
: index 3: [2, 5] => 0 - New smallest Manhattan Distance
3
$ ./nearest-valid-point -v -x=1 -y=1 "2,2" "3,3" "4,4"
-1
$ ./nearest-valid-point -v -x=0 -y=0 "0,1" "1,0" "0,2" "2,0"
: index 0: [0, 1] => 1 - New smallest Manhattan Distance
: index 1: [1, 0] => 1
: index 2: [0, 2] => 2
: index 3: [2, 0] => 2
0
$ ./nearest-valid-point -v -x=5 -y=5 "5,6" "6,5" "5,4" "4,5"
: index 0: [5, 6] => 1 - New smallest Manhattan Distance
: index 1: [6, 5] => 1
: index 2: [5, 4] => 1
: index 3: [4, 5] => 1
0
And that's it.