http://www.perlmonks.org?node_id=624154

Spidy has asked for the wisdom of the Perl Monks concerning the following question:

I'm working on a browsergame where different shops will require the user to have killed different amounts of different enemies. This is accomplished using two tables inside my database: user_kills, and shop_requirements. I loop through the user's kills to build the %player hash, which looks like this:
%player = { 5 => 2, 4 => 3, }

The keys of the %player hash are enemy ID numbers, and their values are the quantity of that enemy that the player has killed.

In order to figure out which shops a player can access, I retrieve all of them and their requirements, before creating an array of hashes - each hash represents a single store, and is made up just like the %players hash:

%shop = { 2 => 4, 5 => 22, }

What I need to do is loop through this array of hashes, and remove any element in the array where the player's kill counts are less than the amount required by that shop in particular.

I understand that grep is what I'd want to use for this, but I don't know how I would use it to achieve what I want to. This is what I've tried:

my @availablePosts = grep{my $item = $_;foreach my $k (keys %$_) {0 un +less $player{$k} >= $item->{$k}}1;} @posts;

Where @posts is the array of hashes of shop requirements.

If someone could explain this to me, or point me in the right direction, I'd be much obliged.

Spidy

Replies are listed 'Best First'.
Re: Using grep for many-to-many relationships
by GrandFather (Saint) on Jun 30, 2007 at 03:20 UTC

    Not a one liner, but I guess you are not looking for golf or obfusication here:

    use strict; use warnings; my @posts = ( {1 => 4, 3=>5}, {1 => 2, 2 => 3, 3 => 3}, {1 => 2, 3 => 6}, {1 => 4, 3 => 2, 4 => 2}, ); my %player = (1 => 3, 2 => 5, 3 => 7); my @availablePosts; for my $shop (@posts) { next if grep {! exists $player{$_} or $player{$_} < $shop->{$_}} k +eys %$shop; push @availablePosts, $shop; } for my $post (@availablePosts) { print join (', ', map {"$_ => $post->{$_}"} sort keys %$post), "\n +"; }

    Prints:

    1 => 2, 2 => 3, 3 => 3 1 => 2, 3 => 6

    DWIM is Perl's answer to Gödel
Re: Using grep for many-to-many relationships
by parv (Parson) on Jun 30, 2007 at 04:14 UTC
    Similar to GrandFather's reponse; this produces the indices (of accessible posts, in posts array).
    #!/usr/local/bin/perl use warnings; use strict; use Data::Dumper; my %pangolin = ( 'ant' => 15 , 'termite' => 34 ); my %komodo = ( 'newt' => 34 , 'crock' => 3 , 'ant' => 60 , 'termite' => 90 ); my @shops = ( { 'ant' => 10 , 'termite' => 14 } , { 'newt' => 25 , 'crock' => 6 , 'termite' => 80 } , { 'crock' => 3 } ); for my $killer ( \%pangolin , \%komodo ) { # Prints player's statistics & accessible shops as indices of @shop +s. my @ok = find_accessible_shops( $killer , \@shops ); print Dumper( $killer , \@ok ); } exit; # Given a player's statistics as hash reference & a array reference o +f hash # references of shops, returns the indices (of the array reference). sub find_accessible_shops { my ( $player , $shops ) = @_; my @accessible; DEALER: for my $dealer ( 0 .. scalar @{ $shops } - 1 ) { my $allow; REQUIRE: for my $victim ( keys %{ $shops->[ $dealer ] } ) { next DEALER unless exists $player->{ $victim }; $allow = ( defined $allow ? $allow : 1 ) && $shops->[ $dealer ]->{ $victim } <= $player->{ $victim } ; } push @accessible , $dealer if $allow; } return @accessible; }

    About 18.5 hours later: Above sub now returns a list instead of array reference, which was originally there to mistakenly simplify the print Dumper( ... ) statement.

    About 2 days later, in the sub, removed explicit sentinel variable (to inidicate first iteration, set to -1) as $allow can be appropriated for the purpose (see my $allow;). That also takes care of the bug in case the inner loop does not run causing @accessible to fill erroneously (note to self: -1 is a true value).

Re: Using grep for many-to-many relationships
by aquarium (Curate) on Jul 02, 2007 at 03:05 UTC
    once you start treating the database as tables with relationships -- rather than as disjoint flat structures -- you'll be able to put in all sorts of such game logic just using sql statements. i'm not shouting from the altar...merely saying that a more thought out and normalized database will be much easier to use, especially if the game is going to be non-trivial. whereas game/database logic expressed in code becomes increasingly difficult to debug/maintain, as rules/relationships are added. If you don't know already about 3rd normal form (databases,) learning it becomes a lifelong asset in any database work.
    all the best with your game
    the hardest line to type correctly is: stty erase ^H
Re: Using grep for many-to-many relationships
by Moron (Curate) on Jul 02, 2007 at 11:49 UTC
    my @availablePosts = grep { for my $enemy( keys %$_ ) { if ( !defined( $player{ $enemy }) || ( $player{ $enemy } < $_ +-> { $enemy } ) ) { $_ = undef(); last; } } } @posts;
    (untested)
    __________________________________________________________________________________

    ^M Free your mind!