Group
Extension

Games-Word-Guess/lib/Games/Word/Guess.pm

## no critic (ControlStructures::ProhibitMutatingListFunctions)

package Games::Word::Guess;

our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
our $DATE = '2021-03-14'; # DATE
our $DIST = 'Games-Word-Guess'; # DIST
our $VERSION = '0.091'; # VERSION

use 5.010001;
use strict;
use warnings;
use experimental 'smartmatch';
use Log::ger;

use Module::List qw(list_modules);
use Module::Load;

sub new {
    my $class = shift;
    my %attrs = @_;

    # select and load default wordlist
    my $mods = list_modules("WordList::", {list_modules=>1, recurse=>1});
    my @wls = map {s/^WordList:://; $_}
        grep {!/^WordList::(Char|Test|Phrase|ZH)::/} keys %$mods;
    print "Available wordlists: ", join(", ", @wls), "\n";
    my $wl = $attrs{word_list};
    if (!$wl) {
        if (($ENV{LANG} // "") =~ /^en/ && "ID::KBBI" ~~ @wls) {
            $wl = "ID::KBBI";
        } elsif (($ENV{LANG} // "") =~ /^id/ && "EN::Enable" ~~ @wls) {
            $wl = "EN::Enable";
        } else {
            if (@wls > 1) {
                @wls = grep {$_ ne 'ID::KBBI'} @wls;
            }
            $wl = $wls[rand @wls];
        }
    }
    die "Can't find module for wordlist '$wl'" unless $wl ~~ @wls;
    my $mod = "WordList::$wl";
    load $mod;
    print "Loaded wordlist module $mod\n";

    $attrs{_wlobj} = $mod->new;
    $attrs{num_words} //= 10;

    bless \%attrs, $class;
}

sub run {
    my $self = shift;

    $self->{num_correct} = 0;
    $self->{score} = 0;

    for my $i (1..$self->{num_words}) {
        print "\nWord $i/$self->{num_words}:\n";
        my ($is_correct, $score) = $self->ask_word;
        $self->{num_correct}++ if $is_correct;
        $self->{score} += $score;
    }
    $self->show_final_score;
}

sub ask_word {
    my $self = shift;

    my $word;
  PICK:
    {
        my $l1 = int($self->{min_len} // 5);
        my $l2 = int($self->{max_len} // $l1 // 5);
        my $tries = 0;
        while (++$tries < 100) {
            ($word) = $self->{_wlobj}->pick;
            log_trace "Got word: $word";
            if ($word =~ qr/\A[a-z]{$l1,$l2}\z/) {
                last PICK;
            }
        }
        die "Can't find any eligible words in wordlist '".
            ref($self->{_wlobj})."'";
    }

    #say "D:word=$word";
    my $wlen  = length($word);

    my $max_guesses = $self->{max_guesses} // $wlen;

    my $i = 0;
    my $gletters = substr($word, 0, 1) . ("*" x ($wlen-1));
    $self->show_diff($word, $gletters);
    while ($i < $max_guesses) {
        $i++;
        print $i < $max_guesses ? "Your guess ($i/$max_guesses)? " :
            "Your last guess? ";
        chomp(my $guess = <STDIN>);
        unless ($self->{_wlobj}->word_exists($guess)) {
            $self->show_diff($word, $gletters);
            print "Sorry, not a word! ";
            $i--;
            next;
        }
        unless (length($guess) eq $wlen) {
            $self->show_diff($word, $gletters);
            print "Sorry, not a $wlen-letter word! ";
            $i--;
            next;
        }
        if (lc($guess) eq $word) {
            my $score = ($wlen*(1-($i-1)/$max_guesses))*20;
            print "Correct ($score points)!\n";
            return (1, $score);
        }
        my $gletters_new = $self->show_diff($word, $guess);
        $gletters = $gletters_new
            if $self->get_num_chars($gletters, '*') >
                $self->get_num_chars($gletters_new, '*');
    }
    print "Chances used up. The correct word is: $word\n";
    return (0, 0);
}

sub get_num_chars {
    my ($self, $s, $char) = @_;
    my $orig = $s;
    $s =~ s/\Q$char//ig;
    length($orig) - length($s);
}

sub show_diff {
    my ($self, $word, $guess) = @_;
    my $gletters = "";
    #print "D:show_diff: $word vs $guess\n";
    $guess = lc($guess);
    my $word2 = $word;

    # what letters are in the incorrect position
    for my $i (0..(length($word)-1)) {
        my $l = substr($word, $i, 1);
        $word2 =~ s/\Q$l// if length($guess)>$i && substr($guess, $i, 1) eq $l;
     }

    for my $i (0..(length($word)-1)) {
        my $l  = substr($word, $i, 1);
        my $gl = length($guess) > $i ? substr($guess, $i, 1) : "";
        if ($l eq $gl) {
            print "$l ";
            $gletters .= $l;
        } elsif (length($gl) && $word2 =~ s/\Q$gl//) {
            print "$gl*";
            $gletters .= "*";
        } else {
            print "_ ";
            $gletters .= "*";
        }
        print " ";
    }
    #print "\n";
    $gletters;
}

sub show_final_score {
    my $self = shift;

    print "\n";
    printf "Number of words guessed correctly: %d/%d\n",
        $self->{num_correct}, $self->{num_words};
    printf "Final score: %.0f\n", $self->{score};
}

1;
# ABSTRACT: Word guess game

__END__

=pod

=encoding UTF-8

=head1 NAME

Games::Word::Guess - Word guess game

=head1 VERSION

This document describes version 0.091 of Games::Word::Guess (from Perl distribution Games-Word-Guess), released on 2021-03-14.

=head1 SYNOPSIS

 % wordguess

=for Pod::Coverage ^(.+)$

=head1 HOMEPAGE

Please visit the project's homepage at L<https://metacpan.org/release/Games-Word-Guess>.

=head1 SOURCE

Source repository is at L<https://github.com/perlancar/perl-Games-Word-Guess>.

=head1 BUGS

Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Games-Word-Guess>

When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.

=head1 SEE ALSO

L<wordguess>

L<Games::GuessWord> which is more akin to Hangman but, at the time of this
writing, does not come with a readily-playable game.

=head1 AUTHOR

perlancar <perlancar@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2021, 2016, 2015, 2014 by perlancar@cpan.org.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.