Advertisement
saltycracker

SudokuSolver9x9.pm

Nov 8th, 2020 (edited)
452
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 8.13 KB | None | 0 0
  1. package SaltyCracker::SudokuSolver;
  2.  
  3. use 5.006;
  4. use strict;
  5. use warnings;
  6. use feature qw<say>;
  7. use boolean;
  8. use List::Util qw<any>;
  9. use Data::Dumper;
  10. use constant SUDOKU_SIZE => 81;
  11. use constant SUDOKU_ROW_SIZE => 9;
  12. use constant SUDOKU_STARTING_CHARACTERS => (0..9);
  13. use constant SUDOKU_BOX_SIZE => 3;
  14.  
  15. use parent qw<Exporter>;
  16.  
  17. our @EXPORT = qw<>;
  18. our @EXPORT_OK = qw<solveSudoku>;
  19. our %EXPORT_TAGS = (ALL => [@EXPORT, @EXPORT_OK]);
  20.  
  21. =head1 NAME
  22.  
  23. SaltyCracker::SudokuSolver - The great new SaltyCracker::SudokuSolver!
  24.  
  25. =head1 VERSION
  26.  
  27. Version 0.01
  28.  
  29. =cut
  30.  
  31. our $VERSION = '0.01';
  32.  
  33. my $sudokuStr;
  34. my @sudokuArray;
  35. my %edges;
  36. my %p_to_elem;
  37. my %elem_to_p;
  38. my $solution = 0;
  39.  
  40. =head1 SYNOPSIS
  41.  
  42. Quick summary of what the module does.
  43.  
  44. Perhaps a little code snippet.
  45.  
  46.     use SaltyCracker::SudokuSolver;
  47.  
  48.     my $foo = SaltyCracker::SudokuSolver->new();
  49.     ...
  50.  
  51. =head1 EXPORT
  52.  
  53. A list of functions that can be exported.  You can delete this section
  54. if you don't export anything, such as for a purely object-oriented module.
  55.  
  56. =head1 SUBROUTINES/METHODS
  57.  
  58. =head2 checkSudoArraySize
  59.  
  60. =cut
  61.  
  62. sub checkSudoArraySize {
  63.   my $c_str = "Check Sudoku Size";
  64.   say "[TEST] $c_str";
  65.   die "[FAILED] $c_str\n" unless @sudokuArray == SUDOKU_SIZE;
  66.   say "[PASSED] $c_str";
  67. }
  68.  
  69. =head2 checkSudoArrayStartingCharacters
  70.  
  71. =cut
  72.  
  73. sub checkSudoArrayStartingCharacters {
  74.   my $failed = false;
  75.   my $c_str = "Check Sudoku Starting Characters";
  76.   say "[TEST] $c_str";
  77.   while (my ($index, $value) = each @sudokuArray) {
  78.     do {
  79.       say "Invalid character '$value' at position ", ($index + 1);
  80.       $failed = true;
  81.     } unless any {$value eq $_} SUDOKU_STARTING_CHARACTERS;
  82.   }
  83.   die "[FAILED] $c_str\n" if $failed;
  84.   say "[PASSED] $c_str";
  85. }
  86.  
  87. =head2 buildPositionToElement
  88.  
  89. =cut
  90.  
  91. sub buildPositionToElement {
  92.   my $counter = 0;
  93.   for (my $i = 1; $i <= SUDOKU_ROW_SIZE; ++$i) {
  94.     for (my $j = 1; $j <= SUDOKU_ROW_SIZE; ++$j) {
  95.       $p_to_elem{$counter++} = $i.$j;
  96.     }
  97.   }
  98.   %elem_to_p = reverse %p_to_elem;
  99. }
  100.  
  101. =head2 buildValues
  102.  
  103. =cut
  104.  
  105. sub buildValues {
  106.   while (my ($e, $p) = each %elem_to_p) {
  107.     $edges{$e} = {
  108.       value => $sudokuArray[$p],
  109.       position => $p,
  110.       fixed => do {$sudokuArray[$p] ? true : false},
  111.     };
  112.   }
  113. }
  114.  
  115. =head2 buildEdges
  116.  
  117. =cut
  118.  
  119. sub buildEdges {
  120.   while (my ($k, $v) = each %edges) {
  121.     if (!$v->{fixed}) {
  122.       my @arr;
  123.       my ($f, $s) = split//,$k;
  124.       for (my $i = 1; $i <= SUDOKU_ROW_SIZE; ++$i) {
  125.         push (@arr, $f.$i) unless $f.$i eq $k;
  126.         push (@arr, $i.$s) unless $i.$s eq $k;
  127.       }
  128.       my $rm = $f % SUDOKU_BOX_SIZE;
  129.       my $cm = $s % SUDOKU_BOX_SIZE;
  130.       my @r;
  131.       my @c;
  132.       #1 => 1, 2
  133.       #2 => -1, 1
  134.       #0 => -2, -1
  135.       if ($rm == 1) {
  136.         @r = (1, 2);
  137.       }elsif ($rm == 2) {
  138.         @r = (-1, 1);
  139.       }else {
  140.         @r = (-2, -1);
  141.       }
  142.       if ($cm == 1) {
  143.         @c = (1, 2);
  144.       }elsif ($cm == 2) {
  145.         @c = (-1, 1);
  146.       }else {
  147.         @c = (-2, -1);
  148.       }
  149.       for my $i (@r) {
  150.         for my $j (@c) {
  151.           push(@arr, ($f + $i).($s + $j));
  152.         }
  153.       }
  154.       $v->{edges} = \@arr;
  155.     }
  156.   }
  157. }
  158.  
  159. =head2 buildReferences
  160.  
  161. =cut
  162.  
  163. sub buildReferences {
  164.   while (my ($k, $v) = each %edges) {
  165.     if (!$v->{fixed}) {
  166.       my @arr;
  167.       for my $e (@{$v->{edges}}) {
  168.         push(@arr, \$sudokuArray[$elem_to_p{$e}]);
  169.       }
  170.       $v->{references} = \@arr;
  171.     }
  172.   }
  173. }
  174.  
  175. =head2 buildStartingValues
  176.  
  177. =cut
  178.  
  179. sub buildStartingValues {
  180.   while (my ($k, $v) = each %edges) {
  181.     if (!$v->{fixed}) {
  182.       my @str = (1..9);
  183.       for my $e (@{$v->{references}}) {
  184.         @str = grep {/[^$$e]/} @str;
  185.       }
  186.       $v->{startingValues} = \@str;
  187.     }
  188.   }  
  189. }
  190.  
  191. =head2 solve
  192.  
  193. =cut
  194.  
  195. sub solve {
  196.   my $pos = 0;
  197.   my $e = $edges{$p_to_elem{$pos}};
  198.   if ($e->{fixed}) {
  199.     solveAux($pos + 1);
  200.   }else {
  201.     for my $v (@{$e->{startingValues}}) {
  202.       $sudokuArray[$pos] = $v;
  203.       solveAux($pos + 1);
  204.     }
  205.   }
  206. }
  207.  
  208. =head2 solveAux
  209.  
  210. =cut
  211.  
  212. sub solveAux {
  213.   my ($pos) = @_;
  214.   if ($pos < SUDOKU_SIZE) {
  215.     my $e = $edges{$p_to_elem{$pos}};
  216.     if ($e->{fixed}) {
  217.       solveAux($pos + 1);
  218.     }else {
  219.       for my $v (@{$e->{startingValues}}) {
  220.         next if any {$v == $$_} @{$e->{references}};
  221.         $sudokuArray[$pos] = $v;
  222.         solveAux($pos + 1);
  223.         $sudokuArray[$pos] = 0;
  224.       }
  225.     }
  226.   }else {
  227.     say "Solution: ", (++$solution), "\n";
  228.     while (my ($index, $elem) = each @sudokuArray) {
  229.       print $elem;
  230.       if (($index + 1) % (SUDOKU_BOX_SIZE * SUDOKU_ROW_SIZE) == 0) {
  231.         print "\n\n";
  232.       }elsif (($index + 1) % SUDOKU_ROW_SIZE == 0) {
  233.         print "\n";
  234.       }elsif (($index + 1) % SUDOKU_BOX_SIZE == 0) {
  235.         print " ";
  236.       }
  237.     }
  238.   }
  239. }
  240.  
  241. =head2 solveSudoku
  242.  
  243. =cut
  244.  
  245. sub solveSudoku {
  246.   my ($str) = @_;
  247.   chomp($sudokuStr = $str);
  248.   @sudokuArray = split//,$str;
  249.   checkSudoArraySize;
  250.   checkSudoArrayStartingCharacters;
  251.   buildPositionToElement;
  252.   buildValues;
  253.   buildEdges;
  254.   buildReferences;
  255.   buildStartingValues;
  256.   solve;
  257. }
  258.  
  259. =head1 AUTHOR
  260.  
  261. SaltyCracker, C<< <SaltyCracker at Hotmail.com> >>
  262.  
  263. =head1 BUGS
  264.  
  265. Please report any bugs or feature requests to C<bug-saltycracker-sudokusolver at rt.cpan.org>, or through
  266. the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=SaltyCracker-SudokuSolver>.  I will be notified, and then you'll
  267. automatically be notified of progress on your bug as I make changes.
  268.  
  269.  
  270.  
  271.  
  272. =head1 SUPPORT
  273.  
  274. You can find documentation for this module with the perldoc command.
  275.  
  276.     perldoc SaltyCracker::SudokuSolver
  277.  
  278.  
  279. You can also look for information at:
  280.  
  281. =over 4
  282.  
  283. =item * RT: CPAN's request tracker (report bugs here)
  284.  
  285. L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=SaltyCracker-SudokuSolver>
  286.  
  287. =item * AnnoCPAN: Annotated CPAN documentation
  288.  
  289. L<http://annocpan.org/dist/SaltyCracker-SudokuSolver>
  290.  
  291. =item * CPAN Ratings
  292.  
  293. L<http://cpanratings.perl.org/d/SaltyCracker-SudokuSolver>
  294.  
  295. =item * Search CPAN
  296.  
  297. L<http://search.cpan.org/dist/SaltyCracker-SudokuSolver/>
  298.  
  299. =back
  300.  
  301.  
  302. =head1 ACKNOWLEDGEMENTS
  303.  
  304.  
  305. =head1 LICENSE AND COPYRIGHT
  306.  
  307. Copyright 2020 SaltyCracker.
  308.  
  309. This program is free software; you can redistribute it and/or modify it
  310. under the terms of the the Artistic License (2.0). You may obtain a
  311. copy of the full license at:
  312.  
  313. L<http://www.perlfoundation.org/artistic_license_2_0>
  314.  
  315. Any use, modification, and distribution of the Standard or Modified
  316. Versions is governed by this Artistic License. By using, modifying or
  317. distributing the Package, you accept this license. Do not use, modify,
  318. or distribute the Package, if you do not accept this license.
  319.  
  320. If your Modified Version has been derived from a Modified Version made
  321. by someone other than you, you are nevertheless required to ensure that
  322. your Modified Version complies with the requirements of this license.
  323.  
  324. This license does not grant you the right to use any trademark, service
  325. mark, tradename, or logo of the Copyright Holder.
  326.  
  327. This license includes the non-exclusive, worldwide, free-of-charge
  328. patent license to make, have made, use, offer to sell, sell, import and
  329. otherwise transfer the Package with respect to any patent claims
  330. licensable by the Copyright Holder that are necessarily infringed by the
  331. Package. If you institute patent litigation (including a cross-claim or
  332. counterclaim) against any party alleging that the Package constitutes
  333. direct or contributory patent infringement, then this Artistic License
  334. to you shall terminate on the date that such litigation is filed.
  335.  
  336. Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
  337. AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
  338. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  339. PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
  340. YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
  341. CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
  342. CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
  343. EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  344.  
  345.  
  346. =cut
  347.  
  348. 1; # End of SaltyCracker::SudokuSolver
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement