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 ]; } }