Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package SaltyCracker::SudokuSolver;
- use 5.006;
- use strict;
- use warnings;
- use feature qw<say>;
- use boolean;
- use List::Util qw<any>;
- use Data::Dumper;
- use constant SUDOKU_SIZE => 81;
- use constant SUDOKU_ROW_SIZE => 9;
- use constant SUDOKU_STARTING_CHARACTERS => (0..9);
- use constant SUDOKU_BOX_SIZE => 3;
- use parent qw<Exporter>;
- our @EXPORT = qw<>;
- our @EXPORT_OK = qw<solveSudoku>;
- our %EXPORT_TAGS = (ALL => [@EXPORT, @EXPORT_OK]);
- =head1 NAME
- SaltyCracker::SudokuSolver - The great new SaltyCracker::SudokuSolver!
- =head1 VERSION
- Version 0.01
- =cut
- our $VERSION = '0.01';
- my $sudokuStr;
- my @sudokuArray;
- my %edges;
- my %p_to_elem;
- my %elem_to_p;
- my $solution = 0;
- =head1 SYNOPSIS
- Quick summary of what the module does.
- Perhaps a little code snippet.
- use SaltyCracker::SudokuSolver;
- my $foo = SaltyCracker::SudokuSolver->new();
- ...
- =head1 EXPORT
- A list of functions that can be exported. You can delete this section
- if you don't export anything, such as for a purely object-oriented module.
- =head1 SUBROUTINES/METHODS
- =head2 checkSudoArraySize
- =cut
- sub checkSudoArraySize {
- my $c_str = "Check Sudoku Size";
- say "[TEST] $c_str";
- die "[FAILED] $c_str\n" unless @sudokuArray == SUDOKU_SIZE;
- say "[PASSED] $c_str";
- }
- =head2 checkSudoArrayStartingCharacters
- =cut
- sub checkSudoArrayStartingCharacters {
- my $failed = false;
- my $c_str = "Check Sudoku Starting Characters";
- say "[TEST] $c_str";
- while (my ($index, $value) = each @sudokuArray) {
- do {
- say "Invalid character '$value' at position ", ($index + 1);
- $failed = true;
- } unless any {$value eq $_} SUDOKU_STARTING_CHARACTERS;
- }
- die "[FAILED] $c_str\n" if $failed;
- say "[PASSED] $c_str";
- }
- =head2 buildPositionToElement
- =cut
- sub buildPositionToElement {
- my $counter = 0;
- for (my $i = 1; $i <= SUDOKU_ROW_SIZE; ++$i) {
- for (my $j = 1; $j <= SUDOKU_ROW_SIZE; ++$j) {
- $p_to_elem{$counter++} = $i.$j;
- }
- }
- %elem_to_p = reverse %p_to_elem;
- }
- =head2 buildValues
- =cut
- sub buildValues {
- while (my ($e, $p) = each %elem_to_p) {
- $edges{$e} = {
- value => $sudokuArray[$p],
- position => $p,
- fixed => do {$sudokuArray[$p] ? true : false},
- };
- }
- }
- =head2 buildEdges
- =cut
- sub buildEdges {
- while (my ($k, $v) = each %edges) {
- if (!$v->{fixed}) {
- my @arr;
- my ($f, $s) = split//,$k;
- for (my $i = 1; $i <= SUDOKU_ROW_SIZE; ++$i) {
- push (@arr, $f.$i) unless $f.$i eq $k;
- push (@arr, $i.$s) unless $i.$s eq $k;
- }
- my $rm = $f % SUDOKU_BOX_SIZE;
- my $cm = $s % SUDOKU_BOX_SIZE;
- my @r;
- my @c;
- #1 => 1, 2
- #2 => -1, 1
- #0 => -2, -1
- if ($rm == 1) {
- @r = (1, 2);
- }elsif ($rm == 2) {
- @r = (-1, 1);
- }else {
- @r = (-2, -1);
- }
- if ($cm == 1) {
- @c = (1, 2);
- }elsif ($cm == 2) {
- @c = (-1, 1);
- }else {
- @c = (-2, -1);
- }
- for my $i (@r) {
- for my $j (@c) {
- push(@arr, ($f + $i).($s + $j));
- }
- }
- $v->{edges} = \@arr;
- }
- }
- }
- =head2 buildReferences
- =cut
- sub buildReferences {
- while (my ($k, $v) = each %edges) {
- if (!$v->{fixed}) {
- my @arr;
- for my $e (@{$v->{edges}}) {
- push(@arr, \$sudokuArray[$elem_to_p{$e}]);
- }
- $v->{references} = \@arr;
- }
- }
- }
- =head2 buildStartingValues
- =cut
- sub buildStartingValues {
- while (my ($k, $v) = each %edges) {
- if (!$v->{fixed}) {
- my @str = (1..9);
- for my $e (@{$v->{references}}) {
- @str = grep {/[^$$e]/} @str;
- }
- $v->{startingValues} = \@str;
- }
- }
- }
- =head2 solve
- =cut
- sub solve {
- my $pos = 0;
- my $e = $edges{$p_to_elem{$pos}};
- if ($e->{fixed}) {
- solveAux($pos + 1);
- }else {
- for my $v (@{$e->{startingValues}}) {
- $sudokuArray[$pos] = $v;
- solveAux($pos + 1);
- }
- }
- }
- =head2 solveAux
- =cut
- sub solveAux {
- my ($pos) = @_;
- if ($pos < SUDOKU_SIZE) {
- my $e = $edges{$p_to_elem{$pos}};
- if ($e->{fixed}) {
- solveAux($pos + 1);
- }else {
- for my $v (@{$e->{startingValues}}) {
- next if any {$v == $$_} @{$e->{references}};
- $sudokuArray[$pos] = $v;
- solveAux($pos + 1);
- $sudokuArray[$pos] = 0;
- }
- }
- }else {
- say "Solution: ", (++$solution), "\n";
- while (my ($index, $elem) = each @sudokuArray) {
- print $elem;
- if (($index + 1) % (SUDOKU_BOX_SIZE * SUDOKU_ROW_SIZE) == 0) {
- print "\n\n";
- }elsif (($index + 1) % SUDOKU_ROW_SIZE == 0) {
- print "\n";
- }elsif (($index + 1) % SUDOKU_BOX_SIZE == 0) {
- print " ";
- }
- }
- }
- }
- =head2 solveSudoku
- =cut
- sub solveSudoku {
- my ($str) = @_;
- chomp($sudokuStr = $str);
- @sudokuArray = split//,$str;
- checkSudoArraySize;
- checkSudoArrayStartingCharacters;
- buildPositionToElement;
- buildValues;
- buildEdges;
- buildReferences;
- buildStartingValues;
- solve;
- }
- =head1 AUTHOR
- SaltyCracker, C<< <SaltyCracker at Hotmail.com> >>
- =head1 BUGS
- Please report any bugs or feature requests to C<bug-saltycracker-sudokusolver at rt.cpan.org>, or through
- the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=SaltyCracker-SudokuSolver>. I will be notified, and then you'll
- automatically be notified of progress on your bug as I make changes.
- =head1 SUPPORT
- You can find documentation for this module with the perldoc command.
- perldoc SaltyCracker::SudokuSolver
- You can also look for information at:
- =over 4
- =item * RT: CPAN's request tracker (report bugs here)
- L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=SaltyCracker-SudokuSolver>
- =item * AnnoCPAN: Annotated CPAN documentation
- L<http://annocpan.org/dist/SaltyCracker-SudokuSolver>
- =item * CPAN Ratings
- L<http://cpanratings.perl.org/d/SaltyCracker-SudokuSolver>
- =item * Search CPAN
- L<http://search.cpan.org/dist/SaltyCracker-SudokuSolver/>
- =back
- =head1 ACKNOWLEDGEMENTS
- =head1 LICENSE AND COPYRIGHT
- Copyright 2020 SaltyCracker.
- This program is free software; you can redistribute it and/or modify it
- under the terms of the the Artistic License (2.0). You may obtain a
- copy of the full license at:
- L<http://www.perlfoundation.org/artistic_license_2_0>
- Any use, modification, and distribution of the Standard or Modified
- Versions is governed by this Artistic License. By using, modifying or
- distributing the Package, you accept this license. Do not use, modify,
- or distribute the Package, if you do not accept this license.
- If your Modified Version has been derived from a Modified Version made
- by someone other than you, you are nevertheless required to ensure that
- your Modified Version complies with the requirements of this license.
- This license does not grant you the right to use any trademark, service
- mark, tradename, or logo of the Copyright Holder.
- This license includes the non-exclusive, worldwide, free-of-charge
- patent license to make, have made, use, offer to sell, sell, import and
- otherwise transfer the Package with respect to any patent claims
- licensable by the Copyright Holder that are necessarily infringed by the
- Package. If you institute patent litigation (including a cross-claim or
- counterclaim) against any party alleging that the Package constitutes
- direct or contributory patent infringement, then this Artistic License
- to you shall terminate on the date that such litigation is filed.
- Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
- AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
- THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
- PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
- YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
- CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
- CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
- EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- =cut
- 1; # End of SaltyCracker::SudokuSolver
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement