File Coverage

blib/lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm
Criterion Covered Total %
statement 192 339 56.6
branch 30 64 46.8
condition n/a
subroutine 20 40 50.0
pod 0 38 0.0
total 242 481 50.3


line stmt bran cond sub pod time code
1              
2             ################################ subroutines ################################
3              
4              
5              
6              
7              
8 1     1   4720 use Tree::AVL;
  1         2  
  1         26  
9 1     1   686 use AI::Pathfinding::SMAstar::Examples::WordObj;
  1         3  
  1         4277  
10             package AI::Pathfinding::SMAstar::Examples::PalUtils;
11              
12              
13             my $max_nodes_in_mem = 0;
14              
15             sub length_no_spaces
16             {
17 0     0 0 0 my ($w) = @_;
18 0         0 $w =~ s/\ //g;
19 0         0 return length($w);
20             }
21              
22              
23              
24             sub get_word_number_of_letters_that_have_repeats
25             {
26 0     0 0 0 my ($word) = @_;
27 0         0 my @letters = split('', $word);
28 0         0 my %letters_hash = ();
29              
30 0         0 foreach my $element (@letters) { $letters_hash{$element}++ }
  0         0  
31              
32 0         0 my $repeated_letters = 0;
33 0         0 foreach my $element (keys %letters_hash){
34 0 0       0 if($letters_hash{$element} > 1){
35 0         0 $repeated_letters++;
36             }
37             }
38            
39 0         0 return $repeated_letters;
40             }
41              
42              
43             #
44             # finds the number of times each letter appears within
45             # an entire list of words. returns a hash of the letters
46             #
47             sub find_letter_frequencies
48             {
49 1     1 0 1227 my (@words) = @_;
50 1         3 my %letters_freq;
51              
52 1         2 foreach my $w (@words)
53             {
54 4         12 @letters = split('', $w);
55            
56 4         5 foreach my $l (@letters)
57             {
58 15         24 $letters_freq{$l}++;
59             }
60             }
61              
62 1         9 return %letters_freq;
63             }
64              
65              
66             sub collisions_per_length
67             {
68 5     5 0 7 my ($w, $phrase) = @_;
69              
70 5 50       8 if(!$w){ $w = "" }
  0         0  
71 5 100       9 if(!$phrase){ $phrase = "" }
  4         5  
72              
73              
74 5         5 my $length = length($w);
75 5         12 $phrase =~ s/ //g;
76 5         13 my @letters = split('', $w);
77 5         9 my @letters_seen = split('', $phrase);
78 5         5 my $collisions = 0;
79 5         9 foreach my $l (@letters){
80 21         29 foreach my $ls (@letters_seen){
81 10 100       18 if($l eq $ls){
82 2         2 $collisions++;
83             }
84             }
85             }
86 5         10 my $val = $collisions/$length;
87              
88 5         20 return $val;
89             }
90              
91              
92              
93              
94             sub get_word_sparsity
95             {
96 14     14 0 21 my ($word) = @_;
97              
98 14         14 my $length = length($word);
99 14         21 my $num_letters = num_chars_in_word_memo($word);
100              
101 14         15 my $sparseness = $length - $num_letters;
102              
103 14         19 return $sparseness;
104             }
105              
106              
107             { my %memo_cache;
108             sub get_word_sparsity_memo
109             {
110 5     5 0 6 my ($word) = @_;
111              
112 5 100       11 if($memo_cache{$word}){
113 2         5 return $memo_cache{$word};
114             }
115             else{
116 3         4 my $length = length($word);
117 3         5 my $num_letters = num_chars_in_word_memo($word);
118            
119 3         4 my $sparseness = $length - $num_letters;
120            
121 3         5 $memo_cache{$word} = $sparseness;
122 3         8 return $sparseness;
123             }
124             }
125             }
126              
127              
128             # get the highest number of times a letter
129             # is repeated within a word.
130             sub get_word_highest_frequency
131             {
132 0     0 0 0 my ($word) = @_;
133 0         0 my @letters = split('', $word);
134 0         0 my %letters_hash = ();
135              
136 0         0 foreach my $element (@letters) { $letters_hash{$element}++ }
  0         0  
137              
138 0         0 my $most_frequent_letter_freq = 0;
139 0         0 foreach my $element (keys %letters_hash){
140 0 0       0 if($letters_hash{$element} > $most_frequent_letter_freq){
141 0         0 $most_frequent_letter_freq = $letters_hash{$element};
142             }
143             }
144 0         0 return $most_frequent_letter_freq;
145             }
146              
147              
148              
149              
150             sub get_letters
151             {
152 0     0 0 0 my ($word) = @_;
153 0         0 my @letter_set = ();
154 0         0 my %letters_hash = ();
155 0         0 my @letters = split('', $word);
156              
157 0         0 foreach my $element (@letters) { $letters_hash{$element}++ }
  0         0  
158              
159 0         0 foreach my $element (keys %letters_hash)
160             {
161 0         0 push(@letter_set, $element);
162             }
163 0         0 return @letter_set;
164             }
165              
166              
167              
168             { my %memo_cache;
169             sub word_collision_memo
170             {
171 3     3 0 4 my ($word,
172             $sorted_letters_seen) = @_;
173              
174 3         5 my $sorted_letters_seen_str = join('', @$sorted_letters_seen);
175 3         5 my $memo_key = $word . $sorted_letters_seen_str;
176            
177             #print "sorted_letters_seen_str: $sorted_letters_seen_str\n";
178            
179 3 100       13 if($memo_cache{$memo_key}){
180 1         1 return @{$memo_cache{$memo_key}};
  1         14  
181             }
182             else{
183 2         7 my @letters = split('', $word);
184            
185 2         2 my @difference = ();
186 2         3 my %letters_hash = ();
187 2         3 my %letters_seen_hash = ();
188            
189 2         3 my $intersect_num = 0;
190 2         1 my @intersection;
191              
192 2         9 foreach my $element (@$sorted_letters_seen) { $letters_seen_hash{$element}++ }
  7         8  
193              
194 2         4 foreach my $element (@letters) { $letters_hash{$element}++ }
  10         12  
195            
196 2         9 foreach my $element (keys %letters_hash) {
197 8 100       11 if($letters_seen_hash{$element}){
198 3         3 push(@intersection, $element);
199 3         4 $intersect_num++;
200             }
201             else{
202 5         17 push(@difference, $element);
203             }
204             }
205            
206 2         4 my @answer = ($intersect_num, @difference);
207              
208 2         4 $memo_cache{$memo_key} = \@answer;
209 2         10 return ($intersect_num, @difference);
210             }
211             }
212             }
213              
214              
215              
216              
217             sub word_collision{
218 14     14 0 16 my ($word,
219             $letters_seen) = @_;
220            
221 14         37 my @letters = split('', $word);
222            
223 14         18 my @difference = ();
224 14         21 my %letters_hash = ();
225 14         16 my %letters_seen_hash = ();
226            
227 14         14 my $intersect_num = 0;
228 14         12 my @intersection;
229              
230 14         21 foreach my $element (@$letters_seen) { $letters_seen_hash{$element}++ }
  22         35  
231            
232 14         16 foreach my $element (@letters) { $letters_hash{$element}++ }
  38         76  
233            
234 14         42 foreach my $element (keys %letters_hash) {
235 36 100       58 if($letters_seen_hash{$element}){
236 6         9 push(@intersection, $element);
237 6         8 $intersect_num++;
238             }
239             else{
240 30         38 push(@difference, $element);
241             }
242             }
243            
244 14         70 return ($intersect_num, @difference);
245             }
246              
247              
248              
249             sub get_cands_from_left
250             {
251              
252 17     17 0 633 my ($word,
253             $dictionary,
254             $dictionary_rev) = @_;
255              
256 17         29 my @cands = get_cands_memo($word, $dictionary_rev);
257            
258 17         28 foreach my $c (@cands){
259 15         34 $c = reverse($c);
260             }
261 17         36 my @sorted_cands = sort(@cands);
262 17         50 return @sorted_cands;
263             }
264              
265             sub get_cands_from_right
266             {
267 6     6 0 10 my ($word,
268             $dictionary,
269             $dictionary_rev) = @_;
270            
271 6         9 my $rev_word = reverse($word);
272              
273 6         8 my @cands = get_cands_memo($rev_word, $dictionary);
274 6         12 my @sorted_cands = sort(@cands);
275 6         19 return @sorted_cands;
276             }
277              
278              
279             {my $memo_hash_ref = {};
280             sub get_cands_memo
281             {
282 23     23 0 30 my ($word, $dictionary_rev) = @_;
283            
284 23         66 my $cand = AI::Pathfinding::SMAstar::Examples::WordObj->new(
285             _word => $word
286             );
287              
288 23         52 my $cache_key = $word . $dictionary_rev;
289 23         34 my $cached_vals = $memo_hash_ref->{$cache_key};
290 23 100       35 if($cached_vals){
291             #print $spaces . "DING DING DING. cache hit!\n";
292 17         60 return @$cached_vals;
293            
294             }
295             else{
296            
297 6         12 my @substr_cands = get_substrs_memo($word, $dictionary_rev);
298 6         43 my @superstr_cands = $dictionary_rev->acc_lookup_memo($cand,
299             \&AI::Pathfinding::SMAstar::Examples::WordObj::compare_up_to,
300             \&AI::Pathfinding::SMAstar::Examples::WordObj::compare);
301 6         76 my @cands = (@substr_cands, @superstr_cands);
302             # these are all the words in the dictionary that could end this pal.
303 6         11 $memo_hash_ref->{$cache_key} = \@cands;
304 6         32 return @cands;
305             }
306             }
307             }
308              
309             sub get_cands
310             {
311 0     0 0 0 my ($word, $dictionary_rev) = @_;
312            
313 0         0 my $cand = AI::Pathfinding::SMAstar::Examples::WordObj->new(
314             _word => $word
315             );
316              
317 0         0 my @substr_cands = get_substrs_memo($word, $dictionary_rev);
318 0         0 my @superstr_cands = $dictionary_rev->acc_lookup($cand,
319             \&AI::Pathfinding::SMAstar::Examples::WordObj::compare_up_to,
320             \&AI::Pathfinding::SMAstar::Examples::WordObj::compare);
321 0         0 my @cands = (@substr_cands, @superstr_cands);
322             # these are all the words in the dictionary that could end this pal.
323 0         0 return @cands;
324             }
325              
326              
327             sub match_remainder
328             {
329 5     5 0 9 my ($word1, $word2) = @_;
330            
331 5         18 $word1 =~ s/\ //g;
332 5         6 $word2 =~ s/\ //g;
333              
334 5         6 my $len1 = length($word1);
335 5         4 my $len2 = length($word2);
336              
337 5 50       13 if(index($word1, $word2) != 0)
338             {
339 0         0 return;
340             }
341 5         10 my $remainder_word = substr($word1, $len2);
342 5         11 return $remainder_word;
343             }
344              
345              
346              
347             #
348             # memoized version of get_substrs- for speed
349             #
350             {my $memo_hash_ref = {};
351             sub get_substrs_memo
352             {
353 6     6 0 10 my ($word, $dictionary) = @_;
354            
355 6         7 my @words;
356             my @matches;
357            
358            
359 6         12 my $cache_key = $word . $dictionary;
360 6         9 my $cached_vals = $memo_hash_ref->{$cache_key};
361 6 50       11 if($cached_vals1){
362             #print $spaces . "DING DING DING. cache hit!\n";
363 0         0 return @$cached_vals;
364            
365             }
366             else{
367 6         18 for(my $i = 1; $i < length($word); $i++){
368 16         44 push(@words, substr($word, 0, $i));
369             }
370            
371 6         9 foreach my $substring (@words){
372             #print "looking for matches on: \"$substring\"\n";
373            
374 16         51 my $cand = AI::Pathfinding::SMAstar::Examples::WordObj->new(
375             _word => $substring
376             );
377 16         46 my $match_word = $dictionary->lookup($cand, \&AI::Pathfinding::SMAstar::Examples::WordObj::compare);
378 16 100       116 if($match_word){
379             # print "FOUND A MATCH: $match_word\n";
380 2         6 push(@matches, $match_word);
381             }
382            
383             }
384             #print "no hashed value yet, creating one.\n";
385 6         14 $memo_hash_ref->{$cache_key} = \@matches;
386 6         19 return @matches;
387             }
388             }
389             }
390              
391              
392             sub get_substrs
393             {
394 0     0 0 0 my ($word, $dictionary) = @_;
395            
396 0         0 my @words;
397             my @matches;
398              
399 0         0 for(my $i = 1; $i < length($word); $i++){
400 0         0 push(@words, substr($word, 0, $i));
401             }
402              
403 0         0 foreach my $substring (@words){
404             #print "looking for matches on: \"$substring\"\n";
405              
406 0         0 my $cand = AI::Pathfinding::SMAstar::Examples::WordObj->new(
407             _word => $substring
408             );
409 0         0 my $match_word = $dictionary->lookup($cand, \&AI::Pathfinding::SMAstar::Examples::WordObj::compare);
410 0 0       0 if($match_word){
411             # print "FOUND A MATCH: $match_word\n";
412 0         0 push(@matches, $match_word);
413             }
414            
415             }
416 0         0 return @matches;
417             }
418              
419              
420              
421             # randomize an array. Accepts a reference to an array.
422             sub fisher_yates_shuffle {
423 0     0 0 0 my ($array) = @_;
424 0         0 my $i;
425 0         0 for ($i = @$array; --$i; ) {
426 0         0 my $j = int rand ($i+1);
427 0 0       0 next if $i == $j;
428 0         0 @$array[$i,$j] = @$array[$j,$i];
429             }
430             }
431              
432             sub process_words
433             {
434 0     0 0 0 my ($words) = @_;
435 0         0 my @word_objs;
436            
437 0         0 for(my $i = 0; $i < @$words; $i++)
438             {
439 0         0 my $word = $words->[$i];
440 0         0 chomp($word);
441              
442 0         0 $word_objs[$i] = AI::Pathfinding::SMAstar::Examples::WordObj->new(
443             _word => $word,
444            
445             );
446             }
447 0         0 return @word_objs;
448             }
449              
450             sub process_words_by_density
451             {
452 1     1 0 253 my ($words,
453             $max_score # 0: no repeats, 1: 1 repeat, etc.
454             ) = @_;
455            
456 1         3 my @word_objs;
457            
458 1         1 my $i = 0;
459 1         6 foreach my $word (@$words)
460             {
461 4         5 chomp($word);
462 4         6 my $sparsity = get_word_sparsity($word);
463              
464 4 50       7 if($sparsity <= $max_score){
465 4         20 $word_objs[$i] = AI::Pathfinding::SMAstar::Examples::WordObj->new(
466             _word => $word,
467             );
468 4         9 $i++;
469             }
470             }
471 1         4 return @word_objs;
472             }
473              
474              
475              
476              
477             sub process_rev_words
478             {
479 0     0 0 0 my ($words) = @_;
480 0         0 my @word_objs;
481            
482 0         0 for(my $i = 0; $i < @$words; $i++)
483             {
484 0         0 my $word = $words->[$i];
485 0         0 chomp($word);
486              
487 0         0 my $rev_word = reverse($word);
488              
489 0         0 $word_objs[$i] = AI::Pathfinding::SMAstar::Examples::WordObj->new(
490             _word => $rev_word,
491             );
492             }
493 0         0 return @word_objs;
494             }
495              
496             sub process_rev_words_by_density
497             {
498 1     1 0 6 my ($words,
499             $max_score # 0: no repeats, 1: 1 repeat, etc.
500             ) = @_;
501            
502 1         1 my @word_objs;
503            
504 1         2 my $i = 0;
505 1         2 foreach my $word (@$words)
506             {
507 4         5 chomp($word);
508              
509 4         6 my $rev_word = reverse($word);
510 4         5 my $sparsity = get_word_sparsity($word);
511              
512 4 50       50 if($sparsity <= $max_score){
513 4         11 $word_objs[$i] = AI::Pathfinding::SMAstar::Examples::WordObj->new(
514             _word => $rev_word,
515             );
516 4         7 $i++;
517             }
518             }
519 1         4 return @word_objs;
520             }
521              
522              
523             sub is_palindrome
524             {
525 6     6 0 6 my ($candidate) = @_;
526 6 100       14 if(!$candidate){
527 2         8 return 0;
528             }
529 4         20 $candidate =~ s/\ //g;
530 4         27 return($candidate eq reverse($candidate));
531             }
532              
533             sub join_strings
534             {
535 0     0 0 0 my ($strings) = @_;
536 0         0 my $candidate = join(' ', @$strings);
537            
538 0         0 return $candidate;
539             }
540              
541             sub make_one_word
542             {
543 0     0 0 0 my ($phrase) = @_;
544 0         0 $phrase =~ s/\s//g;
545 0         0 return $phrase;
546             }
547              
548              
549             sub num_chars_in_word
550             {
551 9     9 0 17 my ($word) = @_;
552 9         7 my %hash;
553            
554 9 100       18 if(!$word) { return 0; }
  2         6  
555            
556 7         35 @hash{ split '', $word} = 1;
557 7         15 my $num_keys = keys(%hash);
558            
559 7         32 return $num_keys;
560             }
561              
562              
563             {my %memo_cache;
564             sub num_chars_in_word_memo
565             {
566 22     22 0 25 my ($word) = @_;
567              
568 22 100       42 if($memo_cache{$word}){
569 17         31 return $memo_cache{$word};
570             }
571             else{
572 5         6 my %hash;
573 5         19 @hash{ split '', $word} = 1;
574 5         7 my $num_keys = keys(%hash);
575            
576 5         8 $memo_cache{$word} = $num_keys;
577 5         10 return $num_keys;
578             }
579             }
580             }
581              
582              
583             {my %memo_cache;
584             sub num_chars_in_pal
585             {
586 2     2 0 4 my ($pal) = @_;
587 2         3 my $num_keys;
588              
589 2         7 $pal =~ s/\ //g;
590 2         4 my $length = length($pal);
591 2         10 my $first_half = substr($pal, 0, $length/2 + 1);
592              
593              
594 2 50       7 if($memo_cache{$first_half}){
595 0         0 return $memo_cache{$first_half};
596             }
597             else{
598              
599 2         2 my %hash;
600 2         12 @hash{ split '', $first_half } = 1;
601 2         6 $num_keys = keys(%hash);
602            
603 2         4 $memo_cache{$pal} = $num_keys;
604 2         13 return $num_keys;
605             }
606             }
607             }
608              
609             sub read_dictionary
610             {
611 0     0 0 0 my ($in_file) = @_;
612            
613 0 0       0 unless(open(READF, "+<$in_file")){
614 0         0 return;
615             }
616            
617 0         0 my @lines = ;
618            
619 0         0 close(READF);
620            
621 0         0 return @lines;
622             }
623              
624             sub read_dictionary_filter_by_density
625             {
626 1     1 0 546 my ($in_file, $max_score) = @_;
627            
628 1 50       45 unless(open(READF, "+<$in_file")){
629 0         0 return;
630             }
631            
632 1         23 my @lines = ;
633 1         3 my $num_lines = @lines;
634            
635 1         10 close(READF);
636              
637 1         2 my @filtered_words;
638            
639 1         3 my $i = 0;
640 1         3 foreach my $word (@lines)
641             {
642 4         4 chomp($word);
643 4         9 my $sparsity = get_word_sparsity($word);
644              
645 4 50       8 if($sparsity <= $max_score){
646 4         7 $filtered_words[$i] = $word;
647 4         6 $i++;
648             }
649             }
650              
651 1         6 return ($num_lines, @filtered_words);
652             }
653              
654             sub read_dictionary_filter_by_density_rev
655             {
656 0     0 0   my ($in_file, $max_score) = @_;
657            
658 0 0         unless(open(READF, "+<$in_file")){
659 0           return;
660             }
661            
662 0           my @lines = ;
663 0           my $num_lines = @lines;
664            
665 0           close(READF);
666              
667 0           my @filtered_words;
668            
669 0           my $i = 0;
670 0           foreach my $word (@lines)
671             {
672 0           chomp($word);
673 0           my $sparsity = get_word_sparsity($word);
674              
675 0 0         if($sparsity <= $max_score){
676 0           my $rev_word = reverse($word);
677 0           $filtered_words[$i] = $rev_word;
678 0           $i++;
679             }
680             }
681              
682 0           return ($num_lines, @filtered_words);
683             }
684              
685              
686              
687             sub flush {
688 0     0 0   my $h = select($_[0]); my $a=$|; $|=1; $|=$a; select($h);
  0            
  0            
  0            
  0            
689             }
690              
691             {my $spinny_thing = "-";
692             my $call_num = 0;
693             my $state;
694             sub show_progress {
695 0     0 0   $call_num++;
696 0           $state = $call_num % 4;
697 0 0         if($state == 0){
    0          
    0          
    0          
698 0           $spinny_thing = "-";
699             }
700             elsif($state == 1){
701 0           $spinny_thing = "\\";
702             }
703             elsif($state == 2){
704 0           $spinny_thing = "|";
705             }
706             elsif($state == 3){
707 0           $spinny_thing = "/";
708             }
709              
710 0           my ($progress) = @_;
711 0           my $stars = '*' x int($progress*10);
712 0           my $percent = sprintf("%.2f", $progress*100);
713 0 0         $percent = $percent >= 100 ? '100.00%' : $percent.'%';
714            
715 0           print("\r$stars $spinny_thing $percent.");
716 0           flush(STDOUT);
717             }
718             }
719              
720              
721              
722             sub show_search_depth_and_percentage {
723 0     0 0   my ($depth, $so_far, $total) = @_;
724 0           my $stars = '*' x int($depth);
725              
726 0           my $amount_completed = $so_far/$total;
727            
728 0           my $percentage = sprintf("%0.2f", $amount_completed*100);
729              
730 0           print("\r$stars depth: $depth. completed: $percentage %");
731 0           flush(STDOUT);
732             }
733              
734              
735             sub show_search_depth_and_num_states {
736 0     0 0   my ($depth, $states) = @_;
737 0           my $stars = '*' x int($depth);
738 0           my $num_states = @$states;
739              
740 0           print("\rdepth: $depth. num_states: $num_states");
741 0           flush(STDOUT);
742             }
743              
744              
745              
746              
747              
748             {my $LINES=`tput lines`; # number of rows in current terminal window
749             my $COLUMNS=`tput cols`; # number of columns in current terminal window
750              
751             sub show_progress_so_far {
752 0     0 0   my ($iteration, $num_states, $str, $opt_datum, $opt_datum2) = @_;
753 0           my $stars = '*' x int($iteration);
754            
755              
756             # print "\e[H"; # Put the cursor on the first line
757             # print "\e[J"; # Clear from cursor to end of screen
758             # print "\e[H\e[J"; # Clear entire screen (just a combination of the above)
759             # print "\e[K"; # Clear to end of current line (as stated previously)
760             # print "\e[m"; # Turn off character attributes (eg. colors)
761             # printf "\e[%dm", $N; # Set color to $N (for values of 30-37, or 100-107)
762             # printf "\e[%d;%dH", $R, $C; # Put cursor at row $R, column $C (good for "drawing")
763              
764            
765            
766            
767             #print "\e[H\e[J"; #clears the entire screen
768 0           printf "\e[%d;%dH", $LINES-1, 1; # Put cursor at row $R, column $C (good for "drawing")
769            
770 0           print "\e[J"; #clears to end of screen
771              
772 0 0         if($num_states > $max_nodes_in_mem){
773 0           $max_nodes_in_mem = $num_states;
774             }
775              
776              
777 0           print "\riteration: $iteration, num_states_in_memory: $num_states, max_states_in_mem: $max_nodes_in_mem\n";
778            
779              
780 0           printf "\e[%d;%dH", $LINES, 1; # Put cursor at row $R, column $C (good for "drawing")
781              
782 0           print "\e[J"; #clears to end of screen
783              
784 0           print "string: $str\e[J";
785              
786              
787 0           flush(STDOUT);
788             }
789             }
790              
791              
792 0     0 0   sub show_search_depth_and_num_states_debug {
793            
794             }
795              
796              
797             {my $LINES=`tput lines`; # number of rows in current terminal window
798             my $COLUMNS=`tput cols`; # number of columns in current terminal window
799              
800             sub show_progress_so_far_debug {
801 0     0 0   my ($depth, $prog, $num_states, $str, $num_successors) = @_;
802 0           my $stars = '*' x int($depth);
803            
804            
805 0           print "depth: $depth, string: $str, num_successors: $num_successors\n";
806              
807 0           flush(STDOUT);
808             }
809             }
810              
811              
812              
813              
814              
815              
816              
817              
818              
819              
820              
821              
822              
823              
824             1;