#!/usr/bin/perl -w
#
# checkpuz.pl
# 
# Given a solved locked .puz file, verify that it is solved correctly by comparing checksums.

use strict;

print "CheckPUZ version 1.0\n";
print "by Alex Boisvert\n";
print "http://www.alexboisvert.com\n\n";


# locate the .puz file
unless ($ARGV[1]) {
	print "To use this app, complete a locked .puz file and save it.\n";
	print "Then drag and drop the saved .puz file onto the CheckPUZ icon.\n";
	print "A message will then appear indicating whether your puzzle has\nbeen correctly solved.\n";
	exit(0);
}
my $infile = $ARGV[1];

# make sure we can read the .puz file
#die "file $infile does not exist\n" unless -e $infile;
#die "file $infile is not readable. Are you sure it's a .puz file?\n" 
#    unless -f _ and -r _;

# read in the .puz file
my $data;
open IN, $infile or die "Can't read $infile";
{
    local $/;
    $data = <IN>;
}
close IN;

my ($cksm,$fm,$CIB,$ml1,$ml2,$ml3,$ml4,$mh1,$mh2,$mh3,$mh4,$version,$R1C,$Unk,$R20,$w,$h,$noClues,$unkBitMask,$unk32) = unpack("v a12 v C4 C4 a4 v2 A12 CC v3",substr($data,0x0,52));

# Check: in a locked .puz file, $unk32 is not 0 (it's 4, usually)
if ($unk32 == 0) {
	print "This .puz file was not locked to begin with.\n";
	print "If you have solved your puzzle but you don't see Mr. Happy Pencil,\n";
	print "you have an error somewhere.\n";
	exit(0);
}

# In a solved locked .puz file, the "grid" is the important thing.
# This is where the user's solution is.
my $user_soln = substr($data,0x34+ $w*$h,$w*$h);
# It looks like we have to change this into column-ordered form.
my @user;	# List of lists
for my $i (0..$h-1){
  # $i = the number of this row
  $user[$i] = [split(//, substr($user_soln, $i*$w, $w))];
}
my $final_user = "";
for my $j (0..$w-1) {
	for my $i (0..$h-1) {
		$final_user .= $user[$i][$j];
	}
}
$final_user =~ s/\.//g;
#print "$final_user\n";

# Now we just compute the checksum over this region and compare it to $Unk.
my @sol = unpack("C*",$final_user);
my $solck = cksum_region(\@sol,0x0000);

#print "$Unk -- $solck\n";
if ($Unk == $solck) {print "The puzzle is solved correctly!\n";}
else {print "There is an error in your solution somewhere.\n";}


sub cksum_region {
	# Usage: cksum_region(\@array,cksum)
	# Calculates the checksum of a region.
	# The region is given as an array in the following way:
	# @array = unpack("C*",DATA);
	# Props to joshisanerd.com/puz
	my @array = @{$_[0]};
	my $cksumTemp = $_[1];
	my $showInfo = $_[2];
	foreach my $s (@array) {
		if ($cksumTemp & 0x0001) {$cksumTemp = ($cksumTemp >> 1) + 0x8000;}
		else {$cksumTemp = $cksumTemp >> 1;}
		$cksumTemp = ($cksumTemp + $s) % 65536;
		if ($showInfo) {print "$cksumTemp ";}
	}
return $cksumTemp;
}