Point Range
with Raku

by Arne Sommer

Point Range with Raku

[358] Published 12. August 2025.

This is my response to The Weekly Challenge #334.

Challenge #334.1: Range Sum

You are given a list integers and pair of indices.

Write a script to return the sum of integers between the given indices (inclusive).

Example 1:
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
File: range-sum
#! /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

Challenge #334.2: Nearest Valid Point

You are given current location as two integers: x and y. You are also given a list of points on the grid.

A point is considered valid if it shares either the same x-coordinate or the same y-coordinate as the current location.

Write a script to return the index of the valid point that has the smallest 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.

The Manhattan distance between two points (x1, y1) and (x2, y2) is calculated as: |x1 - x2| + |y1 - y2|

Example 1:
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.