File Coverage

blib/lib/Games/Sudoku/Component/Controller.pm
Criterion Covered Total %
statement 98 111 88.2
branch 25 32 78.1
condition 7 15 46.6
subroutine 19 21 90.4
pod 14 14 100.0
total 163 193 84.4


line stmt bran cond sub pod time code
1             package Games::Sudoku::Component::Controller;
2             {
3 3     3   44348 use strict;
  3         9  
  3         134  
4 3     3   19 use warnings;
  3         6  
  3         98  
5 3     3   18 use Carp;
  3         6  
  3         361  
6            
7             our $VERSION = '0.02';
8            
9 3     3   2683 use Games::Sudoku::Component::Table;
  3         9  
  3         99  
10 3     3   43 use Games::Sudoku::Component::Table::Item;
  3         6  
  3         80  
11 3     3   2133 use Games::Sudoku::Component::Controller::History;
  3         8  
  3         90  
12 3     3   3176 use Games::Sudoku::Component::Controller::Status;
  3         9  
  3         3828  
13            
14             sub new {
15 6     6 1 1518 my $class = shift;
16 6   33     44 my $this = bless {}, (ref $class || $class);
17            
18 6 50       31 my %options = ref $_[0] ? %{ $_[0] } : @_;
  0         0  
19            
20 6         61 $this->{table} = Games::Sudoku::Component::Table->new(
21             size => $options{size},
22             block_width => $options{block_width},
23             block_height => $options{block_height},
24             );
25            
26 6         97 my $size = $this->{table}->size;
27            
28 6   33     145 $this->{status} = Games::Sudoku::Component::Controller::Status->new(
      33        
29             rewind_max => $options{rewind_max} || $size,
30             retry_max => $options{retry_max} || int(sqrt($size)),
31             );
32 6         43 $this->{history} = Games::Sudoku::Component::Controller::History->new;
33            
34 6         34 $this;
35             }
36            
37 16     16 1 5812 sub table { $_[0]->{table}; }
38 1     1 1 54 sub history { $_[0]->{history}; }
39 1     1 1 6 sub status { $_[0]->{status}; }
40            
41             sub clear {
42 1     1 1 722 my $this = shift;
43            
44 1         6 $this->{table}->clear;
45 1         7 $this->{history}->clear;
46 1         8 $this->{status}->clear;
47             }
48            
49             sub load {
50 1     1 1 7 my $this = shift;
51            
52 1         1004 require Games::Sudoku::Component::Controller::Loader;
53 1         10 my @cells = Games::Sudoku::Component::Controller::Loader->load(@_);
54            
55 1         4 my $table = $this->{table};
56 1         3 foreach my $item (@cells) {
57 36         89 $table->cell($item->row,$item->col)->value($item->value);
58 36 100       132 $table->cell($item->row,$item->col)->lock if $item->value;
59             }
60 1         6 $this->{history}->clear;
61             }
62            
63             sub set {
64 0     0 1 0 my ($this, $row, $col, $value) = @_;
65            
66 0         0 my $table = $this->{table};
67 0         0 my @allowed = $table->cell($row,$col)->allowed;
68            
69 0         0 $table->cell($row,$col)->value($value);
70            
71 0         0 $table->check_tmpvalue($row,$col);
72            
73 0         0 my $item = Games::Sudoku::Component::Table::Item->new(
74             row => $row,
75             col => $col,
76             allowed => \@allowed,
77             value => $value,
78             );
79            
80 0         0 $this->{history}->push($item);
81             }
82            
83             sub find_and_set {
84 481     481 1 654 my ($this, $item) = @_;
85            
86 481         684 my $table = $this->{table};
87            
88 481   100     2153 $item ||= $table->find_next;
89            
90 481 100       1065 return 0 unless defined $item;
91            
92 460         1537 $table->cell($item->row,$item->col)->value($item->random_value);
93            
94 460         2278 $this->{history}->push($item);
95            
96 460         1034 return $item;
97             }
98            
99 0     0 1 0 sub find_hints { $_[0]->{table}->find_all; }
100            
101             sub next {
102 541     541 1 836 my $this = shift;
103            
104 541         797 my $status = $this->{status};
105            
106 541 100       1664 $status->turn_to_ok if $status->is_null;
107            
108 541         694 my $result;
109 541 100       1891 if ($status->is_ok) {
110 469         1319 $result = $this->find_and_set;
111            
112 469 100       1181 unless ($result) {
113 21 100       97 if ($this->{table}->is_finished) {
114 7         44 $status->turn_to_solved;
115             }
116             else {
117 14 100       68 if ($status->can_rewind) {
118 12         50 $status->turn_to_rewind;
119             }
120             else {
121 2 50       12 if ($status->can_retry) {
122 2         14 $this->rewind_all;
123 2         12 $status->turn_to_ok;
124             }
125             else {
126 0         0 $status->turn_to_giveup;
127             }
128             }
129             }
130             }
131             }
132 541 100       2479 if ($status->is_rewind) {
133 84         159 $result = $this->rewind;
134            
135 84 50       139 if ($result) {
136 84 100       212 if ($result->allowed) {
137 12         37 $this->find_and_set($result);
138 12         47 $status->turn_to_ok;
139             }
140             }
141             else {
142 0 0       0 if ($status->can_retry) {
143 0         0 $status->turn_to_ok;
144             }
145             else {
146 0         0 $status->turn_to_giveup;
147             }
148             }
149             }
150 541         6682 return $result;
151             }
152            
153             sub solve {
154 7     7 1 1307 my $this = shift;
155            
156 7         38 $this->{status}->clear;
157            
158 7         49 until($this->{status}->is_finished) {
159 541         1392 $this->next;
160             }
161             }
162            
163             sub make_blank {
164 2     2 1 9 my ($this, $count) = @_;
165            
166 2         6 my $table = $this->{table};
167 2         11 my $size = $table->size;
168            
169 2 50 33     20 croak "Invalid count: $count"
170             if $count > ($size ** 2) || $count < 1;
171            
172 2         13 $table->lock_all;
173            
174 2         10 foreach my $id (1..$count) {
175 62         115 my $row = int(rand($size)) + 1;
176 62         88 my $col = int(rand($size)) + 1;
177 62 100       253 redo unless $table->cell($row,$col)->value;
178 40         122 $table->cell($row,$col)->unlock;
179 40         118 my $prev = $table->cell($row,$col)->value;
180 40         120 $table->cell($row,$col)->value(0);
181             }
182            
183 2         29 $this->{status}->clear;
184 2         14 $this->{history}->clear;
185             }
186            
187             sub rewind {
188 206     206 1 249 my $this = shift;
189            
190 206         377 my $table = $this->{table};
191 206         581 my $item = $this->{history}->pop;
192            
193 206 50       865 $table->cell($item->row,$item->col)->value(0) if defined $item;
194            
195 206         609 $item;
196             }
197            
198             sub rewind_all {
199 4     4 1 25 my $this = shift;
200            
201 4         41 foreach (1..$this->{history}->count) {
202 121         243 $this->rewind;
203             }
204             }
205             }
206            
207             1;
208             __END__