Reputation: 37
i would like to get the smallest distance to a "snaffle" from the following hash:
$VAR1 = {
'0' => {
'y' => '7012',
'snaffle' => {
'5' => {
'y' => '3856',
'x' => '875',
'id' => '5',
'distance' => 9734
},
'6' => {
'x' => '10517',
'id' => '6',
'distance' => 510,
'y' => '6741'
},
'4' => {
'y' => '5291',
'id' => '4',
'x' => '11331',
'target' => 'true',
'distance' => 2125
},
'8' => {
'x' => '11709',
'id' => '8',
'distance' => 2236,
'y' => '5475'
},
'7' => {
'distance' => 8485,
'x' => '4591',
'id' => '7',
'y' => '544'
}
},
'x' => '10084',
'distance2mybase' => 10598,
'distance2enemybase' => 6755,
'type' => 'WIZARD',
'id' => '0',
'state' => 0
},
It is filled early:
# game loop
while (1) {
chomp(my $entities = <STDIN>); # number of entities still in game
for my $i (0..$entities-1) {
chomp($tokens=<STDIN>);
my ($entity_id, $entity_type, $x, $y, $vx, $vy, $state) = split(/ /,$tokens);
my $type;
if ($entity_type eq "WIZARD") {
$type = "wizard";
}
if ($entity_type eq "OPPONENT_WIZARD") {
$type = "enemy";
}
if ($entity_type eq "SNAFFLE") {
$type = "snaffle";
}
if ($entity_type eq "BLUDGER") {
$type = "bludger";
}
$entity{$type}{$entity_id}{x} = $x;
$entity{$type}{$entity_id}{y} = $y;
$entity{$type}{$entity_id}{state} = $state;
$entity{$type}{$entity_id}{id} = $entity_id;
$entity{$type}{$entity_id}{type} = $entity_type;
$entity{$type}{$entity_id}{distance2mybase} = &getdistance($entity{$type}{$entity_id}{x},$entity{$type}{$entity_id}{y},$mybase_x,$mybase_y);
$entity{$type}{$entity_id}{distance2enemybase} = &getdistance($entity{$type}{$entity_id}{x},$entity{$type}{$entity_id}{y},$enemybase_x,$enemybase_y);
}
foreach my $wizard_id (sort keys %{ $entity{'wizard'} }) {
foreach my $snaffle_id (sort keys %{ $entity{'snaffle'} }) {
$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{id} = $snaffle_id;
$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{x} = $entity{'snaffle'}{$snaffle_id}{x};
$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{y} = $entity{'snaffle'}{$snaffle_id}{y};
$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{distance} = &getdistance($entity{'wizard'}{$wizard_id}{x},$entity{'wizard'}{$wizard_id}{y},$entity{'snaffle'}{$snaffle_id}{x},$entity{'snaffle'}{$snaffle_id}{y});
}
&action($wizard_id,"sweep","up");
}
I tried List::Util::min, but i think im searching too deep, because as you can see in the output, it targets the wrong snaffle. (6 distance is lower then 4, which is the current target)
How can i find the overall minimum distance from all snaffles? (in case you wonder, its a codingame(.com))
sub snafflecheck {
my $wizard_id = shift;
my $wizard_x = shift;
my $wizard_y = shift;
if ($entity{'snaffle'}) {
foreach my $snaffle_id (sort keys %{ $entity{'snaffle'} }) {
my $snaffle_x = $entity{'snaffle'}{$snaffle_id}{x};
my $snaffle_y = $entity{'snaffle'}{$snaffle_id}{y};
my $distance2snaffle = &getdistance($wizard_x,$wizard_y,$snaffle_x,$snaffle_y);
my $nearest = min $entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{distance};
if ($distance2snaffle) {
if ($distance2snaffle == $nearest) {
$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{target} = "true";
return("true",$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{id},$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{x},$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{y},$distance2snaffle);
}
}
}
}
Upvotes: 1
Views: 86
Reputation: 126722
You should first extract the reference to the snaffle
hash to make things tidier. Then you can just use map
to extract the distance
field of each hash element and min
to find the smallest of them.
If you want to know the snaffle with the smallest distance then
I suggest that you install List::UtilsBy
and use its min_by
operator
This code shows both operations
The hash is identical to your own, but expressed more compactly using Data::Dump
instead
use strict;
use warnings 'all';
use feature 'say';
use List::Util 'min';
use List::UtilsBy 'min_by';
my %data = (
"0" => {
distance2enemybase => 6755,
distance2mybase => 10598,
id => 0,
snaffle => {
4 => { distance => 2125, id => 4, target => "true", x => 11331, y => 5291 },
5 => { distance => 9734, id => 5, x => 875, y => 3856 },
6 => { distance => 510, id => 6, x => 10517, y => 6741 },
7 => { distance => 8485, id => 7, x => 4591, y => 544 },
8 => { distance => 2236, id => 8, x => 11709, y => 5475 },
},
state => 0,
type => "WIZARD",
x => 10084,
y => 7012,
},
);
my $snaffles = $data{0}{snaffle};
my $min_distance = min map { $snaffles->{$_}{distance} } keys %$snaffles;
# OR
my $min_distance = min map { $_->{distance} } values %$snaffles;
my $closest_snaffle = min_by { $snaffles->{$_}{distance} } keys %$snaffles;
say "\$min_distance = $min_distance";
say "\$closest_snaffle = $closest_snaffle";
$min_distance = 510
$closest_snaffle = 6
Upvotes: 1
Reputation: 66883
Given the shown data, this is the list of all values for key distance
my @dist = map { $_->{distance} } values %{$entity->{0}{snaffle}};
However, getting the minimum value doesn't reveal its key.
One way of finding the key for which the value of distance
is smallest
use List::Util 'reduce';
my $snaff = $entity->{0}{snaffle};
my $min_dist = reduce {
$snaff->{$a}{distance} < $snaff->{$b}{distance} ? $a : $b
} keys %$snaff;
print "Minimal distance: $snaff->{$min_dist}{distance} for key $min_dist\n";
To have more control you can instead iterate over %$snaff
using each.
You can also sort the extracted $snaff
by distance
value, if you'd like to have them all.
Upvotes: 3