File Coverage

lib/Algorithm/MasterMind/Consistent_Set.pm
Criterion Covered Total %
statement 133 137 97.0
branch 14 16 87.5
condition n/a
subroutine 19 20 95.0
pod 14 14 100.0
total 180 187 96.2


line stmt bran cond sub pod time code
1             package Algorithm::MasterMind::Consistent_Set;
2              
3 2     2   16380 use warnings;
  2         4  
  2         68  
4 2     2   12 use strict;
  2         3  
  2         69  
5 2     2   10 use Carp;
  2         4  
  2         159  
6              
7 2         15 use lib qw(../../lib ../../../../Algorithm-Evolutionary/lib/
8             ../../Algorithm-Evolutionary/lib/
9 2     2   22 ../../../lib);
  2         5  
10              
11             our $VERSION = sprintf "%d.%03d", q$Revision: 1.8 $ =~ /(\d+)\.(\d+)/g;
12              
13 2     2   874 use Algorithm::MasterMind qw(partitions);
  2         5  
  2         105  
14 2     2   529 use Algorithm::MasterMind::Secret;
  2         4  
  2         3477  
15              
16             sub new {
17 3     3 1 18 my $class = shift;
18 3         8 my $combinations = shift;
19 3         30 my @secrets = map ( (new Algorithm::MasterMind::Secret $_), @$combinations );
20 3         81 my $self = { _combinations => \@secrets,
21             _partitions => {}};
22 3         13 bless $self, $class;
23 3         12 $self->{'_partitions'} = compute_partitions( \@secrets );
24 3         21 $self->{'_score'} = {}; # To store scores when they're available.
25 3         3872 return $self;
26             }
27              
28             sub compute_partitions {
29 10     10 1 35 my $secrets_ref = shift;
30 10         230 my @secrets = @$secrets_ref;
31 10         13 my %partitions;
32             my %hash_results;
33 10         48 for ( my $i = 0; $i <= $#secrets; $i ++ ) {
34 1762         5786 for (my $j = 0; $j <= $#secrets; $j ++ ) {
35 1749048 100       3474564 next if $i == $j;
36 1747286         15712348 my $result = { blacks => 0,
37             whites => 0 } ;
38 1747286 100       3279982 if ( $i < $j ) {
39 873643         2764542 $secrets[$i]->check_secret ( $secrets[$j], $result );
40 873643         7088925 $hash_results{$secrets[$i]->{'_string'}}{$secrets[$j] ->{'_string'}} = $result;
41             } else {
42 873643         7095420 $result = $hash_results{$secrets[$j]->{'_string'}}{$secrets[$i] ->{'_string'}}
43             }
44 1747286         4854792 $partitions{$secrets[$i]->{'_string'}}{result_as_string($result)}++;
45             }
46             }
47 10         1547010 return \%partitions
48             }
49              
50             sub create_consistent_with {
51 1     1 1 57 my $class = shift;
52 1         2 my $combinations = shift;
53 1         2 my $rules = shift;
54 1         9 my @secrets = map ( (new Algorithm::MasterMind::Secret $_), @$combinations );
55 1         9 my $self = { _combinations => [],
56             _partitions => {}};
57 1         4 bless $self, $class;
58 1         3 my %rule_secrets;
59 1         8 map( ($rule_secrets{$_->{'combination'}} = new Algorithm::MasterMind::Secret $_->{'combination'}),
60             @$rules );
61 1         4 for my $s (@secrets ) {
62 128         128 my $matches;
63 128         229 for my $r (@$rules ) {
64 128         315 my $this_result = { blacks => 0,
65             whites => 0 };
66 128         427 $s->check_secret( $rule_secrets{$r->{'combination'}}, $this_result);
67 128         241 $matches += result_as_string( $this_result ) eq result_as_string( $r );
68             }
69 128 100       339 if ( $matches == @$rules ) {
70 32         34 push @{$self->{'_combinations'}}, $s
  32         92  
71             }
72             }
73 1         9 $self->{'_partitions'} = compute_partitions( $self->{'_combinations'} );
74 1         5 $self->{'_score'} = {}; # To store scores when they're available.
75 1         298 return $self;
76             }
77              
78             sub is_in {
79 130     130 1 61839 my $self = shift;
80 130         212 my $combination = shift;
81 130         659 return exists $self->{'_partitions'}{$combination};
82             }
83              
84             sub add_combination {
85 1     1 1 10 my $self = shift;
86 1         3 my $new_combination = shift;
87 1 50       5 return if $self->is_in( $new_combination );
88 1         11 my $new_secret = new Algorithm::MasterMind::Secret $new_combination;
89 1         3 for (my $i = 0; $i < @{$self->{'_combinations'}}; $i ++ ) {
  33         104  
90 32         86 my $result = { blacks => 0,
91             whites => 0 };
92 32         124 $self->{'_combinations'}[$i]->check_secret ( $new_secret, $result );
93 32         131 $self->{'_partitions'}{$self->{'_combinations'}[$i]->{'_string'}}{result_as_string($result)}++;
94 32         88 $self->{'_partitions'}{$new_combination}{result_as_string($result)}++;
95             }
96 1         3 push @{$self->{'_combinations'}}, $new_secret;
  1         5  
97             }
98              
99             sub result_as_string {
100 1749306     1749306 1 2200709 my $result = shift;
101 1749306         41529993 return $result->{'blacks'}."b-".$result->{'whites'}."w";
102             }
103              
104             sub partitions_for {
105 8     8 1 2461 my $self = shift;
106 8         19 my $string = shift;
107 8         21079 return $self->{'_partitions'}->{$string};
108             }
109              
110             sub cull_inconsistent_with {
111 6     6 1 186 my $self = shift;
112 6         1204 my $string = shift;
113 6         18 my $result = shift;
114              
115 6         45 my $secret = new Algorithm::MasterMind::Secret $string;
116 6         590 my $result_string = result_as_string( $result );
117 6         1515 my @new_set;
118 6         14 for my $s (@{$self->{'_combinations'}} ) {
  6         775  
119 1694         5178 my $this_result = { blacks => 0,
120             whites => 0 };
121 1694         7000 $secret->check_secret( $s, $this_result);
122             # print "Checking ", $s->string, " result " , result_as_string( $this_result), " with $result_string\n";
123 1694 100       4589 if ( $result_string eq result_as_string($this_result) ) {
124             # print "Added\n";
125 302         3469 push @new_set, $s;
126             }
127             }
128             #Compute new partitions
129 6         49 $self->{'_partitions'} = compute_partitions( \@new_set );
130 6         5647 $self->{'_combinations'} = \@new_set;
131 6         16447 $self->{'_score'} = {};
132             }
133              
134             sub compute_most_score {
135 6     6 1 1548 my $self = shift;
136 6         36 $self->{'_score'}->{'_most'} = {};
137 6         878 for my $s (keys %{$self->{'_partitions'}} ) {
  6         275571  
138 1688         8233 $self->{'_score'}->{'_most'}->{$s} = keys %{$self->{'_partitions'}->{$s}};
  1688         18292  
139             }
140             }
141              
142             sub compute_entropy_score {
143 1     1 1 445 my $self = shift;
144 1         6 $self->{'_score'}->{'_entropy'} = {};
145 1         3 for my $s (keys %{$self->{'_partitions'}} ) {
  1         66  
146 122         128 my $sum;
147 122         131 map( ($sum += $self->{'_partitions'}->{$s}->{$_}), keys %{$self->{'_partitions'}->{$s}} );
  122         1332  
148 122         236 my $entropy = 0;
149 122         151 for my $k ( keys %{$self->{'_partitions'}->{$s}} ) {
  122         431  
150 1287         2376 my $fraction = $self->{'_partitions'}->{$s}->{$k}/ $sum;
151 1287         2202 $entropy -= $fraction * log( $fraction );
152             }
153 122         443 $self->{'_score'}->{'_entropy'}->{$s} = $entropy;
154             }
155             }
156            
157             sub score_most {
158 2     2 1 8 my $self = shift;
159 2         4 my $str = shift;
160 2         13 return $self->{'_score'}->{'_most'}->{ $str };
161             }
162              
163             sub score_entropy {
164 0     0 1 0 my $self = shift;
165 0         0 my $str = shift;
166 0         0 return $self->{'_score'}->{'_entropy'}->{ $str };
167             }
168              
169             sub top_scorers {
170 7     7 1 51 my $self = shift;
171 7         32 my $score = "_".shift; # No checks
172 7         15 my @keys = keys %{$self->{'_partitions'}};
  7         1465  
173 7         832 my @top_scorers;
174 7 50       1198 if ( $#keys > 1 ) {
175 7         17 my $top_score = 0;
176 7         553 for my $s ( @keys ) {
177 1810         3706 my $this_score = $self->{'_score'}->{$score}->{ $s } ;
178 1810 100       4864 if ( $this_score > $top_score ) {
179 18         491 $top_score = $this_score;
180             }
181             }
182 7         699 for my $s ( @keys ) {
183 1810 100       5853 if ( $self->{'_score'}{$score}->{ $s } == $top_score ) {
184 1165         2388 push @top_scorers, $s;
185             }
186             }
187             } else { # either 0 or 1
188 0         0 @top_scorers = @keys;
189             }
190 7         613 return @top_scorers;
191             }
192              
193             sub consistent_strings {
194 1     1 1 886 return keys %{shift->{'_partitions'}};
  1         84  
195             }
196              
197             "As Jack the Ripper said..."; # Magic true value required at end of module
198              
199             __END__