Last active
January 28, 2021 18:18
-
-
Save matthewmorrone/4d2b0d6fd12352b6d85581b9a13026f6 to your computer and use it in GitHub Desktop.
run as a console snippet. now with plain and hidden pairs, triplets, all the way up through 8
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.su-candidate-button { | |
-webkit-transition: opacity 1; | |
-moz-transition: opacity 1; | |
-o-transition: opacity 1; | |
transition: opacity 1; | |
-webkit-transition-delay: 0; | |
-moz-transition-delay: 0; | |
-o-transition-delay: 0; | |
transition-delay: 0; | |
opacity: 0; | |
} | |
[single], [unit], .attention { | |
background-color: #00FF0040; | |
} | |
.selected { | |
background-color: #FFDA00FF !important; | |
} | |
#pzm-congrats .su-modal__message { | |
visibility: hidden; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
console.clear(); | |
$(function() { | |
if (!$("span a[href='/crosswords']").length) { | |
$("#portal-game-toolbar").prepend(`<span role="button" class="pz-toolbar-button"><a href="/crosswords">Home</a></span>`); | |
} | |
}); | |
Object.defineProperty(Object.prototype, "define", { | |
writable: false, | |
enumerable: false, | |
configurable: true, | |
value: function(key, value) { | |
if (Object[key]) { | |
delete Object[key]; | |
} | |
Object.defineProperty(this, key, { | |
writable: false, | |
enumerable: false, | |
configurable: true, | |
value: value | |
}); | |
return this.key; | |
} | |
}); | |
function round(numToRound, numToRoundTo) { | |
return Math.round(numToRound / numToRoundTo) * numToRoundTo; | |
} | |
function floor(numToFloor, numToFloorTo) { | |
return Math.floor(numToFloor / numToFloorTo) * numToFloorTo; | |
} | |
Array.prototype.define("each", Array.prototype.forEach); | |
Object.prototype.define("getKeys", function() { | |
return Object.keys(this); | |
}); | |
Object.prototype.define("getValues", function() { | |
return Object.values(this); | |
}); | |
Object.prototype.define("getEntries", function() { | |
return Object.entries(this); | |
}); | |
Object.prototype.define("isEmpty", function() { | |
return Object.entries(this).length === 0 && this.constructor === Object; | |
}); | |
Array.prototype.define("unique", function() { | |
return [...new Set(this)]; | |
}); | |
String.prototype.define("toProperCase", function() { | |
return this.replace(/\w\S*/g, function(txt) { | |
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); | |
}); | |
}); | |
String.prototype.define("int", function() { | |
return Number.parseInt(this, 10); | |
}); | |
String.prototype.define("sort", function() { | |
return this.split("").sort().join(""); | |
}) | |
Object.prototype.define("map", function(f, ctx) { | |
let o = this; | |
ctx = ctx || this; | |
let result = {}; | |
Object.keys(o).forEach(function(k) { | |
result[k] = f.call(ctx, o[k], k, o); | |
}); | |
return result; | |
}); | |
Array.prototype.define("toSet", function() { | |
return new Set(this); | |
}); | |
Array.prototype.define("unique", function() { | |
return Array.from(new Set(this)); | |
}); | |
Object.define("size", function(...args) { | |
let len = 0; | |
for (let arg of args) { | |
len += arg.size(); | |
} | |
return len; | |
}); | |
Object.prototype.define("size", function(...args) { | |
let len = 0; | |
for (let arg of args) { | |
len += arg.size(); | |
} | |
return len + Object.entries(this).length; | |
}); | |
String.prototype.define("intersection", function(s2) { | |
let s1 = this, i, a = {}; | |
for (i = 0, l = s1.length; i < l; i++) { | |
a[s1[i]] = (a[s1[i]] || 0) + 1; | |
} | |
for (i = 0, l = s2.length; i < l; i++) { | |
a[s2[i]] = (a[s2[i]] || 0) + 1; | |
} | |
for (var p in a) { | |
if (a[p] <= 1) { | |
delete a[p]; | |
} | |
} | |
return a.getKeys().join("").trim(); | |
}); | |
function unwrap({candidates, frequencies}, index) { | |
frequencies = frequencies[index]; | |
return {frequencies, candidates}; | |
} | |
$.each({ | |
bottom: "bottom", | |
left: "left", | |
right: "right", | |
top: "top" | |
}, function() { | |
let attr = arguments[0]; | |
$.fn[attr] = function() { | |
if (arguments.length === 0) { | |
return parseFloat(this.css(attr)); | |
} | |
if (arguments.length === 1) { | |
this.css(attr, arguments[0]); | |
} | |
return this; | |
} | |
}); | |
function getActive() { | |
return $(".su-cell.selected"); | |
} | |
function getCandidateContainer(cell) { | |
return $(`div[data-candidate=${$(cell).attr("data-cell")}]`).eq(0); | |
} | |
function getCandidates($cell) { | |
return getCandidateContainer($cell).find("svg") | |
.toArray().filter(el => $(el).attr("class") === "su-candidate") | |
.map(el => $(el).attr("number")).unique().sort().join(""); | |
} | |
function getCoordinates($cell) { | |
$cell = $($cell); | |
let id = Number.parseInt($cell.attr("data-cell"), 10); | |
let x = id % 9 + 1; | |
let y = Math.floor(id / 9) + 1; | |
return { | |
x: x, | |
y: y, | |
id: id, | |
c: getCandidates($cell) | |
}; | |
} | |
function getSec(k) { | |
return getSecs()[k - 1]; | |
} | |
function getSecs() { | |
let rows = getRows(), secs = []; | |
for (let k of Array(9).keys()) { | |
[j, i] = [k % 3 * 3, Math.floor(k / 3) * 3]; | |
let stuff = rows.slice(i, i + 3).map(r => r.slice(j, j + 3)); | |
for (let l = 0; l < rows.length; l++) { | |
stuff.push(rows[l].slice(j, j + 3)); | |
} | |
secs.push(stuff); | |
} | |
secs = secs.map(function($a) { | |
$a = $a.map(a => Array.from(a)); | |
$a = $a[0].concat($a[1]).concat($a[2]); | |
return $a; | |
}); | |
return secs; | |
} | |
function getRow(i) { | |
return $(".su-board").find("[data-cell]").slice(i * 9, (i + 1) * 9); | |
} | |
function getRows() { | |
let cells = $(".su-board").find("[data-cell]"), rows = []; | |
while (cells.length) { | |
rows.push(cells.splice(0, 9)); | |
} | |
return rows; | |
} | |
function getCol(j) { | |
let cells = $(".su-board").find("[data-cell]"), col = []; | |
return cells.filter(function(index) { | |
return index % 9 == j; | |
}); | |
} | |
function getCols($table) { | |
let rows = getRows(), cols = []; | |
for (let i of Array(9).keys()) { | |
let col = []; | |
for (let j of Array(9).keys()) { | |
col.push(rows[j][i]); | |
} | |
cols.push(col); | |
} | |
return cols; | |
} | |
function removeDuplicateCells(cells) { | |
let i, j, coords = [], cell, flag; | |
for (i = 0; i < cells.length; i++) { | |
cell = cells[i]; | |
flag = false; | |
for (j = 0; j < coords.length; j++) { | |
if (cell.id === coords[j].id) { | |
flag = true; | |
} | |
} | |
if (flag === false) { | |
coords.push(cell); | |
} | |
} | |
return coords; | |
} | |
function count(arr) { | |
let a = [], b = [], prev; | |
arr.sort(); | |
for (let i = 0; i < arr.length; i++) { | |
if (arr[i] !== prev) { | |
a.push(arr[i]); | |
b.push(1); | |
} | |
else { | |
b[b.length - 1]++; | |
} | |
prev = arr[i]; | |
} | |
return [a, b]; | |
} | |
function getFrequencies(str, f=1, g=false) { | |
let fre = {}, i, chr, ret = {}; | |
for (i = 0; i < str.length; i++) { | |
chr = str.charAt(i); | |
fre[chr] ? fre[chr]++ : fre[chr] = 1; | |
} | |
if (f == 0) { | |
return fre; | |
} | |
if (g) { | |
Object.entries(fre).forEach(function(entry) { | |
let[key, val] = entry; | |
ret[val] ? ret[val].push(entry) : ret[val] = [entry]; | |
}); | |
return ret; | |
} | |
else { | |
Object.keys(fre).forEach(function(key) { | |
if (fre[key] === f) { | |
ret[key] = fre[key]; | |
} | |
}); | |
return ret; | |
} | |
} | |
function groupData(thing) { | |
let disp = $(thing).map(function(a, sq) { | |
return getCandidates($(sq)); | |
}).toArray(); | |
let fre = getFrequencies(disp.join(""), 1, true); | |
let result = { | |
cells: thing, | |
candidates: disp, | |
frequencies: fre | |
}; | |
if (fre[1] && fre[1].length === 1) { | |
result.answer = fre[1][0][0]; | |
} | |
if (fre[1] && fre[1].length > 1) { | |
result.answer = fre[1].map(x => x[0]).join(", "); | |
} | |
return result; | |
} | |
function perform($things, n=2, info={parity: "", group: ""}) { | |
let results = {}, $single, data, freq; | |
if (n === 1) { | |
$things.forEach(function(thing, x) { | |
data = groupData(thing); | |
$single = ""; | |
if (!data.frequencies.isEmpty()) { | |
if (data.frequencies[n] && data.frequencies[n][0]) { | |
data.cells.each(function(el) { | |
if ($(el).hasClass("prefilled")) return; | |
if (getCandidates($(el)).includes(data.frequencies[n][0][0])) { | |
$single = $(el); | |
} | |
}); | |
if (!$single) return; | |
$single.attr("parity", $things.parity); | |
$single.attr("single", true); | |
data.coordinates = getCoordinates($single); | |
data.frequencies = data.frequencies.map(x => x.map(y => y[0])); | |
results[x] = data; | |
} | |
} | |
}) | |
return results; | |
} | |
$things.forEach(function(thing, x) { | |
let data = groupData(thing); | |
let d1 = data.candidates.filter(Boolean); | |
let d2 = d1.join("").split("").unique().sort().join(""); | |
if (!d1.length || !d2.length) { | |
return; | |
} | |
if ((n === d1.length) && (d1.length === d2.length)) { | |
data.parity = 'bare'; | |
results[x] = data; | |
return; | |
} | |
data.squares = data.cells.map(cell => getCoordinates(cell)); | |
if (data.frequencies[n]) { | |
let nset = data.frequencies[n].map(m => m[0]).join(""); | |
data.squares = data.squares.filter(square => square.c.intersection(nset)); | |
} | |
let d3 = data.candidates.filter(function(a) { | |
return a.length === n; | |
}); | |
let d4 = Array.from(new Set(d3)); | |
if (d3.length > d4.length) { | |
data.set = data.candidates.join("").split("").unique().sort().join(""); | |
if (data.set.replace(d4.join("").sort(), "").length > 0) { | |
data.parity = 'plain'; | |
} | |
else { | |
data.parity = 'bare'; | |
} | |
delete data.set; | |
delete data.squares; | |
data.frequencies = data.frequencies.map(x => x.map(y => y[0])); | |
// data.answer = data.frequencies[n].join(", "); | |
// isolate the relevant pair of squares, and check if their frequencies exceed their parity | |
results[x] = data; | |
return; | |
} | |
let fre = getFrequencies(data.candidates.join(""), 0); | |
let th = [], tn = [], res = {}; | |
Object.entries(fre).forEach(function(entry) { | |
let[key, val] = entry; | |
if (val === n) { | |
th.push(key); | |
} | |
}) | |
if (th.length > 1) { | |
$(thing).each(function() { | |
let obj = getCoordinates($(this)); | |
th.forEach(function(y) { | |
if (obj.c.includes(y)) { | |
tn.push(obj); | |
} | |
}); | |
}); | |
tn = removeDuplicateCells(tn); | |
if (tn.length === n && Object.keys(data.frequencies[n]).length === n && Object.keys(data.frequencies).length > 1) { | |
data.parity = 'mask'; | |
data.frequencies = data.frequencies.map(x => x.map(y => y[0])); | |
delete data.n; | |
delete data.squares; | |
data.answer = data.frequencies[n].join(", ") | |
results[x] = data; | |
} | |
} | |
}); | |
return results; | |
} | |
function log() { | |
const getVarName = varObj => Object.keys(varObj)[0]; | |
let result = arguments.callee.caller.toString(); | |
result = result.substr('function '.length); | |
result = result.substr(0, result.indexOf('(')); | |
console.log(result) | |
console.log(this.name) | |
console.log(arguments) | |
console.log(arguments.callee.name) | |
console.log(arguments.callee.caller) | |
} | |
function sudoku() { | |
// next, implement checks for confinement of possibilities in a sec to a single row or col | |
let display = { | |
unit: [] | |
}; | |
$cells.each(function() { | |
let coordinates = getCoordinates($(this)); | |
if (coordinates.c.length === 1) { | |
$(this).attr("unit", true); | |
display.unit.push(coordinates); | |
} | |
}) | |
if (!$.isEmptyObject(display.unit)) { | |
console.group("Unit"); | |
if (display.unit.length === 1) { | |
console.log("1 unit"); | |
} | |
else { | |
console.log(display.unit.length + " units"); | |
} | |
console.groupEnd("Unit"); | |
} | |
function displayGroups(group, name, index) { | |
if (!$.isEmptyObject(group)) { | |
group.size() === 1 ? name = name.slice(0, -1) : name; | |
console.group(name); | |
Object.entries(group).forEach(function (entry) { | |
console.log(Number.parseInt(entry[0])+1, entry[1]); | |
}); | |
console.groupEnd(name); | |
} | |
} | |
let groups = [ | |
"singles", "doubles"// , "triples" // , "quadruples", "pentuples", "sixtuples", "heptuples", "octuples" | |
]; | |
groups.each(function(group, n) { | |
let secs = perform($secs, n+1, {parity: "sec", group: groups[n-1]}) | |
, rows = perform($rows, n+1, {parity: "row", group: groups[n-1]}) | |
, cols = perform($cols, n+1, {parity: "col", group: groups[n-1]}) | |
, empty = Object.size(rows, cols, secs) < 1 | |
, prevEmpty = display[groups[n-1]] | |
? display[groups[n-1]].empty | |
: false; | |
display[group] = { | |
secs: secs, | |
rows: rows, | |
cols: cols, | |
empty: empty, | |
prevEmpty: prevEmpty | |
}; | |
}); | |
function groupsToConsole(groups, name, collapsed) { | |
if (!collapsed) { | |
console.group(name); | |
} | |
else { | |
console.groupCollapsed(name); | |
} | |
displayGroups(groups.rows, "Rows", name.toLowerCase()); | |
displayGroups(groups.cols, "Cols", name.toLowerCase()); | |
displayGroups(groups.secs, "Secs", name.toLowerCase()); | |
console.groupEnd(name); | |
} | |
let displayed = 0 | |
groups.each(function(group, n) { | |
if (display[group].empty) { | |
return; | |
} | |
displayed++; | |
groupsToConsole(display[group], group.toProperCase(), displayed !== 1); | |
}) | |
let count = $("[single]").length + display.unit.length; | |
console.log(display.unit) | |
console.log($("[single]")) | |
$("#count").text(`(${count})`); | |
} | |
$(".su-keyboard__auto").after(`<div class="su-keyboard__auto"> | |
<label style='display: contents;'> | |
<input class="su-keyboard__checkbox" type="checkbox" name="helper" id="helper" checked="checked"> | |
<div>Helper Mode <span id='count'></span></div> | |
</label> | |
</div>`); | |
$(function() { | |
$cells = $(".su-cell"); | |
$cands = $(".su-candidates"); | |
$rows = getRows(); | |
$cols = getCols(); | |
$secs = getSecs(); | |
let top = $(".su-board").offset().top; | |
let left = $(".su-board").offset().left; | |
let height = $(".su-board").height(); | |
let width = $(".su-board").width(); | |
let x, y, id; | |
$(".su-candidates").each(function(a, b) { | |
x = $(this).find("svg").eq(0).css("left").replace("px", "").int(); | |
y = $(this).find("svg").eq(0).css("top") .replace("px", "").int(); | |
x = round(floor(x, 78) / width, .11) / .11; | |
y = round(floor(y, 78) / height, .11) / .11; | |
id = 0 + Number.parseInt(y, 10) * 9 + Number.parseInt(x, 10); | |
$(this).attr("x", x+1); | |
$(this).attr("y", y+1); | |
$(this).attr("cell", id); | |
}); | |
$cands.each(function(index, cand) { | |
$(cand).removeAttr("style"); | |
let $svg = $(cand).find("svg").eq(0); | |
let cell = $(document.elementsFromPoint($svg.position().left, $svg.position().top)).filter(".su-cell")[0]; | |
$svg.parent().attr("data-candidate", $(cell).attr("data-cell")); | |
}); | |
$("#helper").click(function() { | |
$("[single]").removeAttr("single"); | |
$("[unit]").removeAttr("unit"); | |
$("[parity]").removeAttr("parity"); | |
$(".attention").removeClass("attention"); | |
$("#count").text(""); | |
console.clear(); | |
if (!$("#helper").is(":checked")) return; | |
sudoku(); | |
}); | |
function doku() { | |
$active1 = {x: 0, y: 0}; | |
$active2 = {x: 0, y: 0}; | |
$active1 = getCoordinates(getActive()); | |
setTimeout(function() { | |
$active2 = getCoordinates(getActive()); | |
if ($active1.x !== $active2.x || $active1.y !== $active2.y) return; | |
if ($(".conflicted").length) return; | |
$("[single]").removeAttr("single"); | |
$("[unit]").removeAttr("unit"); | |
$("[parity]").removeAttr("parity"); | |
$(".attention").removeClass("attention"); | |
$("#count").text(""); | |
console.clear(); | |
if (!$("#helper").is(":checked")) return; | |
sudoku(); | |
}); | |
} | |
$(".su-board").off("mouseup").on("mouseup", function(e) { | |
e.preventDefault(); | |
doku(); | |
}); | |
$(document).off("keyup").on("keyup", function(e) { | |
if (e.which === 116) return; | |
if (e.which === 17) return; | |
if (e.which >= 37 && e.which <= 40) return; | |
e.preventDefault(); | |
doku(); | |
}); | |
if ($("#helper").is(":checked")) { | |
sudoku(); | |
} | |
$(".su-app__print").before(`<span role="button" class="pz-toolbar-button su-app__reset">Reset</span>`); | |
$(".su-app__reset").click(function() { | |
$(".su-toolbar__help-menu-toggle").click(); | |
$(".su-toolbar__help-menu .su-button__dropdown-item").eq(5).click(); | |
}); | |
$("#portal-game-toolbar").prepend(`<span role="button" class="pz-toolbar-button"><a href="/crosswords">Home</a></span>`); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment