File Coverage

lib/Games/Nonogram/Grid.pm
Criterion Covered Total %
statement 12 115 10.4
branch 0 52 0.0
condition 0 14 0.0
subroutine 4 20 20.0
pod 14 14 100.0
total 30 215 13.9


line stmt bran cond sub pod time code
1             package Games::Nonogram::Grid;
2            
3 1     1   4 use strict;
  1         2  
  1         24  
4 1     1   3 use warnings;
  1         2  
  1         19  
5 1     1   5 use base qw( Games::Nonogram::Base );
  1         11  
  1         61  
6            
7 1     1   354 use Games::Nonogram::Clue;
  1         3  
  1         1791  
8            
9             sub new {
10 0     0 1   my ($class, %options) = @_;
11            
12 0 0 0       my $height = $options{height} || $options{size} or die "illegal height";
13 0 0 0       my $width = $options{width} || $options{size} or die "illegal width";
14            
15 0           my @rows = map {
16 0           Games::Nonogram::Clue->new( id => "Row $_", size => $width )
17             } ( 1 .. $height );
18 0           my @cols = map {
19 0           Games::Nonogram::Clue->new( id => "Col $_", size => $height )
20             } ( 1 .. $width );
21            
22 0           my $self = bless {
23             height => $height,
24             width => $width,
25             rows => \@rows,
26             cols => \@cols,
27             has_answers => 0,
28             is_dirty => 1,
29             }, $class;
30             }
31            
32             sub new_from {
33 0     0 1   my ($class, $loader, @args) = @_;
34            
35 0           $loader = ucfirst $loader;
36            
37 0           my $pkg = "Games::Nonogram::Loader::$loader";
38 0           eval qq{ require $pkg };
39 0 0         die $@ if $@;
40            
41 0           my ($height, $width, @lines) = $pkg->load( @args );
42            
43 0           my $self = $class->new( height => $height, width => $width );
44            
45 0           $self->load( @lines );
46            
47 0           $self;
48             }
49            
50             sub load {
51 0     0 1   my ($self, @lines) = @_;
52            
53 0           my @clues = $self->clues;
54            
55 0           foreach my $line ( @lines ) {
56 0           chomp $line;
57 0 0         next unless $line =~ /^[\d,]+$/;
58            
59 0           my $clue = shift @clues;
60            
61 0 0         die "clues mismatch" unless ref $clue;
62            
63 0           $clue->set( split ',', $line );
64             }
65            
66 0 0         die "clues mismatch" if @clues;
67            
68 0           $self->clear_stash;
69 0           $self->is_dirty( 1 );
70 0           $self->{has_answers} = 0;
71             }
72            
73 0     0 1   sub rows { @{ shift->{rows} } }
  0            
74 0     0 1   sub cols { @{ shift->{cols} } }
  0            
75 0     0 1   sub clues { my $self = shift; return ( $self->rows, $self->cols ) }
  0            
76 0     0 1   sub row { my ($self, $id) = @_; $self->{rows}->[$id - 1]; }
  0            
77 0     0 1   sub col { my ($self, $id) = @_; $self->{cols}->[$id - 1]; }
  0            
78            
79             sub is_dirty {
80 0     0 1   my $self = shift;
81 0 0         @_ ? $self->{is_dirty} = shift : $self->{is_dirty};
82             }
83            
84             sub as_string {
85 0     0 1   my $self = shift;
86            
87 0           my $str = '';
88 0           foreach my $row ( $self->rows ) {
89 0           $str .= sprintf "%s\n", $row->as_string;
90             }
91 0 0         if ( $self->debug ) {
92 0           $str .= "\n";
93            
94 0           foreach my $col ( $self->cols ) {
95 0           $str .= sprintf "%s\n", $col->as_string;
96             }
97             }
98            
99 0 0         defined wantarray ? return $str : print $str;
100             }
101            
102             sub update {
103 0     0 1   my ($self, $mode) = @_;
104            
105 0           $self->log( 'updating' );
106            
107 0           $self->is_dirty( 0 );
108 0           foreach my $row ( 1 .. $self->{height} ) {
109 0           my $clue = $self->row( $row );
110            
111 0 0 0       next if $clue->is_done && !$clue->line->is_dirty;
112            
113 0           $self->_update( $clue, $mode );
114            
115 0           foreach my $col ( $clue->line->dirty_items ) {
116 0           $self->is_dirty( 1 );
117 0           $self->_update_dirty_item(
118             $self->col( $col ),
119             $row,
120             $clue->line->value( $col )
121             );
122             }
123             }
124            
125 0           foreach my $col ( 1 .. $self->{width} ) {
126 0           my $clue = $self->col( $col );
127            
128 0 0 0       next if $clue->is_done && !$clue->line->is_dirty;
129            
130 0           $self->_update( $clue, $mode );
131            
132 0           foreach my $row ( $clue->line->dirty_items ) {
133 0           $self->is_dirty( 1 );
134 0           $self->_update_dirty_item(
135             $self->row( $row ),
136             $col,
137             $clue->line->value( $row )
138             );
139             }
140             }
141 0 0         return if $self->is_dirty;
142 0 0         return if $self->is_done;
143            
144 0 0         unless ( $mode ) {
    0          
145 0           $self->update( 'more' );
146             }
147             elsif ( $mode eq 'more' ) {
148 0           require Games::Nonogram::BruteForce;
149 0           Games::Nonogram::BruteForce->run( $self );
150            
151 0 0         if ( $self->answers ) {
152 0           $self->{has_answers} = 1;
153             }
154             }
155             }
156            
157 0     0 1   sub has_answers { shift->{has_answers} }
158            
159             sub answers {
160 0     0 1   my $self = shift;
161            
162 0           my %seen;
163 0 0         my @answers = grep { defined && !$seen{$_}++ }
  0 0          
164 0           @{ $self->stash->{answers} || [] };
165             }
166            
167             sub is_done {
168 0     0 1   my $self = shift;
169            
170 0           foreach my $clue ( $self->clues ) {
171 0 0         return unless $clue->is_done;
172             }
173 0 0         unless ( $self->{has_answers} ) {
174 0           $self->{has_answers} = 1;
175 0   0       push @{ $self->stash->{answers} ||= [] }, $self->as_string;
  0            
176             }
177 0           return 1;
178             }
179            
180             sub _update {
181 0     0     my ($self, $clue, $mode) = @_;
182            
183 0           my ($before, $after);
184 0 0         if ( $self->debug ) {
185 0           $before = $clue->dump_blocks;
186 0           $self->log( $before );
187             }
188            
189 0           $clue->update( $mode );
190            
191 0 0         if ( $self->debug ) {
192 0           $after = $clue->dump_blocks;
193 0 0         $self->log( "TO: \n$after" ) if $before ne $after;
194             }
195             }
196            
197             sub _update_dirty_item {
198 0     0     my ($self, $clue, $id, $value) = @_;
199            
200 0 0         return unless $clue->value( $id ) != $value;
201            
202 0 0         $self->log( $clue->dump_blocks ) if $self->debug;
203            
204 0           $clue->value( $id, $value );
205            
206 0 0         $self->log( "TO:\n", $clue->dump_blocks ) if $self->debug;
207             }
208            
209             1;
210            
211             __END__