perl logo Perl logo (Thanks to Olaf Alders)

The weekly challenge 334 - Task 2: Nearest Valid Point

  1 #!/usr/bin/env perl
  2 # https://theweeklychallenge.org/blog/perl-weekly-challenge-334/#TASK2
  3 #
  4 # Task 2: Nearest Valid Point
  5 # ===========================
  6 #
  7 # You are given current location as two integers: x and y. You are also given a
  8 # list of points on the grid.
  9 #
 10 # A point is considered valid if it shares either the same x-coordinate or the
 11 # same y-coordinate as the current location.
 12 #
 13 # Write a script to return the index of the valid point that has the smallest
 14 # Manhattan distance to the current location. If multiple valid points are tied
 15 # for the smallest distance, return the one with the lowest index. If no valid
 16 # points exist, return -1.
 17 #
 18 ####  The Manhattan distance between two points (x1, y1) and (x2, y2) is
 19 ####  calculated as: |x1 - x2| + |y1 - y2|
 20 #
 21 #
 22 ## Example 1
 23 ##
 24 ## Input: $x = 3, $y = 4, @points ([1, 2], [3, 1], [2, 4], [2, 3])
 25 ## Output: 2
 26 ##
 27 ## Valid points: [3, 1] (same x), [2, 4] (same y)
 28 ##
 29 ## Manhattan distances:
 30 ##     [3, 1] => |3-3| + |4-1| = 3
 31 ##     [2, 4] => |3-2| + |4-4| = 1
 32 ##
 33 ## Closest valid point is [2, 4] at index 2.
 34 #
 35 #
 36 ## Example 2
 37 ##
 38 ## Input: $x = 2, $y = 5, @points ([3, 4], [2, 3], [1, 5], [2, 5])
 39 ## Output: 3
 40 ##
 41 ## Valid points: [2, 3], [1, 5], [2, 5]
 42 ##
 43 ## Manhattan distances:
 44 ##     [2, 3] => 2
 45 ##     [1, 5] => 1
 46 ##     [2, 5] => 0
 47 ##
 48 ## Closest valid point is [2, 5] at index 3.
 49 #
 50 #
 51 ## Example 3
 52 ##
 53 ## Input: $x = 1, $y = 1, @points ([2, 2], [3, 3], [4, 4])
 54 ## Output: -1
 55 ##
 56 ## No point shares x or y with (1, 1).
 57 #
 58 #
 59 ## Example 4
 60 ##
 61 ## Input: $x = 0, $y = 0, @points ([0, 1], [1, 0], [0, 2], [2, 0])
 62 ## Output: 0
 63 ##
 64 ## Valid points: all of them
 65 ##
 66 ## Manhattan distances:
 67 ##     [0, 1] => 1
 68 ##     [1, 0] => 1
 69 ##     [0, 2] => 2
 70 ##     [2, 0] => 2
 71 ##
 72 ## Tie between index 0 and 1, pick the smaller index: 0
 73 #
 74 #
 75 ## Example 5
 76 ##
 77 ## Input: $x = 5, $y = 5, @points ([5, 6], [6, 5], [5, 4], [4, 5])
 78 ## Output: 0
 79 ##
 80 ## Valid points: all of them
 81 ##     [5, 6] => 1
 82 ##     [6, 5] => 1
 83 ##     [5, 4] => 1
 84 ##     [4, 5] => 1
 85 ##
 86 ## All tie, return the one with the lowest index: 0
 87 #
 88 ############################################################
 89 ##
 90 ## discussion
 91 ##
 92 ############################################################
 93 #
 94 # We just walk the array of points from the first element to the
 95 # last, calculating the Manhattan distance for all valid points.
 96 # If we have a new lowest distance, we keep track of both the
 97 # distance and the index. By initializing both values to -1, we
 98 # have a perfect way to make sure we can keep track of whether we
 99 # found any valid points so far and return -1 if we don't find any.
100 
101 use v5.36;
102 
103 nearest_valid_point( 3, 4, ([1, 2], [3, 1], [2, 4], [2, 3]) );
104 nearest_valid_point( 2, 5, ([3, 4], [2, 3], [1, 5], [2, 5]) );
105 nearest_valid_point( 1, 1, ([2, 2], [3, 3], [4, 4]) );
106 nearest_valid_point( 0, 0, ([0, 1], [1, 0], [0, 2], [2, 0]) );
107 nearest_valid_point( 5, 5, ([5, 6], [6, 5], [5, 4], [4, 5]) );
108 
109 sub nearest_valid_point( $x, $y, @points ) {
110     say "Input: $x, $y, (" . join(", ", map { "[$_->[0], $_->[1]]" } @points) . ")";
111     my $index = -1;
112     my $dist = -1;
113     foreach my $i (0..$#points) {
114         my $tmp_dist;
115         if($x == $points[$i]->[0]) {
116             my $d = abs($y - $points[$i]->[1]);
117             if($dist == -1 || $dist > $d) {
118                 $dist = $d;
119                 $index = $i;
120             }
121         } elsif ($y == $points[$i]->[1]) {
122             my $d = abs($x - $points[$i]->[0]);
123             if($dist == -1 || $dist > $d) {
124                 $dist = $d;
125                 $index = $i;
126             }
127         } else {
128             # neither $x nor $y match the point we're looking at, skip it
129         }
130     }
131     say "Output: $index";
132 }
133