I've been doing a lot of work with Sudoku this week because the next issue of The Perl Review it almost entirely devoted to it. We'll have things to make the puzzle and things to solve it.

Eric Maki's puzzle generator output text, so I wanted to turn that into something a bit nicer. I figured it would be a snap for PDF::API2, and it mostly was when I figured out what the methods actually did.

I was surprised that I couldn't find more PDF::API2 examples, so I offer this one, with some notes at the end.

#!/usr/bin/perl =head1 NAME sudoku_maker - create Sudoku puzzles with PDF::API2 =head1 SYNOPSIS % perl sudoku_maker > sudoku.pdf - - 6 8 - 4 - - - - - - - 9 - 7 - 8 - - - 5 - - - 9 - 1 - - - 4 - - - 9 - - - - - - 5 - - 4 6 - - - 1 - 3 - 8 7 - - - - 4 - - - - - - 5 - 2 - - - - - - - 2 - 1 - =head1 DESCRIPTION This is a proof-of-concept script. Eric Maki created a Sudoku puzzle generator, but he output the text you see in the SYNOPSIS. I wanted to turn that into a nice puzzle so I started tinkering with PDF::API2. Eric's source will be part of the Spring 2006 issue of The Perl Review +. If you want to change the input, change C<get_puzzle> to parse it correctly. =head1 TO DO =over 4 =item * most things can be configurable, but I hardcoded them =item * i'd like to generate several puzzles per page =item * the C<place_digit> routine is a bit of guess work for font cen +tering. =back =head1 AUTHOR brian d foy, C<< <bdfoy@cpan.org> > =head1 COPYRIGHT and LICENSE Copyright 2006, brian d foy, All rights reserved. This software is available under the same terms as perl. =cut use strict; use warnings; use PDF::API2; use constant PAGE_WIDTH => 595; use constant PAGE_HEIGHT => 842; use constant MARGIN => 25; use constant WIDE_LINE_WIDTH => 3; use constant LINE_WIDTH => 2; use constant THIN_LINE_WIDTH => 1; use constant SQUARE_SIDE => 270; use constant FONT_SIZE => int( 0.70 * SQUARE_SIDE / 9 ); my $pdf = PDF::API2->new; my $font = $pdf->corefont( 'Helvetica-Bold' ); run() unless caller; sub run { $pdf->mediabox( PAGE_WIDTH, PAGE_HEIGHT ); my $gfx = $pdf->page->gfx; $gfx->strokecolor( '#000' ); $gfx->linewidth( WIDE_LINE_WIDTH ); make_grid( $gfx, ( PAGE_WIDTH - SQUARE_SIDE ) / 2 , # x PAGE_HEIGHT - SQUARE_SIDE - MARGIN, # y ); populate_puzzle( $gfx, get_puzzle() ); print $pdf->stringify; } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # sub populate_puzzle { my( $gfx, $array ) = @_; foreach my $row ( 0 .. $#$array ) { my $row_array = $array->[$row]; foreach my $column ( 0 .. $#$row_array ) { next unless defined $row_array->[$column]; place_digit( $gfx, $row, $column, $row_array->[$column] ) } } } sub place_digit { my( $gfx, $row, $column, $digit ) = @_; my $x_start = ( PAGE_WIDTH - SQUARE_SIDE ) / 2; my $y_start = PAGE_HEIGHT - SQUARE_SIDE - MARGIN; my $x_offset = 0.30 * SQUARE_SIDE / 9; # empirically derived my $y_offset = 0.25 * SQUARE_SIDE / 9; my $x = $x_start + $column * SQUARE_SIDE / 9 + $x_offset; my $y = $y_start + $row * SQUARE_SIDE / 9 + $y_offset; $gfx->textlabel( $x, $y, $font, FONT_SIZE, $digit ); } sub get_puzzle { my @array; print STDERR "Waiting for puzzle input!\n"; while( <STDIN> ) { chomp; s/^\s|\s$//g; next unless length $_; push @array, [ map { $_ eq '-' ? undef : $_ } split ]; } return \@array; } sub make_grid { my( $gfx, $lower_left_x, $lower_left_y ) = @_; make_outline( $gfx, $lower_left_x, $lower_left_y ); $gfx->linewidth( THIN_LINE_WIDTH ); make_blocks( $gfx, $lower_left_x, $lower_left_y, 9 ); $gfx->linewidth( LINE_WIDTH ); make_blocks( $gfx, $lower_left_x, $lower_left_y, 3 ); } sub make_blocks { my( $gfx, $lower_left_x, $lower_left_y, $cells ) = @_; my( $xs, $ys ) = map { my $point = $_; [ map { $point + $_ * SQUARE_SIDE / $cells } 1 .. $cells - + 1 ]; } ( $lower_left_x, $lower_left_y ); foreach my $x ( @$xs ) { make_line( $gfx, $x, $lower_left_y, $x, $lower_left_y + SQUARE_SIDE, ); } foreach my $y ( @$ys ) { make_line( $gfx, $lower_left_x, $y, $lower_left_x + SQUARE_SIDE, $y, ); } } sub make_outline { my( $gfx, $lower_left_x, $lower_left_y ) = @_; my( $upper_right_x, $upper_right_y ) = map { $_ + SQUARE_SIDE } ( $lower_left_x, $lower_left_y ); my @points = ( [ $lower_left_x, $lower_left_y - WIDE_LINE_WIDTH / 2, $lower_left_x, $upper_right_y ], [ $lower_left_x, $upper_right_y - WIDE_LINE_WIDTH / 2, $upper_right_x, $upper_right_y ], [ $upper_right_x - WIDE_LINE_WIDTH / 2, $upper_right_y, $upper_right_x, $lower_left_y ], [ $upper_right_x, $lower_left_y + WIDE_LINE_WIDTH / 2, $lower_left_x, $lower_left_y ], ); foreach my $tuple ( @points ) { make_line( $gfx, @$tuple ) } } sub make_line { my( $gfx, $x, $y, $x2, $y2 ) = @_; $gfx->move( $x, $y ); $gfx->line( $x2, $y2 ); $gfx->fillstroke; } __END__ - - 6 8 - 4 - - - - - - - 9 - 7 - 8 - - - 5 - - - 9 - 1 - - - 4 - - - 9 - - - - - - 5 - - 4 6 - - - 1 - 3 - 8 7 - - - - 4 - - - - - - 5 - 2 - - - - - - - 2 - 1 -

Some notes

Some of this is just my newness to the inner workings of PDF, and some to the interface issues. PDF::API2 is really a low level module, so I can't really complain. We're supposed to build stuff on top of it. Some of you may be able to elaborate on or correct these simple observations:

That's enough for 6 AM, I think. :)

--
brian d foy <brian@stonehenge.com>
Subscribe to The Perl Review

In reply to Making Sudoku Puzzles Using PDF::API2 by brian_d_foy

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • Outside of code tags, you may need to use entities for some characters:
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.