-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjigidi-bingo-solver.user.js
329 lines (287 loc) · 16.6 KB
/
jigidi-bingo-solver.user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
// ==UserScript==
// @name Jigidi Bingo Solver
// @namespace to.soon.userjs.jigidi
// @match https://www.jigidi.com/solve*
// @match https://www.jigidi.com/s/*
// @grant GM_getValue
// @grant GM_setValue
// @version 1.4
// @author Fox <https://github.com/f-o>
// @description Script to help solve Jigidi puzzles, by rendering columns in a colourful grid gradient, and marking each piece with numbers.
// @license MIT
// ==/UserScript==
(function () {
'use strict';
// Custom Gradients array:
var gradientsArray = {
"Rainbow": ['#FF0000', '#FFFF00', '#00FF00', '#0000FF'],
"Distinct": ['#191970', '#006400', '#ff0000', '#00ff00', '#00ffff', '#ff00ff', '#ffb6c1'],
"Rastafari": ['#1E9600', "#FFF200", "#FF0000"],
"Sublime Vivid": ['#FC466B', "#3F5EFB"],
"DanQ": ['#FF0000', '#EE82EE'],
"Instagram": ['#833ab4', "#fd1d1d", "#fcb045"],
"Hacker": ['#ff0000', "#000000", "#00ff11", "#000000", "#0077ff"]
}
const $verbose = false;
function generateSpectrum(rows, colors) {
if (!Array.isArray(colors) || colors.length < 2) {
throw new Error('Colors array must have at least two colors.');
}
const spectrum = [];
const numColors = colors.length - 1; // Number of gradients between colors
const increment = numColors / (rows - 1); // Increment between adjacent rows
for (let i = 0; i < rows; i++) {
const colorIndex = Math.floor(i * increment); // Index of the color gradient
const startColor = colors[colorIndex]; // Start color of the gradient
const endColor = colors[Math.min(colorIndex + 1, colors.length - 1)]; // End color of the gradient
const t = (i * increment) % 1; // Interpolation parameter
spectrum.push(interpolateColors(startColor, endColor, t)); // Interpolate color
}
return spectrum;
}
function interpolateColors(color1, color2, t) {
// Extract RGB components of the colors
const [r1, g1, b1] = color1.match(/\w\w/g).map(hex => parseInt(hex, 16));
const [r2, g2, b2] = color2.match(/\w\w/g).map(hex => parseInt(hex, 16));
// Interpolate RGB components
const r = Math.round(r1 + (r2 - r1) * t);
const g = Math.round(g1 + (g2 - g1) * t);
const b = Math.round(b1 + (b2 - b1) * t);
// Convert interpolated RGB components to hex format
return '#' + [r, g, b].map(component => component.toString(16).padStart(2, '0')).join('');
}
// Wait for page to load
window.addEventListener('load', function () {
// If URL ends with "solve.php", get the puzzle ID from a tag under h1.puzzle-title and redirect
if (window.location.href.endsWith('solve.php')) {
const puzzleId = document.querySelector('h1.puzzle-title').querySelector('a').href;
window.location.href = puzzleId.replace('https://www.jigidi.com/jigsaw-puzzle/', 'https://www.jigidi.com/solve/');
}
// If URL contains "/s/", get the puzzle ID from share-url and redirect
else if (window.location.href.includes('/s/')) {
// Loop through all script tags and find the one containing "ShareEmbed.url"
for (const script of document.querySelectorAll('script')) {
if (script.innerText.includes('ShareEmbed.url')) {
const puzzleId = script.innerText.match(/ShareEmbed.url = "(.+)";/)[1];
window.location.href = puzzleId;
break;
}
}
}
// Prepare Bingo Solver UI
const JigidiBingoSolver = document.createElement('div');
JigidiBingoSolver.id = 'jigidi-bingo-solver';
// Inject Bingo Solver UI after the tool info panel
const creatorElem = document.getElementById('tool-info-panel');
creatorElem.after(JigidiBingoSolver);
// Cleanup the page
const elementWithAds = document.querySelector('.show-ad');
if (elementWithAds) {
elementWithAds.classList.remove('show-ad');
if ($verbose) {
console.log('Removed element with class "show-ad".');
}
}
// Get the canvas element
const canvas = document.querySelector('canvas');
if (!canvas) {
if ($verbose) {
console.log('Canvas not found.');
}
// Sleep for 2 seconds and try again
setTimeout(() => { window.location.reload(); }, 2000);
return;
}
// Global settings
const bingoSolverSettingsGlobal = GM_getValue('bingoSolverSettingsGlobal', {
showNumbers: true,
showColours: true,
showColoursBy: 'length',
gradient: 'Rainbow',
fontSize: 26
});
// Unique jigsaw ID settings
const jigsawId = window.location.href.match(/solve\/(\w+)\//)[1];
const bingoSolverSettings = GM_getValue(`bingoSolverSettings_${jigsawId}`, { col: 1 });
// Log settings
if ($verbose) {
console.log(`Bingo Solver: jigsawId=${jigsawId}`);
console.log(`Bingo Solver: bingoSolverSettingsGlobal=${JSON.stringify(bingoSolverSettingsGlobal)}`);
console.log(`Bingo Solver: bingoSolverSettings=${JSON.stringify(bingoSolverSettings)}`);
}
const jDimensions = creatorElem.innerText.match(/(\d+)×(\d+)/);
const jCols = parseInt(jDimensions[1]);
const jRows = parseInt(jDimensions[2]);
if ($verbose) {
console.log(`Bingo Solver: jCols=${jCols} jRows=${jRows}`);
}
// Initialize an empty string to store the HTML options
let optionsHTML = '';
// Iterate over the keys of the gradientsArray object
for (const gradientName in gradientsArray) {
// Check if the current property is a direct property of the object and not inherited
if (gradientsArray.hasOwnProperty(gradientName)) {
// Create an option element with the gradient name as the value and label
optionsHTML += `<option value="${gradientName}" ${bingoSolverSettingsGlobal.gradient === gradientName ? 'selected' : ''}>${gradientName}</option>`;
}
}
JigidiBingoSolver.innerHTML = `
<hr>
<div class="hide-complete" style="margin-bottom:2rem;">
<strong>Bingo Solver</strong><br>
<p>Help with column? (0 to disable)</p>
<div class="panel-tool " style="display: flex; justify-content: space-between; gap: 1rem;" id="animated-border">
<input type="number" id="magicStripesCol" value="${bingoSolverSettings.col}" min="0" max="${jCols}" style="width: 25%; \
text-align: center !important; \
padding: 0.5rem; \
font-size: 2rem; \
font-weight: bold; \
border-radius: 0.5rem; \
background: white; \
color: black; \
border: none;">
<button title="Go!" class="btn em" id="magicStripesGo" style="width: 25%;"><span style="font-size: 2rem; font-weight: bold; cursor: pointer;">Go!</span></button>
<div title="Go +1!" class="btn em" id="magicStripesPlusOne" style="width: 50%;"><span style="font-size: 2rem; font-weight: bold; cursor: pointer;">Go +1!</span></div>
</div>
<div id="tool-settings-panel" class="panel-tool">
<label class="checkbox icon-plus">Show numbers on pieces <input type="checkbox" id="show-numbers" ${bingoSolverSettingsGlobal.showNumbers ? 'checked' : ''}><i style="${bingoSolverSettingsGlobal.showNumbers ? 'background: green;' : 'background: firebrick;'}"></i></label>
<label for="font-size" class="checkbox icon-plus">Font size: <select name="font-size" id="font-size" style="padding: 0.5rem; font-weight: bold; border-radius: 0.5rem; background: white; color: black; border: none; float: right;">
<option value="12" ${bingoSolverSettingsGlobal.fontSize === 12 ? 'selected' : ''}>12</option>
<option value="16" ${bingoSolverSettingsGlobal.fontSize === 16 ? 'selected' : ''}>16</option>
<option value="22" ${bingoSolverSettingsGlobal.fontSize === 22 ? 'selected' : ''}>22</option>
<option value="26" ${bingoSolverSettingsGlobal.fontSize === 26 ? 'selected' : ''}>26</option>
<option value="30" ${bingoSolverSettingsGlobal.fontSize === 30 ? 'selected' : ''}>30</option>
<option value="36" ${bingoSolverSettingsGlobal.fontSize === 36 ? 'selected' : ''}>36</option>
<option value="40" ${bingoSolverSettingsGlobal.fontSize === 40 ? 'selected' : ''}>40</option>
</select> </label>
<label for="gradients" class="checkbox icon-plus">Gradient: <select name="gradients" id="gradients" style="padding: 0.5rem; font-weight: bold; border-radius: 0.5rem; background: white; color: black; border: none; float: right;">
${optionsHTML}
</select> </label>
</div>
</div>
<hr>
`;
const magicStripesCol = document.getElementById('magicStripesCol');
const magicStripesGo = document.getElementById('magicStripesGo');
magicStripesGo.addEventListener('click', () => {
bingoSolverSettings.col = parseInt(magicStripesCol.value);
GM_setValue(`bingoSolverSettings_${jigsawId}`, bingoSolverSettings);
window.location.reload();
});
document.getElementById('magicStripesPlusOne').addEventListener('click', () => {
magicStripesCol.value = (parseInt(magicStripesCol.value) + 1) % (jCols + 1);
magicStripesGo.dispatchEvent(new Event('click'));
});
var fontSize = GM_getValue('bingoSolverSettingsGlobal', bingoSolverSettingsGlobal).fontSize;
// Check whether to show numbers
const showNumbers = document.getElementById('show-numbers');
showNumbers.addEventListener('change', () => {
if ($verbose) {
console.log(`Bingo Solver: showNumbers=${showNumbers.checked}`);
}
showNumbers.parentElement.querySelector('i').style.background = showNumbers.checked ? 'green' : 'firebrick';
// Store the new value
bingoSolverSettingsGlobal.showNumbers = showNumbers.checked;
GM_setValue('bingoSolverSettingsGlobal', bingoSolverSettingsGlobal);
window.location.reload();
})
// Check which gradient to use
const gradient = document.getElementById('gradients');
gradient.addEventListener('change', () => {
if ($verbose) {
console.log(`Bingo Solver: gradient=${gradient.value}`);
}
// Store the new value
bingoSolverSettingsGlobal.gradient = gradient.value;
GM_setValue('bingoSolverSettingsGlobal', bingoSolverSettingsGlobal);
window.location.reload();
})
// Check which font size to use
const fontSizeSelect = document.getElementById('font-size');
fontSizeSelect.addEventListener('change', () => {
if ($verbose) {
console.log(`Bingo Solver: fontSize=${fontSizeSelect.value}`);
}
// Store the new value
bingoSolverSettingsGlobal.fontSize = parseInt(fontSizeSelect.value);
bingoSolverSettingsGlobal.showNumbers = true;
GM_setValue('bingoSolverSettingsGlobal', bingoSolverSettingsGlobal);
window.location.reload();
})
// Check whether to show numbers
if (!bingoSolverSettingsGlobal.showNumbers) {
fontSize = 0;
}
// Generate spectrum to use
if ($verbose) {
console.log(`Bingo Solver: colors=${JSON.stringify(gradientsArray[bingoSolverSettingsGlobal.gradient])}`);
}
const spectrum = generateSpectrum(jRows, gradientsArray[bingoSolverSettingsGlobal.gradient]);
if ($verbose) {
console.log(spectrum);
}
const jColors = spectrum.map(color => `${color}`);
let jC = 0;
const targetCol = parseInt(bingoSolverSettings.col);
if (targetCol > 0) {
// Override putImageData with a manipulated version for THIS page load
CanvasRenderingContext2D.prototype.putImageData = function (imageData, dx, dy) {
const targetCol = parseInt(bingoSolverSettings.col);
const col = jC % jCols;
const row = Math.floor(jC / jCols);
if ((col + 1) === targetCol) {
// Target column: color and number multiple times
this.fillStyle = jColors[row];
if ($verbose) {
console.log("Column", col, "Row", row + 1, "Color", "https://www.color-hex.com/color/" + jColors[row % jColors.length].replace('#', ''));
}
this.fillRect(-1000, -1000, 2000, 2000);
// Font size and text
this.font = `bold ${fontSize}px sans-serif`;
const text = `${row + 1} `.repeat(100);
const x = -100;
this.fillStyle = 'black'; // Outline color
// Linewidth based on font size
this.lineWidth = fontSize / 4;
//this.lineWidth = 7; // Adjust the thickness of the outline
// Draw the outline text with a thicker stroke
this.strokeStyle = 'black'; // Set the stroke color
this.strokeText(text, x, 0); // Draw the outline text at the top
// Draw the inner text in white
this.fillStyle = 'white'; // Inner color
this.fillText(text, x, 0); // Draw the text in white at the top
// Draw the text in multiple rows
for (let i = -100; i <= 100; i++) {
const y = i * (fontSize * 1.2); // Adjust the spacing between rows
this.strokeText(text, x, y); // Outline
this.fillText(text, x, y); // Fill
}
}
else if ((col + 2) === targetCol) {
// Previous column: lightly color and number once
this.fillStyle = jColors[row % jColors.length];
this.fillRect(-1000, -1000, 2000, 2000);
// Fill with semi-transparent white
this.fillStyle = '#ffffffbb';
this.fillRect(-1000, -1000, 2000, 2000);
this.font = `bold ${fontSize}px sans-serif`;
this.fillStyle = 'black';
// Write in center, taking into account the size of the number
if (row < 10) {
this.fillText(`${row + 1}`, 0, 0);
}
else {
var textWidth = this.measureText(`${row}`).width;
this.fillText(`${row + 1}`, -textWidth / 2, 0);
}
}
else {
// Other columns: white-out
this.fillStyle = '#ffffff';
this.fillRect(-1000, -1000, 2000, 2000);
}
jC++;
}
}
});
})();