http://www.perlmonks.org/index.pl?node_id=343941

Description: This was ripped off from the Tk widget demo, but modified by me to run standalone, and with a gif or jpg of your choice. I had it laying around and this node -> Keep the kids amused prompted me to post it.
#!/usr/bin/perl -w

#updated April 20,2004 to accept a gif or jpg.
# 
# This program is described in the Perl/Tk column from Volume 1, Issue
+ 4 of 
# The Perl Journal (http://tpj.com/tpj), and is included in the Perl/T
+k 
# distribution with permission.   
# It has been modified by zentara to run outside the widget demo, 
# and use any gif image  

use Tk;
use Tk::Dialog;
use strict;
use subs qw(beep create_puz create_ui puz_fini move_piece new_puz rand
+omly xy);
use Tk::JPEG;
                            
my $IMAGE;                      # gif or jpg Photo image 
my $IMAGE_HEIGHT;               # image height 
my $IMAGE_WIDTH;                # image width 
my (@LEVELS) = (9, 16, 36, 64); # possible puzzle piece counts 
my $MW = MainWindow->new;       # program's main window 
my @ORDER;                      # random puzzle piece ordinals 
my $PIECES = $LEVELS[2];        # total puzzle piece count 
my $OLD_PIECES = -1;            # previous puzzle piece count 
my $PF;                         # puzzle Frame 
my @PUZ;                        # puzzle piece information 
my $SIDE;                       # pieces per side of puzzle 
my $SPACE;                      # shortcut to puzzle space piece 
my $SPACE_IMAGE;                # space piece image 
my $mf;                         # menubar 
my $file = shift || die "need gif or jpg graphic image $!";
my $update = 0;

create_ui;
create_puz;

sub beep {$MW->bell}

sub create_puz {

    return if $PIECES == $OLD_PIECES;


    if (Exists $PF) {
        my $image;
        foreach (@PUZ) {
            $image = $_->cget(-image);
            $image = $SPACE_IMAGE if not defined $image;
            $image->delete;
        }
        $PF->destroy;
    }

    $PF = $MW->Frame->grid;     # create the puzzle frame grid master 
    $OLD_PIECES = $PIECES;
    $#PUZ = $#ORDER = $PIECES - 1;
    $SIDE = sqrt $PIECES;
                           
    my($i, $o, $c, $r, $w, $h, $x, $y, $but, $gif);
                           
    foreach (0..$#ORDER) {$ORDER[$_] = [$_, undef]}
    
    for($i = 0; $i <= $#PUZ; $i++) {
        $o = $ORDER[$i]->[0];
        ($c, $r) = xy $o;       # puzzle ordinal to column/row 
        $w = $IMAGE_WIDTH  / $SIDE;
        $h = $IMAGE_HEIGHT / $SIDE;
        $x = $c * $w;           # x/column pixel offset 
        $y = $r * $h;           # y/row    pixel offset 
        $gif = $PF->Photo;      # new, empty, GIF image 
        $gif->copy($IMAGE, -from => $x, $y, $x+$w, $y+$h);
        $but = $PF->Button(-image              => $gif,
                           -relief             => 'flat',
                           -borderwidth        => 0,
                           -command            => \&beep,
                           -highlightthickness => 0,
                           );
        $PUZ[$o] = $but;
        ($c, $r) = xy $i;
        $but->grid(-column => $c, -row => $r, -sticky => 'nsew');
        if ($o == 0) {
            $SPACE_IMAGE = $gif;
            $SPACE = $but;
        }
    } # forend all puzzle pieces 
    
} # end create_puz 

sub create_ui {

    # Create a color Photo image of the Xcamel puzzle. 

    $IMAGE = $MW->Photo(-file => $file);
    $IMAGE_WIDTH  = $IMAGE->image('width');
    $IMAGE_HEIGHT = $IMAGE->image('height');
                            
    # Create the menubar. 
if($update == 1){ $MW->update} else{
    $mf = $MW->Frame(-bg => 'blue')->grid(-sticky => 'ew');
    $mf->gridColumnconfigure(1, -weight => 1);
}
    my $mbf = $mf->Menubutton(-text => 'File', -relief => 'raised');
    $mbf->command(-label => 'New Puzzle', -command => \&new_puz);
    $mbf->separator;
    $mbf->command(-label => 'Quit', -command => sub {Tk::exit});

    my $mbp = $mf->Menubutton(-text => 'Prefs', -relief => 'raised');
    my $pieces = 'Pieces';
    $mbp->cascade(-label => $pieces);
    my $mbpm = $mbp->cget(-menu);
    my $mbpmp = $mbpm->Menu;
    $mbp->entryconfigure($pieces, -menu => $mbpmp);
    foreach (@LEVELS) {
        $mbpmp->radiobutton(-label    => $_,
                            -variable => \$PIECES,
                            -value    => $_,
                            -command  => sub{
                                     $update = 1;
                                     &create_ui;
                                     &create_puz
                                 },

                            );
         }

    my $mbq = $mf->Menubutton(-text => 'Help', -relief => 'raised');
    my $about = $MW->Dialog(-text => <<"END"
npuz Version 1.0\n
Select \"File/New Puzzle\", then click around the red \"space\" to rea
+rrange the pieces an
END
    );
    $about->configure(-wraplength => '6i');
    $mbq->command(-label => 'About', -command => [$about => 'Show']);

    $mbf->grid(-row => 0, -column => 0, -sticky => 'w');
    $mbp->grid(-row => 0, -column => 1, -sticky => 'w');
    $mbq->grid(-row => 0, -column => 2, -sticky => 'e');


} # end create_ui 

sub puz_fini {

    # Return true iff all puzzle pieces are in order. 

    my($i, $c, $r, %info);
    for($i = 0; $i <= $#PUZ; $i++) {
        ($c, $r) = xy $i;
        %info = $PUZ[$i]->gridInfo;
        return 0 if $c != $info{-column} or $r != $info{-row};
    }
    return 1;

} # end puz_fini 

sub move_piece {

    my($piece) = @_;

    my(%info, $c, $r, $sc, $sr);
    %info = $piece->gridInfo; ($c, $r)   = @info{-column,-row};
    %info = $SPACE->gridInfo; ($sc, $sr) = @info{-column,-row};
    if ( ($sr == $r and ($sc == $c-1 or $sc == $c+1)) or
         ($sc == $c and ($sr == $r-1 or $sr == $r+1)) ) {
        $SPACE->grid(-column => $c,  -row => $r);
        $piece->grid(-column => $sc, -row => $sr);
    }
    if (puz_fini) {
        my $color = ($SPACE->configure(-activebackground))[3];
        $SPACE->configure(-image            => $SPACE_IMAGE,
                          -activebackground => $color,
                          -background       => $color,
                          -relief           => 'flat',
                          );
        foreach (@PUZ) {$_->configure(-command => \&beep)}
    }

} # end move_piece 
sub new_puz {
    srand time;
    foreach (0..$#ORDER) {$ORDER[$_]->[1] = rand $#ORDER}
    my @order = sort randomly @ORDER;
    #@order = @ORDER; # here's how I solve the puzzle (; 
    my($i, $o, $c, $r, $but);

    for($i = 0; $i <= $#PUZ; $i++) {
        $o = $order[$i]->[0];
        $but = $PUZ[$o];
        if ($o == 0) {
            $but->configure(-background       => 'red',
                            -relief           => 'sunken',
                            -image            => undef,
                            -activebackground => 'red',
                            );
        } else {
            $but->configure(-command => [\&move_piece, $but]);
        }
        ($c, $r)   = xy $i;
        $but->grid(-column => $c, -row => $r, -sticky => 'nsew');
    }

} # end new_puz 

sub randomly {$a->[1] <=> $b->[1]} # randomize order of puzzle pieces 

sub xy {my($n) = @_; ($n % $SIDE, int $n / $SIDE)} # ordinal to X/Y 

MainLoop;


comment on tk-picture-scramble-puzzle
Download Code
Re: tk-picture-scramble-puzzle
by muralikrishna on Apr 20, 2004 at 12:21 UTC
    Nice job zentata. This does not read JPEGs, what needs to be done for that???
 [reply]
      Put use Tk::JPEG; on top of the script and install the Tk::JPEG or Tk::JPEG::Lite module from CPAN or via PPM. With Tk804.* Tk::JPEG is already part of the standard Tk module set.
 [reply]
d/l code
Re: tk-picture-scramble-puzzle
by zentara on Apr 20, 2004 at 14:19 UTC
    Yeah, that's easy to do, like eserte shows. I will modify the snippet. :-)

    I'm not really a human, but I play one on earth. flash japh
 [reply]
      But I think the existance of Tk::JPEG should be optional, e.g. with these lines:
      eval q{ use Tk::JPEG }; warn "No JPEG support" if $@;
      eval q{ use Tk::PNG  }; warn "No PNG support"  if $@;
      eval q{ use Tk::TIFF }; warn "No TIFF support" if $@;
      
      with ot without the warnings...
 [reply]
d/l code

Back to Snippets Section