File Coverage

blib/lib/Spreadsheet/HTML/Presets/Sudoku.pm
Criterion Covered Total %
statement 7 49 14.2
branch 0 14 0.0
condition 0 7 0.0
subroutine 3 7 42.8
pod 1 1 100.0
total 11 78 14.1


line stmt bran cond sub pod time code
1             package Spreadsheet::HTML::Presets::Sudoku;
2 5     5   17 use strict;
  5         88  
  5         132  
3 5     5   16 use warnings FATAL => 'all';
  5         7  
  5         2811  
4              
5 5     5   721 eval "use Games::Sudoku::Component";
  0            
  0            
6             our $NO_SUDOKU = $@;
7              
8             sub sudoku {
9 0     0 1   my ($self,$data,$args);
10 0 0         $self = shift if ref($_[0]) =~ /^Spreadsheet::HTML/;
11 0 0         ($self,$data,$args) = $self ? $self->_args( @_ ) : Spreadsheet::HTML::_args( @_ );
12 0 0 0       $args->{attempts} = defined $args->{attempts} ? int($args->{attempts} || 0) : 4;
13 0   0       $args->{blanks} = int($args->{blanks} || 0) || 50;
14 0           $args->{size} = 9; # Games::Sudoku::Component only accepts perfect squares and only 9 is fast
15              
16 0           $data = [];
17 0           my @cells;
18 0           my ($solved,$unsolved) = ('','');
19 0 0         unless ($NO_SUDOKU) {
20 0           my $board = Games::Sudoku::Component->new( size => $args->{size} );
21 0   0       while (!$board->is_solved and $args->{attempts}-- > 0) {
22 0           $board->generate( blanks => $args->{blanks} );
23 0           $unsolved = $board->as_string;
24 0           $board->solve;
25             }
26              
27 0           my %td_attr = ( style => { border => 'solid thin', 'text-align' => 'center', padding => '.5em', 'font-family' => 'Lucida Grande' } );
28 0           my $auto = HTML::AutoTag->new;
29 0           my %input_attr = ( class => 'sudoku', size=> 1, style => { 'text-align' => 'center', border => '0px', 'font-size' => 'medium', color => 'red' } );
30              
31 0 0         if ($board->is_solved) {
32 0           $solved = $board->as_string;
33 0           my @lines = split /\n/, $unsolved;
34 0           for my $row (0 .. $#lines) {
35 0           my @chars = split /\s/, $lines[$row];
36 0           for my $col (0 .. $#chars) {
37 0           my $id = "${row}-${col}";
38 0 0   0     my $sub = $chars[$col] ? sub { $chars[$col] } : sub { $auto->tag( tag => 'input', attr => { %input_attr, id => "input-$id" } ) };
  0            
  0            
39 0           push @cells, ( "-r${row}c${col}" => [ { %td_attr, id => "td-$id" }, $sub ] );
40             }
41             }
42             }
43             else {
44 0           $data = [ ['Error'], ['could not find solution'], ['please try again'] ];
45             }
46             }
47             else {
48 0           $data = [ ['Error'], ['Games::Sudoku::Component not installed'] ];
49             }
50              
51 0           my $sqrt = int(sqrt( $args->{size} ));
52             my @args = (
53             @_,
54             @cells,
55             table => { id => 'sudoku', style => { 'border-collapse' => 'collapse' } },
56             tbody => { style => { border => 'solid medium' } },
57             tr => { id => [ map "sudoku-$_", 0 .. $sqrt - 1] },
58             colgroup => [ ({ style => { border => 'solid medium' } }) x $sqrt ],
59             col => [ ({}) x $sqrt ],
60             data => $data,
61 0           fill => sprintf( '%sx%s', ($args->{size}) x 2 ),
62             wrap => 0,
63             tgroups => 1,
64             group => $sqrt,
65             matrix => 1,
66             headless => 0,
67             theta => 0,
68             scroll => 0,
69             );
70              
71 0           my $js = _javascript( %$args );
72 0 0         my $table = $self ? $self->generate( @args ) : Spreadsheet::HTML::generate( @args );
73 0           return $js . $table;
74             }
75              
76             sub _javascript {
77 0     0     my %args = @_;
78              
79             my $js = sprintf _js_tmpl(),
80             $args{size},
81             $args{size},
82 0           ;
83              
84 0           return Spreadsheet::HTML::Presets::_js_wrapper( code => $js, %args );
85             }
86              
87             sub _js_tmpl {
88 0     0     return <<'END_JAVASCRIPT';
89              
90             /* Copyright 2017 Jeff Anderson */
91             /* install JavaScript::Minifier to minify this code */
92              
93             var ROW = %s;
94             var COL = %s;
95             var MATRIX;
96             var next_x = 0;
97             var next_y = 0;
98              
99             $(document).ready( function() {
100              
101             MATRIX = new Array();
102             for (var row = 0; row < ROW; row++) {
103             var rows = new Array();
104             for (var col = 0; col < ROW; col++) {
105             var id = row + '-' + col;
106             if ($('#input-' + id).attr( 'id' )) {
107             rows.push( $('#input-' + id).val() );
108             } else {
109             rows.push( $('#td-' + id).html() );
110             }
111             }
112             MATRIX.push( rows );
113             }
114              
115             $('input.sudoku').keyup(function () {
116             this.value = this.value.replace( /[^0-9]/g, '' );
117              
118             var matches = this.id.match( /(\d+)-(\d+)/ );
119             var id_r = matches[1];
120             var id_c = matches[2];
121              
122             var seen_r = {};
123             var seen_c = {};
124             for (var i = 0; i < ROW; i++) {
125             seen_r[ MATRIX[id_r][i] ] = true;
126             seen_c[ MATRIX[i][id_c] ] = true;
127             }
128              
129             if (seen_r[this.value] || seen_c[this.value]) {
130             this.value = '';
131             }
132              
133             MATRIX[matches[1]][matches[2]] = this.value;
134             });
135              
136             });
137              
138             END_JAVASCRIPT
139             }
140              
141             =head1 NAME
142              
143             Spreadsheet::HTML::Presets::Sudoku - Generates 9x9 sudoku boards via HTML tables.
144              
145             =head1 DESCRIPTION
146              
147             This is a container for L preset methods.
148             These methods are not meant to be called from this package.
149             Instead, use the Spreadsheet::HTML interface:
150              
151             use Spreadsheet::HTML;
152             my $generator = Spreadsheet::HTML->new;
153             print $generator->sudoku;
154              
155             # or
156             use Spreadsheet::HTML qw( sudoku );
157             print sudoku();
158              
159             =head1 METHODS
160              
161             =over 4
162              
163             =item * C
164              
165             Generates a static unsolved 9x9 sudoku board. You must have
166             L installed, which currently
167             has no dependencies and is very fast and reliable. You can
168             specify how many cells to leave unsolved with the C param.
169              
170             sudoku( blanks => 50 )
171              
172             Four attempts are made to find a solveable board, you can
173             override that default with the C param.
174              
175             sudoku( attempts => 1 )
176              
177             =back
178              
179             =head1 SEE ALSO
180              
181             =over 4
182              
183             =item L
184              
185             The interface for this functionality.
186              
187             =item L
188              
189             More presets.
190              
191             =back
192              
193             =head1 AUTHOR
194              
195             Jeff Anderson, C<< >>
196              
197             =head1 LICENSE AND COPYRIGHT
198              
199             Copyright 2017 Jeff Anderson.
200              
201             This program is free software; you can redistribute it and/or modify it
202             under the terms of the the Artistic License (2.0). You may obtain a
203             copy of the full license at:
204              
205             L
206              
207             Any use, modification, and distribution of the Standard or Modified
208             Versions is governed by this Artistic License. By using, modifying or
209             distributing the Package, you accept this license. Do not use, modify,
210             or distribute the Package, if you do not accept this license.
211              
212             If your Modified Version has been derived from a Modified Version made
213             by someone other than you, you are nevertheless required to ensure that
214             your Modified Version complies with the requirements of this license.
215              
216             This license does not grant you the right to use any trademark, service
217             mark, tradename, or logo of the Copyright Holder.
218              
219             This license includes the non-exclusive, worldwide, free-of-charge
220             patent license to make, have made, use, offer to sell, sell, import and
221             otherwise transfer the Package with respect to any patent claims
222             licensable by the Copyright Holder that are necessarily infringed by the
223             Package. If you institute patent litigation (including a cross-claim or
224             counterclaim) against any party alleging that the Package constitutes
225             direct or contributory patent infringement, then this Artistic License
226             to you shall terminate on the date that such litigation is filed.
227              
228             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
229             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
230             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
231             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
232             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
233             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
234             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
235             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
236              
237             =cut
238              
239             1;