var MySudoku=new Array(81);

var Hint=1;
var Solve=2;
var LockIn=3;

var	kount=new Array (3);
var posn1=new Array (3);
var posn2=new Array (3);
    for (j=0;j<3;j++) {
        kount[j]=new Array (10);
        posn1[j]=new Array (10);
        posn2[j]=new Array (10);
    }


function ClearMessage() {
	m=document.getElementById('Message');
	if (m.value!="Puzzle not locked in") m.value="";
}

function abcd(a,b,c,d) 
{
	return(a*27+b*9+c*3+d);
}

function Sudoku(i) {
	 y=parseInt(i/9); 
	 x=i-y*9; 
	 x++;
	 y++;
	e=document.getElementById(''+x+y);
	return e;
}

function ScratchField(i) {
	 y=parseInt(i/9); 
	 x=i-y*9; 
	 x++;
	 y++;
	e=document.getElementById('s'+x+y);
	return e;
}

function DelString(MyString,Index1,Length) {
	if (Index1>0) S=MyString.substring(0,Index1); else S="";
	S=S+MyString.substring(Index1+Length,255);
	return S;
}

function mod(y,x) {
	var z=parseInt(y/x);
	return (y-z*x);
}

function basicEliminate(Sender) {
    var again=0;
    	// Start eliminating
    	for (i=0;i<81;i++) {
			e=MySudoku[i];
        	n=parseInt(e);
        	if (n>=1 && n<=9) {
            	N=""+n;
				var y=parseInt(i/9);
				var x=i-y*9;
            	// Eliminate across the row
            	for (k=0;k<9;k++) {
                	if (k!=x) {
                    	var j=y*9+k;
                    	f=MySudoku[j];
						var Pos=f.indexOf(N);
                    	if (Pos>=0) {
                        	MySudoku[j]=DelString(f,Pos,1);
							again++;
                        	if (MySudoku[j].length==1) return (-1);
                    	}
                	}
            	}
            	// Eliminate down the column
            	for (k=0;k<9;k++) {
                	if (k!=y) {
                    	j=k*9+x;
						f=MySudoku[j];
    	                Pos=f.indexOf(N);
        	            if (Pos>=0) {
            	            MySudoku[j]=DelString(f,Pos,1);
							again++;
                    	    if (MySudoku[j].length==1) return (-1);
                    	}
                	}
            	}
            	// Eliminate within 3x3 box
            	var base=(parseInt(y/3)*3)*9 + parseInt(x/3)*3;
            	for (k=0;k<3;k++) {
                	for (m=0;m<3;m++) {
                    	j=base+k*9+m;
						f=MySudoku[j];
                    	if (j!=i) {
                        	Pos=f.indexOf(N);
                        	if (Pos>=0) {
                        		MySudoku[j]=DelString(f,Pos,1);
								again++;
                            	if (MySudoku[j].length==1 ) return (-1);
                        	}
                    	}
                	}
            	}
        	}
    	}
        return (again);
}

function twoPairs(Sender) {
	// Revision to locate 2 numbers in 2 spots

    var again=0;
		for (i=0;i<81;i++) {
        	y=parseInt(i/9);
        	x=i-y*9;
			for (j=0;j<3;j++) {
				for (k=0;k<10;k++) {
					kount[j][k]=0;
					posn1[j][k]=0;
					posn2[j][k]=0;
				}
			}

			l=0;
			while (l<MySudoku[i].length && MySudoku[i].length>1) {
            	n=parseInt(MySudoku[i].substring(l,l+1));
            	N=""+n;
    	        // Eliminate within 3x3 box
            	base=(parseInt(y/3)*3)*9 + parseInt(x/3)*3;
            	for (k=0;k<3;k++) {
                	for (m=0;m<3;m++) {
                    	j=base+k*9+m;
	                    if (j!=i) {
							f=MySudoku[j];
                        	Pos=f.indexOf(N);
        	                if (Pos>=0) {kount[0][n]++; posn1[0][n]=i; posn2[0][n]=j;}
            	        }
                	}
            	}
	            // Check across the row
    	        for (k=0;k<9;k++) {
        	        j=y*9+k;
            	    if (j!=i) {
						f=MySudoku[j];
                        Pos=f.indexOf(N);
                    	if (Pos>=0) {kount[1][n]++; posn1[1][n]=i; posn2[1][n]=j;}
                	}
            	}
	            // Check down the column
    	        for (k=0;k<9;k++) {
        	        j=k*9+x;
            	    if (j!=i) {
						f=MySudoku[j];
                        Pos=f.indexOf(N);
                    	if (Pos>=0) {kount[2][n]++; posn1[2][n]=i; posn2[2][n]=j;}
                	}
            	}
				l++;
        	}

        	for (z=0;z<3;z++) {
            	for (x=1;x<10;x++) {
                	if (kount[z][x]==1) {
                    	for (y=x+1;y<10;y++) {
                        	if (kount[z][y]==1) {
								if ((posn1[z][x]==posn1[z][y] && posn2[z][x]==posn2[z][y])
                            		|| (posn1[z][x]==posn2[z][y] && posn2[z][x]==posn1[z][y])) {
                            		// Eliminate all others from this positions
                            		N=""+x; M=""+y;
									if (MySudoku[posn1[z][x]].length>2) {
                            			MySudoku[posn1[z][x]]=N+M; again++;
									}
									if (MySudoku[posn2[z][x]].length>2) {
                            			MySudoku[posn2[z][x]]=N+M; again++;
									}
								}
                        	}
                    	}
                	}
            	}
        	}
		}
    return(again);
}

function restricted(Sender) {
    // Elimination based upon possibility being restricted to row or column
    var again=0;

    	for (i=0;i<81;i++) {
        	y=parseInt(i/9);
        	x=i-y*9;
			l=0;
        	while (l<MySudoku[i].length && MySudoku[i].length>1) {
            	found=0;
            	n=parseInt(MySudoku[i].substring(l,l+1));
            	N=""+n;
            	// Eliminate within 3x3 box
            	base=(parseInt(y/3)*3)*9 + parseInt(x/3)*3;
            	for (k=0;k<3;k++) {
                	if (k!=mod(y,3)) {
                    	for (m=0;m<3;m++) {
                        	j=base+k*9+m;
							f=MySudoku[j];
                        	var Pos=f.indexOf(N);
                        	if (Pos>=0) found++;
                    	}
                	}
            	}
            	if (found==0) {
                	// Restricted to this row - eliminate from rest within rows
                	for (k=0;k<9;k++) {
                    	if (k<parseInt(x/3)*3 || k>=(parseInt(x/3)*3+3)) {
                        	j=y*9+k;
							f=MySudoku[j];
                        	Pos=f.indexOf(N);
                        	if (Pos>=0) {
                        		MySudoku[j]=DelString(f,Pos,1);
								again++;
                            	if (MySudoku[j].length==1) return (-1);
                        	}
                    	}
                	}
            	}

            	// Eliminate within 3x3 box
            	found=0;
            	for (k=0;k<3;k++) {
                	for (m=0;m<3;m++) {
                    	if (m!=mod(x,3)) {
                        	j=base+k*9+m;
                        	f=MySudoku[j];
                        	Pos=f.indexOf(N);
                        	if (Pos>=0) found++;
                    	}
                	}
            	}
            	if (found==0) {
                	// Restricted to this row - eliminate from rest within column
                	for (k=0;k<9;k++) {
                    	if (k<parseInt(y/3)*3 || k>=(parseInt(y/3)*3+3)) {
                        	j=k*9+x;
                        	f=MySudoku[j];
                        	Pos=f.indexOf(N);
                        	if (Pos>=0) {
                        		MySudoku[j]=DelString(f,Pos,1);
								again++;
                            	if (MySudoku[j].length==1) return (-1);
                        	}
                    	}
                	}
            	}
				l++;
        	}
    	}
    return(again);
}

function doubleCheck(Sender) {
    // See if in one & only one position
    var again=0;

    for (i=0;i<81;i++) {
		l=0;
        while (l<MySudoku[i].length && MySudoku[i].length>1) {
            n=parseInt(MySudoku[i].substring(l,l+1));
            OnlyChoice=0;
            N=""+n;
            y=parseInt(i/9);
            x=i-y*9;
            // Check across the row
            found=0;
            for (k=0;k<9;k++) {
                if (k!=x) {
                    j=y*9+k;
                    f=MySudoku[j];
                    Pos=f.indexOf(N);
                    if (Pos>=0) found++;
                }
            }
            if (found==0) OnlyChoice++;

            // Check down the column
            found=0;
            for (k=0;k<9;k++) {
                if (k!=y) {
                    j=k*9+x;
                    f=MySudoku[j];
                    Pos=f.indexOf(N);
                    if (Pos>=0) found++;
                }
            }
            if (found==0) OnlyChoice++;

            // Eliminate within 3x3 box
            base=(parseInt(y/3)*3)*9 + parseInt(x/3)*3;
            found=0;
            for (k=0;k<3;k++) {
                for (m=0;m<3;m++) {
                    j=base+k*9+m;
                    if (j!=i) {
                        f=MySudoku[j];
                        Pos=f.indexOf(N);
                        if (Pos>=0) found++;
                    }
                }
            }
            if (found==0) OnlyChoice++;

            if (OnlyChoice) {
				MySudoku[i]=N; again++;
                if (MySudoku[i].length==1) return(-1);
            }
			l++;
		}
    }
    return(again);
}


function HintClick2(Sender)
{

    var again=1;
    var repeat=0;
    while (again>0) {
        // Start eliminating
        repeat=0;
        again=1;
        while (again>0) {again=basicEliminate(Sender); if (again>0) repeat++;}
        if (again==0 && repeat==0) {
           again=1;
           while (again>0) {again=restricted(Sender); if (again>0) repeat++;}
           if (again==0 && repeat==0) {
                again=1;
                while (again>0) {again=doubleCheck(Sender); if (again>0) repeat++;}
                if (again==0 && repeat==0) {
                    again=1;
                    while (again>0) {again=twoPairs(Sender); if (again>0) repeat++;}
                }
            }
        }
        if (repeat && again==0) again=1;
    }
    if (again<0) return(1);
	return 0;
}

function checkPuzzle() {
    var ok=1;
    for (i=0;i<81;i++) {
        l=MySudoku[i].length;
        if (l!=1) ok=0;
        if (l==0) return(-1); // error
    }
    return(ok);
}

function HintClick3(Sender) {
    var	Save=new Array(81);
    var len=new Array(81);
    var rank=new Array(81);

    var ok=HintClick2(Sender);
    if (ok) return(ok);  // done

    ok=checkPuzzle();
    if (ok<0) {
        window.alert("UNSOLVABLE");
        
        return(0);
    }
    if (ok==1) return(0); // done

    // Time to make a guess
    //window.alert("MAYBE SOLVABLE");

    for (i=0;i<81;i++) {
        S=MySudoku[i];
        l=S.length;
        Save[i]=S; len[i]=l; rank[i]=0;
    }

    // Rank them
    var highest=0;
    for (i=0;i<81;i++) {
        if (len[i]==2) {
            y=parseInt(i/9);
            x=i-y*9;
            // Check across the row
            for (k=0;k<9;k++) {
                if (k!=x) {
                    j=y*9+k;
                    if (len[j]==1) rank[i]++;
                }
            }

            // Check down the column
            for (k=0;k<9;k++) {
                if (k!=y) {
                    j=k*9+x;
                    if (len[j]==1) rank[i]++;
                }
            }

            // Eliminate within 3x3 box
            base=(parseInt(y/3)*3)*9 + parseInt(x/3)*3;
            for (k=0;k<3;k++) {
                for (m=0;m<3;m++) {
                    j=base+k*9+m;
                    if (j!=i) {
                        if (len[j]==1) rank[i]++;
                    }
                }
            }
            if (rank[i]>highest) highest=rank[i];
        }
    }


    var again=1;
    while (again) {
        // Have a guess
        var trial=-1;
        for (i=0;i<81;i++) {
            if (len[i]==2 && rank[i]==highest) trial=i;
        }
        if (trial<0) {
           window.alert("CANNOT GUESS");
           return(0);
        }

       //window.alert(MySudoku[trial]);

        MySudoku[trial]=Save[trial].substring(0,1);
        while (HintClick2(Hint)) again++;
        ok=checkPuzzle();
        for (i=0;i<81;i++) MySudoku[i]=Save[i];

        if (ok==1) {
            //colorPuzzle();
            MySudoku[trial]=Save[trial].substring(0,1);
            //Sudoku[trial]->Color=clYellow;
            return(1);
        }
        // Try the other

        MySudoku[trial]=Save[trial].substring(1,2);
        while (HintClick2(Hint)) again++;
        ok=checkPuzzle();
        for (i=0;i<81;i++) MySudoku[i]=Save[i];

        if (ok==1) {
            //colorPuzzle();
            MySudoku[trial]=Save[trial].substring(1,2);
            //Sudoku[trial]->Color=clYellow;
            return(1);
        }
        // Bugger try another
        len[trial]=0;
        highest=0;
        for (i=0;i<81;i++) {
            if (len[i]==2) {
                if (rank[i]>highest) highest=rank[i];
            }
        }

     }
     return(0);
}

function prepareCheck() {
 	// Erase everything except where just 1 number
	count=0;
	for (i=0;i<81;i++) {
		if (Sudoku(i).readOnly==false) {
            MySudoku[i]="123456789";
       	} else {
			MySudoku[i]=Sudoku(i).value; count++;
		}
    }
	if (count==0) {
		// Nothing locked in
		for (i=0;i<81;i++) {
			if (Sudoku(i).value.length==1) {
				MySudoku[i]=Sudoku(i).value;
			}
		}
	}

}
function CheckClick() {
	m=document.getElementById('Message');
	m.value="No Errors";

    prepareCheck();

	entered=0;
	while (HintClick3(Solve));
	for (i=0;i<81;i++) {
		e=Sudoku(i);
		if (e.className=="wrongBox") e.className="textBox";
		if (e.value.length==1) entered++;

		if (e.value.length==1 && MySudoku[i].length==0) {
				e.className="wrongBox";
				m=document.getElementById('Message');
				m.value="Errors Detected";
				entered=0;
		} else if (e.value.length==1 && MySudoku[i].length>0 && parseInt(MySudoku[i])>0) {
			if (MySudoku[i].indexOf(e.value)<0) {
				e.className="wrongBox";
				m=document.getElementById('Message');
				m.value="Errors Detected";
				entered=0;
			}
		}
	}

	if (entered==81) document.getElementById('winner').className='show'

	return false;
}

function LockInClick() {
	m=document.getElementById('Message');
	m.value="";
	for (i=0;i<81;i++) {
		e=Sudoku(i);
		if (e.value.length==1) {
			e.readOnly=true;
			if (e.className!="highlight") e.className="textBox2";
		}
	}
	m=document.getElementById('Message');
	m.value="Locked In";
	return false;
}

function CheckSolution() {
	ok=0;
	for (i=0;i<81;i++) {
		e=MySudoku[i];
        n=parseInt(e);
        if (n>=1 && n<=9) ok++;
	}
	if (ok!=81) {
		m=document.getElementById('Message');
		m.value="Unsolvable";
	}
}

function HintClick(Sender) {
	CheckClick();
	m=document.getElementById('Message');
	if (m.value=="Errors Detected") return false;

	LockInClick();
	CheckSolution();
	if (Sender==LockIn) return false;
	if (Sender==Solve) {
		if (confirm("Do you give up and want to see the solution?")==false) return false;
	}

    prepareCheck();

    var repeat=1;
	while (repeat!=0) {
		repeat=0;
        if (Sender==Solve) {
             while (HintClick3(Sender)) ;
        } else {
            HintClick3(Sender);
    		m=document.getElementById('Message');
        	m.value="Hint Given";
        }
	    // Highlight newly ones found this loop
    	for (i=0;i<81;i++) {
			e=MySudoku[i];
        	n=parseInt(e);
        	if (n>=1 && n<=9) {
				Sudoku(i).tag=n;
				Sudoku(i).value=""+n;
            } 
        }
  	} // repeat
  
	return false;
}

function ClearPuzzle()
{
	ClearMessage();
	for (i=0;i<81;i++) {
		e=Sudoku(i);
		e.readOnly=false;
		e.value="";
		e.className="textBox";
		e.tag=0;
		e=ScratchField(i);
		e.className="entryField";
		e.value="";
	}
	return false;
}

function ClearClick() {
	if (confirm("This will blank out the puzzle! Continue?")==false) return false;
	ClearPuzzle();
	return false;
}


function HighlightCol(y) {
	ClearMessage();
	kount=0;
	for (i=0;i<9;i++) {
		e=Sudoku((y-1)*9+i);
		if (e.className=="highlight") kount++;
	}


	for (i=0;i<9;i++) {
		e=Sudoku((y-1)*9+i);
		if (kount<4) {
			e.className="highlight";
			e=ScratchField((y-1)*9+i);
			e.className="entryField2";
		} else {
			if (e.readOnly) {
				e.className="textBox2";
			} else {
				e.className="textBox";
			}
			e=ScratchField((y-1)*9+i);
			e.className="entryField";
		}
	}
	return false;
}

function ClearHighlighted() {
	ClearMessage();
	for (i=0;i<81;i++) {
			e=Sudoku(i);
			if (e.readOnly) {
				e.className="textBox2";
			} else {
				e.className="textBox";
			}
			e=ScratchField(i);
			e.className="entryField";
	}
	return false;
}

function HighlightRow(x) {
	ClearMessage();
	kount=0;
	for (i=0;i<9;i++) {
		e=Sudoku(i*9+x-1);
		if (e.className=="highlight") kount++;
	}
	
	for (i=0;i<9;i++) {
		e=Sudoku(i*9+x-1);
		if (kount<4) {
			e.className="highlight";
			e=ScratchField(i*9+x-1);
			e.className="entryField2";
		} else {
			if (e.readOnly) {
				e.className="textBox2";
			} else {
				e.className="textBox";
			}
			e=ScratchField(i*9+x-1);
			e.className="entryField";
		}
	}
	return false;
}
// Sets cookie values. Expiration date is optional//

function setCookie(name, value, expire) {
	document.cookie = name + "=" + escape(value) + 
	((expire == null) ? "" : ("; expires=" + expire.toGMTString()));
}

function getCookie(Name) {
	var search = Name + "=";
	if (document.cookie.length > 0) { 
		// if there are any cookies                    
		offset = document.cookie.indexOf(search);                     
		if (offset != -1) { 
			// if cookie exists
			offset += search.length;                               
			// set index of beginning of value                              
			end = document.cookie.indexOf(";", offset);                               
			// set index of end of cookie value                              
			if (end == -1)
				end = document.cookie.length;                              
			return unescape(document.cookie.substring(offset, end));                    
		}       
	}
	return null;
}

function SaveGame() {
	S="";
	for (i=0;i<81;i++) {
		e=Sudoku(i);
		if (e.value.length==1) {
			S=S+e.value;
		} else {
			S=S+"0";
		}
	}
	var today = new Date();
	var expires = new Date();
	expires.setTime(today.getTime() + 60*60*24*365);
	setCookie("Sudoku", S, expires);
		m=document.getElementById('Message');
		m.value="Game Saved";
	
	return false;
}

function LoadGame() {
	if (confirm("Continue to load the last saved game?")==false) return false;
	ClearPuzzle();
	var S=getCookie("Sudoku");
	if (S!=null && S.length>=81) {
		for (i=0;i<81;i++) {
			e=Sudoku(i);
			n=parseInt(S.substring(i,i+1));
			if (n>=1 && n<=9) {
				e.value=""+n;
			} else {
				e.value="";
			}
		}
		m=document.getElementById('Message');
		m.value="Game Loaded";
		
	} else {
		m=document.getElementById('Message');
		m.value="Unable to load game";
	}
	return false;
}