File Coverage

blib/lib/Games/Boggle.pm
Criterion Covered Total %
statement 33 33 100.0
branch 10 10 100.0
condition n/a
subroutine 7 7 100.0
pod 2 2 100.0
total 52 52 100.0


line stmt bran cond sub pod time code
1             package Games::Boggle;
2              
3             =head1 NAME
4              
5             Games::Boggle - find words on a boggle board
6              
7             =head1 SYNOPSIS
8              
9             use Games::Boggle;
10              
11             my $board = Games::Boggle->new("TRTO XIHP TEEB MQYP");
12              
13             foreach my $word (@wordlist) {
14             print "OK $word\n" if $board->has_word($word);
15             }
16              
17             =head1 DESCRIPTION
18              
19             This module lets you set up a Boggle board, and then query it for whether
20             or not it is possible to find words on that board.
21              
22             =head1 METHODS
23              
24             =head2 new
25              
26             my $board = Games::Boggle->new("TRTO XIHP TEEB MEQP");
27              
28             You initialize the board with a series of 16 letters representing the
29             letters that are shown. Optional spaces may be inserted to make the
30             board string more readable.
31              
32             A 'Qu' should be entered solely as a 'Q'.
33              
34             =head2 has_word
35              
36             print "OK $word\n" if $board->has_word('tithe');
37             print "NOT OK $word\n" unless $board->has_word('queen');
38              
39             Given any word, we return whether or not that word can be found on the
40             board following the normal rules of Boggle.
41              
42             In scalar context this returns the number of possible ways of finding
43             this word. In list context it returns the starting squares from which this
44             word can be found (but only once per square, no matter how many times it
45             can be found there).
46              
47             Words containing the letter Q should be entered in full ('Queen', rather
48             than 'qeen'). Words containing a 'Q' not immediately followed by a 'U'
49             are never playable.
50              
51             =head1 AUTHOR
52              
53             Tony Bowden
54              
55             =head1 BUGS and QUERIES
56              
57             Please direct all correspondence regarding this module to:
58             bug-Games-Boggle@rt.cpan.org
59              
60             =head1 COPYRIGHT AND LICENSE
61              
62             Copyright (C) 2002-2005 Tony Bowden.
63              
64             This program is free software; you can redistribute it and/or modify it under
65             the terms of the GNU General Public License; either version 2 of the License,
66             or (at your option) any later version.
67              
68             This program is distributed in the hope that it will be useful, but WITHOUT
69             ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
70             FOR A PARTICULAR PURPOSE.
71              
72             =head1 SEE ALSO
73              
74             Advanced Perl Programming, 2nd Edition, by Simon Cozens
75              
76             =cut
77              
78             $VERSION = '1.01';
79              
80 1     1   28804 use strict;
  1         2  
  1         32  
81 1     1   4 use warnings;
  1         2  
  1         500  
82              
83             sub _unique {
84 5     5   11 my %list = map { $_ => 1 } @_;
  17         46  
85 5         42 return sort { $a <=> $b } keys %list;
  2         22  
86             }
87              
88             my $play = [
89             [1 .. 16],
90             [2,5,6],[1,3,5..7],[2,4,6..8],[3,7,8],
91             [1,2,6,9,10],[1..3,5,7,9..11],[2..4,6,8,10..12],[3,4,7,11,12],
92             [5,6,10,13,14],[5..7,9,11,13..15],[6..8,10,12,14..16],[7,8,11,15,16],
93             [9,10,14],[9..11,13,15],[10..12,14,16],[11,12,15]
94             ];
95              
96             sub new {
97 1     1 1 74 my ($class, $string) = @_;
98 1         25 my @board = grep /\S/, split //, uc $string;
99 16         53 bless {
100             _board => ["-", @board],
101 1         8 _has => { map { $_ => 1 } @board },
102             }, $class;
103             }
104              
105             sub has_word {
106 16     16 1 9097 my $self = shift;
107 16         173 my $word = uc shift;
108 16 100       63 return if $word =~ /Q(?!U)/; # Can't have lone Q in boggle.
109 15         31 $word =~ s/QU/Q/;
110 15 100       33 return unless $self->_have_letters($word);
111 13         40 my @starts = _can_play($self->{_board}, $word, 0);
112 13 100       151 return wantarray ? _unique @starts : scalar @starts;
113             }
114              
115             # Quick sanity check to stop us looking for words with letters we don't
116             # have. We don't check to ensure that we have ENOUGH copies of each
117             # letter in the word, as that is considerably slower.
118             sub _have_letters {
119 15     15   26 my ($self, $word) = @_;
120 15 100       47 while (my $let = chop $word) { return unless $self->{_has}->{$let}; }
  57         254  
121 13         36 return 1;
122             }
123              
124             sub _can_play {
125 204     204   513 my ($board, $word, $posn) = @_;
126 204 100       477 if (length $word > 1) {
127 83         191 my $last = chop $word;
128 108         157 return map {
129 83         230 local $board->[$_] = "-";
130 108         175 _can_play($board, $word, $_);
131             } _can_play($board, $last, $posn);
132             }
133 121         132 return grep $board->[$_] eq $word, @{ $play->[$posn] };
  121         774  
134             }
135              
136             return q/
137             AGGReGaTeD HeRBS ALLoW EXoTiC FLaVoR; OVeRZeaLouS PeoPLe ReaLiZe We USe
138             PReMiXeD CaViaR & DRiNK UP HuMBLeD GRoG IN MeGaDoSeS
139             /;