#!/usr/bin/perl -w
# sudoku.pl, a perl CGI script to create and solve Sudoku puzzles
# Copyright (C) 2005-06-02  Christian Wolff
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
# Or, download it from http://www.gnu.org/copyleft/gpl.html
#
# You can reach the author via email: sub-gpl <at> scara.com
#

use strict;

my $script = $ENV{'SCRIPT_NAME'} . '?';

# get CGI parameter: 8 digit date, new selection
my %query = ();
my %queryraw = ();
my $query = $ENV{'QUERY_STRING'};
chomp $query;
for (split (/&/, $query)) {
	if (/=/) {
		my ($param,$content) = split(/=/, $_, 2);
		$queryraw{$param} = $content;
		$content =~ tr/+/ /;
		$content =~ s/%([0-9a-fA-F]{2})/pack('c',hex($1))/ge;
		$query{$param} = $content;
	} else {
		$query{'sel'} = $_;
	}
}

$query{'type'} = 'S' unless $query{'type'};
$script .= 'type=' . $query{'type'} . '&';
if ($query{'map'}) {
	if (length($query{'map'}) < 9) {
		$query{'map'} .= substr('123456789', length($query{'map'}), 9 - length($query{'map'}));
		$queryraw{'map'} = $query{'map'};
#TODO		$queryraw{'map'} = s/./%
	}
} else {
	$query{'map'} = '123456789';
	$queryraw{'map'} = $query{'map'};
}
$script .= 'map=' . $queryraw{'map'} . '&';
$query{'sel'} = '' unless $query{'sel'};

# print HTTP header
print "MIME-Version: 1.0\nContent-Type: text/html\n";
print "\n";

# print HTML header
print <<END;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>Sudoku</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<style>
<!--
body { font-family:Arial,sans-serif; background-color:#FFFFFF; }
a { text-decoration:none; }
-->
</style>
</head>
<body>

END

my @field = ();
my @cut = ();

sub map_cell
{
	my $n = shift;
	if ($n eq ' ') {
		return '&nbsp;';
	} elsif (($n >= 1) && ($n <= 9)) {
		return substr($query{'map'}, $n - 1, 1);
	} else {
		return $n;
	}
}

# cell with potential numbers
sub print_pot_cell
{
	my ($mask, $row, $col) = @_;
	my ($x, $y, $n, $i);
	$i = 0;
	print "<table border=0>";
	for ($x = 0; $x < 3; $x++) {
		print "<tr>";
		for ($y = 0; $y < 3; $y++) {
			$n = 1 + $x * 3 + $y;
			print "<td><font size=\"-2\">";
			if ($mask & (1 << ($n - 1))) {
				$i++;
				print "<a href=\"${script}sel=$query{'sel'}${row}${col}${n}\">&nbsp;" . map_cell($n) . "&nbsp;</a>";
			} else {
				print "&nbsp;&nbsp;&nbsp;";
			}
			if (($n == 9) && ($i == 2)) {
				print "<a href=\"${script}sel=$query{'sel'}&guess=${row}${col}\">&nbsp;G&nbsp;</a>";
			}
			print "</font></td>";
		}
		print "</tr>";
	}
	print "</table>\n";
}

# dummy cell with all numbers
sub print_dum_cell
{
	my ($x, $y, $n);
	print "<table border=0>";
	for ($x = 0; $x < 3; $x++) {
		print "<tr>";
		for ($y = 0; $y < 3; $y++) {
			$n = 1 + $x * 3 + $y;
			print "<td><font size=\"-2\">";
			print "&nbsp;" . map_cell($n) . "&nbsp;";
			print "</font></td>";
		}
		print "</tr>";
	}
	print "</table>\n";
}

# final cell with one big number
sub print_fin_cell
{
	my ($n, $color, $link) = @_;
	print "<a href=\"${script}${link}\">" if $link;
	print "&nbsp;<br><font size=\"+2\" color=${color}>&nbsp;&nbsp;&nbsp;&nbsp;" . map_cell($n) . "&nbsp;&nbsp;&nbsp;&nbsp;</font><br>&nbsp;";
	print "</a>" if $link;
	print "\n";
}

# sole cell for solution grid
sub print_sol_cell
{
	my ($n, $color) = @_;
	print "<font color=${color}>&nbsp;" . map_cell($n) . "&nbsp;</font>\n";
}

sub print_cell
{
	my ($mask, $row, $col, $level) = @_;
	my ($i, $n);
	
	$n = 0;
	for ($i = 0; $i < 9; $i++) {
		if ($mask & (1 << $i)) {
			if ($n) {
				print_pot_cell($mask, $row, $col);
				return;
			} else {
				$n = $i + 1;
			}
		}
	}
	if ($level == 0) {
		print_fin_cell(
			$n ? $n : 'X', 
			! $n ? 'orange' : ($mask & 512) ? 'red' : 'black', 
			($mask & 512) ? "sel=$cut[$row][$col]" : '');
	} elsif ($level == 1) {
		if ($mask & 512) {
			print_fin_cell($n ? $n : 'X', ! $n ? 'orange' : 'red', "sel=$cut[$row][$col]");
		} else {
			print_fin_cell(' ', 'white', '');
#			print_dum_cell();
		}
	} elsif ($level == 2) {
		print_sol_cell($n ? $n : 'X', ! $n ? 'orange' : ($mask & 512) ? 'red' : 'black');
	}
}

sub print_table
{
	my $level = shift;
	my ($row, $col);
	
	print "<table border=5 cellspacing=0>\n";
	for ($row = 0; $row < 9; $row++) {
		print "\t<tr>\n";
		for ($col = 0; $col < 9; $col++) {
			print "\t\t<td align=center";
			print " bgcolor=\"#C0C0C0\"" if ($query{'type'} eq 'X') && (($row == $col) || ($row == 8 - $col));
			print ">\n\t\t\t";
			print_cell($field[$row][$col], $row, $col, $level);
			print "\t\t</td>\n";
			print "\t\t<td></td>\n" if ($col == 2) || ($col == 5);
			#print "\t\t<td rowspan=3></td>\n" if ($col == 2) && ($row == 0);
			#print "\t\t<td rowspan=7></td>\n" if ($col == 5) && ($row == 0);
			#print "\t\t<td rowspan=7></td>\n" if ($col == 2) && ($row == 3);
			#print "\t\t<td rowspan=3></td>\n" if ($col == 5) && ($row == 6);
		}
		print "\t</tr>\n";
		print "\t<tr><td colspan=11></td></tr>\n" if ($row == 2) || ($row == 5);
		#print "\t<tr><td colspan=7></td><td colspan=3></td></tr>\n" if $row == 2;
		#print "\t<tr><td colspan=3></td><td colspan=7></td></tr>\n" if $row == 5;
	}
	print "</table>\n";
}

# eliminate same number in row, column and segment
sub elim
{
	my $ret = 0;
	my $n = shift;
	my ($r, $c, $row, $col, $i);
	
	my $b = 1 << ($n - 1);
	for ($r = 0; $r < 9; $r++) {
		for ($c = 0; $c < 9; $c++) {
			next unless (($field[$r][$c] & 511) == $b);
			
			my $m = 1023 - $b;
			for ($row = 0; $row < 9; $row++) {
				if (($row != $r) && ($field[$row][$c] & $b)) {
					$field[$row][$c] &= $m;
					$ret |= 2;
					for ($i = 0; $i < 9; $i++) {
						$ret |= elim($i + 1) if (($field[$row][$c] & 511) == (1 << $i));
					}
				}
			}
			for ($col = 0; $col < 9; $col++) {
				if (($col != $c) && ($field[$r][$col] & $b)) {
					$field[$r][$col] &= $m;
					$ret |= 4;
					for ($i = 0; $i < 9; $i++) {
						$ret |= elim($i + 1) if (($field[$r][$col] & 511) == (1 << $i));
					}
				}
			}
			my ($ro, $co);
			$ro = $r - ($r % 3);
			$co = $c - ($c % 3);
			for ($row = 0; $row < 3; $row++) {
				for ($col = 0; $col < 3; $col++) {
					if ((($ro + $row != $r) || ($co + $col != $c)) && ($field[$ro + $row][$co + $col] & $b)) {
						$field[$ro + $row][$co + $col] &= $m;
						$ret |= 8;
						for ($i = 0; $i < 9; $i++) {
							$ret |= elim($i + 1) if (($field[$ro + $row][$co + $col] & 511) == (1 << $i));
						}
					}
				}
			}
			if ($query{'type'} eq 'X') {
				if ($r == $c) {
					for ($row = 0; $row < 9; $row++) {
						if (($row != $r) && ($field[$row][$row] & $b)) {
							$field[$row][$row] &= $m;
							$ret |= 8192;
							for ($i = 0; $i < 9; $i++) {
								$ret |= elim($i + 1) if (($field[$row][$row] & 511) == (1 << $i));
							}
						}
					}
				}
				if ($r == 8 - $c) {
					for ($row = 0; $row < 9; $row++) {
						if (($row != $r) && ($field[$row][8 - $row] & $b)) {
							$field[$row][8 - $row] &= $m;
							$ret |= 8192;
							for ($i = 0; $i < 9; $i++) {
								$ret |= elim($i + 1) if (($field[$row][8 - $row] & 511) == (1 << $i));
							}
						}
					}
				}
			}
		}
	}
	return $ret;
}

# eliminate twins and triplets
sub twin_elim
{
	my $ret = 0;
	my ($n, $b, $m, $r, $ro, $row, $c, $co, $col, $i, @comb, $o1, $o2);
	
	for ($ro = 0; $ro < 9; $ro += 3) {
		for ($co = 0; $co < 9; $co += 3) {
			for ($n = 0; $n < 9; $n++) {
				$b = 1 << $n;
				$m = 1023 - $b;
				
				for ($row = 0; $row < 3; $row++) {
					$comb[$row] = 0;
					for ($col = 0; $col < 3; $col++) {
						$comb[$row] |= $field[$ro + $row][$co + $col];
					}
				}
				for ($row = 0; $row < 3; $row++) {
					$o1 = ($row + 1) % 3;
					$o2 = ($row + 2) % 3;
					if (($comb[$row] & $b) && (! ($comb[$o1] & $b)) && (! ($comb[$o2] & $b))) {
						for ($col = 0; $col < 9; $col++) {
							if (($col < $co) || ($col >= $co + 3)) {
								if (($field[$ro + $row][$col] & $b) && ($field[$ro + $row][$col] & 511 & $m)) {
									$field[$ro + $row][$col] &= $m;
									$ret |= 16;
								}
							}
						}
					}
				}
				
				for ($col = 0; $col < 3; $col++) {
					$comb[$col] = 0;
					for ($row = 0; $row < 3; $row++) {
						$comb[$col] |= $field[$ro + $row][$co + $col];
					}
				}
				for ($col = 0; $col < 3; $col++) {
					$o1 = ($col + 1) % 3;
					$o2 = ($col + 2) % 3;
					if (($comb[$col] & $b) && (! ($comb[$o1] & $b)) && (! ($comb[$o2] & $b))) {
						for ($row = 0; $row < $ro; $row++) {
							if (($row < $ro) || ($row >= $ro + 3)) {
								if (($field[$row][$co + $col] & $b) && ($field[$row][$co + $col] & 511 & $m)) {
									$field[$row][$co + $col] &= $m;
									$ret |= 32;
								}
							}
						}
					}
				}
			}
		}
	}
	return $ret;
}

# eliminate paired numbers in row, column and segment
sub pair_elim
{
	my ($r, $c, $row, $col, $i, $b, $n);
	my $ret = 0;
	
	for ($r = 0; $r < 9; $r++) {
		for ($c = 0; $c < 9; $c++) {
			$b = $field[$r][$c] & 511;
			$n = 0;
			for ($i = 0; $i < 9; $i++) {
				$n++ if $b & (1 << $i);
			}
			next unless $n == 2;
			
			my $m = 1023 - $b;
			
			$n = 0;
			for ($row = 0; $row < 9; $row++) {
				if (($row != $r) && (($field[$row][$c] & 511) == $b)) {
					last if $n;
					$n = $row + 1;
				}
			}
			if ($n) {
				for ($row = 0; $row < 9; $row++) {
					if (($row != $r) && ($row != ($n - 1)) && ($field[$row][$c] & $b)) {
						$field[$row][$c] &= $m;
						for ($i = 0; $i < 9; $i++) {
							if (($field[$row][$c] & 511) == (1 << $i)) {
								$ret |= elim($i + 1) | 64;
							}
						}
					}
				}
			}
			
			$n = 0;
			for ($col = 0; $col < 9; $col++) {
				if (($col != $c) && (($field[$r][$col] & 511) == $b)) {
					last if $n;
					$n = $col + 1;
				}
			}
			if ($n) {
				for ($col = 0; $col < 9; $col++) {
					if (($col != $c) && ($col != ($n - 1)) && ($field[$r][$col] & $b)) {
						$field[$r][$col] &= $m;
						for ($i = 0; $i < 9; $i++) {
							if (($field[$r][$col] & 511) == (1 << $i)) {
								$ret |= elim($i + 1) | 128;
							}
						}
					}
				}
			}
			
			my ($ro, $co, $rn, $cn);
			$ro = $r - ($r % 3);
			$co = $c - ($c % 3);
			($rn, $cn) = (0, 0);
			for ($row = 0; $row < 3; $row++) {
				for ($col = 0; $col < 3; $col++) {
					if ((($ro + $row != $r) || ($co + $col != $c)) && (($field[$ro + $row][$co + $col] & 511) == $b)) {
						last if ($rn && $cn);
						$rn = $ro + $row + 1;
						$cn = $co + $col + 1;
					}
				}
			}
			if ($rn && $cn) {
				for ($row = 0; $row < 3; $row++) {
					for ($col = 0; $col < 3; $col++) {
						if ((($ro + $row != $r) || ($co + $col != $c)) && (($ro + $row != ($rn - 1)) || ($co + $col != ($cn - 1))) && ($field[$ro + $row][$co + $col] & $b)) {
							$field[$ro + $row][$co + $col] &= $m;
							for ($i = 0; $i < 9; $i++) {
								if (($field[$ro + $row][$co + $col] & 511) == (1 << $i)) {
									$ret |= elim($i + 1) | 256;
								}
							}
						}
					}
				}
			}
		}
	}
	return $ret;
}

sub def
{
	my ($r, $c, $n, $s) = @_;
	$field[$r][$c] = 1024 | ($s ? 512 : 0) | (1 << ($n - 1));
	return elim($n);
}

# find single number in segment
sub seg_elim
{
	my $ret = 0;
	my ($i, $m, $r, $ro, $row, $c, $co, $col);
	for ($i = 0; $i < 9; $i++) {
		for ($ro = 0; $ro < 9; $ro += 3) {
			for ($co = 0; $co < 9; $co += 3) {
				$m = 1 << $i;
				($r, $c) = (0, 0);
				for ($row = 0; $row < 3; $row++) {
					for ($col = 0; $col < 3; $col++) {
						if ($field[$ro + $row][$co + $col] & $m) {
							$m = 0 if ($r || $c);
							last unless $m;
							($r, $c) = ($ro + $row + 1, $co + $col + 1);
						}
					}
					last unless $m;
				}
				if ($m && $r && $c && (($field[$r - 1][$c - 1] & 511) != $m)) {
					$ret |= def($r - 1, $c - 1, $i + 1, 0) | 512;
				}
			}
		}
	}
	return $ret;
}

# find single number in row
sub row_elim
{
	my $ret = 0;
	my ($i, $m, $row, $c, $col);
	for ($i = 0; $i < 9; $i++) {
		for ($row = 0; $row < 9; $row++) {
			$m = 1 << $i;
			$c = 0;
			for ($col = 0; $col < 9; $col++) {
				if ($field[$row][$col] & $m) {
					$m = 0 if $c;
					last unless $m;
					$c = $col + 1;
				}
			}
			if ($m && $c && (($field[$row][$c - 1] & 511) != $m)) {
				$ret |= def($row, $c - 1, $i + 1, 0) | 1024;
			}
		}
	}
	return $ret;
}

# find single number in column
sub col_elim
{
	my $ret = 0;
	my ($i, $m, $r, $row, $col);
	for ($i = 0; $i < 9; $i++) {
		for ($col = 0; $col < 9; $col++) {
			$m = 1 << $i;
			$r = 0;
			for ($row = 0; $row < 9; $row++) {
				if ($field[$row][$col] & $m) {
					$m = 0 if $r;
					last unless $m;
					$r = $row + 1;
				}
			}
			if ($m && $r && (($field[$r - 1][$col] & 511) != $m)) {
				$ret |= def($r - 1, $col, $i + 1, 0) | 2048;
			}
		}
	}
	return $ret;
}

# find single number in column
sub diag_elim
{
	my $ret = 0;
	my ($i, $m, $r, $row);
	for ($i = 0; $i < 9; $i++) {
		$m = 1 << $i;
		$r = 0;
		for ($row = 0; $row < 9; $row++) {
			if ($field[$row][$row] & $m) {
				$m = 0 if $r;
				last unless $m;
				$r = $row + 1;
			}
		}
		if ($m && $r && (($field[$r - 1][$r - 1] & 511) != $m)) {
			$ret |= def($r - 1, $r - 1, $i + 1, 0) | 16384;
		}
		$m = 1 << $i;
		$r = 0;
		for ($row = 0; $row < 9; $row++) {
			if ($field[$row][8 - $row] & $m) {
				$m = 0 if $r;
				last unless $m;
				$r = $row + 1;
			}
		}
		if ($m && $r && (($field[$r - 1][9 - $r] & 511) != $m)) {
			$ret |= def($r - 1, 9 - $r, $i + 1, 0) | 16384;
		}
	}
	return $ret;
}

sub fill_field
{
	my $ret = 0;
	my $sel = shift;
	my ($row, $col);
	
	# clear array
	for ($row = 0; $row < 9; $row++) {
		for ($col = 0; $col < 9; $col++) {
			$field[$row][$col] = 511;
		}
	}
	
	# step through 'sel' entries
	my $pre = '';
	while ($sel =~ s/^(\d)(\d)(\d)//) {
		my ($r, $c, $n) = ($1, $2, $3);
		$ret |= def($r, $c, $n, 1);
		$cut[$r][$c] = $pre . $sel;
		$pre .= $r . $c . $n;
		
	}
	
	return $ret;
}

sub solve
{
	my $ret = 0;
	my ($r, $rt, $ri, $i);
	my ($row, $col);
	
	do {
		$rt = 0;
		
		$ri = $ret;
		do {
			$r = seg_elim(); $rt |= $r;
		} while ($r);
		$ret |= $rt;
		
		if ($ret == $ri) {
			$ri = $ret;
			do {
				$r = row_elim(); $rt |= $r;
			} while ($r);
			$ret |= $rt;
		}
		
		if ($ret == $ri) {
			$ri = $ret;
			do {
				$r = col_elim(); $rt |= $r;
			} while ($r);
			$ret |= $rt;
		}
		
		if ($query{'type'} eq 'X') {
			if ($ret == $ri) {
				$ri = $ret;
				do {
					$r = diag_elim(); $rt |= $r;
				} while ($r);
				$ret |= $rt;
			}
		}
		
		if ($ret == $ri) {
			$ri = $ret;
			do {
				$r = twin_elim(); $rt |= $r;
			} while ($r);
			$ret |= $rt;
		}
		
		if ($ret == $ri) {
			$ri = $ret;
			do {
				$r = pair_elim(); $rt |= $r;
			} while ($r);
			$ret |= $rt;
		}
		
		if ($ret == $ri) {
			for ($i = 0; $i < 9; $i++) {
				$r = elim($i + 1); $rt |= $r;
			}
			$ret |= $rt;
		}
	} while $rt;
	
	return $ret;
}

sub check
{
	my ($solved, $invalid) = (1, 0);
	my ($row, $col);
	
	for ($row = 0; $row < 9; $row++) {
		for ($col = 0; $col < 9; $col++) {
			my $i;
			my $n = 0;
			my $f = 0;
			for ($i = 0; $i < 9; $i++) {
				$n = $i + 1 if (($field[$row][$col] & 511) == (1 << $i));
			}
			$solved = 0 unless $n;
			$invalid = 2 unless $field[$row][$col] & 511;
		}
	}
	
	return $invalid | $solved;
}

#$query{'add'} $query{'hide'}

my $level = fill_field($query{'sel'});
$level |= solve();
my $solved = check() & 1;

if ((! $solved) && defined($query{'guess'}) && ($query{'guess'} =~ /^(\d)(\d)$/)) {
	my ($row, $col) = ($1, $2);
	my ($a, $b, $i, $as, $bs);
	for ($i = 0; $i < 9; $i++) {
		if ($field[$row][$col] & (1 << $i)) {
			if ($a) {
				$b = 1 << $i;
			} else {
				$a = 1 << $i;
			}
		}
	}
	$level = fill_field($query{'sel'});
	$field[$row][$col] = $a;
	$level |= solve();
	$as = check();
	$level = fill_field($query{'sel'});
	$field[$row][$col] = $b;
	$level |= solve();
	$bs = check();
	if (($as == 1) && ($bs == 2)) {
		$level = fill_field($query{'sel'});
		$field[$row][$col] = $a;
		$level |= solve();
		$solved = $as & 1;
		$level |= 4096;
	} elsif (($bs == 1) && ($as == 2)) {
		$solved = $bs & 1;
		$level |= 4096;
	} else {
		$level = fill_field($query{'sel'});
		$level |= solve();
	}
}

print "<table border=0>\n";
print "<tr><td colspan=3>\n";
print_table($solved);
print "</td></tr><tr><td colspan=3 align=center>\n";
print "<hr>\n";
print "(fold back to hide solution)<br>\n" if $solved;
print "</td></tr><tr><td valign=top>\n";
print_table(2) if $solved;
print "</td><td valign=top>\n";
#print "Difficulty level: ";
#if ($level & 4096) {
#	if ($level & (64 | 128 | 256)) {
#		print '"Diabolical"';
#	} else {
#		print '"Tough"';
#	}
#} elsif ($level & (64 | 128 | 256)) {
#	print '"Tough"';
#} elsif ($level & (16 | 32)) {
#	print '"Moderate"';
#} else {
#	print '"Gentle"';
#}
#print "<br>\n";
print "Strategies used:\n";
print "<ul>\n";
print "<li>Row elimination<br>\n" if $level & 2;
print "<li>Column elimination<br>\n" if $level & 4;
print "<li>Box elimination<br>\n" if $level & 8;
print "<li>Diagonal elimination<br>\n" if $level & 8192;
print "<li>Twin/Triplet elimination in row<br>\n" if $level & 16;
print "<li>Twin/Triplet elimination in column<br>\n" if $level & 32;
print "<li>Pair elimination in row<br>\n" if $level & 64;
print "<li>Pair elimination in column<br>\n" if $level & 128;
print "<li>Pair elimination in box<br>\n" if $level & 256;
print "<li>Single number in row<br>\n" if $level & 1024;
print "<li>Single number in column<br>\n" if $level & 2048;
print "<li>Single number in box<br>\n" if $level & 512;
print "<li>Single number in Diagonal<br>\n" if $level & 16384;
print "<li>Guessing among two<br>\n" if $level & 4096;
print "</ul>\n";
if (! $solved) {
	print "<form method=\"get\">\n";
	print "Type:<br>\n";
	print "<input type=\"radio\" name=\"type\" value=\"S\"";
	print " checked" if $query{'type'} eq 'S';
	print ">Sudoku<br>\n";
	print "<input type=\"radio\" name=\"type\" value=\"X\"";
	print " checked" if $query{'type'} eq 'X';
	print ">Sudoku - X<br>\n";
	print "Mapping: <input type=\"text\" size=\"9\" maxlength=\"9\" name=\"map\" value=\"$query{'map'}\"><br>\n";
	print "<input type=\"hidden\" name=\"sel\" value=\"$query{'sel'}\">\n";
	print "<input type=\"submit\" value=\"change\"><br>\n";
	print "</form>\n";
}
print "</td><td align=right valign=bottom>\n";
if ($solved) {
	print "<p>\n";
	if ($query{'map'} =~ /^\d+$/) {
		print "Fill in numbers 1 trough 9.<br>\n";
		print "Each number once per row,<br>\n";
	} elsif ($query{'map'} =~ /^[a-zA-Z]+$/) {
		print "Fill in the letters " . join(',', split(//, $query{'map'})) . ".<br>\n";
		print "Each letter once per row,<br>\n";
	} else {
		print "Fill in the symbols " . join(',', split(//, $query{'map'})) . ".<br>\n";
		print "Each symbol once per row,<br>\n";
	}
	if ($query{'type'} eq 'X') {
		print "column, 3-by-3 box and<br>\n";
		print "each of the two diagonals.</p>\n";
	} else {
		print "column, and 3-by-3 box.</p>\n";
	}
} else {
	print "<p>\n";
	print "Select a number from the choices in a cell.<br>\n";
	print "Repeat, until puzzle is solvable.<br>\n";
	print "If a 'G' appears in a cell, click it for guessing<br>(Works only if one choice solves and the other<br>ends in invalid puzzle.)<br>\n";
	print "Click selected numbers to un-select.<br>\n";
	print "An orange 'X' denotes an error, un-select<br>one or more numbers to fix the problem.\n";
	print "</p>\n";
}
print "<p>Copyright <a href=\"http://scarabaeus.org/playground/sudoku\">&copy;</a> 2005 Christian Wolff<br><a href=\"http://scarabaeus.org/cgi-bin/sudoku\">scarabaeus.org/cgi-bin/sudoku</a></p>\n";
print "</td></tr></table>\n";

print "</body>\n";
print "</html>\n";
