#!/usr/bin/perl =head1 NAME pentominos - solver for the pentominos paving puzzle =head1 SYNOPSIS pentominos --help # usage help pentominos --list # list of builtin boards pentominos [--box | --raw | --html] [-solutions ] [board] Computes solutions for the pentomino paving puzzle. Solutions are displayed as boxed ASCII (the default), as raw ASCII (the pentomino letter names), or in HTML format. The board is either the name of a builtin board, or the name of a file containing a board description (according to the format described in L). Filename '-' is accepted and means standard input. =head1 AUTHOR Laurent Dami, C<< >> =head1 COPYRIGHT & LICENSE Copyright 2007 Laurent Dami, all rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut use strict; use warnings; use IO::Handle; use Getopt::Long; use Pod::Usage; use Games::Pentominos; # read board definitions from my %builtin_board = do { local $/; # "slurp mode" =~ /^(.+)\n # name of board ((?:.+\n)+) # lines of board /mgx; }; # parse command-line options my %option; GetOptions(\%option, qw/raw box html list help solutions=i/); my $solution_printer = $option{raw} ? \&raw : $option{html} ? \&htmlize : \&boxify; # the default $option{solutions} ||= -1; # no limit on number of solutions $option{help} and pod2usage(); $option{list} and printf "Builtin board names are : %s\n" , join " ", sort keys %builtin_board and exit; # choose board (either builtin or from file) my $board_name = $ARGV[0] || "6x10"; my $board = $builtin_board{$board_name}; if (!$board) { my $fh; for ($board_name) { $_ eq '-' and do {$fh = 'STDIN'; last;}; -f and do {open $fh, "<", $_ or die "open $_ : $!"; last}; die "no such board: $_"; # otherwise } local $/; # "slurp mode" $board = <$fh>; } # HTML needs a header and footer if ($option{html}) { html_header(); $SIG{INT} = sub {html_footer(); exit}; # footer get printed even if SIGINT } # compute solutions my $callback = wrap_callback($solution_printer, $option{solutions}); Games::Pentominos->solve($board, $callback); if ($option{html}) { html_footer(); } #====================================================================== # END OF MAIN PROGRAM #====================================================================== #---------------------------------------------------------------------- sub wrap_callback { # print solution, flush, and countdown # of solutions #---------------------------------------------------------------------- my ($solution_printer, $n_wanted) = @_; return sub { $solution_printer->(@_); STDOUT->flush; return --$n_wanted; # stop searching if reaching 0 } } #---------------------------------------------------------------------- sub raw { #---------------------------------------------------------------------- my ($placed, $n_solutions, $t_solution, $t_tot) = @_; printf "Solution %d (%.3f / tot %.3f / avg %.3f)\n%s\n", $n_solutions, $t_solution, $t_tot, $t_tot/$n_solutions, $placed; } #---------------------------------------------------------------------- sub boxify { #---------------------------------------------------------------------- my ($placed, $n_solutions, $t_solution, $t_tot) = @_; # transform into a 2-dimensional array, with additional border rows and cols my @rows = split /\n/, $placed; my $add_row = "." x length($rows[0]); my @cell = map {[split //, ".$_."]} ($add_row, @rows, $add_row); my $n_rows = @cell - 2; my $n_cols = @{$cell[0]} - 2; # build solution string, comparing adjacent cells on rows and cols my $sol = ""; for my $i (0 .. $n_rows) { for my $j (0 .. $n_cols) { my $cmp_x = $cell[$i][$j] eq $cell[$i][$j+1] ? "sx" : "dx"; my $cmp_y = $cell[$i][$j] eq $cell[$i+1][$j] ? "sy" : "dy"; my $cell = {sx => {sy => " ", dy => "__"}, dx => {sy => " |", dy => "_|"}}->{$cmp_x}{$cmp_y}; $sol .= $cell; } $sol .= "\n"; } # print and return printf "Solution %d (%.3f s. / tot %.3f s. / avg %.3f s.)\n%s\n", $n_solutions, $t_solution, $t_tot, $t_tot/$n_solutions, $sol; } #---------------------------------------------------------------------- sub htmlize { #---------------------------------------------------------------------- my ($placed, $n_solutions, $t_solution, $t_tot) = @_; $placed =~ s[(\w)][]g; # inside cells $placed =~ s[\.] []g; # outside cells $placed =~ s[(.*)][$1]g; # rows printf "

Solution %d (%.3f s. / tot %.3f s. / avg %.3f s.)

" . "%s
\n", $n_solutions, $t_solution, $t_tot, $t_tot/$n_solutions, $placed; } #---------------------------------------------------------------------- sub html_header { #---------------------------------------------------------------------- print <<_EOHTML; _EOHTML } #---------------------------------------------------------------------- sub html_footer { #---------------------------------------------------------------------- print "\n"; } __DATA__ 6x10 xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx 4x15 xxxxxxxxxxxxxxx xxxxxxxxxxxxxxx xxxxxxxxxxxxxxx xxxxxxxxxxxxxxx 3x20 xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx 8x8_corners .xxxxxx. xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx .xxxxxx. 8x8_center xxxxxxxx xxxxxxxx xxxxxxxx xxx..xxx xxx..xxx xxxxxxxx xxxxxxxx xxxxxxxx X ....xxxx.... ....xxxx.... ....xxxx.... xxxxxxxxxxxx xxxxxxxxxxxx xxxxxxxxxxxx ....xxxx.... ....xxxx.... ....xxxx.... Z xxxx........ xxxx........ xxxx........ xxxxxxxxxxxx xxxxxxxxxxxx xxxxxxxxxxxx ........xxxx ........xxxx ........xxxx U xxxx....xxxx xxxx....xxxx xxxx....xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxxxxxxxxxx W xxxxxxxx.... xxxxxxxx.... xxxxxxxx.... ....xxxxxxxx ....xxxxxxxx ....xxxxxxxx ........xxxx ........xxxx ........xxxx F xxxxxxxx.... xxxxxxxx.... xxxxxxxx.... ....xxxxxxxx ....xxxxxxxx ....xxxxxxxx ....xxxx.... ....xxxx.... ....xxxx.... T xxxxxxxxxxxx xxxxxxxxxxxx xxxxxxxxxxxx ....xxxx.... ....xxxx.... ....xxxx.... ....xxxx.... ....xxxx.... ....xxxx.... V xxxx........ xxxx........ xxxx........ xxxx........ xxxx........ xxxx........ xxxxxxxxxxxx xxxxxxxxxxxx xxxxxxxxxxxx L xxxx.... xxxx.... xxxx.... xxxx.... xxxx.... xxxx.... xxxx.... xxxx.... xxxx.... xxxxxxxx xxxxxxxx xxxxxxxx P xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxx.... xxxx.... xxxx....