Skip to content

Instantly share code, notes, and snippets.

@YoOoster
Forked from gray/depuzzlefy.pl
Last active January 18, 2020 13:36
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save YoOoster/8ea3644bb24c531271188fa40c90879b to your computer and use it in GitHub Desktop.
Save YoOoster/8ea3644bb24c531271188fa40c90879b to your computer and use it in GitHub Desktop.
Solves sudoku and image jigsaw puzzles created by http://flash-gear.com/puzzle/ and https://www.jigidi.com/
use 5.012;
use strict;
use warnings;
use File::Temp qw(tempfile);
use IPC::System::Simple qw(capture system);
use LWP::UserAgent;
use URI::QueryParam;
use Win32::GUI;
use Data::Dumper;
use integer;
use constant false => 0;
use constant true => 1;
my $PicJigsaw = Win32::GUI::Bitmap->new("jigsaw.bmp");
my $PicSudoku = Win32::GUI::Bitmap->new("sudoku.bmp");
my @board;
my $solveCount = 0;
my $Window_Main;
my $Window_Jigsaw;
my $Window_Sudoku;
#########################################################
# Main Window #
#########################################################
sub Create_Window_Main {
my $W1 = Win32::GUI::Window->new(
-name => "WindowMain",
-title => "Depuzzlefy",
-size => [ 800, 475 ],
-pos => [ Win32::GUI::Width(Win32::GUI::GetDesktopWindow())/2-400, Win32::GUI::Height(Win32::GUI::GetDesktopWindow())/2-237.50 ],
-sizable => 0,
-hasmaximize => 0,
-maximizebox => 0,
-hasminimize => 0,
-minimizebox => 0,
);
$W1->AddButton(
-name => "ButtonMainJigsaw",
-text => "Jigsaw puzzles",
-align => "center",
-valign => "center",
-picture=> $PicJigsaw,
-pos => [ 25, 60 ],
-size => [ 350, 350],
);
$W1->AddButton(
-name => "ButtonMainSudoku",
-text => "Sudoku",
-align => "center",
-valign => "center",
-picture=> $PicSudoku,
-pos => [ 400, 60 ],
-size => [ 350, 350],
);
$W1->AddLabel(
-name => "LabelMainJigsaw",
-text => "Jigsaw Puzzles",
-pos => [175, 45],
-size => [350, 15],
);
$W1->AddLabel(
-name => "LabelMainSudoku",
-text => "Sudoku",
-pos => [550, 45],
-size => [350, 15],
);
return $W1;
}
sub WindowMain_Terminate {
return -1;
}
sub ButtonMainJigsaw_Click {
$Window_Main->Hide();
$Window_Jigsaw->DoModal();
}
sub ButtonMainSudoku_Click {
$Window_Main->Hide();
$Window_Sudoku->DoModal();
}
#########################################################
# Jigsaw Window #
#########################################################
sub Create_Window_Jigsaw {
my $W1 = Win32::GUI::Window->new(
-name => "WindowJigsaw",
-title => "Depuzzlefy - Jigsaw",
-size => [ 300, 150 ],
-pos => [ Win32::GUI::Width(Win32::GUI::GetDesktopWindow())/2-150, Win32::GUI::Height(Win32::GUI::GetDesktopWindow())/2-75 ],
-sizable=> 0,
-parent => $Window_Main,
-hasmaximize => 0,
-maximizebox => 0,
-hasminimize => 0,
-minimizebox => 0,
);
$W1->AddButton(
-name => "ButtonWJDepuzzlefy",
-text => "Depuzzlefy",
-pos => [ 20, 60 ],
-size => [ 250, 30],
);
$W1->AddLabel(
-name => "LabelWJUrl",
-text => "Insert URL",
-pos => [20, 20],
-size => [250, 20],
);
$W1->AddTextfield(
-name => "TextfieldWJUrl",
-pos => [20,40],
-size => [250, 20],
);
return $W1;
}
sub WindowJigsaw_Terminate {
$Window_Main->Show();
return -1;
}
sub ButtonWJDepuzzlefy_Click {
my $url = $Window_Jigsaw->TextfieldWJUrl->Text();
if ($url =~ m[flash-gear.com]gi) {
FlashGearRequest();
} elsif ($url =~ m[jigidi.com]gi) {
JigidiRequest();
} else {
$Window_Jigsaw->TextfieldWJUrl->SelectAll();
$Window_Jigsaw->TextfieldWJUrl->Clear();
print "Website isn't supported.'\n\n";
return;
}
}
#########################################################
# Sudoku Window #
#########################################################
sub Create_Window_Sudoku {
my $W1 = Win32::GUI::Window->new(
-name => "WindowSudoku",
-title => "Depuzzlefy - Sudoku",
-size => [ 315, 370 ],
-pos => [ Win32::GUI::Width(Win32::GUI::GetDesktopWindow())/2-157.5, Win32::GUI::Height(Win32::GUI::GetDesktopWindow())/2-185 ],
-sizable => 0,
-parent => $Window_Main,
-hasmaximize => 0,
-maximizebox => 0,
-hasminimize => 0,
-minimizebox => 0,
);
for (my $i = 0; $i < 9; $i++) {
for (my $j = 0; $j <9; $j++) {
my $tf = $W1->AddTextfield(
-name => "TextFieldSudoku$i$j",
-pos => [($j*30)+20, ($i*30)+30],
-size => [25,25],
-align => "center",
-number => 1,
);
$tf->MaxLength(1);
}
}
$W1->AddButton(
-name => "ButtonSudokuSolve",
-text => "Solve",
-pos => [ 120, 295 ],
-size => [ 85, 30 ],
);
return $W1;
}
sub WindowSudoku_Terminate {
$Window_Main->Show();
return -1;
}
sub ButtonSudokuSolve_Click {
undef @board;
$solveCount = 0;
for (my $i = 0; $i < 9; $i++) {
for (my $j = 0; $j < 9; $j++) {
my $method = "TextFieldSudoku$i$j";
if (($Window_Sudoku->$method->Text() + 0) != "") {
push @board, (($Window_Sudoku->$method->Text()) + 0);
} else {
push @board, 0;
}
}
}
SolveSudoku();
}
#########################################################
sub SolveSudoku {
printf "Trying to solve Sudoku. Count:%s\n", $solveCount++;
my $i;
foreach $i ( 0 .. 80 ) {
next if $board[$i];
my %t = map {
$_ / 9 == $i / 9 ||
$_ % 9 == $i % 9 ||
$_ / 27 == $i / 27 && $_ % 9 / 3 == $i % 9 / 3
? $board[$_] : 0,
1;
} 0 .. 80;
SolveSudoku( $board[$i] = $_ ) for grep !$t{$_}, 1 .. 9;
return $board[$i] = 0;
}
# $i = 0;
# foreach (@board) {
# print "-----+-----+-----\n" if !($i%27) && $i;
# print !($i%9) ? '': $i%3 ? ' ' : '|', $_;
# print "\n" unless ++$i%9;
# }
for (my $i = 0; $i < 9; $i++) {
for (my $j = 0; $j < 9; $j++) {
my $method = "TextFieldSudoku$i$j";
my $value = $board[($i * 9) + $j];
$Window_Sudoku->$method->SelectAll();
$Window_Sudoku->$method->Clear();
$Window_Sudoku->$method->Append("$value");
}
}
}
sub ClearHideJigsaw {
$Window_Jigsaw->TextfieldWJUrl->SelectAll();
$Window_Jigsaw->TextfieldWJUrl->Clear();
$Window_Jigsaw->Hide();
}
sub SetUserAgent {
my $ua = LWP::UserAgent->new();
$ua->agent('Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0');
$ua->cookie_jar({});
$ua->protocols_allowed(['http','https']);
$ua->ssl_opts(verify_hostname => 0);
$ua->default_header('Accept-Encoding' => scalar HTTP::Message::decodable());
return $ua;
}
# TODO: Find a way to make use of the normal temp map. using c:/dpf but this map is getting deleted at the end. Might be troublesome if someone made a own map.
# Using c:/dpf because otherwise large puzzles were getting errors because of too long commandprompt commands.
# This may still happen on really large puzzles but I haven't encountered them yet.
sub FlashGearRequest {
my $url = $Window_Jigsaw->TextfieldWJUrl->Text();
ClearHideJigsaw();
print "Requesting URL data\n\n";
my $ua = SetUserAgent();
my $res = $ua->get($url);
if(!$res->is_success) {
printf "%s\n\n", $res->status_line;
$Window_Jigsaw->Show();
return;
}
my $content = $res->decoded_content // $res->content // die 'no content';
my @url = $content =~ m[<embed(?: [^>]*?)? src="([^"]+)"]gi;
if(!@url) {
print "No Flash file found. Please insert a correct URL\n\n";
$Window_Jigsaw->Show();
return;
}
print "Found Flash file\n\n";
print "Extracting pictures from the Flash file\n";
print "This may take a while depending on the size of the puzzle\n\n";
my $dir = "C:\\dpf";
my $img = '';
$url = $url[1];
my $uri = URI->new($url);
my ($h, $w, $id) = map $uri->query_param($_), qw(h w id);
next unless $h and $w and $id;
$uri->query_param(c => 'z');
my $swf = File::Temp->new(suffix => '.swf');
$res = $ua->get($uri, ':content_file' => $swf->filename);
my $out = capture swfextract => $swf;
my ($ids) = $out =~ /^\s*\[-j\] \d+ JPEGs: ID\(s\) (\d+,.*)/m;
my @ids = split(", ", $ids);
mkdir $dir, 0666;
foreach my $id (@ids) {
system swfextract => '-j', $id, '--outputformat', "$dir/%05d.%s", $swf;
}
# FIX: Other jpgs will crash it.
my @jpg = glob "$dir/*.jpg";
$out = capture identify => $jpg[0];
my ($x, $y) = $out =~ / JPEG (\d+)x(\d+)/;
next unless $x and $y and $x == $y;
$_ = int $_ / $x * 2 for $w, $h;
# The edges of each puzzle piece image need to be overlapped.
$_ = int $_ / 4 for $x, $y;
$img = "$id.jpg";
system montage => '-tile', "${w}x$h", '-geometry', "-$x-$y", @jpg, $img;
unlink @jpg;
system "cmd /c start $img";
print "Done\n\n";
$Window_Jigsaw->Show();
}
sub JigidiRequest {
my $url = $Window_Jigsaw->TextfieldWJUrl->Text();
ClearHideJigsaw();
my @id = $url =~ m[\?id=([^"]+)];
print "Requesting URL data\n\n";
my $ua = SetUserAgent();
my $res = $ua->get($url);
if(!$res->is_success) {
printf "%s\n\n", $res->status_line;
$Window_Jigsaw->Show();
return;
}
my $content = $res->decoded_content // $res->content // die 'no content\n';
my @token = $content =~ m[token:\"([^"]+)];
printf "%s\n\n", "Found token";
$res = $ua->get("https://www.jigidi.com/ajax/api/2/game_lookup.php?token=$token[0]");
if(!$res->is_success) {
printf "%s\n\n", $res->status_line;
$Window_Jigsaw->Show();
return;
}
$content = $res->decoded_content // $res->content // die 'no content\n';
my @pic = $content =~ m[image\":\"([^"]+)];
$pic[0] =~ s/[\\]//g ;
$pic[0] =~ s/.{5}$//;
print "Saving picture\n\n";
unlink "$id[0].jpg";
$res = $ua->mirror($pic[0], "$id[0].jpg");
unless($res->is_success) {
printf "%s\n\n", $res->status_line;
$Window_Jigsaw->Show();
return;
}
system "cmd /c start $id[0].jpg";
print "Done\n\n";
$Window_Jigsaw->Show();
}
$Window_Main = Create_Window_Main();
$Window_Jigsaw = Create_Window_Jigsaw();
$Window_Sudoku = Create_Window_Sudoku();
$Window_Main->Show();
Win32::GUI::Dialog();
@YoOoster
Copy link
Author

@kaischmitt
Which puzzle?

@schettina
Copy link

@lingkau
Copy link

lingkau commented Jul 9, 2018

I have the same problem with this URL: http://six.flash-gear.com/npuz/puz.php?c=v&id=3544876&k=77212470.
I've installed the latest version on the latest version of Windows10. Maybe that helps a little.

@Skiet
Copy link

Skiet commented Aug 31, 2018

I had it working in the past, i tried it on a certain puzzle on flash-gear.com wich i tried to solve again today, but as the others above, i get two files and the same kind of pictures
4163607-0
4163607-1

@PepePellini
Copy link

PepePellini commented Jan 1, 2019

Super Tool! I am also interested in a completion message. Is there any posibility?

@rickrich
Copy link

Super Tool! I am also interested in a completion message. Is there any posibility?

Ditto!

@pinkunicorn
Copy link

Apart from the completion message which many people have already asked for, I'd very much like a non-windows option.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment