Skip to content

Instantly share code, notes, and snippets.

@matthewmorrone
Last active January 28, 2021 18:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matthewmorrone/4d2b0d6fd12352b6d85581b9a13026f6 to your computer and use it in GitHub Desktop.
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
.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;
}
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&nbsp;<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