I came up with code that's a lot more complicated than yours, but gives the same result.
my %hints = (
bumps => 2,
seams => 2,
domes => 3,
shake => 3,
pokes => 3,
dukes => 3,
);
my %letters;
foreach my $w (keys %hints)
{
my %w = make_hash( $w );
while (my ($k,$v) = each %w)
{
$letters{$k} ||= 0;
$letters{$k} = $v
if $letters{$k} < $v;
}
}
my $iter = combo( 5, sort keys %letters );
while ( my @w = $iter->() )
{
next unless is_legal( @w );
print @w, $/;
}
sub is_legal
{
my %w = make_hash( join '', @_ );
keys %hints;
while (my ($h, $n) = each %hints)
{
my %h = make_hash( $h );
my $num = 0;
while (my ($l,$c) = each %h)
{
next unless exists $w{$l};
$num += $c < $w{$l} ? $c : $w{$l};
}
return 0 if $num != $n;
}
return 1;
}
sub make_hash { my %w; $w{$_}++ for split '', $_[0]; %w }
sub combo {
my $by = shift;
return sub { () } if ! $by || $by =~ /\D/ || @_ < $by;
my @list = @_;
my @position = (0 .. $by - 2, $by - 2);
my @stop = @list - $by .. $#list;
my $end_pos = $#position;
my $done = undef;
return sub {
return () if $done;
my $cur = $end_pos;
{
if ( ++$position[ $cur ] > $stop[ $cur ] ) {
$position[ --$cur ]++;
redo if $position[ $cur ] > $stop[ $cur ];
my $new_pos = $position[ $cur ];
@position[ $cur .. $end_pos ] = $new_pos .. $new_pos +
+ $by;
}
}
$done = 1 if $position[0] == $stop[0];
return @list[ @position ];
}
}
Being right, does not endow the right to be rude; politeness costs nothing.
Being unknowing, is not the same as being stupid.
Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.