var x = 3;
var y = 3;
function cross(A, B){
//Cross product of elements in A and elements in B.
var arr = [];
var alen = A.length;
var blen = B.length;
for (var i=0;i<alen;i++){
for (var ii=0;ii<blen;ii++){
arr.push(A[i]+"-"+B[ii])
}
}
return arr
}
var digits = [];
var rows = [];
var cols = [];
var squares;
var unitlist = [];
var subrows = [];
var subcols = [];
var units = {};
var peers = {};
function fillable_puzzle(){
digits = [];
rows = [];
cols = [];
for (var i=1;i<x*y+1;i++){
rows.push(i);
cols.push(i);
digits.push(i);
}
squares = cross(rows, cols)
unitlist = [];
subrows = [];
subcols = [];
for (var c in cols){
unitlist.push(cross(rows,[cols[c]]));
}
for (var r in rows){
unitlist.push(cross([rows[r]],cols));
}
for (var i=0;i<y;i++){
var A = [];
for (var ii=0;ii<x;ii++){
A.push(i*x+ii+1);
}
for (var ii=0;ii<x;ii++){
var B = [];
for (var iii=0;iii<y;iii++){
B.push(ii*y+iii+1);
subcols.push(cross(A,[ii*y+iii+1]));
}
unitlist.push(cross(A,B));
}
}
for (var ii=0;ii<x;ii++){
var B = [];
for (var iii=0;iii<y;iii++){
B.push(ii*y+iii+1);
}
for (var i=0;i<y;i++){
for (var iii=0;iii<x;iii++){
subrows.push(cross([i*x+iii+1],B));
}
}
}
units = {};
peers = {};
for (var i in squares){
var s = squares[i];
units[s]=[];
peers[s]={};
for (var ii in unitlist){
var u = unitlist[ii];
for (var iii in u){
var uu = u[iii];
if (s == uu){
units[s].push(u);
for (var iiii in u){
if (u[iiii] != s){
peers[s][u[iiii]]=true;
}
}
break;
}
}
}
}
var fills = {};
for (var i in squares){
var s = squares[i];
fills[s]=0;
}
return fills
}
function fitSpotBasic(fills,s,v){
for (var u in units[s]){
for (var i in units[s][u]){
var ss = units[s][u][i];
if (fills[ss] == v){
return false;
}
}
}
return true;
}
function allDigitsBasic(fills,s){
var dposs = [];
if (fills[s] != 0){dposs = [fills[s]]; return dposs;}
for (var vi in digits){
var v = digits[vi];
if (fitSpotBasic(fills,s,v)){
dposs.push(v);
}
}
return dposs;
}
function findNakedPairs(fills,returnInfo=false){
var nps = {};
var info = [];
for (var ui in unitlist){
var unit = unitlist[ui];
var pairs = [];
for (var i in unit){
var ss = unit[i];
if (fills[ss] != 0){
continue;
}
var dposs = allDigitsBasic(fills,ss);
if (dposs.length == 2){
dposs.sort();
var str = dposs.join(";");
pairs.push([str,ss]);
}
}
if (pairs.length > 1){
for (var i=0;i<pairs.length;i++){
for (var ii=i+1;ii<pairs.length;ii++){
if (pairs[i][0] == pairs[ii][0]){
var dposs = pairs[i][0].split(";");
if (returnInfo == dposs[0]){
info.push([dposs[0],dposs[1],pairs[ii][1],pairs[i][1]]);
}
else if (returnInfo == dposs[1]){
info.push([dposs[1],dposs[0],pairs[ii][1],pairs[i][1]]);
}
for (var iii in unit){
var ss = unit[iii];
if (ss != pairs[i][1] && ss != pairs[ii][1]){
if (!nps[ss]){nps[ss]={};}
nps[ss][dposs[0]]=true;
nps[ss][dposs[1]]=true;
}
}
}
}
}
}
}
if (returnInfo){return info;}
return nps;
}
function fitSpotNakedPair(fills,s,v,nps){
if (nps[s] && nps[s][v]){
return false;
}
for (var u in units[s]){
for (var i in units[s][u]){
var ss = units[s][u][i];
if (fills[ss] == v){
return false;
}
}
}
return true;
}
function findHiddenPairs(fills,returnInfo=false){
var hps = {};
var info = [];
for (var ui in unitlist){
var unit = unitlist[ui];
var sposs = {};
for (var i in unit){
var ss = unit[i];
if (fills[ss] != 0){
continue;
}
var dposs = allDigitsBasic(fills,ss);
for (var di=0;di<dposs.length;di++){
if (!sposs[dposs[di]]){sposs[dposs[di]]=[];}
sposs[dposs[di]].push(ss);
}
}
var pairs = [];
for (var si in sposs){
if (sposs[si].length == 2){
sposs[si].sort();
pairs.push([sposs[si].join(";"),si]);
}
}
if (pairs.length > 1){
for (var i=0;i<pairs.length;i++){
for (var ii=i+1;ii<pairs.length;ii++){
if (pairs[i][0] == pairs[ii][0]){
var sposs = pairs[i][0].split(";");
if (returnInfo){
try{
var idx = returnInfo.indexOf("-");
if (idx == -1){asdfasd;}
if (returnInfo == sposs[0]){
info.push([pairs[i][1],pairs[ii][1],sposs[0],sposs[1]]);
}
else if (returnInfo == sposs[1]){
info.push([pairs[ii][1],pairs[i][1],sposs[0],sposs[1]]);
}
}
catch {
if (returnInfo == pairs[i][1]){
info.push([pairs[i][1],pairs[ii][1],sposs[0],sposs[1]]);
}
else if (returnInfo == pairs[ii][1]){
info.push([pairs[ii][1],pairs[i][1],sposs[0],sposs[1]]);
}
}
}
if (!hps[sposs[0]]){hps[sposs[0]]={};}
if (!hps[sposs[1]]){hps[sposs[1]]={};}
for (var iii in digits){
var v = digits[iii];
if (v != pairs[i][1] && v != pairs[ii][1]){
hps[sposs[1]][v]=true;
hps[sposs[0]][v]=true;
}
}
}
}
}
}
}
if (returnInfo){return info;}
return hps;
}
function findDefiniteSubunitInBlock(fills,unitt,returnInfo=false){
var nps = {};
var info = [];
var subs;
if (unitt == 1){
subs = subrows;
}
else {
subs = subcols;
}
for (var vi in digits){
var v = digits[vi];
var pblocks = {};
for (var ui in subs){
var subunit = subs[ui];
var count = 0;
for (var i in subunit){
if (fills[subunit[i]] == 0){
if (fitSpotBasic(fills,subunit[i],v)){
count++;
}
}
}
if (count > 0){
var block = units[subunit[0]][2][0];
if (pblocks[block]){
pblocks[block].push(ui);
pblocks[block].push(count);
}
else {
pblocks[block] = [ui,count];
}
}
}
for (var bi in pblocks){
if (pblocks[bi].length == 2){
if (pblocks[bi][1] > 1){
if (returnInfo == v){info.push(subs[pblocks[bi][0]]);}
var row = units[subs[pblocks[bi][0]][0]][unitt];
for (var ri in row){
var block = units[row[ri]][2][0];
if (block != bi){
if (fills[row[ri]] == 0 && fitSpotBasic(fills,row[ri],v)){
if (!nps[row[ri]]){nps[row[ri]]={};}
nps[row[ri]][v]=true;
}
}
}
}
}
}
}
if (returnInfo){return info;}
return nps;
}
function findDefiniteSubunitInUnit(fills,unitt,returnInfo=false){
var nps = {};
var info = [];
var subs;
if (unitt == 1){
subs = subrows;
}
else {
subs = subcols;
}
for (var vi in digits){
var v = digits[vi];
var prows = {};
for (var ui in subs){
var subunit = subs[ui];
var count = 0;
for (var i in subunit){
if (fills[subunit[i]] == 0){
if (fitSpotBasic(fills,subunit[i],v)){
count++;
}
}
}
if (count > 0){
var row = units[subunit[0]][unitt][0];
if (prows[row]){
prows[row].push(ui);
prows[row].push(count);
}
else {
prows[row] = [ui,count];
}
}
}
for (var ri in prows){
if (prows[ri].length == 2){
if (prows[ri][1] > 1){
if (returnInfo == v){info.push(subs[prows[ri][0]]);}
var block = units[subs[prows[ri][0]][0]][2];
for (var bi in block){
var row = units[block[bi]][unitt][0];
if (row != ri){
if (fills[block[bi]] == 0 && fitSpotBasic(fills,block[bi],v)){
if (!nps[block[bi]]){nps[block[bi]]={};}
nps[block[bi]][v]=true;
}
}
}
}
}
}
}
if (returnInfo){return info;}
return nps;
}
function finishUnit(fills){
var possiblePlays = [];
for (var ui in unitlist){
var unit = unitlist[ui];
var count = 0;
var cell = -1;
var current = {};
for (var i in unit){
if (fills[unit[i]] == 0){
count++;
cell = unit[i];
}
else {
current[fills[unit[i]]]=true;
}
}
if (count == 1){
for (var i in digits){
if (!current[digits[i]]){
if (ui < unitlist.length/3){
possiblePlays.push([cell,digits[i],1]);
}
else if (ui < unitlist.length*2/3){
possiblePlays.push([cell,digits[i],2]);
}
else if (ui < unitlist.length){
possiblePlays.push([cell,digits[i],3]);
}
break;
}
}
}
}
return possiblePlays;
}
function oneSpotInUnit(fills, nps = false, level = 2) {
var possiblePlays = [];
for (var ui in unitlist) {
var unit = unitlist[ui];
for (var vi in digits) {
var v = digits[vi];
var count = 0;
var cell = -1;
for (var i in unit) {
if (fills[unit[i]] == 0) {
if (nps) {
if (fitSpotNakedPair(fills, unit[i], v, nps)) {
count++;
cell = unit[i];
}
}
else {
if (fitSpotBasic(fills, unit[i], v)) {
count++;
cell = unit[i];
}
}
}
else if (fills[unit[i]] == v) {
//value already in unit
count = 0;
break;
}
}
if (count == 1) {
if (nps) {
if (ui < unitlist.length / 3) {
possiblePlays.push([cell, v, level, 4]);
}
else if (ui < unitlist.length * 2 / 3) {
possiblePlays.push([cell, v, level, 5]);
}
else if (ui < unitlist.length) {
possiblePlays.push([cell, v, level, 6]);
}
}
else {
if (ui < unitlist.length / 3) {
possiblePlays.push([cell, v, 4]);
}
else if (ui < unitlist.length * 2 / 3) {
possiblePlays.push([cell, v, 5]);
}
else if (ui < unitlist.length) {
possiblePlays.push([cell, v, 6]);
}
}
}
}
}
return possiblePlays;
}
function oneDigitInSpot(fills, nps = false, level = 2) {
var possiblePlays = [];
for (var si in squares) {
var s = squares[si];
if (fills[s] != 0) { continue; }
var count = 0;
var out = -1;
for (var vi in digits) {
var v = digits[vi];
if (nps) {
if (fitSpotNakedPair(fills, s, v, nps)) {
count++;
if (count > 1) {
break;
}
out = v;
}
}
else {
if (fitSpotBasic(fills, s, v)) {
count++;
if (count > 1) {
break;
}
out = v;
}
}
}
if (count == 1) {
if (nps) {
possiblePlays.push([s, out, level]);
}
else {
possiblePlays.push([s, out, 7]);
}
}
}
return possiblePlays;
}
function removeEmpties(possiblePlays, fills) {
for (var i = possiblePlays.length - 1; i >= 0; i--) {
if (fills[possiblePlays[i][0]] != 0) {
possiblePlays.splice(i, 1);
}
}
return possiblePlays;
}
function simpleSolve(fills, all = false) {
var possiblePlays = [];
possiblePlays = possiblePlays.concat(finishUnit(fills));
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills));
//if (possiblePlays.length > 0 && !all){return possiblePlays;}
possiblePlays = removeEmpties(possiblePlays, fills);
if (possiblePlays.length > 0 && !all) {
return possiblePlays;
}
var fps = {};
var dps = findDefiniteSubunitInBlock(fills, 1);
if (Object.keys(dps).length > 0) {
for (var d in dps) {
if (!fps[d]) { fps[d] = {}; }
for (var dd in dps[d]) {
fps[d][dd] = true;
}
}
//console.log(dps);
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills, dps, 8));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills, dps, 9));
}
var dps = findDefiniteSubunitInBlock(fills, 0);
if (Object.keys(dps).length > 0) {
for (var d in dps) {
if (!fps[d]) { fps[d] = {}; }
for (var dd in dps[d]) {
fps[d][dd] = true;
}
}
//console.log(dps);
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills, dps, 10));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills, dps, 11));
}
//if (possiblePlays.length > 0 && !all){return possiblePlays;}
var dps = findDefiniteSubunitInUnit(fills, 1);
if (Object.keys(dps).length > 0) {
for (var d in dps) {
if (!fps[d]) { fps[d] = {}; }
for (var dd in dps[d]) {
fps[d][dd] = true;
}
}
//console.log(dps);
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills, dps, 12));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills, dps, 13));
}
//if (possiblePlays.length > 0 && !all){return possiblePlays;}
//if (possiblePlays.length > 0 && !all){return possiblePlays;}
var dps = findDefiniteSubunitInUnit(fills, 0);
if (Object.keys(dps).length > 0) {
for (var d in dps) {
if (!fps[d]) { fps[d] = {}; }
for (var dd in dps[d]) {
fps[d][dd] = true;
}
}
//console.log(dps);
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills, dps, 14));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills, dps, 15));
}
//if (possiblePlays.length > 0 && !all){return possiblePlays;}
/*
if (Object.keys(fps).length > 0){
//console.log(dps);
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills,fps,4));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills,fps,4));
}
*/
var nps = findNakedPairs(fills);
if (Object.keys(nps).length > 0) {
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills, nps, 16));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills, nps, 17));
}
//if (possiblePlays.length > 0 && !all){return possiblePlays;}
var hps = findHiddenPairs(fills);
if (Object.keys(hps).length > 0) {
//console.log(hps);
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills, hps, 18));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills, hps, 19));
}
possiblePlays = removeEmpties(possiblePlays, fills);
return possiblePlays;
}
var x = 3;
var y = 3;
var chalID = 1;
function cross(A, B){
//Cross product of elements in A and elements in B.
var arr = [];
var alen = A.length;
var blen = B.length;
for (var i=0;i<alen;i++){
for (var ii=0;ii<blen;ii++){
arr.push(A[i]+"-"+B[ii])
}
}
return arr
}
var digits = [];
var rows = [];
var cols = [];
var squares;
var unitlist = [];
var subrows = [];
var subcols = [];
var units = {};
var peers = {};
function reset_puzzle(){
digits = [];
rows = [];
cols = [];
for (var i=1;i<x*y+1;i++){
rows.push(i);
cols.push(i);
digits.push(i);
}
squares = cross(rows, cols)
unitlist = [];
rowlist = [];
collist = [];
blocklist = [];
for (var c in cols){
unitlist.push(cross(rows,[cols[c]]));
collist.push(cross(rows,[cols[c]]));
}
for (var r in rows){
unitlist.push(cross([rows[r]],cols));
rowlist.push(cross([rows[r]],cols));
}
for (var i=0;i<y;i++){
var A = [];
for (var ii=0;ii<x;ii++){
A.push(i*x+ii+1);
}
for (var ii=0;ii<x;ii++){
var B = [];
for (var iii=0;iii<y;iii++){
B.push(ii*y+iii+1);
}
unitlist.push(cross(A,B));
blocklist.push(cross(A,B));
}
}
units = {};
peers = {};
for (var i in squares){
var s = squares[i];
units[s]=[];
peers[s]={};
for (var ii in unitlist){
var u = unitlist[ii];
for (var iii in u){
var uu = u[iii];
if (s == uu){
units[s].push(u);
for (var iiii in u){
if (u[iiii] != s){
peers[s][u[iiii]]=true;
}
}
break;
}
}
}
}
//console.log(squares);
//console.log(units);
//console.log(peers);
}
function create_grid(){
//To start, every square can be any digit;
reset_puzzle();
var values = {};
for (var i in squares){
var s = squares[i];
values[s]=digits;
}
return values
}
function assign(values, s, d){
//Eliminate all the other values (except d) from values[s] and propagate.
//Return values, except return False if a contradiction is detected.
var other_values = values[s].slice()
for (var i in other_values){
if (other_values[i] != d){
var elim = eliminate(values, s, other_values[i]);
if (!elim){return false;}
else {values = elim;}
}
}
return values;
}
function eliminate(values, s, d){
//Eliminate d from values[s]; propagate when values or places <= 2.
//Return values, except return False if a contradiction is detected.
var skipped = false;
var new_vals = values[s].slice();
for (var i=0;i<new_vals.length;i++){
if (new_vals[i] == d){
new_vals.splice(i,1);
skipped = true;
break;
}
}
if (!skipped){
return values //Already eliminated
}
values[s] = new_vals;
//If a square s is reduced to one value d2, then eliminate d2 from the peers.
if (values[s].length == 0){
return false //Contradiction: removed last value
}
else if (values[s].length == 1){
var d2 = values[s][0]
var my_peers = peers[s];
for (var i in my_peers){
var s2 = i;
var elim = eliminate(values, s2, d2);
if (!elim){
return false;
}
else {
values = elim;
}
}
}
//If a unit u is reduced to only one place for a value d, then put it there.
for (var i in units[s]){
var u = units[s][i];
var dplaces = 0;
var sz1 = u.length;
for (var ii=0;ii<sz1;ii++){
var s = u[ii];
var sz2 = values[s].length;
for (var iii=0;iii<sz2;iii++){
if (d == values[s][iii]){
if (dplaces != 0){
dplaces = -1;
ii=sz1+5;
}
else {
dplaces = s;
}
break;
}
else if (d < values[s][iii]) {
break;
}
}
}
if (dplaces == 0){
return false //Contradiction: no place for this value
}
else if (dplaces != -1){
//d can only be in one place in unit; assign it there
var a = assign(values, dplaces, d);
if (!a){
return false
}
else {
values = a;
}
}
}
return values
}
function random_puzzle(xx=3,yy=3){
//Make a random puzzle with N or more assignments. Restart on contradictions.
x = xx;
y = yy;
var values = create_grid();
var totalT = 0;
var misses = 0;
for (var i=0;i<x*y;i++){
var s = squares[i];
var v = digits[i];
values[s]=[v];
}
for (var i=x*y;i<x*y*x;i++){
var s = squares[i];
var minV = Math.floor((i%(x*y))/y);
minV *= y;
var maxV = minV + y;
var arr = [];
var idx = 0;
for (var ii=0;ii<x*y;ii++){
if (ii < minV || ii >= maxV){
arr[idx]=digits[ii];
idx++;
}
}
values[s] = arr;
}
for (var i=x*y*x;i<x*y*x*y;i++){
var s = squares[i];
var minV = i%(x*y);
var maxV = minV + 1;
var arr = [];
var idx = 0;
for (var ii=0;ii<x*y;ii++){
if (ii < minV || ii >= maxV){
arr[idx]=digits[ii];
idx++;
}
}
values[s] = arr;
}
var valuesOld = [];
var t1 = Date.now();
for (var i=0;i<x*y*x*y*200;i++){
var minOptions = x*y*2;
var sqs = [];
for (var ii in squares){
var ss = squares[ii];
if (values[ss].length > 1){
if (values[ss].length < minOptions){
minOptions = values[ss].length;
sqs = [ss];
}
else if (values[ss].length == minOptions){
sqs.push(ss);
}
}
}
if (minOptions == x*y*2){
break;
}
var s = sqs[Math.floor(Math.random()*sqs.length)];
var v = values[s][Math.floor(Math.random()*values[s].length)];
var vvOld = {};
for (var vv in values){
vvOld[vv]=values[vv].slice();
}
valuesOld.push(vvOld);
if (valuesOld.length >= 5){
valuesOld.splice(0,1);
}
values = assign(values, s, v);
if (!values){
misses++;
if (misses > 10){
break;
}
values = {};
for (var vv in valuesOld[0]){
values[vv]=valuesOld[0][vv].slice();
}
continue;
}
}
var t2 = Date.now();
totalT += t2-t1;
var pout = [];
for (var i=0;i<x*y;i++){
for (var ii=0;ii<x*y;ii++){
var s = squares[i*x*y+ii];
if (!values[s] || values[s].length != 1){return false;}
var v = values[s][0];
pout.push(v);
}
}
return pout;
}
function fillable_puzzle(){
digits = [];
rows = [];
cols = [];
for (var i=1;i<x*y+1;i++){
rows.push(i);
cols.push(i);
digits.push(i);
}
squares = cross(rows, cols)
unitlist = [];
subrows = [];
subcols = [];
for (var c in cols){
unitlist.push(cross(rows,[cols[c]]));
}
for (var r in rows){
unitlist.push(cross([rows[r]],cols));
}
for (var i=0;i<y;i++){
var A = [];
for (var ii=0;ii<x;ii++){
A.push(i*x+ii+1);
}
for (var ii=0;ii<x;ii++){
var B = [];
for (var iii=0;iii<y;iii++){
B.push(ii*y+iii+1);
subcols.push(cross(A,[ii*y+iii+1]));
}
unitlist.push(cross(A,B));
}
}
for (var ii=0;ii<x;ii++){
var B = [];
for (var iii=0;iii<y;iii++){
B.push(ii*y+iii+1);
}
for (var i=0;i<y;i++){
for (var iii=0;iii<x;iii++){
subrows.push(cross([i*x+iii+1],B));
}
}
}
units = {};
peers = {};
for (var i in squares){
var s = squares[i];
units[s]=[];
peers[s]={};
for (var ii in unitlist){
var u = unitlist[ii];
for (var iii in u){
var uu = u[iii];
if (s == uu){
units[s].push(u);
for (var iiii in u){
if (u[iiii] != s){
peers[s][u[iiii]]=true;
}
}
break;
}
}
}
}
var fills = {};
for (var i in squares){
var s = squares[i];
fills[s]=0;
}
return fills
}
function fitSpotBasic(fills,s,v){
for (var u in units[s]){
for (var i in units[s][u]){
var ss = units[s][u][i];
if (fills[ss] == v){
return false;
}
}
}
return true;
}
function allDigitsBasic(fills,s){
var dposs = [];
if (fills[s] != 0){dposs = [fills[s]]; return dposs;}
for (var vi in digits){
var v = digits[vi];
if (fitSpotBasic(fills,s,v)){
dposs.push(v);
}
}
return dposs;
}
function findNakedPairs(fills,returnInfo=false){
var nps = {};
var info = [];
for (var ui in unitlist){
var unit = unitlist[ui];
var pairs = [];
for (var i in unit){
var ss = unit[i];
if (fills[ss] != 0){
continue;
}
var dposs = allDigitsBasic(fills,ss);
if (dposs.length == 2){
dposs.sort();
var str = dposs.join(";");
pairs.push([str,ss]);
}
}
if (pairs.length > 1){
for (var i=0;i<pairs.length;i++){
for (var ii=i+1;ii<pairs.length;ii++){
if (pairs[i][0] == pairs[ii][0]){
var dposs = pairs[i][0].split(";");
if (returnInfo == dposs[0]){
info.push([dposs[0],dposs[1],pairs[ii][1],pairs[i][1]]);
}
else if (returnInfo == dposs[1]){
info.push([dposs[1],dposs[0],pairs[ii][1],pairs[i][1]]);
}
for (var iii in unit){
var ss = unit[iii];
if (ss != pairs[i][1] && ss != pairs[ii][1]){
if (!nps[ss]){nps[ss]={};}
nps[ss][dposs[0]]=true;
nps[ss][dposs[1]]=true;
}
}
}
}
}
}
}
if (returnInfo){return info;}
return nps;
}
function fitSpotNakedPair(fills,s,v,nps){
if (nps[s] && nps[s][v]){
return false;
}
for (var u in units[s]){
for (var i in units[s][u]){
var ss = units[s][u][i];
if (fills[ss] == v){
return false;
}
}
}
return true;
}
function findHiddenPairs(fills,returnInfo=false){
var hps = {};
var info = [];
for (var ui in unitlist){
var unit = unitlist[ui];
var sposs = {};
for (var i in unit){
var ss = unit[i];
if (fills[ss] != 0){
continue;
}
var dposs = allDigitsBasic(fills,ss);
for (var di=0;di<dposs.length;di++){
if (!sposs[dposs[di]]){sposs[dposs[di]]=[];}
sposs[dposs[di]].push(ss);
}
}
var pairs = [];
for (var si in sposs){
if (sposs[si].length == 2){
sposs[si].sort();
pairs.push([sposs[si].join(";"),si]);
}
}
if (pairs.length > 1){
for (var i=0;i<pairs.length;i++){
for (var ii=i+1;ii<pairs.length;ii++){
if (pairs[i][0] == pairs[ii][0]){
var sposs = pairs[i][0].split(";");
if (returnInfo){
try{
var idx = returnInfo.indexOf("-");
if (idx == -1){asdfasd;}
if (returnInfo == sposs[0]){
info.push([pairs[i][1],pairs[ii][1],sposs[0],sposs[1]]);
}
else if (returnInfo == sposs[1]){
info.push([pairs[ii][1],pairs[i][1],sposs[0],sposs[1]]);
}
}
catch {
if (returnInfo == pairs[i][1]){
info.push([pairs[i][1],pairs[ii][1],sposs[0],sposs[1]]);
}
else if (returnInfo == pairs[ii][1]){
info.push([pairs[ii][1],pairs[i][1],sposs[0],sposs[1]]);
}
}
}
if (!hps[sposs[0]]){hps[sposs[0]]={};}
if (!hps[sposs[1]]){hps[sposs[1]]={};}
for (var iii in digits){
var v = digits[iii];
if (v != pairs[i][1] && v != pairs[ii][1]){
hps[sposs[1]][v]=true;
hps[sposs[0]][v]=true;
}
}
}
}
}
}
}
if (returnInfo){return info;}
return hps;
}
function findDefiniteSubunitInBlock(fills,unitt,returnInfo=false){
var nps = {};
var info = [];
var subs;
if (unitt == 1){
subs = subrows;
}
else {
subs = subcols;
}
for (var vi in digits){
var v = digits[vi];
var pblocks = {};
for (var ui in subs){
var subunit = subs[ui];
var count = 0;
for (var i in subunit){
if (fills[subunit[i]] == 0){
if (fitSpotBasic(fills,subunit[i],v)){
count++;
}
}
}
if (count > 0){
var block = units[subunit[0]][2][0];
if (pblocks[block]){
pblocks[block].push(ui);
pblocks[block].push(count);
}
else {
pblocks[block] = [ui,count];
}
}
}
for (var bi in pblocks){
if (pblocks[bi].length == 2){
if (pblocks[bi][1] > 1){
if (returnInfo == v){info.push(subs[pblocks[bi][0]]);}
var row = units[subs[pblocks[bi][0]][0]][unitt];
for (var ri in row){
var block = units[row[ri]][2][0];
if (block != bi){
if (fills[row[ri]] == 0 && fitSpotBasic(fills,row[ri],v)){
if (!nps[row[ri]]){nps[row[ri]]={};}
nps[row[ri]][v]=true;
}
}
}
}
}
}
}
if (returnInfo){return info;}
return nps;
}
function findDefiniteSubunitInUnit(fills,unitt,returnInfo=false){
var nps = {};
var info = [];
var subs;
if (unitt == 1){
subs = subrows;
}
else {
subs = subcols;
}
for (var vi in digits){
var v = digits[vi];
var prows = {};
for (var ui in subs){
var subunit = subs[ui];
var count = 0;
for (var i in subunit){
if (fills[subunit[i]] == 0){
if (fitSpotBasic(fills,subunit[i],v)){
count++;
}
}
}
if (count > 0){
var row = units[subunit[0]][unitt][0];
if (prows[row]){
prows[row].push(ui);
prows[row].push(count);
}
else {
prows[row] = [ui,count];
}
}
}
for (var ri in prows){
if (prows[ri].length == 2){
if (prows[ri][1] > 1){
if (returnInfo == v){info.push(subs[prows[ri][0]]);}
var block = units[subs[prows[ri][0]][0]][2];
for (var bi in block){
var row = units[block[bi]][unitt][0];
if (row != ri){
if (fills[block[bi]] == 0 && fitSpotBasic(fills,block[bi],v)){
if (!nps[block[bi]]){nps[block[bi]]={};}
nps[block[bi]][v]=true;
}
}
}
}
}
}
}
if (returnInfo){return info;}
return nps;
}
function finishUnit(fills){
var possiblePlays = [];
for (var ui in unitlist){
var unit = unitlist[ui];
var count = 0;
var cell = -1;
var current = {};
for (var i in unit){
if (fills[unit[i]] == 0){
count++;
cell = unit[i];
}
else {
current[fills[unit[i]]]=true;
}
}
if (count == 1){
for (var i in digits){
if (!current[digits[i]]){
if (ui < unitlist.length/3){
possiblePlays.push([cell,digits[i],1]);
}
else if (ui < unitlist.length*2/3){
possiblePlays.push([cell,digits[i],2]);
}
else if (ui < unitlist.length){
possiblePlays.push([cell,digits[i],3]);
}
break;
}
}
}
}
return possiblePlays;
}
function oneSpotInUnit(fills, nps = false, level = 2) {
var possiblePlays = [];
for (var ui in unitlist) {
var unit = unitlist[ui];
for (var vi in digits) {
var v = digits[vi];
var count = 0;
var cell = -1;
for (var i in unit) {
if (fills[unit[i]] == 0) {
if (nps) {
if (fitSpotNakedPair(fills, unit[i], v, nps)) {
count++;
cell = unit[i];
}
}
else {
if (fitSpotBasic(fills, unit[i], v)) {
count++;
cell = unit[i];
}
}
}
else if (fills[unit[i]] == v) {
//value already in unit
count = 0;
break;
}
}
if (count == 1) {
if (nps) {
if (ui < unitlist.length / 3) {
possiblePlays.push([cell, v, level, 4]);
}
else if (ui < unitlist.length * 2 / 3) {
possiblePlays.push([cell, v, level, 5]);
}
else if (ui < unitlist.length) {
possiblePlays.push([cell, v, level, 6]);
}
}
else {
if (ui < unitlist.length / 3) {
possiblePlays.push([cell, v, 4]);
}
else if (ui < unitlist.length * 2 / 3) {
possiblePlays.push([cell, v, 5]);
}
else if (ui < unitlist.length) {
possiblePlays.push([cell, v, 6]);
}
}
}
}
}
return possiblePlays;
}
function oneDigitInSpot(fills, nps = false, level = 2) {
var possiblePlays = [];
for (var si in squares) {
var s = squares[si];
if (fills[s] != 0) { continue; }
var count = 0;
var out = -1;
for (var vi in digits) {
var v = digits[vi];
if (nps) {
if (fitSpotNakedPair(fills, s, v, nps)) {
count++;
if (count > 1) {
break;
}
out = v;
}
}
else {
if (fitSpotBasic(fills, s, v)) {
count++;
if (count > 1) {
break;
}
out = v;
}
}
}
if (count == 1) {
if (nps) {
possiblePlays.push([s, out, level]);
}
else {
possiblePlays.push([s, out, 7]);
}
}
}
return possiblePlays;
}
function removeEmpties(possiblePlays, fills) {
for (var i = possiblePlays.length - 1; i >= 0; i--) {
if (fills[possiblePlays[i][0]] != 0) {
possiblePlays.splice(i, 1);
}
}
return possiblePlays;
}
function simpleSolve(fills, all = false) {
var possiblePlays = [];
possiblePlays = possiblePlays.concat(finishUnit(fills));
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills));
//if (possiblePlays.length > 0 && !all){return possiblePlays;}
possiblePlays = removeEmpties(possiblePlays, fills);
if (possiblePlays.length > 0 && !all) {
return possiblePlays;
}
var fps = {};
var dps = findDefiniteSubunitInBlock(fills, 1);
if (Object.keys(dps).length > 0) {
for (var d in dps) {
if (!fps[d]) { fps[d] = {}; }
for (var dd in dps[d]) {
fps[d][dd] = true;
}
}
//console.log(dps);
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills, dps, 8));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills, dps, 9));
}
var dps = findDefiniteSubunitInBlock(fills, 0);
if (Object.keys(dps).length > 0) {
for (var d in dps) {
if (!fps[d]) { fps[d] = {}; }
for (var dd in dps[d]) {
fps[d][dd] = true;
}
}
//console.log(dps);
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills, dps, 10));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills, dps, 11));
}
//if (possiblePlays.length > 0 && !all){return possiblePlays;}
var dps = findDefiniteSubunitInUnit(fills, 1);
if (Object.keys(dps).length > 0) {
for (var d in dps) {
if (!fps[d]) { fps[d] = {}; }
for (var dd in dps[d]) {
fps[d][dd] = true;
}
}
//console.log(dps);
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills, dps, 12));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills, dps, 13));
}
//if (possiblePlays.length > 0 && !all){return possiblePlays;}
//if (possiblePlays.length > 0 && !all){return possiblePlays;}
var dps = findDefiniteSubunitInUnit(fills, 0);
if (Object.keys(dps).length > 0) {
for (var d in dps) {
if (!fps[d]) { fps[d] = {}; }
for (var dd in dps[d]) {
fps[d][dd] = true;
}
}
//console.log(dps);
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills, dps, 14));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills, dps, 15));
}
//if (possiblePlays.length > 0 && !all){return possiblePlays;}
/*
if (Object.keys(fps).length > 0){
//console.log(dps);
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills,fps,4));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills,fps,4));
}
*/
var nps = findNakedPairs(fills);
if (Object.keys(nps).length > 0) {
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills, nps, 16));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills, nps, 17));
}
//if (possiblePlays.length > 0 && !all){return possiblePlays;}
var hps = findHiddenPairs(fills);
if (Object.keys(hps).length > 0) {
//console.log(hps);
possiblePlays = possiblePlays.concat(oneSpotInUnit(fills, hps, 18));
possiblePlays = possiblePlays.concat(oneDigitInSpot(fills, hps, 19));
}
possiblePlays = removeEmpties(possiblePlays, fills);
return possiblePlays;
}
function updatePuzzle(anyChal=false) {
var x = 3;
var y = 3;
selected = {}
var r = 25+Math.floor(Math.random()*15);
for (var ii=0;ii<r;ii++){
selected[Math.floor(Math.random()*x*x*y*y)]=true;
}
var puzzleOrdered = random_puzzle(x,y);
for (var i=0;i<20;i++){
if (puzzleOrdered){break;}
puzzleOrdered = random_puzzle(x,y);
}
puzzle = puzzleOrdered.slice();
var digitList = digits.slice();
for (var d in digits){
var dd = Math.floor(Math.random()*digitList.length);
var ddd = digitList[dd];
digitList.splice(dd,1);
for (var i in puzzleOrdered){
if (puzzleOrdered[i]==digits[d]){
puzzle[i]=ddd;
}
}
}
return partial_puzzle(anyChal);
}
function partial_puzzle(anyChal=false){
var x = 3;
var y = 3;
puzzleData = {1:0,2:0,3:0,4:0,5:0,'blanks':x*y*x*y};
var fills = fillable_puzzle();
for (var i in selected){
if (!selected[i]){continue;}
var s = squares[i];
var v = parseInt(puzzle[i]);
fills[s] = v;
}
//console.log(Date.now());
var updated = true;
var chalFound = false;
while (updated){
updated = false;
var pp = simpleSolve(fills);
pp.sort((a,b) => {return a[2] - b[2];})
var ppp = 1000;
var chalCount = {};
var sqCount = {};
for (var i in pp){
var s = pp[i][0];
var v = pp[i][1];
if (fills[s] == 0){
if (!chalCount[pp[i][2]]){chalCount[pp[i][2]]=[]}
if (pp[i].length > 3){
chalCount[pp[i][2]].push([s,v,pp[i][3]]);
}
else {
chalCount[pp[i][2]].push([s,v]);
}
if (!sqCount[s]){sqCount[s]=0}
sqCount[s]++;
}
}
/*
if (chalCount[1] && chalCount[1].length == 1){
if (chalCount[2] && chalCount[2].length == 1){
if (chalCount[3] && chalCount[3].length == 1){
var js1 = JSON.stringify(chalCount[1]);
var js2 = JSON.stringify(chalCount[2]);
var js3 = JSON.stringify(chalCount[3]);
if (js1 != js2 && js2 != js3 && js1 != js3){
console.log(js1,1);
console.log(js2,2);
console.log(js3,3);
updated = false;
chalFound = true;
pp = false;
}
}
}
}
*/
if (chalCount[chalID] && chalCount[chalID].length == 1){
console.log(sqCount);
if (sqCount[chalCount[chalID][0][0]] == 1 || anyChal){
var js1 = JSON.stringify(chalCount[chalID]);
console.log(js1,chalID);
var rr = chalCount[chalID][0][0].split("-")[0];
var cc = chalCount[chalID][0][0].split("-")[1];
var jsonout;
if (chalCount[chalID][0].length > 2){
jsonout = {fills:fills,chalID:chalID,chalType:chalCount[chalID][0][2],solve:{'n':chalCount[chalID][0][1],'r':rr,'c':cc}};
}
else {
jsonout = {fills:fills,chalID:chalID,solve:{'n':chalCount[chalID][0][1],'r':rr,'c':cc}};
}
postMessage(['success',jsonout]);
updated = false;
chalFound = true;
pp = false;
}
}
for (var i in pp){
if (pp[i][2] > ppp && pp[i][2] > 7){break;}
var s = pp[i][0];
var v = pp[i][1];
if (fills[s] == 0){
/*if (pp[i][2] == 1){
console.log(s,v,pp[i][2]);
updated = false;
chalFound = true;
break;
}*/
fills[s] = v;
ppp = pp[i][2];
puzzleData[ppp]++;
updated = true;
}
}
}
for (var f in fills){
if (fills[f] != 0){
puzzleData.blanks--;
}
}
//if (puzzleData.blanks == 0){
// console.log(JSON.stringify(puzzleData));
//}
if (puzzleData[3]+puzzleData[4]+puzzleData[5] > 0){
//console.log(JSON.stringify(puzzleData));
}
if (!chalFound){return false;}
//console.log(Date.now());
var chars = ["1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G"]
var pStr = "";
var aout = [];
for (var i in puzzle){
var ss = squares[i];
if (selected[i] || (chalFound && fills[ss])){
pStr += chars[puzzle[i]-1];
aout.push(chars[puzzle[i]-1]);
}
else {
pStr += ".";
aout.push("");
}
}
pStr += ",";
for (var i in puzzle){
pStr += chars[puzzle[i]-1];
}
if (chalFound){
return JSON.stringify(aout);
}
//return [fills,puzzle];
return false;
}
function findChallenge(id){
chalID = id;
var foundChal = false;
for (var i=0;i<100;i++){
postMessage("attempt-"+i);
var anyChal = false;
if (chalID < 4){
anyChal = true;
}
var chal = updatePuzzle(anyChal);
if (chal){
console.log("success", chalID);
console.log(chal);
foundChal = true;
break;
}
}
if (!foundChal){
for (var i=0;i<100;i++){
postMessage("attempt-"+(i+100));
var chal = updatePuzzle(true);
if (chal){
console.log("success", chalID);
console.log(chal);
foundChal = true;
break;
}
}
}
if (!foundChal){
postMessage('nothing');
}
console.log(Date.now());
}
onmessage = (e) => {
console.log('Message received from main script');
console.log(e.data);
if (e.data[0] == "chalID"){
findChallenge(parseInt(e.data[1]));
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/png" href="favicon.png">
<title></title>
<meta name="description" content="" />
<link href="style.css" rel="stylesheet" type="text/css" />
<style>
#container2 {
display: block;
position: absolute;
left: 0;
}
#spinner {
display: block;
position: absolute;
left: 0;
height: calc(2rem * 11);
width: calc(2rem * 11);
background: rgba(0,0,0,0.75);
color: white;
font-size: 2rem;
}
#message {
display: inline-block;
position: relative;
left: calc(2rem * 11);
top: 2rem;
width: calc(20rem);
}
.cell {
border: 1px dotted gray;
display: inline-block;
height: calc(2rem - 2px);
width: calc(2rem - 2px);
position: absolute;
line-height: 2rem;
text-align: center;
cursor: pointer;
background: white;
}
{% for i in range(1,10) %}
.row-{{i}} {
top: calc(2rem * {{i}});
}
.col-{{i}} {
left: calc(2rem * {{i}});
}
{% endfor %}
.row-3, .row-6, .row-9 {
border-bottom: 1px solid black;
}
.row-1, .row-4, .row-7 {
border-top: 1px solid black;
}
.col-3, .col-6, .col-9 {
border-right: 1px solid black;
}
.col-1, .col-4, .col-7 {
border-left: 1px solid black;
}
</style>
</head>
<body>
<div id="menu">
<button onclick="loop100();">Refresh Challenge</button>
Challenge: <select id="challenge">
<option value="1">Finish Unit (col)</option>
<option value="2">Finish Unit (row)</option>
<option value="3">Finish Unit (blk)</option>
<option value="4">One Spot (col)</option>
<option value="5">One Spot (row)</option>
<option value="6">One Spot (blk)</option>
<option value="7">One Digit in Spot</option>
<option value="8">Subrow in Block (spot)</option>
<option value="9">Subrow in Block (digit)</option>
<option value="10">Subcol in Block (spot)</option>
<option value="11">Subcol in Block (digit)</option>
<option value="12">Subrow in Row (spot)</option>
<option value="13">Subrow in Row (digit)</option>
<option value="14">Subcol in Col (spot)</option>
<option value="15">Subcol in Col (digit)</option>
<option value="16">Naked Pair (spot)</option>
<option value="17">Naked Pair (digit)</option>
<option value="18">Hidden Pair (spot)</option>
<option value="19">Hidden Pair (digit)</option>
</select>
PuzzleStr: <span id="puzzleStr"></span>
</div>
<div id="container2">
{% for r in range(1,10) %}
{% for c in range(1,10) %}
<div class="cell row-{{r}} col-{{c}}" id="fillable-{{r}}-{{c}}">
</div>
{% endfor %}
{% endfor %}
</div>
<div id="spinner">
</div>
<div id="message">
</div>
<script src="challengeSolver.js" ></script>
<script src="hintMaker.js" ></script>
<script>
const challengeWorker = new Worker('challengeWorker.js');
challengeWorker.onmessage = (e) => {
if (e.data == "nothing"){
document.getElementById('spinner').style.display = "none";
document.getElementById('message').innerHTML = "No acceptable puzzle found.";
}
else if (e.data[0] == "success"){
makeHints(e.data[1]);
}
else if (e.data.substring(0,8) == "attempt-"){
var idx = parseInt(e.data.substring(8));
document.getElementById('spinner').textContent = idx+"/200";
}
}
fillable_puzzle();
var els = document.querySelectorAll('.cell');
els.forEach((el) => {
el.addEventListener('click',clickCell);
})
var puzzle;
var puzzleData = {1:0,2:0,3:0,4:0,5:0,'blanks':0};
var selected = {};
var chalID = 1;
function clickCell(evt){
var id = evt.currentTarget.id;
var rc = id.substring(5).split("-");
var r = parseInt(rc[0])-1;
var c = parseInt(rc[1])-1;
var s = r*x*y+c;
}
function loop100() {
console.log(Date.now());
document.getElementById('spinner').style.display = "block";
document.getElementById('message').innerHTML = "";
chalID = parseInt(document.getElementById('challenge').value);
challengeWorker.postMessage(['chalID',chalID])
}
</script>
</body>
</html>
var hints = {};
hints['1'] = {'hints':["Look at column {{column}}."],'spoiler':"The number {{number}} must go in row {{row}}, column {{column}} because the column already contains every other number."}
hints['2'] = {'hints':["Look at row {{row}}."],'spoiler':"The number {{number}} must go in row {{row}}, column {{column}} because the row already contains every other number."}
hints['3'] = {'hints':["Look at the {{block}} block."],'spoiler':"The number {{number}} must go in row {{row}}, column {{column}} because the 3x3 block already contains every other number."}
hints['4'] = {'hints':["Look at column {{column}}.","The number {{number}} must go somewhere in column {{column}}."],'spoiler':"The number {{number}} must go in row {{row}}, column {{column}}.{{occupied-rows}}{{blocked-rows}}{{blocked-blocks-column}}"}
hints['5'] = {'hints':["Look at row {{row}}.","The number {{number}} must go somewhere in row {{row}}."],'spoiler':"The number {{number}} must go in row {{row}}, column {{column}}.{{occupied-columns}}{{blocked-columns}}{{blocked-blocks-row}}"}
hints['6'] = {'hints':["Look at the {{block}} block.","The number {{number}} must go somewhere in the {{block}} block."],'spoiler':"The number {{number}} must go in row {{row}}, column {{column}}.{{blocked-rows-block}}{{blocked-columns-block}}"}
hints['7'] = {'hints':["Look at row {{row}}.","Look at column {{column}} in row {{row}}."],'spoiler':"The number {{number}} must go in row {{row}}, column {{column}}.{{eliminated-rows}}{{eliminated-columns}}{{eliminated-blocks}}"}
function replaceText(type,n,r,c,fills){
if (type == "occupied-rows"){
var rows = [];
for (var i=0;i<9;i++){
var cell = (i+1)+"-"+c;
if (fills[cell]){
rows.push(i+1);
}
}
if (rows.length == 0){
return "";
}
else if (rows.length == 1){
return " Row "+rows[0]+" is already occupied.";
}
else {
var rowStr = "";
for (var ii=0;ii<rows.length-1;ii++){
rowStr += rows[ii]+", ";
}
rowStr += "and "+rows[rows.length-1];
return " Rows "+rowStr+" are already occupied.";
}
}
else if (type == "occupied-columns"){
var cols = [];
for (var i=0;i<9;i++){
var cell = r+"-"+(i+1);
if (fills[cell]){
cols.push(i+1);
}
}
if (cols.length == 0){
return "";
}
else if (cols.length == 1){
return " Column "+cols[0]+" is already occupied.";
}
else {
var colStr = "";
for (var ii=0;ii<cols.length-1;ii++){
colStr += cols[ii]+", ";
}
colStr += "and "+cols[cols.length-1];
return " Columns "+colStr+" are already occupied.";
}
}
else if (type == "blocked-rows"){
var rows = [];
for (var i=0;i<9;i++){
var cell = (i+1)+"-"+c;
if (fills[cell]){
//ignore this row
}
else {
for (var ii=0;ii<9;ii++){
var cell2 = (i+1)+"-"+(ii+1);
if (fills[cell2] == n){
rows.push(i+1);
break;
}
}
}
}
if (rows.length == 0){
return "";
}
else if (rows.length == 1){
return " The number "+n+" is already in row "+rows[0]+".";
}
else {
var rowStr = "";
for (var ii=0;ii<rows.length-1;ii++){
rowStr += rows[ii]+", ";
}
rowStr += "and "+rows[rows.length-1];
return " The number "+n+" is already in rows "+rowStr+".";
}
}
else if (type == "blocked-columns"){
var cols = [];
for (var i=0;i<9;i++){
var cell = r+"-"+(i+1);
if (fills[cell]){
//ignore this col
}
else {
for (var ii=0;ii<9;ii++){
var cell2 = (ii+1)+"-"+(i+1);
if (fills[cell2] == n){
cols.push(i+1);
break;
}
}
}
}
if (cols.length == 0){
return "";
}
else if (cols.length == 1){
return " The number "+n+" is already in column "+cols[0]+".";
}
else {
var colStr = "";
for (var ii=0;ii<cols.length-1;ii++){
colStr += cols[ii]+", ";
}
colStr += "and "+cols[cols.length-1];
return " The number "+n+" is already in columns "+colStr+".";
}
}
else if (type == "blocked-blocks-row"){
var blocksMap = {};
for (var i=0;i<9;i++){
var cell = r+"-"+(i+1);
if (fills[cell]){
//ignore this block
}
else {
var rr = Math.floor((r-1)/3);
var cc = Math.floor((i)/3);
for (var ii=0;ii<3;ii++){
for (var iii=0;iii<3;iii++){
var cell2 = (rr*3+ii+1)+"-"+(cc*3+iii+1);
if (fills[cell2] == n){
blocksMap[cc] = true;
break;
}
}
}
}
}
var blocks = [];
for (var i in blocksMap){
var b = "";
if (r < 4){b += "top-";}
else if (r < 7){b += "middle-";}
else if (r < 10){b += "bottom-";}
if (i == 0){b += "left";}
else if (i == 1){b += "middle";}
else if (i == 2){b += "right";}
blocks.push(b);
}
if (blocks.length == 0){
return "";
}
else if (blocks.length == 1){
return " The number "+n+" is already in the "+blocks[0]+" block.";
}
else {
var blockStr = "";
for (var ii=0;ii<blocks.length-1;ii++){
blockStr += blocks[ii]+", ";
}
blockStr += "and "+blocks[blocks.length-1];
return " The number "+n+" is already in "+blockStr+" blocks.";
}
}
else if (type == "blocked-blocks-column"){
var blocksMap = {};
for (var i=0;i<9;i++){
var cell = (i+1)+"-"+c;
if (fills[cell]){
//ignore this block
}
else {
var rr = Math.floor((i)/3);
var cc = Math.floor((c-1)/3);
for (var ii=0;ii<3;ii++){
for (var iii=0;iii<3;iii++){
var cell2 = (rr*3+ii+1)+"-"+(cc*3+iii+1);
if (fills[cell2] == n){
blocksMap[rr] = true;
break;
}
}
}
}
}
var blocks = [];
for (var i in blocksMap){
var b = "";
if (i == 0){b += "top-";}
else if (i == 1){b += "middle-";}
else if (i == 2){b += "bottom-";}
if (c < 4){b += "left";}
else if (c < 7){b += "middle";}
else if (c < 10){b += "right";}
blocks.push(b);
}
if (blocks.length == 0){
return "";
}
else if (blocks.length == 1){
return " The number "+n+" is already in the "+blocks[0]+" block.";
}
else {
var blockStr = "";
for (var ii=0;ii<blocks.length-1;ii++){
blockStr += blocks[ii]+", ";
}
blockStr += "and "+blocks[blocks.length-1];
return " The number "+n+" is already in "+blockStr+" blocks.";
}
}
else if (type == "blocked-rows-block"){
var rows = [];
var rr = Math.floor((r-1)/3);
var cc = Math.floor((c-1)/3);
for (var ii=0;ii<3;ii++){
var ignoreRow = true;
for (var iii=0;iii<3;iii++){
var cell = (rr*3+ii+1)+"-"+(cc*3+iii+1);
if (!fills[cell]){
ignoreRow = false;
break;
}
}
if (!ignoreRow){
for (var iii=0;iii<9;iii++){
var cell2 = (rr*3+ii+1)+"-"+(iii+1);
if (fills[cell2] == n){
rows.push(rr*3+ii+1);
break;
}
}
}
}
if (rows.length == 0){
return "";
}
else if (rows.length == 1){
return " The number "+n+" is already in row "+rows[0]+".";
}
else {
var rowStr = "";
for (var ii=0;ii<rows.length-1;ii++){
rowStr += rows[ii]+", ";
}
rowStr += "and "+rows[rows.length-1];
return " The number "+n+" is already in rows "+rowStr+".";
}
}
else if (type == "blocked-columns-block"){
var cols = [];
var rr = Math.floor((r-1)/3);
var cc = Math.floor((c-1)/3);
for (var ii=0;ii<3;ii++){
var ignoreCol = true;
for (var iii=0;iii<3;iii++){
var cell = (rr*3+iii+1)+"-"+(cc*3+ii+1);
if (!fills[cell]){
ignoreCol = false;
break;
}
}
if (!ignoreCol){
for (var iii=0;iii<9;iii++){
var cell2 = (iii+1)+"-"+(cc*3+ii+1);
if (fills[cell2] == n){
cols.push(cc*3+ii+1);
break;
}
}
}
}
if (cols.length == 0){
return "";
}
else if (cols.length == 1){
return " The number "+n+" is already in column "+cols[0]+".";
}
else {
var colStr = "";
for (var ii=0;ii<cols.length-1;ii++){
colStr += cols[ii]+", ";
}
colStr += "and "+cols[cols.length-1];
return " The number "+n+" is already in columns "+colStr+".";
}
}
else if (type == "eliminated-rows"){
var nums = [];
for (var i=0;i<9;i++){
var cell = r+"-"+(i+1);
if (fills[cell]){
nums.push(fills[cell])
}
}
if (nums.length == 0){
return "";
}
else if (nums.length == 1){
return " The number "+nums[0]+" is already in row "+r+".";
}
else {
var numStr = "";
for (var ii=0;ii<nums.length-1;ii++){
numStr += nums[ii]+", ";
}
numStr += "and "+nums[nums.length-1];
return " The numbers "+numStr+" are already in row "+r+".";
}
}
else if (type == "eliminated-columns"){
var nums = [];
for (var i=0;i<9;i++){
var cell = (i+1)+"-"+c;
if (fills[cell]){
nums.push(fills[cell])
}
}
if (nums.length == 0){
return "";
}
else if (nums.length == 1){
return " The number "+nums[0]+" is already in column "+c+".";
}
else {
var numStr = "";
for (var ii=0;ii<nums.length-1;ii++){
numStr += nums[ii]+", ";
}
numStr += "and "+nums[nums.length-1];
return " The numbers "+numStr+" are already in column "+c+".";
}
}
else if (type == "eliminated-blocks"){
var nums = [];
var rr = Math.floor((r-1)/3);
var cc = Math.floor((c-1)/3);
for (var i=0;i<3;i++){
for (var ii=0;ii<3;ii++){
var cell = (rr*3+i+1)+"-"+(cc*3+ii+1);
if (fills[cell]){
nums.push(fills[cell])
}
}
}
if (nums.length == 0){
return "";
}
else if (nums.length == 1){
return " The number "+nums[0]+" is already in that block.";
}
else {
var numStr = "";
for (var ii=0;ii<nums.length-1;ii++){
numStr += nums[ii]+", ";
}
numStr += "and "+nums[nums.length-1];
return " The numbers "+numStr+" are already in that block.";
}
}
}
function makeHints(chal){
console.log(chal);
var fills = chal.fills;
var chalID = chal.chalID;
var solve = chal.solve;
var h = [];
var b = "";
if (solve.r < 4){b += "top-";}
else if (solve.r < 7){b += "middle-";}
else if (solve.r < 10){b += "bottom-";}
if (solve.c < 4){b += "left";}
else if (solve.c < 7){b += "middle";}
else if (solve.c < 10){b += "right";}
var chalMain = 0;
if (chalID > 7){
chalMain = chalID;
if (chal.chalType){
chalID = chal.chalType;
}
else {
chalID = 7;
}
}
document.getElementById('spinner').style.display = "none";
document.getElementById('message').innerHTML = "";
var x = 3;
var y = 3;
var pStr = "";
for (var i=0;i<x*y;i++){
for (var ii=0;ii<x*y;ii++){
var elf = document.getElementById('fillable-'+(i+1)+"-"+(ii+1));
var s = i*x*y+ii;
var ss = (i+1)+"-"+(ii+1);
if (fills[ss]){
pStr += fills[ss];
elf.textContent = fills[ss];
continue;
}
pStr += ".";
elf.textContent = "";
}
}
document.getElementById('puzzleStr').textContent = pStr;
for (var i in hints[chalID].hints){
var details = document.createElement('details');
var summary = document.createElement('summary');
summary.textContent = "Hint";
details.appendChild(summary);
var str = hints[chalID].hints[i];
str = str.replace(/{{\s*column\s*}}/g,solve.c);
str = str.replace(/{{\s*row\s*}}/g,solve.r);
str = str.replace(/{{\s*number\s*}}/g,solve.n);
str = str.replace(/{{\s*block\s*}}/g,b);
str = str.replace(/{{\s*occupied-rows\s*}}/g,replaceText("occupied-rows",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*occupied-columns\s*}}/g,replaceText("occupied-columns",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*blocked-rows\s*}}/g,replaceText("blocked-rows",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*blocked-columns\s*}}/g,replaceText("blocked-columns",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*blocked-blocks-row\s*}}/g,replaceText("blocked-blocks-row",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*blocked-blocks-column\s*}}/g,replaceText("blocked-blocks-column",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*blocked-rows-block\s*}}/g,replaceText("blocked-rows-block",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*blocked-columns-block\s*}}/g,replaceText("blocked-columns-block",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*eliminated-columns\s*}}/g,replaceText("eliminated-columns",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*eliminated-rows\s*}}/g,replaceText("eliminated-rows",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*eliminated-blocks\s*}}/g,replaceText("eliminated-blocks",solve.n,solve.r,solve.c,fills));
console.log(str);
details.innerHTML += str;
document.getElementById('message').appendChild(details);
}
var details = document.createElement('details');
var summary = document.createElement('summary');
summary.textContent = "Spoiler";
details.appendChild(summary);
var str = hints[chalID].spoiler;
str = str.replace(/{{\s*column\s*}}/g,solve.c);
str = str.replace(/{{\s*row\s*}}/g,solve.r);
str = str.replace(/{{\s*number\s*}}/g,solve.n);
str = str.replace(/{{\s*block\s*}}/g,b);
str = str.replace(/{{\s*occupied-rows\s*}}/g,replaceText("occupied-rows",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*occupied-columns\s*}}/g,replaceText("occupied-columns",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*blocked-rows\s*}}/g,replaceText("blocked-rows",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*blocked-columns\s*}}/g,replaceText("blocked-columns",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*blocked-blocks-row\s*}}/g,replaceText("blocked-blocks-row",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*blocked-blocks-column\s*}}/g,replaceText("blocked-blocks-column",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*blocked-rows-block\s*}}/g,replaceText("blocked-rows-block",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*blocked-columns-block\s*}}/g,replaceText("blocked-columns-block",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*eliminated-columns\s*}}/g,replaceText("eliminated-columns",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*eliminated-rows\s*}}/g,replaceText("eliminated-rows",solve.n,solve.r,solve.c,fills));
str = str.replace(/{{\s*eliminated-blocks\s*}}/g,replaceText("eliminated-blocks",solve.n,solve.r,solve.c,fills));
if (chalMain == 8 || chalMain == 10 || chalMain == 12 || chalMain == 14){
var unitt = 1;
if (chalMain == 10 || chalMain == 14){
unitt = 0;
}
var info;
if (chalMain < 12){
info = findDefiniteSubunitInBlock(fills,unitt,solve.n);
}
else {
info = findDefiniteSubunitInUnit(fills,unitt,solve.n);
}
var unit = units[solve.r+"-"+solve.c][chalID-4];
var possible = [];
for (var i=0;i<9;i++){
if (fills[unit[i]] == 0 && fitSpotBasic(fills,unit[i],solve.n)){
possible.push(unit[i]);
}
}
str += " So the number "+solve.n+" can only go in "+possible.join(", ")+".\n";
for (var i=0;i<info.length;i++){
var b = "";
var rr = Math.floor((parseInt(info[i][0].split("-")[0])-1)/3);
var cc = Math.floor((parseInt(info[i][0].split("-")[1])-1)/3);
if (rr == 0){b += "top-";}
else if (rr == 1){b += "middle-";}
else if (rr == 2){b += "bottom-";}
if (cc == 0){b += "left";}
else if (cc == 1){b += "middle";}
else if (cc == 2){b += "right";}
if (chalMain == 8 || chalMain == 10){
if (info[i][0].split("-")[0] == info[i][1].split("-")[0]){
str += "The number "+solve.n+" must go somewhere in row "+info[i][0].split("-")[0]+" in the "+b+" block.\n";
}
else if (info[i][0].split("-")[1] == info[i][1].split("-")[1]){
str += "The number "+solve.n+" must go somewhere in column "+info[i][0].split("-")[1]+" in the "+b+" block.\n";
}
}
else {
if (info[i][0].split("-")[0] == info[i][1].split("-")[0]){
str += "The number "+solve.n+" must go somewhere in the "+b+" block in row "+info[i][0].split("-")[0]+".\n";
}
else if (info[i][0].split("-")[1] == info[i][1].split("-")[1]){
str += "The number "+solve.n+" must go somewhere in the "+b+" block in column "+info[i][0].split("-")[1]+".\n";
}
}
}
console.log(info);
}
else if (chalMain == 9 || chalMain == 11 || chalMain == 13 || chalMain == 15){
var unitt = 1;
if (chalMain == 11 || chalMain == 15){
unitt = 0;
}
var possible = [];
for (var i=0;i<9;i++){
if (fitSpotBasic(fills,solve.r+"-"+solve.c,i+1)){
possible.push(i+1);
}
}
str += " So only the numbers "+possible.join(", ")+" can go in the cell.\n";
for (var vi in possible){
var vv = possible[vi];
if (vv == solve.n){continue;}
var info;
if (chalMain < 12){
info = findDefiniteSubunitInBlock(fills,unitt,vv);
}
else {
info = findDefiniteSubunitInUnit(fills,unitt,vv);
}
for (var i=0;i<info.length;i++){
var b = "";
var rr = Math.floor((parseInt(info[i][0].split("-")[0])-1)/3);
var cc = Math.floor((parseInt(info[i][0].split("-")[1])-1)/3);
if (rr == 0){b += "top-";}
else if (rr == 1){b += "middle-";}
else if (rr == 2){b += "bottom-";}
if (cc == 0){b += "left";}
else if (cc == 1){b += "middle";}
else if (cc == 2){b += "right";}
if (chalMain == 9 || chalMain == 11){
if (info[i][0].split("-")[0] == info[i][1].split("-")[0]){
if (info[i][0].split("-")[0] == solve.r){
str += "The number "+vv+" must go somewhere in row "+info[i][0].split("-")[0]+" in the "+b+" block.\n";
}
}
else if (info[i][0].split("-")[1] == info[i][1].split("-")[1]){
if (info[i][0].split("-")[1] == solve.c){
str += "The number "+vv+" must go somewhere in column "+info[i][0].split("-")[1]+" in the "+b+" block.\n";
}
}
}
else {
if (info[i][0].split("-")[0] == info[i][1].split("-")[0]){
str += "The number "+vv+" must go somewhere in the "+b+" block in row "+info[i][0].split("-")[0]+".\n";
}
else if (info[i][0].split("-")[1] == info[i][1].split("-")[1]){
str += "The number "+vv+" must go somewhere in the "+b+" block in column "+info[i][0].split("-")[1]+".\n";
}
}
}
console.log(info);
}
}
if (chalMain == 16){
var info;
if (chalMain == 16){
info = findNakedPairs(fills,solve.n);
}
var unit = units[solve.r+"-"+solve.c][chalID-4];
var possible = [];
for (var i=0;i<9;i++){
if (fills[unit[i]] == 0 && fitSpotBasic(fills,unit[i],solve.n)){
possible.push(unit[i]);
}
}
str += " So the number "+solve.n+" can only go in "+possible.join(", ")+".\n";
for (var i=0;i<info.length;i++){
str += "The pair "+info[i][0]+" and "+info[i][1]+" must go in "+info[i][2]+" and "+info[i][3]+".\n";
}
console.log(info);
}
if (chalMain == 18){
var unit = units[solve.r+"-"+solve.c][chalID-4];
var possible = [];
for (var i=0;i<9;i++){
if (fills[unit[i]] == 0 && fitSpotBasic(fills,unit[i],solve.n)){
possible.push(unit[i]);
}
}
str += " So the number "+solve.n+" can only go in "+possible.join(", ")+".\n";
for (var vi in possible){
var ss = possible[vi];
var info;
info = findHiddenPairs(fills,ss);
for (var i=0;i<info.length;i++){
str += "The pair "+info[i][0]+" and "+info[i][1]+" must go in "+info[i][2]+" and "+info[i][3]+".\n";
}
}
console.log(info);
}
if (chalMain == 17){
var possible = [];
for (var i=0;i<9;i++){
if (fitSpotBasic(fills,solve.r+"-"+solve.c,i+1)){
possible.push(i+1);
}
}
str += " So only the numbers "+possible.join(", ")+" can go in the cell.\n";
for (var vi in possible){
var vv = possible[vi];
var info;
info = findNakedPairs(fills,vv);
for (var i=0;i<info.length;i++){
str += "The pair "+vv+" and "+info[i][1]+" must go in "+info[i][2]+" and "+info[i][3]+".\n";
}
console.log(info);
}
}
if (chalMain == 19){
var possible = [];
for (var i=0;i<9;i++){
if (fitSpotBasic(fills,solve.r+"-"+solve.c,i+1)){
possible.push(i+1);
}
}
str += " So only the numbers "+possible.join(", ")+" can go in the cell.\n";
for (var vi in possible){
var vv = possible[vi];
var info;
info = findHiddenPairs(fills,vv);
for (var i=0;i<info.length;i++){
str += "The pair "+info[i][0]+" and "+info[i][1]+" must go in "+info[i][2]+" and "+info[i][3]+".\n";
}
}
console.log(info);
}
details.innerHTML += str;
document.getElementById('message').appendChild(details);
console.log(str);
}
/*
var chal = {"fills":{"1-1":0,"1-2":0,"1-3":0,"1-4":0,"1-5":2,"1-6":1,"1-7":8,"1-8":9,"1-9":0,"2-1":0,"2-2":0,"2-3":0,"2-4":8,"2-5":4,"2-6":0,"2-7":1,"2-8":6,"2-9":5,"3-1":1,"3-2":0,"3-3":0,"3-4":0,"3-5":0,"3-6":0,"3-7":7,"3-8":0,"3-9":0,"4-1":0,"4-2":0,"4-3":0,"4-4":1,"4-5":6,"4-6":4,"4-7":2,"4-8":3,"4-9":8,"5-1":0,"5-2":3,"5-3":0,"5-4":0,"5-5":8,"5-6":0,"5-7":9,"5-8":5,"5-9":1,"6-1":0,"6-2":0,"6-3":0,"6-4":3,"6-5":5,"6-6":9,"6-7":6,"6-8":4,"6-9":7,"7-1":5,"7-2":0,"7-3":0,"7-4":4,"7-5":0,"7-6":8,"7-7":3,"7-8":1,"7-9":0,"8-1":0,"8-2":0,"8-3":0,"8-4":0,"8-5":0,"8-6":0,"8-7":4,"8-8":8,"8-9":0,"9-1":0,"9-2":0,"9-3":0,"9-4":0,"9-5":0,"9-6":0,"9-7":5,"9-8":7,"9-9":0},"chalID":1,"solve":{"n":2,"r":"3","c":"8"}};
var chal = {"fills":{"1-1":0,"1-2":9,"1-3":2,"1-4":8,"1-5":5,"1-6":4,"1-7":7,"1-8":3,"1-9":1,"2-1":0,"2-2":4,"2-3":5,"2-4":0,"2-5":0,"2-6":0,"2-7":8,"2-8":9,"2-9":6,"3-1":1,"3-2":8,"3-3":0,"3-4":9,"3-5":0,"3-6":6,"3-7":4,"3-8":5,"3-9":2,"4-1":0,"4-2":0,"4-3":8,"4-4":0,"4-5":9,"4-6":3,"4-7":5,"4-8":0,"4-9":0,"5-1":0,"5-2":0,"5-3":0,"5-4":7,"5-5":0,"5-6":5,"5-7":0,"5-8":2,"5-9":8,"6-1":5,"6-2":0,"6-3":0,"6-4":0,"6-5":0,"6-6":0,"6-7":0,"6-8":0,"6-9":3,"7-1":4,"7-2":0,"7-3":7,"7-4":0,"7-5":0,"7-6":0,"7-7":0,"7-8":0,"7-9":0,"8-1":9,"8-2":0,"8-3":1,"8-4":5,"8-5":6,"8-6":0,"8-7":3,"8-8":0,"8-9":0,"9-1":0,"9-2":0,"9-3":0,"9-4":4,"9-5":0,"9-6":0,"9-7":0,"9-8":1,"9-9":0},"chalID":2,"solve":{"n":6,"r":"1","c":"1"}};
var chal = {"fills":{"1-1":6,"1-2":7,"1-3":3,"1-4":4,"1-5":2,"1-6":0,"1-7":0,"1-8":0,"1-9":0,"2-1":0,"2-2":0,"2-3":0,"2-4":8,"2-5":5,"2-6":7,"2-7":3,"2-8":0,"2-9":0,"3-1":0,"3-2":0,"3-3":0,"3-4":1,"3-5":3,"3-6":0,"3-7":9,"3-8":2,"3-9":7,"4-1":1,"4-2":0,"4-3":7,"4-4":2,"4-5":0,"4-6":0,"4-7":0,"4-8":0,"4-9":0,"5-1":0,"5-2":0,"5-3":4,"5-4":7,"5-5":8,"5-6":0,"5-7":2,"5-8":0,"5-9":9,"6-1":0,"6-2":8,"6-3":2,"6-4":3,"6-5":0,"6-6":0,"6-7":0,"6-8":0,"6-9":0,"7-1":0,"7-2":0,"7-3":0,"7-4":6,"7-5":7,"7-6":3,"7-7":4,"7-8":0,"7-9":0,"8-1":0,"8-2":0,"8-3":6,"8-4":9,"8-5":4,"8-6":2,"8-7":0,"8-8":0,"8-9":0,"9-1":0,"9-2":0,"9-3":0,"9-4":5,"9-5":1,"9-6":0,"9-7":6,"9-8":0,"9-9":2},"chalID":3,"solve":{"n":8,"r":"9","c":"6"}};
var chal = {"fills":{"1-1":6,"1-2":3,"1-3":9,"1-4":0,"1-5":0,"1-6":7,"1-7":0,"1-8":5,"1-9":8,"2-1":8,"2-2":2,"2-3":7,"2-4":0,"2-5":0,"2-6":5,"2-7":0,"2-8":9,"2-9":6,"3-1":1,"3-2":4,"3-3":5,"3-4":0,"3-5":6,"3-6":0,"3-7":0,"3-8":7,"3-9":2,"4-1":7,"4-2":1,"4-3":3,"4-4":5,"4-5":8,"4-6":6,"4-7":9,"4-8":2,"4-9":4,"5-1":9,"5-2":5,"5-3":8,"5-4":1,"5-5":4,"5-6":2,"5-7":6,"5-8":3,"5-9":7,"6-1":2,"6-2":6,"6-3":4,"6-4":7,"6-5":0,"6-6":0,"6-7":5,"6-8":8,"6-9":1,"7-1":5,"7-2":7,"7-3":6,"7-4":0,"7-5":0,"7-6":0,"7-7":0,"7-8":1,"7-9":3,"8-1":4,"8-2":9,"8-3":1,"8-4":0,"8-5":7,"8-6":0,"8-7":0,"8-8":6,"8-9":5,"9-1":3,"9-2":8,"9-3":2,"9-4":6,"9-5":5,"9-6":1,"9-7":7,"9-8":4,"9-9":9},"chalID":4,"solve":{"n":4,"r":"7","c":"6"}};
var chal = {"fills":{"1-1":0,"1-2":0,"1-3":0,"1-4":0,"1-5":0,"1-6":0,"1-7":0,"1-8":0,"1-9":2,"2-1":0,"2-2":0,"2-3":0,"2-4":2,"2-5":0,"2-6":0,"2-7":3,"2-8":0,"2-9":0,"3-1":0,"3-2":2,"3-3":0,"3-4":8,"3-5":0,"3-6":9,"3-7":0,"3-8":5,"3-9":0,"4-1":0,"4-2":1,"4-3":3,"4-4":0,"4-5":0,"4-6":0,"4-7":2,"4-8":6,"4-9":0,"5-1":2,"5-2":0,"5-3":9,"5-4":0,"5-5":0,"5-6":0,"5-7":0,"5-8":0,"5-9":1,"6-1":0,"6-2":0,"6-3":0,"6-4":7,"6-5":1,"6-6":2,"6-7":0,"6-8":9,"6-9":3,"7-1":0,"7-2":0,"7-3":0,"7-4":1,"7-5":8,"7-6":0,"7-7":0,"7-8":0,"7-9":0,"8-1":0,"8-2":6,"8-3":0,"8-4":0,"8-5":2,"8-6":0,"8-7":0,"8-8":0,"8-9":0,"9-1":0,"9-2":0,"9-3":0,"9-4":6,"9-5":5,"9-6":0,"9-7":1,"9-8":3,"9-9":0},"chalID":5,"solve":{"n":2,"r":"9","c":"3"}};
var chal = {"fills":{"1-1":7,"1-2":6,"1-3":0,"1-4":1,"1-5":9,"1-6":0,"1-7":0,"1-8":0,"1-9":0,"2-1":5,"2-2":1,"2-3":0,"2-4":0,"2-5":6,"2-6":0,"2-7":7,"2-8":0,"2-9":0,"3-1":8,"3-2":4,"3-3":0,"3-4":7,"3-5":0,"3-6":3,"3-7":0,"3-8":0,"3-9":6,"4-1":2,"4-2":0,"4-3":0,"4-4":4,"4-5":3,"4-6":6,"4-7":0,"4-8":0,"4-9":7,"5-1":1,"5-2":0,"5-3":6,"5-4":0,"5-5":0,"5-6":9,"5-7":3,"5-8":4,"5-9":8,"6-1":0,"6-2":0,"6-3":0,"6-4":0,"6-5":1,"6-6":0,"6-7":6,"6-8":0,"6-9":0,"7-1":0,"7-2":2,"7-3":1,"7-4":3,"7-5":0,"7-6":0,"7-7":0,"7-8":0,"7-9":0,"8-1":0,"8-2":0,"8-3":5,"8-4":6,"8-5":0,"8-6":1,"8-7":0,"8-8":0,"8-9":0,"9-1":0,"9-2":3,"9-3":7,"9-4":9,"9-5":0,"9-6":0,"9-7":0,"9-8":0,"9-9":1},"chalID":6,"solve":{"n":8,"r":"8","c":"2"}};
var chal = {"fills":{"1-1":0,"1-2":0,"1-3":7,"1-4":0,"1-5":0,"1-6":0,"1-7":0,"1-8":0,"1-9":6,"2-1":8,"2-2":0,"2-3":0,"2-4":0,"2-5":0,"2-6":0,"2-7":0,"2-8":0,"2-9":9,"3-1":0,"3-2":0,"3-3":4,"3-4":0,"3-5":8,"3-6":0,"3-7":7,"3-8":0,"3-9":0,"4-1":0,"4-2":0,"4-3":0,"4-4":0,"4-5":3,"4-6":7,"4-7":0,"4-8":9,"4-9":0,"5-1":9,"5-2":0,"5-3":0,"5-4":0,"5-5":4,"5-6":5,"5-7":0,"5-8":6,"5-9":0,"6-1":0,"6-2":0,"6-3":0,"6-4":9,"6-5":0,"6-6":0,"6-7":0,"6-8":2,"6-9":4,"7-1":2,"7-2":0,"7-3":0,"7-4":0,"7-5":0,"7-6":3,"7-7":0,"7-8":0,"7-9":0,"8-1":0,"8-2":0,"8-3":6,"8-4":0,"8-5":5,"8-6":0,"8-7":0,"8-8":3,"8-9":0,"9-1":0,"9-2":0,"9-3":9,"9-4":0,"9-5":0,"9-6":0,"9-7":0,"9-8":0,"9-9":0},"chalID":6,"solve":{"n":7,"r":"5","c":"9"}};
var chal = {"fills":{"1-1":0,"1-2":0,"1-3":9,"1-4":0,"1-5":0,"1-6":0,"1-7":0,"1-8":0,"1-9":4,"2-1":0,"2-2":2,"2-3":8,"2-4":6,"2-5":0,"2-6":0,"2-7":0,"2-8":0,"2-9":5,"3-1":0,"3-2":1,"3-3":0,"3-4":0,"3-5":0,"3-6":0,"3-7":0,"3-8":0,"3-9":0,"4-1":0,"4-2":0,"4-3":0,"4-4":0,"4-5":0,"4-6":0,"4-7":5,"4-8":0,"4-9":0,"5-1":5,"5-2":0,"5-3":0,"5-4":0,"5-5":0,"5-6":0,"5-7":0,"5-8":0,"5-9":0,"6-1":7,"6-2":9,"6-3":2,"6-4":4,"6-5":5,"6-6":1,"6-7":0,"6-8":6,"6-9":0,"7-1":0,"7-2":0,"7-3":0,"7-4":0,"7-5":0,"7-6":8,"7-7":7,"7-8":0,"7-9":0,"8-1":0,"8-2":0,"8-3":7,"8-4":0,"8-5":0,"8-6":0,"8-7":8,"8-8":0,"8-9":0,"9-1":2,"9-2":0,"9-3":0,"9-4":0,"9-5":0,"9-6":0,"9-7":0,"9-8":0,"9-9":6},"chalID":7,"solve":{"n":3,"r":"6","c":"7"}};
var chal = {"fills":{"1-1":0,"1-2":8,"1-3":0,"1-4":0,"1-5":2,"1-6":4,"1-7":0,"1-8":1,"1-9":5,"2-1":0,"2-2":0,"2-3":0,"2-4":5,"2-5":0,"2-6":0,"2-7":6,"2-8":0,"2-9":0,"3-1":0,"3-2":0,"3-3":0,"3-4":9,"3-5":0,"3-6":0,"3-7":0,"3-8":0,"3-9":0,"4-1":7,"4-2":0,"4-3":1,"4-4":8,"4-5":4,"4-6":0,"4-7":0,"4-8":0,"4-9":9,"5-1":2,"5-2":0,"5-3":8,"5-4":0,"5-5":0,"5-6":0,"5-7":4,"5-8":7,"5-9":6,"6-1":9,"6-2":0,"6-3":0,"6-4":2,"6-5":0,"6-6":7,"6-7":3,"6-8":8,"6-9":1,"7-1":5,"7-2":0,"7-3":0,"7-4":4,"7-5":0,"7-6":0,"7-7":1,"7-8":0,"7-9":7,"8-1":0,"8-2":0,"8-3":0,"8-4":6,"8-5":0,"8-6":5,"8-7":8,"8-8":0,"8-9":3,"9-1":8,"9-2":0,"9-3":0,"9-4":0,"9-5":0,"9-6":0,"9-7":0,"9-8":0,"9-9":0},"chalID":8,"chalType":4,"solve":{"n":6,"r":"1","c":"1"}};
makeHints(chal)
*/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/png" href="favicon.png">
<title></title>
<meta name="description" content="" />
<link href="style.css" rel="stylesheet" type="text/css" />
<style>
#container2 {
display: block;
position: absolute;
left: 0;
}
#spinner {
display: block;
position: absolute;
left: 0;
height: calc(2rem * 11);
width: calc(2rem * 11);
background: rgba(0,0,0,0.75);
color: white;
font-size: 2rem;
}
#message {
display: inline-block;
position: relative;
left: calc(2rem * 11);
top: 2rem;
width: calc(20rem);
}
.cell {
border: 1px dotted gray;
display: inline-block;
height: calc(2rem - 2px);
width: calc(2rem - 2px);
position: absolute;
line-height: 2rem;
text-align: center;
cursor: pointer;
background: white;
}
.row-1 {
top: calc(2rem * 1);
}
.col-1 {
left: calc(2rem * 1);
}
.row-2 {
top: calc(2rem * 2);
}
.col-2 {
left: calc(2rem * 2);
}
.row-3 {
top: calc(2rem * 3);
}
.col-3 {
left: calc(2rem * 3);
}
.row-4 {
top: calc(2rem * 4);
}
.col-4 {
left: calc(2rem * 4);
}
.row-5 {
top: calc(2rem * 5);
}
.col-5 {
left: calc(2rem * 5);
}
.row-6 {
top: calc(2rem * 6);
}
.col-6 {
left: calc(2rem * 6);
}
.row-7 {
top: calc(2rem * 7);
}
.col-7 {
left: calc(2rem * 7);
}
.row-8 {
top: calc(2rem * 8);
}
.col-8 {
left: calc(2rem * 8);
}
.row-9 {
top: calc(2rem * 9);
}
.col-9 {
left: calc(2rem * 9);
}
.row-3, .row-6, .row-9 {
border-bottom: 1px solid black;
}
.row-1, .row-4, .row-7 {
border-top: 1px solid black;
}
.col-3, .col-6, .col-9 {
border-right: 1px solid black;
}
.col-1, .col-4, .col-7 {
border-left: 1px solid black;
}
</style>
</head>
<body>
<div id="menu">
<button onclick="loop100();">Refresh Challenge</button>
Challenge: <select id="challenge">
<option value="1">Finish Unit (col)</option>
<option value="2">Finish Unit (row)</option>
<option value="3">Finish Unit (blk)</option>
<option value="4">One Spot (col)</option>
<option value="5">One Spot (row)</option>
<option value="6">One Spot (blk)</option>
<option value="7">One Digit in Spot</option>
<option value="8">Subrow in Block (spot)</option>
<option value="9">Subrow in Block (digit)</option>
<option value="10">Subcol in Block (spot)</option>
<option value="11">Subcol in Block (digit)</option>
<option value="12">Subrow in Row (spot)</option>
<option value="13">Subrow in Row (digit)</option>
<option value="14">Subcol in Col (spot)</option>
<option value="15">Subcol in Col (digit)</option>
<option value="16">Naked Pair (spot)</option>
<option value="17">Naked Pair (digit)</option>
<option value="18">Hidden Pair (spot)</option>
<option value="19">Hidden Pair (digit)</option>
</select>
PuzzleStr: <span id="puzzleStr"></span>
</div>
<div id="container2">
<div class="cell row-1 col-1" id="fillable-1-1">
</div>
<div class="cell row-1 col-2" id="fillable-1-2">
</div>
<div class="cell row-1 col-3" id="fillable-1-3">
</div>
<div class="cell row-1 col-4" id="fillable-1-4">
</div>
<div class="cell row-1 col-5" id="fillable-1-5">
</div>
<div class="cell row-1 col-6" id="fillable-1-6">
</div>
<div class="cell row-1 col-7" id="fillable-1-7">
</div>
<div class="cell row-1 col-8" id="fillable-1-8">
</div>
<div class="cell row-1 col-9" id="fillable-1-9">
</div>
<div class="cell row-2 col-1" id="fillable-2-1">
</div>
<div class="cell row-2 col-2" id="fillable-2-2">
</div>
<div class="cell row-2 col-3" id="fillable-2-3">
</div>
<div class="cell row-2 col-4" id="fillable-2-4">
</div>
<div class="cell row-2 col-5" id="fillable-2-5">
</div>
<div class="cell row-2 col-6" id="fillable-2-6">
</div>
<div class="cell row-2 col-7" id="fillable-2-7">
</div>
<div class="cell row-2 col-8" id="fillable-2-8">
</div>
<div class="cell row-2 col-9" id="fillable-2-9">
</div>
<div class="cell row-3 col-1" id="fillable-3-1">
</div>
<div class="cell row-3 col-2" id="fillable-3-2">
</div>
<div class="cell row-3 col-3" id="fillable-3-3">
</div>
<div class="cell row-3 col-4" id="fillable-3-4">
</div>
<div class="cell row-3 col-5" id="fillable-3-5">
</div>
<div class="cell row-3 col-6" id="fillable-3-6">
</div>
<div class="cell row-3 col-7" id="fillable-3-7">
</div>
<div class="cell row-3 col-8" id="fillable-3-8">
</div>
<div class="cell row-3 col-9" id="fillable-3-9">
</div>
<div class="cell row-4 col-1" id="fillable-4-1">
</div>
<div class="cell row-4 col-2" id="fillable-4-2">
</div>
<div class="cell row-4 col-3" id="fillable-4-3">
</div>
<div class="cell row-4 col-4" id="fillable-4-4">
</div>
<div class="cell row-4 col-5" id="fillable-4-5">
</div>
<div class="cell row-4 col-6" id="fillable-4-6">
</div>
<div class="cell row-4 col-7" id="fillable-4-7">
</div>
<div class="cell row-4 col-8" id="fillable-4-8">
</div>
<div class="cell row-4 col-9" id="fillable-4-9">
</div>
<div class="cell row-5 col-1" id="fillable-5-1">
</div>
<div class="cell row-5 col-2" id="fillable-5-2">
</div>
<div class="cell row-5 col-3" id="fillable-5-3">
</div>
<div class="cell row-5 col-4" id="fillable-5-4">
</div>
<div class="cell row-5 col-5" id="fillable-5-5">
</div>
<div class="cell row-5 col-6" id="fillable-5-6">
</div>
<div class="cell row-5 col-7" id="fillable-5-7">
</div>
<div class="cell row-5 col-8" id="fillable-5-8">
</div>
<div class="cell row-5 col-9" id="fillable-5-9">
</div>
<div class="cell row-6 col-1" id="fillable-6-1">
</div>
<div class="cell row-6 col-2" id="fillable-6-2">
</div>
<div class="cell row-6 col-3" id="fillable-6-3">
</div>
<div class="cell row-6 col-4" id="fillable-6-4">
</div>
<div class="cell row-6 col-5" id="fillable-6-5">
</div>
<div class="cell row-6 col-6" id="fillable-6-6">
</div>
<div class="cell row-6 col-7" id="fillable-6-7">
</div>
<div class="cell row-6 col-8" id="fillable-6-8">
</div>
<div class="cell row-6 col-9" id="fillable-6-9">
</div>
<div class="cell row-7 col-1" id="fillable-7-1">
</div>
<div class="cell row-7 col-2" id="fillable-7-2">
</div>
<div class="cell row-7 col-3" id="fillable-7-3">
</div>
<div class="cell row-7 col-4" id="fillable-7-4">
</div>
<div class="cell row-7 col-5" id="fillable-7-5">
</div>
<div class="cell row-7 col-6" id="fillable-7-6">
</div>
<div class="cell row-7 col-7" id="fillable-7-7">
</div>
<div class="cell row-7 col-8" id="fillable-7-8">
</div>
<div class="cell row-7 col-9" id="fillable-7-9">
</div>
<div class="cell row-8 col-1" id="fillable-8-1">
</div>
<div class="cell row-8 col-2" id="fillable-8-2">
</div>
<div class="cell row-8 col-3" id="fillable-8-3">
</div>
<div class="cell row-8 col-4" id="fillable-8-4">
</div>
<div class="cell row-8 col-5" id="fillable-8-5">
</div>
<div class="cell row-8 col-6" id="fillable-8-6">
</div>
<div class="cell row-8 col-7" id="fillable-8-7">
</div>
<div class="cell row-8 col-8" id="fillable-8-8">
</div>
<div class="cell row-8 col-9" id="fillable-8-9">
</div>
<div class="cell row-9 col-1" id="fillable-9-1">
</div>
<div class="cell row-9 col-2" id="fillable-9-2">
</div>
<div class="cell row-9 col-3" id="fillable-9-3">
</div>
<div class="cell row-9 col-4" id="fillable-9-4">
</div>
<div class="cell row-9 col-5" id="fillable-9-5">
</div>
<div class="cell row-9 col-6" id="fillable-9-6">
</div>
<div class="cell row-9 col-7" id="fillable-9-7">
</div>
<div class="cell row-9 col-8" id="fillable-9-8">
</div>
<div class="cell row-9 col-9" id="fillable-9-9">
</div>
</div>
<div id="spinner">
</div>
<div id="message">
</div>
<script src="challengeSolver.js" ></script>
<script src="hintMaker.js" ></script>
<script>
const challengeWorker = new Worker('challengeWorker.js');
challengeWorker.onmessage = (e) => {
if (e.data == "nothing"){
document.getElementById('spinner').style.display = "none";
document.getElementById('message').innerHTML = "No acceptable puzzle found.";
}
else if (e.data[0] == "success"){
makeHints(e.data[1]);
}
else if (e.data.substring(0,8) == "attempt-"){
var idx = parseInt(e.data.substring(8));
document.getElementById('spinner').textContent = idx+"/200";
}
}
fillable_puzzle();
var els = document.querySelectorAll('.cell');
els.forEach((el) => {
el.addEventListener('click',clickCell);
})
var puzzle;
var puzzleData = {1:0,2:0,3:0,4:0,5:0,'blanks':0};
var selected = {};
var chalID = 1;
function clickCell(evt){
var id = evt.currentTarget.id;
var rc = id.substring(5).split("-");
var r = parseInt(rc[0])-1;
var c = parseInt(rc[1])-1;
var s = r*x*y+c;
}
function loop100() {
console.log(Date.now());
document.getElementById('spinner').style.display = "block";
document.getElementById('message').innerHTML = "";
chalID = parseInt(document.getElementById('challenge').value);
challengeWorker.postMessage(['chalID',chalID])
}
</script>
</body>
</html>
const fs = require('fs');
const nunjucks = require('nunjucks');
var html = nunjucks.render("genChallenges.html",{});
fs.writeFileSync("index.html",html);
This repl generate some sudoku puzzles that have a particular challenge. Not every puzzle is completely solvable, but it is possible to use a particular strategy to solve one more cell.
To view this on TripleLog, click here. Read this blog post to learn a bit more about the strategies.
The index.html file is a generated file from the genChallenges.html template. Choose the challenge type and then click refresh challenge. Every challenge type should eventually return a puzzle but you may need to click a few times. Each puzzle should include one or more hints as well as a solution if you get stuck.
Add CSS by editing the style.css file that is linked to in the head section or inline edit the style section in the head.
The makehtml.js file will generate the index.html. The challenge finder runs in a worker.
The hintMaker.js file adds hints and solutions for a given challenge.