File Coverage

blib/lib/Games/Sudoku/DLX.pm
Criterion Covered Total %
statement 65 65 100.0
branch 7 8 87.5
condition 3 5 60.0
subroutine 6 6 100.0
pod 1 2 50.0
total 82 86 95.3


line stmt bran cond sub pod time code
1             package Games::Sudoku::DLX;
2              
3 1     1   102356 use strict;
  1         2  
  1         28  
4 1     1   3 use warnings;
  1         18  
  1         67  
5              
6             our $VERSION = 0.02;
7              
8 1     1   398 use Algorithm::DLX;
  1         1974  
  1         38  
9              
10 1     1   8 use Exporter qw( import );
  1         2  
  1         859  
11             our @EXPORT_OK = qw( solve_sudoku ); # symbols to export on request
12              
13             sub sudoku_to_dlx {
14 4     4 0 14 my %params = @_;
15 4         9 my $puzzle = $params{puzzle};
16 4         7 my $regions = $params{regions};
17 4         24 my $dlx = Algorithm::DLX->new();
18              
19 4         185 my $order = @$puzzle;
20 4         7 my $number_of_regions = @$regions;
21              
22 4         6 my @cols;
23             # Each cell can only have one symbol
24 4         12 for my $r (0..$order - 1) {
25 30         2737 for my $c (0..$order - 1) {
26 234         4759 push @cols, $dlx->add_column("cell_$r$c");
27             }
28             }
29              
30             # Each symbol can only appear once in each row, column, and region
31 4         96 for my $r (1..$order) {
32 30         557 for my $c (0..$order - 1) {
33 234         5533 for my $region (@$regions) {
34 738         10844 my ($a, $b) = @$region;
35 738         991 my $block = (int($r/$a) * $a) + int($c/$b);
36 738         1442 push @cols, $dlx->add_column("r#$r c#$c R#$a,$b B#$block");
37             }
38             }
39             }
40              
41 4         96 for my $r (0..$order - 1) {
42 30         2121 for my $c (0..$order - 1) {
43 234 100       16229 if ($puzzle->[$r][$c]) {
44 122         144 my $n = $puzzle->[$r][$c];
45 122         119 my @columns;
46 122         175 for my $region (0..@$regions - 1) {
47 375         356 my ($a, $b) = @{$regions->[$region]};
  375         464  
48 375         645 my $block = (int($r/$a) * $a) + int($c/$b);
49              
50 375         523 push @columns, $cols[(($region+1) * $order**2) + ($block*$order)+($n-1)];
51             }
52              
53             # Add the cell column
54 122         180 push @columns, $cols[$r*$order+$c];
55 122         247 $dlx->add_row("$r $c $n", @columns);
56             } else {
57 112         176 for my $n (1..$order) {
58 846         68820 my @columns;
59 846         1490 for my $region (0..scalar @$regions - 1) {
60 2700         7455 my ($a, $b) = @{$regions->[$region]};
  2700         3830  
61 2700         4100 my $block = (int($r/$a) * $a) + int($c/$b);
62              
63 2700         4240 push @columns, $cols[(($region+1) * $order**2) + ($block*$order)+($n-1)];
64             }
65              
66             # Add the cell column
67 846         1140 push @columns, $cols[$r*$order+$c];
68 846         2252 $dlx->add_row("$r $c $n", @columns);
69             }
70             }
71             }
72             }
73              
74 4         376 return $dlx;
75             }
76              
77             # When we me this a module, this is what we will export
78             sub solve_sudoku {
79 6     6 1 142576 my %params = @_;
80              
81 6         13 my $puzzle = $params{puzzle};
82 6         15 my $regions = $params{regions};
83 6   50     42 my $number_of_solutions = $params{number_of_solutions} || 0;
84              
85             # validate the regions
86 6         11 my $puzzle_size = scalar @$puzzle;
87 6         14 for my $region (@$regions) {
88 17         27 my ($a, $b) = @$region;
89 17 100       53 die "Invalid region size: $a x $b for puzzle of size $puzzle_size\n" if $a * $b != @$puzzle;
90             }
91              
92             # validate the puzzle size
93 5         10 for my $row (@$puzzle) {
94 36 50       52 die "Invalid row size: @$row should have size $puzzle_size\n" if @$row != $puzzle_size;
95              
96             # validate the cell values
97 36         47 for my $cell (@$row) {
98 288 100 66     566 die "Invalid cell value: $cell should be between 0 and $puzzle_size\n" if $cell < 0 || $cell > $puzzle_size;
99             }
100             }
101              
102 4         29 my $dlx = sudoku_to_dlx(
103             regions => $regions,
104             puzzle => $puzzle,
105             );
106 4         18 my $solutions = $dlx->solve(
107             number_of_solutions => $number_of_solutions
108             );
109              
110 4         110896 return $solutions;
111             }
112              
113             1
114              
115             __END__