mlText={

language_precursor:"",

xName:["1","2","3","4","5","6","7","8","9"],
yName:["A","B","C","D","E","F","G","H","I"],

setCell:"Nastavit políčko %cell% na %val%.. vytahuji %val% z řády, sloupce & bloku (%togo% krok!)",
startingSolveMethod:"Řeší se metoudou %methodCode%",
endingSolveMethod:"Konec řešení metodou %methodCode%",

notes:"Kroky:",
resetWorkingGrid:"Vynulovat pracovní pole?",
sureClearStartGrid:"Jste jistý, že vynulujete pole zadání?",
runningStartGrid:"Pracuje",

wantToSetCell:"Chce se nastavit políčko %cell% na hodnotu %val% - ale je již hotovo!",

checkValid:"Kontroluji, jestli je to platné..",
invalidSolution:"Chybné řečení",
validSolution:"Platné řešení!",
difficultyScore:"Obtížnost: %difficultyScore%",
numberOfGuesses:"Použití tipu: %guesses%",
invalidSolutionCantDelete:"Chybné sudoku nebo neřešitelné - nelze smazat %val% na %cell%",

removing1:"Vytáhnout %val% z %cell% dopsat %cell% as %matrixValue%",
foundMulticountSet:"Došlo k několika kombinacím [%newSet%] a nelze eliminovat k řešení:",
setToNewSet:"Nastavit políčko %cell% na %newSet%",

endLogic:"Konec logiky",
guessing:"Tip %val% na %cell%..",
guessingSucceeded:"Tip %val% na %cell% uspěl",
guessingFailed:"Tip %val% na %cell% neuspěl",
endOfAriadne:"Konec.",
stepSolveCancelled:"Řešení krok za krokem se ruší.",
checkingGridValid:"Kontrola pole, jestli jeplatné",
couldNotSolveBySteps:"Nelze řešit krokem - Je třeba tipnout číslo!",
timeElapsed:"Čas: %secs% sekund",
solvedByLogicOnly:"Jedinečné řešení!",
guessedBut1Solution:"Vyluštěno, ale byla třeba tipnout (ale bylo zjištěno, že je jen jedno řešení)",
guessedMaybeMoreThan1Solution:"Vyluštěno, ale byla třeba tipnout (možná je víc řešení)",
solvedButMoreThan1Solution:"Vyluštěno, ale je víc než jedno řešení(Viz Kroky)",
solvedButNSolutions:"Vyluštěno, ale je %solutions% možných řešení (Viz Kroky)",
didntSolve:"Neřešilo!",
noSolution:"Tip a kontrola byly zaškrtnuté \n řešitel pokouší veškeré možnosti,\n ale není řešení",
SolveOptions:"Nebo:\n(1) Zaškrtnout 'Tip a kontrola' políčko, nebo\n(2) použitím stávajícího pole, pokuste zjistit nějaké číslo logikou,\n napište to do pole zadání a zkuste znovu .",
submittingGrid:"Odesláno do databáze.",

column:"column %col%",
columns:"columns",
row:"row %row%",
rows:"rows",
block:"block na %block%",

methodA_column:"sloupec %col% obsahuje jen jedno číslo %val% na řáda %row%..",
methodA_row:"řáda %row% obsahuje jen jedno číslo %val% na sloupci %col%..",
methodA_block:"Blok na %block% obsahuje pouze jeno číslo %val% v políčku %cell%..",

methodB_row_block:"Číslo %val% na řádě %row% musí být v bloku na %block%..",
methodB_col_block:"Číslo %val% na sloupci %col% musí být v bloku na %block%..",
methodB_block_col:"Číslo %val% v bloku na %block% musí být ve sloupci %col%..",
methodB_block_row:"Číslo %val% v bloku na %block% musí být na řádě %row%..",

methodD_name:"D: Hledám řetězce čísel",
methodD_info:"Checking for chains of size %size%",
methodD_check_row:"Checking for chains in grid rows..",
methodD_check_col:"Checking for chains in grid columns..",
methodD_check_block:"Checking for chains in grid blocks..",
methodD_row:"row %row%",
methodD_column:"column %col%",
methodD_block:"block at %block%",

methodD2_found:"Found a chain in %name%: { %items% } MUST be in cells %chain%",
methodD2_removing:".. vytáhnout %item% z %cell% použítím chain rule (method D)",
methodD2_nothing_removed:".. but not able to eliminate any numbers based on this finding",

methodF3_name2:"F(2): Finding X-Wing patterns...",
methodF3_name3:"F(3): Finding Swordfish patterns...",
methodF3_name4:"F(4): Finding Jellyfish(?) patterns...",
methodF3_name5:"F(5): Finding Squirmbag (who names these things?) patterns...",
methodF3_nameN:"F(%n%): Finding Uber-Squirmbag (who names these things?) patterns...",

methodF3_found:"Found one for value %val% in %row_name% %row_chain% at %col_name% %col_chain%",
methodF3_removing:".. vytáhnout %val% z %cell% použitím pattern rule (method F).",

cookie_not_set:"Could not set the cookie.\nIf you want to use cookies,\nplease check your security settings.",
cookie_unavailable:"Unable to get cookie data or no cookie set.",
cookie_invalid:"Unable to set using cookie data",
string_format_message:"Please enter the Sudoku string by rows - '_' for a blank, '+' for a new row",
string_error_in_format:"Error.. Please try again. Use '_' for a blank, '+' for a new row",
copy_string_to_clipboard:"Copy this to the clipboard and paste it where you want it, then hit OK",

noGridsToday:"No grids today, or the database is off line.",
loadThisOne:"Load this one",
uniqueSolution:"jednotné řešení: %isUnique%",
hitCount:"Hit count: %hits%",
yes:"Yes",
noGreaterThanOne:"No (>1)",
noNone:"No (None)"

};
Date.prototype.addDays=function(d)
{ with(this) setTime(getTime()+d*86400000);}
String.prototype.trim=function()
{ return this.replace(/(^\s*)|(\s*$)/g, "");}
String.prototype.anyMatch=function(compareString)
{ var i,c; for(i=0;i<compareString.length;i++)
{ c=compareString.charAt(i); if(this.indexOf(c)>=0) return true;}
return false;}
String.prototype.firstMatch=function(compareString)
{ var i,c; for(i=0;i<compareString.length;i++)
{ c=compareString.charAt(i); if(this.indexOf(c)>=0) return c;}
return false;}
Object.prototype.complexCopyOf=function(copyTo)
{ var el; for(el in this)
{ copyTo[el]=this[el].copyOf();}
return copyTo;}
Object.prototype.copyOf=function()
{ if(this.constructor==Function) return this.constructor; if(this.constructor==Boolean || this.constructor==Number || this.constructor==String) return this.valueOf(); if(this.constructor==Array) return this.complexCopyOf(new Array()); if(this.constructor==Date) return new Date(this.getTime()); return this.complexCopyOf(new this.constructor);}
Array.prototype.showHTML=function()
{ var i,a0; a0=""; for(i=0;i<this.length;i++)
{ a0+=this[i].showHTML();}
return a0;}
function setCookie(name,value,daysToExpiry)
{ var expDate; expDate=new Date(); expDate.addDays(daysToExpiry); document.cookie = name+"="+escape(value)+"; expires=" + expDate.toGMTString() + ";"
}
function getCookie(cookieName)
{ var dc,valuePairStringArray,i,valuePairArray; if(document.cookie)
{ dc=document.cookie; valuePairStringArray=dc.split(";"); for(i=0;i<valuePairStringArray.length;i++)
{ valuePairArray=valuePairStringArray[i].split("="); if(valuePairArray[0].trim()==cookieName) return valuePairArray[1].trim();}
}
return null;}
function getSudokuString(delimeter)
{ var a0,x,y,formEl,chr; a0=""; for(y=0;y<9;y++)
{ for(x=0;x<9;x++)
{ formEl=document.getElementById("sgd_"+x+"_"+y); chr=formEl.value+" "; chr=chr.charAt(0); if(chr==" ") chr="_"
a0+=chr.charAt(0);}
if(y!=8) a0+=delimeter;}
return a0;}
function isValidSudokuString(a0,delimeter)
{ var a1,sArr,x,y,chr; a1="123456789_."; sArr=a0.split(delimeter); if(sArr.length!=9) return false; for(y=0;y<9;y++)
{ if(sArr[y].length!=9) return false; for(x=0;x<9;x++)
{ chr=sArr[y].charAt(x); if(a1.indexOf(chr)==-1) return false;}
}
return true;}
function setSudokuString(a0,delimeter)
{ var sArr,x,y,formEl,chr; if(!isValidSudokuString(a0,delimeter)) return false; sArr=a0.split(delimeter); for(y=0;y<9;y++)
{ for(x=0;x<9;x++)
{ chr=sArr[y].charAt(x); if(chr=="_") chr=""; if(chr==".") chr=""; formEl=document.getElementById("sgd_"+x+"_"+y); formEl.value=chr;}
}
return true;}
function saveSudokuCookie()
{ var a0; a0=getSudokuString("+"); setCookie("sudoku",a0,365); if(a0!=getCookie("sudoku")) alert(mlText.cookie_not_set);}
function loadSudokuCookie()
{ var a0; a0=getCookie("sudoku"); if(a0==null) return alert(mlText.cookie_unavailable); var set = setSudokuString(a0,"+"); if(set==false) alert(mlText.cookie_invalid);}
function saveCurrentGrid()
{ var a0; a0=getSudokuString("+"); setCookie("currentgrid",a0,0.1);}
function currentGridWatch()
{ saveCurrentGrid(); setTimeout("currentGridWatch()", 2000)
}
function loadCurrentGrid()
{ var a0; a0=getCookie("currentgrid"); if(a0==null) return false; setSudokuString(a0,"+"); return true;}
function enterSudoku()
{ var a0,done,promptStr; done=false; a0=getSudokuString("+"); promptStr=mlText.string_format_message; while(done==false)
{ a0=prompt(promptStr, a0); if (a0==null) return; done=setSudokuString(a0,"+"); if(done==false)
{ done=setSudokuString(a0,"x"); if(done==false) promptStr=mlText.string_error_in_format;}
}
}
function createSudokuString()
{ var a0; a0=prompt(mlText.copy_string_to_clipboard, getSudokuString("+"));}
function emailer()
{ var a0="mailto:comments"; a0+="@"; a0+="sudokusolver.co.uk?body=%0a%0aStart Sudoku:%0a%0a"+getSudokuString("%0a")+"%0a%0aCookie String:%0a"+getSudokuString("+")+"%0a"; window.location=a0;}
var mostPopularGrid; var sudokuLoaded=false; var mostPopularGridLoaded=false; function setTodaysMostPopularGrid()
{ var popFrameEl=document.getElementById("mostPopularGridFrame"); var oDoc = (popFrameEl.contentWindow || popFrameEl.contentDocument); if (oDoc.document) oDoc = oDoc.document; mostPopularGrid=oDoc.body.innerHTML; if(sudokuLoaded==true) setSudokuString(mostPopularGrid,"x"); if(isValidSudokuString(mostPopularGrid,"x")) mostPopularGridLoaded=true;}
function submitGridString(a0,d,g,s)
{ strPage = "submitGrid2.php?grid="+a0+"&difficulty="+d+"&guesses="+g+"&solutions="+s; var submitFrameEl=document.getElementById("submitGridFrame"); if(submitFrameEl) submitFrameEl.src=strPage;}
function submitGrid(d,g,s)
{ var a0=getSudokuString("x"); submitGridString(a0,d,g,s);}
function goToPage(pageURL)
{ document.location=mlText.language_precursor+pageURL;}
border1="#303090"; border2="#9090FF"; shading1="#FFFFFF"; function Sudoku(startGridDiv,workingGridDiv,notesName)
{ this.xName=mlText.xName; this.yName=mlText.yName; this.startVals="_3___1___x__6____5_x5_____983x_8___63_2x____5____x9_38___6_x714_____9x_2____8__x___4___3_"; this.lastSubmittedGrid=this.startVals; this.matrix=new Array(); this.matrixEl=new Array(); this.matrixColor=new Array(); this.startMatrix=new Array(); this.hasChangedFlag=false; this.solvedCount=0; this.startGridDiv=startGridDiv; this.workingGridDiv=workingGridDiv; if(document.frames) this.notesDoc=document.frames(notesName).document; if(document.getElementById(notesName).contentDocument) this.notesDoc=document.getElementById(notesName).contentDocument; this.found=null; this.lastFoundX=0; this.lastFoundY=0; this.step=false; this.breakOut=false; this.lastHighlightedX=null; this.lastHighlightedY=null; this.solutions=0; this.difficultyScore=0; this.guesses=0; this.latestSolution=""; this.doAriadne=true; this.returnFirstAnswer=true; this.stopAfterTwoSolutions=true; this.doAlert=true; this.lsEl=null; var x,y; for(y=0;y<9;y++)
{ this.matrix[y]=new Array(); this.matrixEl[y]=new Array(); this.matrixColor[y]=new Array(); this.startMatrix[y]=new Array();}
this.initialiseDivs(); this.bufferNotes=""; this.lastNote=""; this.lastNoteButOne=""; this.notesDoc.body.innerHTML=mlText.notes+"<br>";}
Sudoku.prototype.cellString=function(x,y)
{ return "["+this.yName[y]+this.xName[x]+"]";}
Sudoku.prototype.parseString=function(str,replaceObj)
{ var el,myReg; for(el in replaceObj)
{ if(typeof replaceObj[el]=='function') continue; myReg=new RegExp("%"+el+"%","g"); str=str.replace(myReg,replaceObj[el]);}
return str;}
Sudoku.prototype.addNote=function(str,replaceObj)
{ str=this.parseString(str,replaceObj); this.lastNoteButOne=this.lastNote; if(this.lastNoteButOne && this.lastNoteButOne.indexOf("<pre>")!=-1) this.lastNoteButOne=""; this.lastNote=str; this.bufferNotes+=str+"<br>";}
Sudoku.prototype.echoNotes=function()
{ this.notesDoc.body.innerHTML+=this.bufferNotes; this.bufferNotes="";}
Sudoku.prototype.clearNoteBuffer=function()
{ this.bufferNotes="";}
Sudoku.prototype.statusNote=function()
{ this.addNote("<pre>"+this.statusNoteText("<br>")+"</pre>");}
Sudoku.prototype.statusNoteText=function(eolTxt)
{ var x,y,a0,sps; sps="          "; a0="   |  "; for(x=0;x<9;x++)
{ a0+=this.xName[x]+"         ";}
a0+=eolTxt; a0+="---+--"; for(x=0;x<9;x++)
{ a0+="----------";}
a0+=eolTxt; for(y=0;y<9;y++)
{ a0 += this.yName[y]+"  |  "; for(x=0;x<9;x++)
{ a0+=this.matrix[y][x]; a0+=sps.substring(0,10-this.matrix[y][x].length);}
a0+=eolTxt;}
return a0;}
Sudoku.prototype.startMethodNote=function(methodCode)
{ this.addNote("=============================="); this.addNote(mlText.startingSolveMethod,{methodCode:methodCode}); this.statusNote();}
Sudoku.prototype.endMethodNote=function(methodCode)
{ if(this.step==true) this.refreshWorkingGrid(); this.addNote(mlText.endingSolveMethod,{methodCode:methodCode}); this.addNote("==============================");}
Sudoku.prototype.resetWorkingGrid=function()
{ var x,y; for(y=0;y<9;y++)
{ for(x=0;x<9;x++)
{ this.matrixColor[y][x]='#FFFFFF'; this.matrixEl[y][x].style.fontWeight='normal'; this.matrixEl[y][x].style.color='#404040'; this.matrix[y][x]='123456789'; this.hasChangedFlag=true;}
}
this.lastHighlightedX=null; this.lastHighlightedY=null; this.refreshWorkingGrid(); this.notesDoc.body.innerHTML=mlText.notes+"<br>"; this.addNote(mlText.resetWorkingGrid); this.solvedCount=0; this.difficultyScore=0; this.guesses=0;}
Sudoku.prototype.clearStartGrid=function()
{ var csg=confirm(mlText.sureClearStartGrid); if(csg!=true) return; var x,y; for(y=0;y<9;y++)
{ for(x=0;x<9;x++)
{ formEl=document.getElementById("sgd_"+x+"_"+y); formEl.value="";}
}
}
Sudoku.prototype.clearWorkingGrid=function()
{ var x,y; for(y=0;y<9;y++)
{ for(x=0;x<9;x++)
{ formEl=document.getElementById("sgw_"+x+"_"+y); formEl.value="";}
}
}
Sudoku.prototype.initialiseDivs=function()
{ this.startGridDiv.innerHTML=this.showHTML("\"<input type='text' id='sgd_\"+x+\"_\"+y+\"' value='' size='1' maxlength='1' class='startGrid'>\""); this.workingGridDiv.innerHTML=this.showHTML("\"<input type='text' id='sgw_\"+x+\"_\"+y+\"' value='' size='9' maxlength='9' class='workingGrid'>\""); for(y=0;y<9;y++)
{ for(x=0;x<9;x++)
{ this.matrixEl[y][x]=document.getElementById("sgw_"+x+"_"+y);}
}
this.resetWorkingGrid(); if(loadCurrentGrid()==true) return; if(mostPopularGridLoaded==true)
{ setSudokuString(mostPopularGrid,"x"); this.lastSubmittedGrid=mostPopularGrid; return;}
setSudokuString(this.startVals,"x"); sudokuLoaded=true;}
Sudoku.prototype.refreshWorkingGrid=function()
{ for(y=0;y<9;y++)
{ for(x=0;x<9;x++)
{ this.matrixEl[y][x].value=this.matrix[y][x]; this.matrixEl[y][x].style.backgroundColor=this.matrixColor[y][x];}
}
}
Sudoku.prototype.showHTML=function(evalStr)
{ var a0,styleStr; a0="<table cellpadding=0 cellspacing=0>"; var x,y; a0+="<tr>"; a0+="<td></td>"; for(x=0;x<9;x++)
{ a0+="<td>"+this.xName[x]+"</td>";}
a0+="</tr>"; for(y=0;y<9;y++)
{ a0+="<tr>"; a0+="<td>"+this.yName[y]+"</td>"; for(x=0;x<9;x++)
{ styleStr=" style='border-color:"; styleStr+=(y%3==0)?border1:border2; styleStr+=" "; styleStr+=(x%3==2)?border1:border2; styleStr+=" "; styleStr+=(y%3==2)?border1:border2; styleStr+=" "; styleStr+=(x%3==0)?border1:border2; styleStr+="'"; a0+="<td"+styleStr+">"+eval(evalStr)+"</td>";}
a0+="</tr>";}
a0+="</table>"; return a0;}
Sudoku.prototype.runStartGrid=function()
{ var x,y; this.breakOut=false; this.addNote("=============================="); this.addNote("<b>"+mlText.runningStartGrid+"</b>"); for(y=0;y<9;y++)
{ for(x=0;x<9;x++)
{ formEl=document.getElementById("sgd_"+x+"_"+y); if(formEl.value==" ") formEl.value=""; if(formEl.value!="")
{ this.startMatrix[y][x]=formEl.value; this.set(x,y,formEl.value,true,true);}
if(this.breakOut) return this.refreshWorkingGrid();}
}
this.refreshWorkingGrid();}
Sudoku.prototype.getMatrixString=function()
{ var a0,x,y; a0=""; for(y=0;y<9;y++)
{ for(x=0;x<9;x++)
{ a0+=this.matrix[y][x]; if(x!=8) a0+=",";}
if(y!=8) a0+="+";}
return a0;}
Sudoku.prototype.setMatrixString=function(a0)
{ var x,y,sArr,tArr; sArr=a0.split("+"); for(y=0;y<9;y++)
{ tArr=sArr[y].split(","); for(x=0;x<9;x++)
{ this.matrix[y][x]=tArr[x];}
}
return;}
Sudoku.prototype.isInvalidSolutionSubset=function(setArray)
{ setArray.sort(); for(i=0;i<setArray.length;i++)
{ if(setArray[i].toString()!=(i+1).toString()) return true;}
return false;}
Sudoku.prototype.checkSolution=function()
{ var ix1,ix2,ix3,ix4,row,col,block; for(ix1=0;ix1<3;ix1++)
{ for(ix2=0;ix2<3;ix2++)
{ row=new Array(); col=new Array(); block=new Array(); for(ix3=0;ix3<3;ix3++)
{ for(ix4=0;ix4<3;ix4++)
{ row[ix3*3+ix4]=this.matrix[ix1*3+ix2][ix3*3+ix4]; col[ix3*3+ix4]=this.matrix[ix3*3+ix4][ix1*3+ix2]; block[ix3*3+ix4]=this.matrix[ix1*3+ix3][ix2*3+ix4];}
}
if(this.isInvalidSolutionSubset(row) || this.isInvalidSolutionSubset(col) || this.isInvalidSolutionSubset(block)) return false;}
}
return true;}
Sudoku.prototype.explainAndPause=function(highlightArray)
{ var i,y,x; this.refreshWorkingGrid(); for(i=0;i<highlightArray.length;i++)
{ if(highlightArray[i][0]=='row')
{ for(x=0;x<9;x++)
{ this.matrixEl[highlightArray[i][1]][x].style.backgroundColor=highlightArray[i][2];}
}
if(highlightArray[i][0]=='col')
{ for(y=0;y<9;y++)
{ this.matrixEl[y][highlightArray[i][1]].style.backgroundColor=highlightArray[i][2];}
}
if(highlightArray[i][0]=='block')
{ for(x=0;x<3;x++)
{ for(y=0;y<3;y++)
{ this.matrixEl[y+highlightArray[i][2]][x+highlightArray[i][1]].style.backgroundColor=highlightArray[i][3];}
}
}
if(highlightArray[i][0]=='cell')
{ this.matrixEl[highlightArray[i][2]][highlightArray[i][1]].style.backgroundColor=highlightArray[i][3];}
}
var a0=prompt(this.lastNoteButOne,this.lastNote); if (a0==null)
{ this.breakOut=true;}
}
Sudoku.prototype.shade=function(x,y)
{ this.matrixColor[y][x]=shading1; this.matrixEl[y][x].style.backgroundColor=this.matrixColor[y][x]; this.matrixEl[y][x].style.color='#000000';}
Sudoku.prototype.set=function(x,y,val,test,dontReport)
{ var ix,iy,xBlockStart,yBlockStart; if(test && this.matrix[y][x]==val.toString())
{ if(dontReport==null || dontReport!=true) this.addNote(mlText.wantToSetCell,{cell:this.cellString(x,y),val:val}); return;}
this.solvedCount++; this.addNote(mlText.setCell,{cell:this.cellString(x,y),val:val,togo:(81-this.solvedCount)}); if(this.solvedCount==81)
{ this.shade(x,y); this.addNote(mlText.checkValid); this.statusNote(); if(this.checkSolution()!=true)
{ throw mlText.invalidSolution;}
this.solutions++; this.latestSolution=this.getMatrixString(); this.addNote(mlText.validSolution); this.addNote(mlText.difficultyScore,{difficultyScore:this.difficultyScore}); this.addNote(mlText.numberOfGuesses,{guesses:this.guesses}); return;}
xBlockStart=3*parseInt(x/3); yBlockStart=3*parseInt(y/3); if(this.step) this.explainAndPause(new Array(new Array('block',xBlockStart,yBlockStart,'#8470FF'),new Array('row',y,'#98FB98'),new Array('col',x,'pink'),new Array('cell',x,y,'#FFD700'))); this.matrix[y][x]=val.toString(); this.shade(x,y); this.hasChangedFlag=true; for(ix=0;ix<9;ix++)
{ if(ix!=x) this.remove(ix,y,val);}
for(iy=0;iy<9;iy++)
{ if(iy!=y) this.remove(x,iy,val);}
for(ix=xBlockStart;ix<xBlockStart+3;ix++)
{ for(iy=yBlockStart;iy<yBlockStart+3;iy++)
{ if(ix!=x && iy!=y) this.remove(ix,iy,val);}
}
}
Sudoku.prototype.isValAtXY=function(x,y,val)
{ var vpos=this.matrix[y][x].indexOf(val); if(vpos>=0) return true; return false;}
Sudoku.prototype.remove=function(x,y,val)
{ var vpos=this.matrix[y][x].indexOf(val); if(vpos>=0)
{ if(this.matrix[y][x].length==1)
{ throw this.parseString(mlText.invalidSolutionCantDelete,{val:val,cell:this.cellString(x,y)});}
this.matrix[y][x]=this.matrix[y][x].substr(0,vpos)+this.matrix[y][x].substr(vpos+1); this.hasChangedFlag=true; if(this.matrix[y][x].length==1)
{ this.addNote(mlText.removing1,{val:val,cell:this.cellString(x,y),matrixValue:this.matrix[y][x]}); if(document.getElementById("sgd_"+x+"_"+y)!=this.matrix[y][x]) this.difficultyScore+=1; this.set(x,y,this.matrix[y][x],false);}
}
}
Sudoku.prototype.columnCount=function(x,vals)
{ this.found=null; this.found=new Array(); vals=vals.toString(); var vp,vc; var y; var ct=0; for(y=0;y<9;y++)
{ vc=0; for(vp=0;vp<vals.length;vp++)
{ if(this.matrix[y][x].indexOf(vals.charAt(vp))>=0)
{ vc++;}
}
if(vc==vals.length)
{ ct++; this.lastFoundY=y; this.found.push(new Array(x,y));}
}
return ct;}
Sudoku.prototype.rowCount=function(y,vals)
{ this.found=null; this.found=new Array(); vals=vals.toString(); var vp,vc; var x; var ct=0; for(x=0;x<9;x++)
{ vc=0; for(vp=0;vp<vals.length;vp++)
{ if(this.matrix[y][x].indexOf(vals.charAt(vp))>=0)
{ vc++;}
}
if(vc==vals.length)
{ ct++; this.lastFoundX=x; this.found.push(new Array(x,y));}
}
return ct;}
Sudoku.prototype.blockCount=function(bx,by,vals)
{ this.found=null; this.found=new Array(); vals=vals.toString(); var vp,vc; var x,y; var ct=0; for(x=bx*3;x<3+bx*3;x++)
{ for(y=by*3;y<3+by*3;y++)
{ vc=0; for(vp=0;vp<vals.length;vp++)
{ if(this.matrix[y][x].indexOf(vals.charAt(vp))>=0)
{ vc++;}
}
if(vc==vals.length)
{ ct++; this.lastFoundX=x; this.lastFoundY=y; this.found.push(new Array(x,y));}
}
}
return ct;}
Sudoku.prototype.findUnique=function(foundCountOf,c)
{ var curEl,compareEl,matches,i,j,matchedEl,elimOne,newSet; elimOne=false; while(foundCountOf.length>=c)
{ curEl = foundCountOf.shift(); matches=null; matches=new Array(); for(i=0;i<foundCountOf.length;i++)
{ compareEl=foundCountOf[i]; matchedEl=true; for(j=0;j<c;j++)
{ if(curEl[1][j][0]!=compareEl[1][j][0] || curEl[1][j][1]!=compareEl[1][j][1])
{ matchedEl=false; break;}
}
if(matchedEl)
{ matches.push(compareEl);}
}
if(matches.length==c-1)
{ newSet=curEl[0].toString(); for(j=0;j<c-1;j++)
{ newSet+=matches[j][0].toString();}
elimOne=false; for(j=0;j<c;j++)
{ if(this.matrix[curEl[1][j][1]][curEl[1][j][0]]!=newSet) elimOne=true;}
if(elimOne)
{ this.addNote(mlText.foundMulticountSet,{newSet:newSet}); this.difficultyScore+=16; if(this.step)
{ multiCountSet=new Array(); for(j=0;j<c;j++)
{ multiCountSet[j]=new Array('cell',curEl[1][j][0],curEl[1][j][1],'#8470FF');}
this.explainAndPause(multiCountSet);}
for(j=0;j<c;j++)
{ this.addNote(mlText.setToNewSet,{cell:this.cellString(curEl[1][j][0],curEl[1][j][1]),newSet:newSet}); this.matrix[curEl[1][j][1]][curEl[1][j][0]]=newSet; this.hasChangedFlag=true;}
}
}
}
return elimOne;}
Sudoku.prototype.logicSolve=function()
{ this.hasChangedFlag=true; while(this.breakOut==false && this.hasChangedFlag==true && this.solvedCount<81)
{ this.echoNotes(); this.hasChangedFlag=false; this.solveMethodA(); if(this.hasChangedFlag==true) this.echoNotes(); if(this.solvedCount==81 || this.breakOut==true) continue; this.clearNoteBuffer(); this.solveMethodB(); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer(); this.solveMethodC(2); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer(); this.solveMethodC(3); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer(); this.solveMethodC(4); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer(); this.solveMethodC(5); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer(); this.solveMethodC(6); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer(); this.solveMethodC(7); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer(); this.solveMethodC(8); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer(); this.hasChangedFlag=false; this.solveMethodD2(2,32); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer(); this.hasChangedFlag=false; this.solveMethodD2(3,48); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer(); this.hasChangedFlag=false; this.solveMethodD2(4,64); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer(); this.hasChangedFlag=false; this.solveMethodF3(2,48); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer(); this.hasChangedFlag=false; this.solveMethodF3(3,64); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer(); this.hasChangedFlag=false; this.solveMethodF3(4,64); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer(); this.hasChangedFlag=false; this.solveMethodF3(5,64); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer(); this.hasChangedFlag=false; this.solveMethodF3(6,64); if(this.hasChangedFlag==true || this.breakOut==true) continue; this.clearNoteBuffer();}
this.echoNotes();}
Sudoku.prototype.ariadnesThread=function()
{ var x,y,n,val,tempSolvedCount,savedMatrix,solvedAtAll; this.logicSolve(); if(this.solvedCount==81)
{ return true;}
found=false; for(x=0;x<9;x++)
{ for(y=0;y<9;y++)
{ if(this.matrix[y][x].length>1)
{ found=true; break;}
}
if(found==true) break;}
if(found==false) return false; this.addNote("<br><b>"+mlText.endLogic+"</b></br>"); this.statusNote(); tempSolvedCount=this.solvedCount; savedMatrix=this.getMatrixString(); solvedAtAll=false; for(n=0;n<this.matrix[y][x].length;n++)
{ val=this.matrix[y][x].charAt(n); this.addNote("<br><b>"+mlText.guessing+"</b><br>",{val:val,cell:this.cellString(x,y)}); this.statusNote(); try
{ this.difficultyScore+=1000; this.guesses++; this.set(x,y,val,true); solved=this.ariadnesThread();}
catch (e)
{ this.addNote(e); solved=false;}
solvedAtAll|=solved; if(solved)
{ this.addNote("<br><b>"+mlText.guessingSucceeded+"</b><br>",{val:val,cell:this.cellString(x,y)});}
else
{ this.addNote("<br><b>"+mlText.guessingFailed+"</b><br>",{val:val,cell:this.cellString(x,y)});}
if(solved && this.returnFirstAnswer) return true; if(solved && this.solutions>=2 && this.stopAfterTwoSolutions) return true; this.solvedCount=tempSolvedCount; this.setMatrixString(savedMatrix);}
this.addNote("<br><b>"+mlText.endOfAriadne+"</b></br>"); return solvedAtAll;}
Sudoku.prototype.solveStartGrid=function()
{ shading1="#C0C0C0"; this.step=false; this.breakOut=false; this.solutions=0; this.resetWorkingGrid(); this.hasChangedFlag=false; this.runStartGrid(); this.echoNotes();}
Sudoku.prototype.solveBySteps=function()
{ shading1="#C0C0C0"; this.step=true; this.breakOut=false; this.hasChangedFlag=false; this.runStartGrid(); this.logicSolve(); if(this.breakOut==true) this.addNote(mlText.stepSolveCancelled); if(this.solvedCount==81)
{ this.addNote(mlText.checkingGridValid); this.statusNote(); if(this.checkSolution()!=true)
{ this.addNote(mlText.invalidSolution);}
else
{ this.addNote(mlText.validSolution); if(this.doAlert) alert(mlText.validSolution);}
}
else if(this.breakOut==false)
{ this.addNote(mlText.couldNotSolveBySteps); if(this.doAlert) alert(mlText.couldNotSolveBySteps);}
this.echoNotes(); this.refreshWorkingGrid();}
Sudoku.prototype.suggestAMove=function()
{ shading1="#C0C0C0"; this.solveStartGrid(); this.solveBySteps();}
Sudoku.prototype.solveFromScratch=function()
{ this.step=false; this.breakOut=false; var startTime=new Date(); if(document.getElementById("ariadneCheckBox")) this.doAriadne=document.getElementById("ariadneCheckBox").checked; if(document.getElementById("returnFirstAnswerCheckBox")) this.returnFirstAnswer=document.getElementById("returnFirstAnswerCheckBox").checked; var ss0=getSudokuString("x"); saveCurrentGrid(); this.solutions=0; var trySubmit=false; try
{ this.resetWorkingGrid(); this.hasChangedFlag=false; this.runStartGrid(); if(this.doAriadne)
{ this.ariadnesThread();}
else
{ this.logicSolve();}
this.refreshWorkingGrid(); var endTime=new Date()
var elapsedTime=endTime.getTime()-startTime.getTime(); this.addNote(mlText.timeElapsed,{secs:(elapsedTime/1000)}); this.echoNotes(); if(this.solutions>0)
{ this.setMatrixString(this.latestSolution); this.refreshWorkingGrid(); if(this.solutions==1 && this.guesses==0 )
{ if(this.doAlert) alert(mlText.solvedByLogicOnly); trySubmit=true;}
else if(this.solutions==1 && this.guesses>0 && !this.returnFirstAnswer)
{ if(this.doAlert) alert(mlText.guessedBut1Solution); trySubmit=true;}
else if(this.solutions==1 && this.guesses>0 && this.returnFirstAnswer)
{ if(this.doAlert) alert(mlText.guessedMaybeMoreThan1Solution);}
else if(this.solutions==2 && this.stopAfterTwoSolutions)
{ if(this.doAlert) alert(mlText.solvedButMoreThan1Solution); trySubmit=true;}
else
{ if(this.doAlert) alert(this.parseString(mlText.solvedButNSolutions,{solutions:this.solutions}));}
}
else
{ if(this.doAriadne)
{ if(this.doAlert) alert(mlText.didntSolve+"\n"+mlText.noSolution);}
else
{ if(this.doAlert) alert(mlText.didntSolve+"\n"+mlText.noSolveOptions);}
}
}
catch (e)
{ this.echoNotes(); this.refreshWorkingGrid(); if(this.doAlert) alert(e);}
if(trySubmit && this.lastSubmittedGrid!=ss0)
{ this.addNote(mlText.submittingGrid); this.echoNotes(); submitGrid(this.difficultyScore,this.guesses,this.solutions); this.lastSubmittedGrid=ss0;}
}
Sudoku.prototype.solveMethodA=function()
{ var x,y,bx,by,n, foundOne; this.startMethodNote("A"); do
{ foundOne=false; for(n=1;n<=9;n++)
{ for(x=0;x<9;x++)
{ if(this.columnCount(x,n)==1 && this.matrix[this.lastFoundY][x]!=n)
{ this.addNote(mlText.methodA_column,{col:this.xName[x],val:n,row:this.yName[this.lastFoundY]}); if(this.step) this.explainAndPause(new Array(new Array('col',x,'#98FB98'),new Array('cell',x,this.lastFoundY,'#FFD700'))); this.difficultyScore+=4; this.set(x,this.lastFoundY,n,true); foundOne=true; if(this.breakOut==true) return this.endMethodNote("A");}
}
for(y=0;y<9;y++)
{ if(this.rowCount(y,n)==1 && this.matrix[y][this.lastFoundX]!=n)
{ this.addNote(mlText.methodA_row,{row:this.yName[y],val:n,col:this.xName[this.lastFoundX]}); if(this.step) this.explainAndPause(new Array(new Array('row',y,'#98FB98'),new Array('cell',this.lastFoundX,y,'#FFD700'))); this.difficultyScore+=4; this.set(this.lastFoundX,y,n,true); foundOne=true; if(this.breakOut==true) return this.endMethodNote("A");}
}
for(bx=0;bx<3;bx++)
{ for(by=0;by<3;by++)
{ if(this.blockCount(bx,by,n)==1 && this.matrix[this.lastFoundY][this.lastFoundX]!=n)
{ this.addNote(mlText.methodA_block,{block:this.cellString(bx*3,by*3),val:n,cell:this.cellString(this.lastFoundX,this.lastFoundY)}); if(this.step) this.explainAndPause(new Array(new Array('block',bx*3,by*3,'#98FB98'),new Array('cell',this.lastFoundX,this.lastFoundY,'#FFD700'))); this.difficultyScore+=4; this.set(this.lastFoundX,this.lastFoundY,n,true); foundOne=true; if(this.breakOut==true) return this.endMethodNote("A");}
}
}
}
} while(foundOne==true && this.breakOut==false)
this.endMethodNote("A");}
Sudoku.prototype.solveMethodB=function()
{ var x,y,bx,by,sx,sy,i,n, foundOne,b0,b1,b2,bix; var c,cc; var allSameRow,allSameCol; this.startMethodNote("B"); do
{ foundOne=false; for(x=0;x<9;x++)
{ for(n=1;n<=9;n++)
{ cc=this.columnCount(x,n); if(cc<=3 && cc>1)
{ b0=1;b1=1;b2=1; for(i=0;i<this.found.length;i++)
{ bix=this.found[i][1]; if(bix<=2) b0=2; else if(bix>=6) b2=2; else b1=2;}
if(b0*b1*b2==2)
{ bx=3*parseInt(x/3); by=b1*3+b2*6-9; if(this.blockCount(bx/3,by/3,n)>cc)
{ foundOne=true; this.addNote(mlText.methodB_block_col,{val:n,block:this.cellString(bx,by),col:this.xName[x]}); if(this.step) this.explainAndPause(new Array(new Array('block',bx,by,'#8470FF'),new Array('col',x,'#98FB98'),new Array('cell',x,by,'pink'),new Array('cell',x,by+1,'pink'),new Array('cell',x,by+2,'pink'))); for(i=0;i<this.found.length;i++)
{ if(this.found[i][0]!=x)
{ this.difficultyScore+=8; this.remove(this.found[i][0],this.found[i][1],n);}
}
if(this.breakOut==true) return this.endMethodNote("B");}
}
}
}
}
for(y=0;y<9;y++)
{ for(n=1;n<=9;n++)
{ cc=this.rowCount(y,n); if(cc<=3 && cc>1)
{ b0=1;b1=1;b2=1; for(i=0;i<this.found.length;i++)
{ bix=this.found[i][0]; if(bix<=2) b0=2; else if(bix>=6) b2=2; else b1=2;}
if(b0*b1*b2==2)
{ by=3*parseInt(y/3); bx=b1*3+b2*6-9; if(this.blockCount(bx/3,by/3,n)>cc)
{ foundOne=true; this.addNote(mlText.methodB_block_row,{val:n,block:this.cellString(bx,by),row:this.yName[y]}); if(this.step) this.explainAndPause(new Array(new Array('block',bx,by,'#8470FF'),new Array('row',y,'#98FB98'),new Array('cell',bx,y,'pink'),new Array('cell',bx+1,y,'pink'),new Array('cell',bx+2,y,'pink'))); for(i=0;i<this.found.length;i++)
{ if(this.found[i][1]!=y)
{ this.difficultyScore+=8; this.remove(this.found[i][0],this.found[i][1],n);}
}
if(this.breakOut==true) return this.endMethodNote("B");}
}
}
}
}
for(bx=0;bx<3;bx++)
{ for(by=0;by<3;by++)
{ for(n=1;n<=9;n++)
{ cc=this.blockCount(bx,by,n); if(cc<=3 && cc>1)
{ allSameRow=true; allSameCol=true; for(i=1;i<this.found.length;i++)
{ if(this.found[0][1]!=this.found[i][1]) allSameRow=false; if(this.found[0][0]!=this.found[i][0]) allSameCol=false;}
if(allSameRow)
{ if(this.rowCount(this.found[0][1],n)>cc)
{ foundOne=true; this.addNote(mlText.methodB_row_block,{val:n,row:this.yName[this.found[0][1]],block:this.cellString(bx*3,by*3)}); if(this.step) this.explainAndPause(new Array(new Array('block',bx*3,by*3,'#8470FF'),new Array('row',this.found[0][1],'#98FB98'),new Array('cell',bx*3,this.found[0][1],'pink'),new Array('cell',bx*3+1,this.found[0][1],'pink'),new Array('cell',bx*3+2,this.found[0][1],'pink'))); for(i=0;i<this.found.length;i++)
{ if(this.found[i][0]<bx*3 || this.found[i][0]>2+bx*3)
{ this.difficultyScore+=8; this.remove(this.found[i][0],this.found[i][1],n);}
}
if(this.breakOut==true) return this.endMethodNote("B");}
}
if(allSameCol)
{ if(this.columnCount(this.found[0][0],n)>cc)
{ foundOne=true; this.addNote(mlText.methodB_col_block,{val:n,col:this.xName[this.found[0][0]],block:this.cellString(bx*3,by*3)}); if(this.step) this.explainAndPause(new Array(new Array('block',bx*3,by*3,'#8470FF'),new Array('col',this.found[0][0],'#98FB98'),new Array('cell',this.found[0][0],by*3,'pink'),new Array('cell',this.found[0][0],by*3+1,'pink'),new Array('cell',this.found[0][0],by*3+2,'pink'))); for(i=0;i<this.found.length;i++)
{ if(this.found[i][1]<by*3 || this.found[i][1]>2+by*3)
{ this.difficultyScore+=8; this.remove(this.found[i][0],this.found[i][1],n);}
}
if(this.breakOut==true) return this.endMethodNote("B");}
}
}
}
}
}
} while(foundOne==true)
this.endMethodNote("B");}
Sudoku.prototype.solveMethodC=function(c)
{ var x,y,bx,by,n, foundOne,foundCountOf; this.startMethodNote("C"); do
{ foundOne=false; for(x=0;x<9;x++)
{ foundCountOf=null; foundCountOf=new Array(); for(n=1;n<=9;n++)
{ if(this.columnCount(x,n)==c)
{ foundCountOf.push(new Array(n,this.found.copyOf()));}
}
foundOne|=this.findUnique(foundCountOf,c)
if(this.breakOut==true) return this.endMethodNote("C");}
for(y=0;y<9;y++)
{ foundCountOf=null; foundCountOf=new Array(); for(n=1;n<=9;n++)
{ if(this.rowCount(y,n)==c)
{ foundCountOf.push(new Array(n,this.found.copyOf()));}
}
foundOne|=this.findUnique(foundCountOf,c)
if(this.breakOut==true) return this.endMethodNote("C");}
for(bx=0;bx<3;bx++)
{ for(by=0;by<3;by++)
{ foundCountOf=null; foundCountOf=new Array(); for(n=1;n<=9;n++)
{ if(this.blockCount(bx,by,n)==c)
{ foundCountOf.push(new Array(n,this.found.copyOf()));}
}
foundOne|=this.findUnique(foundCountOf,c)
if(this.breakOut==true) return this.endMethodNote("C");}
}
} while(foundOne==true)
return this.endMethodNote("C");}
function CellArrays()
{ this.ROW_INDICES = new Array(9)
this.ROW_INDICES[0] = new Array( 0, 1, 2, 3, 4, 5, 6, 7, 8); this.ROW_INDICES[1] = new Array( 9, 10, 11, 12, 13, 14, 15, 16, 17); this.ROW_INDICES[2] = new Array(18, 19, 20, 21, 22, 23, 24, 25, 26); this.ROW_INDICES[3] = new Array(27, 28, 29, 30, 31, 32, 33, 34, 35); this.ROW_INDICES[4] = new Array(36, 37, 38, 39, 40, 41, 42, 43, 44); this.ROW_INDICES[5] = new Array(45, 46, 47, 48, 49, 50, 51, 52, 53); this.ROW_INDICES[6] = new Array(54, 55, 56, 57, 58, 59, 60, 61, 62); this.ROW_INDICES[7] = new Array(63, 64, 65, 66, 67, 68, 69, 70, 71); this.ROW_INDICES[8] = new Array(72, 73, 74, 75, 76, 77, 78, 79, 80); this.COLUMN_INDICES = new Array(9); this.COLUMN_INDICES[0] = new Array( 0, 9, 18, 27, 36, 45, 54, 63, 72); this.COLUMN_INDICES[1] = new Array( 1, 10, 19, 28, 37, 46, 55, 64, 73); this.COLUMN_INDICES[2] = new Array( 2, 11, 20, 29, 38, 47, 56, 65, 74); this.COLUMN_INDICES[3] = new Array( 3, 12, 21, 30, 39, 48, 57, 66, 75); this.COLUMN_INDICES[4] = new Array( 4, 13, 22, 31, 40, 49, 58, 67, 76); this.COLUMN_INDICES[5] = new Array( 5, 14, 23, 32, 41, 50, 59, 68, 77); this.COLUMN_INDICES[6] = new Array( 6, 15, 24, 33, 42, 51, 60, 69, 78); this.COLUMN_INDICES[7] = new Array( 7, 16, 25, 34, 43, 52, 61, 70, 79); this.COLUMN_INDICES[8] = new Array( 8, 17, 26, 35, 44, 53, 62, 71, 80); this.BLOCK_INDICES = new Array(9); this.BLOCK_INDICES[0] = new Array( 0, 1, 2, 9, 10, 11, 18, 19, 20); this.BLOCK_INDICES[1] = new Array( 3, 4, 5, 12, 13, 14, 21, 22, 23); this.BLOCK_INDICES[2] = new Array( 6, 7, 8, 15, 16, 17, 24, 25, 26); this.BLOCK_INDICES[3] = new Array(27, 28, 29, 36, 37, 38, 45, 46, 47); this.BLOCK_INDICES[4] = new Array(30, 31, 32, 39, 40, 41, 48, 49, 50); this.BLOCK_INDICES[5] = new Array(33, 34, 35, 42, 43, 44, 51, 52, 53); this.BLOCK_INDICES[6] = new Array(54, 55, 56, 63, 64, 65, 72, 73, 74); this.BLOCK_INDICES[7] = new Array(57, 58, 59, 66, 67, 68, 75, 76, 77); this.BLOCK_INDICES[8] = new Array(60, 61, 62, 69, 70, 71, 78, 79, 80);}
function VectorIterator(size, range)
{ this.range = range; this.indices = new Array(); for (var i = 0; i < size; ++i)
{ this.indices[i] = i;}
}
VectorIterator.prototype.increment = function()
{ var size = this.indices.length; while (true)
{ for (var i = size - 1; i >= 0; --i)
{ ++this.indices[i]; for (var j = i + 1; j < size; ++j)
this.indices[j] = this.indices[j - 1] + 1; if (this.indices[i] < this.range + i + 1 - size)
return true;}
if (this.indices[0] >= this.range + 1 - size)
return false;}
}
function Subset()
{ this.reference = 0; this.items = new Array();}
function createUnionOfSubsets(iter, subsets, items, references)
{ var size = iter.indices.length; for (var i = 0; i < size; ++i)
{ var index = iter.indices[i]; var subset = subsets[index]; references.push(subset.reference); for (var j = 0; j < subset.items.length; ++j)
{ var already_present = false; for (var k = 0; k < items.length; ++k)
{ if (items[k] == subset.items[j])
{ already_present = true; break;}
}
if (!already_present)
items.push(subset.items[j]);}
}
items.sort(); return items.length;}
Sudoku.prototype.getCandidates = function(cell, candidates)
{ var row = Math.floor(cell/9); var col = cell%9; for (var candidate = 1; candidate <= 9; ++candidate)
{ if (this.matrix[row][col].indexOf(candidate) >= 0)
candidates.push(candidate);}
return candidates.length;}
Sudoku.prototype.solveMethodD2 = function(size, score)
{ this.startMethodNote(mlText.methodD_name); var cells = new CellArrays(); this.addNote(""); this.addNote(mlText.methodD_info,{size:size}); { var removed = 0; this.addNote(mlText.methodD_check_col); for (var unit = 0; unit < 9 && !this.breakOut; ++unit)
{ var name = this.parseString(mlText.column,{col:this.xName[unit]}); removed += this.solveSubsetsByCandidate(cells.COLUMN_INDICES[unit], size, name, score, new Array('col',unit,'#8470FF'));}
}
{ var removed = 0; this.addNote(mlText.methodD_check_row); for (var unit = 0; unit < 9 && !this.breakOut; ++unit)
{ var name = this.parseString(mlText.row,{row:this.yName[unit]}); this.solveSubsetsByCandidate(cells.ROW_INDICES[unit], size, name, score, new Array('row',unit,'#8470FF'));}
}
{ var removed = 0; this.addNote(mlText.methodD_check_block); for (var unit = 0; unit < 9 && !this.breakOut; ++unit)
{ var row = Math.floor(cells.BLOCK_INDICES[unit][0]/9); var col = cells.BLOCK_INDICES[unit][0]%9; var name = this.parseString(mlText.block,{block:this.cellString(col,row)}); this.solveSubsetsByCandidate(cells.BLOCK_INDICES[unit], size, name, score, new Array('block',col,row,'#8470FF'));}
}
this.addNote(""); this.endMethodNote("D");}
Sudoku.prototype.solveSubsetsByCandidate = function(cells, subsetSize, name, score, baseHighlightEl)
{ var highlightArray; var subsets = new Array(); for (var index = 0; index < 9; ++index)
{ var subset = new Subset; var size = this.getCandidates(cells[index], subset.items); if (size > 1 && size <= subsetSize)
{ subset.reference = index; subsets.push(subset);}
}
if (subsets.length < subsetSize)
return 0; var removedTotal = 0; var iter = new VectorIterator(subsetSize, subsets.length); do
{ var items = new Array(); var references = new Array(); if (createUnionOfSubsets(iter, subsets, items, references) == subsetSize)
{ var chain = " "; if(this.step) highlightArray=new Array(baseHighlightEl); for (var i = 0; i < references.length; ++i)
{ var cell = cells[references[i]]; var row = Math.floor(cell/9); var col = cell%9; chain = chain + " " + this.cellString(col,row) + " "; if(this.step) highlightArray.push(new Array('cell',col,row,'#98FB98'));}
this.addNote(mlText.methodD2_found,{name:name,items:items.join(),chain:chain}); var removed = 0; for (var index = 0; index < 9; ++index)
{ var reset_allowed = true; for (var i = 0; i < references.length; ++i)
{ if (references[i] == index)
{ reset_allowed = false; break;}
}
if (reset_allowed)
{ for (var j = 0; j < items.length; ++j)
{ var row = Math.floor(cells[index]/9); var col = cells[index]%9; if (this.isValAtXY(col, row, items[j]))
{ this.addNote(mlText.methodD2_removing,{item:items[j],cell:this.cellString(col,row)}); if(this.step)
{ highlightArray.push(new Array('cell',col,row,'pink')); this.explainAndPause(highlightArray); highlightArray.pop();}
this.remove(col, row, items[j]); ++removed;}
}
}
}
if (removed > 0)
{ this.difficultyScore += score; removedTotal += removed;}
else
{ this.addNote(mlText.methodD2_nothing_removed);}
}
}
while (iter.increment() && !this.breakOut); return removedTotal;}
function BlockArrays()
{ this.BLOCK_ROW_INDICES = new Array(9); this.BLOCK_ROW_INDICES[0] = new Array(0, 1, 2); this.BLOCK_ROW_INDICES[1] = new Array(1, 2, 0); this.BLOCK_ROW_INDICES[2] = new Array(2, 0, 1); this.BLOCK_ROW_INDICES[3] = new Array(3, 4, 5); this.BLOCK_ROW_INDICES[4] = new Array(4, 5, 3); this.BLOCK_ROW_INDICES[5] = new Array(5, 3, 4); this.BLOCK_ROW_INDICES[6] = new Array(6, 7, 8); this.BLOCK_ROW_INDICES[7] = new Array(7, 8, 6); this.BLOCK_ROW_INDICES[8] = new Array(8, 6, 7); this.BLOCK_COLUMN_INDICES = new Array(9); this.BLOCK_COLUMN_INDICES[0] = new Array(0, 3, 6); this.BLOCK_COLUMN_INDICES[1] = new Array(3, 6, 0); this.BLOCK_COLUMN_INDICES[2] = new Array(6, 0, 3); this.BLOCK_COLUMN_INDICES[3] = new Array(1, 4, 7); this.BLOCK_COLUMN_INDICES[4] = new Array(4, 7, 1); this.BLOCK_COLUMN_INDICES[5] = new Array(7, 1, 4); this.BLOCK_COLUMN_INDICES[6] = new Array(2, 5, 8); this.BLOCK_COLUMN_INDICES[7] = new Array(5, 8, 2); this.BLOCK_COLUMN_INDICES[8] = new Array(8, 2, 4);}
Sudoku.prototype.getUnitIndicesInBlock = function(block, candidate, calc, units)
{ var indices = new Array(); this.getIndicesInUnit(block, candidate, indices); for (var i = 0; i < indices.length; ++i)
{ var unit = calc.func(block[indices[i]]); if (!arrayContains(units, unit))
{ units.push(unit);}
}
units.sort(); return units.length;}
Sudoku.prototype.getIndicesInUnit = function(cells, candidate, indices)
{ for (var i = 0; i < 9; ++i)
{ var cell = cells[i]; var row = Math.floor(cell/9); var col = cell%9; if (this.matrix[row][col].indexOf(candidate) >= 0)
indices.push(i);}
return indices.length;}
function arrayContains(container, value)
{ var result = false; for (var i = 0; i < container.length; ++i)
{ if (container[i] == value)
{ result = true; break;}
}
return result
}
Sudoku.prototype.solveMethodF3 = function(size,score)
{ switch (size)
{ case 2: this.startMethodNote(mlText.methodF3_name2); break; case 3: this.startMethodNote(mlText.methodF3_name3); break; case 4: this.startMethodNote(mlText.methodF3_name4); break; case 5: this.startMethodNote(mlText.methodF3_name5); break; default: this.startMethodNote(this.parseString(mlText.methodF3_nameN,{n:size}));}
this.cells = new CellArrays(); var removed=0; for (var candidate = 1; candidate <= 9 && !this.breakOut; ++candidate)
{ removed+=this.solveNxNSubgridsImpl(this.cells.COLUMN_INDICES, candidate, size, 1, score);}
for (var candidate = 1; candidate <= 9 && !this.breakOut; ++candidate)
{ removed+=this.solveNxNSubgridsImpl(this.cells.ROW_INDICES, candidate, size, 0, score);}
if (size == 2)
{ removed+=this.solveBlocksWithSharedUnits(score);}
this.endMethodNote("F");}
Sudoku.prototype.solveNxNSubgridsImpl = function(cells, candidate, subsetSize, type, score)
{ var subsets = new Array(); var totalRemoved=0; for (var unit = 0; unit < 9; ++unit)
{ var subset = new Subset; var size = this.getIndicesInUnit(cells[unit], candidate, subset.items); if (size > 1 && size <= subsetSize)
{ subset.reference = unit; subsets.push(subset);}
}
if (subsets.length < subsetSize)
return 0; var iter = new VectorIterator(subsetSize, subsets.length); do
{ var indices = new Array(); var units = new Array(); var highlightArray = []; var removed=0; if (createUnionOfSubsets(iter, subsets, indices, units) == subsetSize)
{ var row_chain = " "; var row_name = (type == 1) ? mlText.columns : mlText.rows; for (var i = 0; i < units.length; ++i)
row_chain = row_chain + "[" + ((type == 0) ? this.yName[units[i]] : this.xName[units[i]]) + "] "; var col_chain = " "; var col_name = (type == 0) ? mlText.columns : mlText.rows; for (var i = 0; i < indices.length; ++i)
col_chain = col_chain + "[" + ((type == 1) ? this.yName[indices[i]] : this.xName[indices[i]]) + "] "; for (var i = 0; i < units.length; ++i)
{ for (var j = 0; j < indices.length; ++j)
{ if(type==0) highlightArray.push(new Array('cell',indices[j],units[i],'#98FB98')); else highlightArray.push(new Array('cell',units[i],indices[j],'#98FB98'));}
}
this.addNote(mlText.methodF3_found,{val:candidate,row_name:row_name,row_chain:row_chain,col_name:col_name,col_chain:col_chain}); for (var unit = 0; unit < 9; ++unit)
{ if (!arrayContains(units, unit))
{ for (var i = 0; i < subsetSize; ++i)
{ var index = indices[i]; removed+=this.removeSubgridCandidate(cells[unit][index], candidate, highlightArray);}
}
}
if (subsetSize == 2)
{ var unit1 = units[0]; var unit2 = units[1]; if (Math.floor(unit1/3) == Math.floor(unit2/3))
{ removed+=this.clearCandidateFromBlock(cells, cells[unit1][indices[0]], cells[unit2][indices[0]], candidate); removed+=this.clearCandidateFromBlock(cells, cells[unit1][indices[1]], cells[unit2][indices[1]], candidate);}
}
if(removed>0)
{ this.difficultyScore+=score; totalRemoved+=removed;}
}
}
while (iter.increment() && !this.breakOut); return totalRemoved;}
Sudoku.prototype.clearCandidateFromBlock = function(cells, cellA, cellB, candidate, highlightArray)
{ var block = Math.floor(cellA/27)*3 + Math.floor((cellA%9)/3); var blockCells = this.cells.BLOCK_INDICES[block]; var removed=0; for (var index = 0; index < 9; ++index)
{ if (blockCells[index] != cellA && blockCells[index] != cellB)
{ removed+=this.removeSubgridCandidate(blockCells[index], candidate, highlightArray);}
}
return removed;}
Sudoku.prototype.removeSubgridCandidate = function(cell, candidate, highlightArray)
{ var row = Math.floor(cell/9); var col = cell%9; var removed=0; if (this.isValAtXY(col, row, candidate))
{ removed++; this.addNote(mlText.methodF3_removing,{val:candidate,cell:this.cellString(col,row)}); if(highlightArray)
{ highlightArray.push(new Array('cell',col,row,'pink')); if(this.step) this.explainAndPause(highlightArray); highlightArray.pop();}
this.remove(col, row, candidate);}
return removed;}
function ColFromCell()
{ this.dummy = 1;}
ColFromCell.prototype.func = function(cell) { return cell%9;}
function RowFromCell()
{ this.dummy = 1;}
RowFromCell.prototype.func = function(cell) { return Math.floor(cell/9);}
Sudoku.prototype.solveBlocksWithSharedUnits = function(score)
{ this.cells = new CellArrays(); var blocks = new BlockArrays(); var rowFunc = new RowFromCell(); var colFunc = new ColFromCell(); var removed = 0; for (var candidate = 1; candidate <= 9 ; ++candidate)
{ for (var index = 0; index < 9 ; ++index)
{ removed+=this.solveBlocksWithSharedUnitsImpl(blocks.BLOCK_ROW_INDICES[index], candidate, rowFunc);}
for (var index = 0; index < 9 ; ++index)
{ removed+=this.solveBlocksWithSharedUnitsImpl(blocks.BLOCK_COLUMN_INDICES[index], candidate, colFunc);}
}
if(removed>0) this.difficultyScore+=score; return removed;}
Sudoku.prototype.solveBlocksWithSharedUnitsImpl = function(blocks, candidate, calc)
{ var units1 = new Array(); var removed = 0; if (this.getUnitIndicesInBlock(this.cells.BLOCK_INDICES[blocks[0]], candidate, calc, units1) == 2)
{ var units2 = new Array(); if (this.getUnitIndicesInBlock(this.cells.BLOCK_INDICES[blocks[1]], candidate, calc, units2) == 2)
{ if (units1[0] == units2[0] && units1[1] == units2[1])
{ for (var index = 0; index < 9; ++index)
{ var cell = this.cells.BLOCK_INDICES[blocks[2]][index]; var unit = calc.func(cell); if (unit == units1[0] || unit == units1[1])
{ removed+=this.removeSubgridCandidate(cell, candidate);}
}
}
}
}
return removed;}
function indexLink(index,cvs)
{ this.index=index; this.cvs=cvs;}
indexLink.prototype.showHTML=function()
{ return "["+this.index+" - "+this.cvs+" ]"
}
function ChainFinder(chainArray)
{ this.chainArray=chainArray; this.foundChainSet=new Array(); this.foundChainSetNumbers=new Array(); this.inChain=new Array();}
ChainFinder.prototype.findChains=function()
{ var i,linkSet; this.foundChainSet=null; this.foundChainSet=new Array(); linkSet=new Array(); for(i=0;i<this.chainArray.length;i++)
{ linkSet[i]=new indexLink(i,this.chainArray[i]); this.inChain[i]=null;}
this.getChain(linkSet);}
ChainFinder.prototype.debug=function(str)
{ }
ChainFinder.prototype.showHTML=function()
{ var i,j,a0; a0="<table><tr><td>Index</td><td>Chain</td></tr>"; for(i=0;i<this.foundChainSet.length;i++)
{ a0+="<tr><td>"+i+":</td><td>"
for(j=0;j<this.foundChainSet[i].length;j++)
{ a0+="["+this.foundChainSet[i][j].index+" - "+this.foundChainSet[i][j].cvs+" ]";}
a0+="</td></tr>";}
a0+="</table>"; return a0;}
ChainFinder.prototype.isChain=function(linkedSet,link)
{ var i,j,ct,cArr,cv; cArr=new Array(); for(i=1;i<=9;i++)
{ cArr[i]=0;}
ct=link.cvs.length; for(i=0;i<linkedSet.length;i++)
{ if(linkedSet[i].cvs.length!=ct) return false;}
for(i=0;i<linkedSet.length;i++)
{ for(j=0;j<linkedSet[i].cvs.length;j++)
{ cv=linkedSet[i].cvs.charCodeAt(j)-48; cArr[cv]++;}
}
for(j=0;j<link.cvs.length;j++)
{ cv=link.cvs.charCodeAt(j)-48; cArr[cv]++;}
for(i=1;i<=9;i++)
{ if(cArr[i]!=0 && cArr[i]!=ct) return false;}
return true;}
ChainFinder.prototype.areLinked=function(linkA,linkB)
{ var i,c; if(linkA.cvs.length!=linkB.cvs.length) return false; return linkA.cvs.anyMatch(linkB.cvs);}
ChainFinder.prototype.chainElements=function(chain)
{ var a0,c,i,j; a0=""; for(i=0;i<chain.length;i++)
{ for(j=0;j<chain[i].cvs.length;j++)
{ c=chain[i].cvs.charAt(j)
if(a0.indexOf(c)<0) a0+=c;}
}
return a0;}
ChainFinder.prototype.saveChain=function(chain)
{ var i,copyChain; copyChain=new Array(); for(i=0;i<chain.length;i++)
{ copyChain[i]=new indexLink(chain[i].index,chain[i].cvs)
this.inChain[chain[i].index]=this.foundChainSet.length;}
this.foundChainSet.push(copyChain); this.foundChainSetNumbers.push(this.chainElements(copyChain));}
ChainFinder.prototype.getChain=function(remainingLinks,link,linkedSet,unLinkedSet)
{ var i,testLink; this.debug("<br>Getting chain length "+remainingLinks.length); this.debug(remainingLinks.showHTML()); if(link==null || linkedSet==null || unLinkedSet==null)
{ while(remainingLinks.length>0)
{ link=remainingLinks.shift(); this.getChain(remainingLinks,link,new Array(),new Array());}
}
else
{ this.debug(link.showHTML()); this.debug(linkedSet.showHTML()); this.debug(unLinkedSet.showHTML());}
if(link.cvs.length==1) return; while(remainingLinks.length>0)
{ testLink=remainingLinks.shift(); if(this.areLinked(link, testLink))
{ this.debug(link.showHTML()+" and "+testLink.showHTML()+" are linked."); linkedSet.push(link); link=testLink; if(this.isChain(linkedSet,link))
{ this.debug("Found set "+linkedSet.showHTML()); linkedSet.push(link); this.saveChain(linkedSet); while(remainingLinks.length>0)
{ unLinkedSet.push(remainingLinks.shift());}
break;}
else
{ this.debug("Not a chain though - recursing.."); for(i=0;i<unLinkedSet.length;i++)
{ if(unLinkedSet[i].cvs.length==link.cvs.length)
{ spliceArr=unLinkedSet.splice(i,1); if(spliceArr[0]) {spliceItem=spliceArr[0];} else {spliceItem=spliceArr;}
remainingLinks.push(spliceItem);}
}
this.getChain(remainingLinks,testLink,linkedSet,unLinkedSet); link=linkedSet.pop();}
}
else
{ unLinkedSet.push(testLink);}
}
if(unLinkedSet!=null && unLinkedSet.length>0) this.getChain(unLinkedSet)
}


