// ==UserScript==
// @name Karla's Neggsweeper Autoplayer
// @namespace karla@neopointskarla
// @license GPL3
// @version 0.0.2
// @description Auto plays neggsweeper, for Neopoints, trophy, and random events!
// @author Karla
// @match *://*.neopets.com/games/neggsweeper/neggsweeper.phtml*
// @homepage https://neopointskarla.com
// @icon https://www.google.com/s2/favicons?sz=64&domain=neopets.com
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
const random_in_range = (start, end) => {
return Math.floor(Math.random() * (end - start + 1) + start);
};
function shuffle(array) {
let currentIndex = array.length;
while (currentIndex != 0) {
let randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]];
}
}
function getCombinations(arr, n) {
const result = [];
function helper(start, combo) {
if (combo.length === n) {
result.push([...combo]);
return;
}
for (let i = start; i < arr.length; i++) {
combo.push(arr[i]);
helper(i + 1, combo);
combo.pop();
}
}
helper(0, []);
return result;
}
function buildGameboard() {
const gameboard = [];
const trs = document.querySelectorAll('[bgcolor="white"]');
for (let i = 1; i < trs.length; i += 1) {
const tr = trs[i];
const tds = tr.querySelectorAll('td');
const row = [];
for (let j = 0; j < tds.length; j += 1) {
if (tds[j].querySelector('img[src="//images.neopets.com/x/gn.gif"], img[src="//images.neopets.com/games/neggsweeper/old/badnegg.gif"]')) {
row.push('?');
} else if (tds[j].querySelector('img[src="//images.neopets.com/games/neggsweeper/old/flagnegg.gif"]')) {
row.push('x');
} else {
row.push(tds[j].textContent.replace(/\xA0/g, '') || '0');
}
}
gameboard.push(row);
}
for (let i = 0; i < gameboard.length; i += 1) {
const row = gameboard[i];
for (let j = 0; j < row.length; j += 1) {
const item = row[j];
if (item === 'x') {
for (let x = 0; x < directions1.length; x += 1) {
if (/^\d+$/g.test(gameboard[i + directions1[x]]?.[j + directions2[x]])) {
gameboard[i + directions1[x]][j + directions2[x]] -= 1;
}
}
}
}
}
return gameboard;
}
const directions1 = [-1, -1, -1, 0, 0, 1, 1, 1];
const directions2 = [-1, 0, 1, -1, 1, -1, 0, 1];
const totalMines = {
9: 10,
12: 25,
14: 40
}
function scanGameboard(gameboard) {
const globalPositions = [];
for (let i = 0; i < gameboard.length; i += 1) {
for (let j = 0; j < gameboard[i].length; j += 1) {
if (gameboard[i][j] === '?') {
globalPositions.push(`${i},${j}`);
}
}
}
// let minesLeft = totalMines[gameboard.length];
let mineGroups = [];
for (let i = 0; i < gameboard.length; i += 1) {
const row = gameboard[i];
for (let j = 0; j < row.length; j += 1) {
const item = row[j];
if (item !== '?' && item !== 'x' && item !== 'b' && item !== '0') {
const count = Number(item);
const arr = [count];
for (let x = 0; x < directions1.length; x += 1) {
if (gameboard[i + directions1[x]]?.[j + directions2[x]] === '?') {
arr.push(`${i + directions1[x]},${j + directions2[x]}`);
const globalIndex = globalPositions.indexOf(`${i + directions1[x]},${j + directions2[x]}`);
if (globalIndex > -1) {
globalPositions.splice(globalIndex, 1);
}
}
}
if (arr.length > 1) {
mineGroups.push(arr);
}
}
}
}
let pointer = 0;
let changed = false;
while (pointer < mineGroups.length) {
mineGroups = mineGroups.filter(n => n.length > 1);
const mineCell = mineGroups.find((mg) => mg.length - 1 === mg[0]);
if (mineCell) {
return { pos: mineCell[1], mine: true };
}
const safeCell = mineGroups.find((mg) => mg[0] <= 0);
if (safeCell) {
return { pos: safeCell[1], mine: false };
}
const mineGroup = mineGroups[pointer];
for (let i = mineGroups.length - 1; i >= 0; i -= 1) {
if (i === pointer) {
continue;
}
const otherMineGroup = mineGroups[i];
if (
mineGroup
.slice(1)
.reduce((acc, mg) => acc || otherMineGroup.indexOf(mg) > -1, false)
) {
const unionCells = mineGroup.slice(1).filter((c) => otherMineGroup.indexOf(c) >= 0);
const groupALeft = mineGroup.slice(1).filter(c => unionCells.indexOf(c) < 0);
const groupBLeft = otherMineGroup.slice(1).filter(c => unionCells.indexOf(c) < 0);
const mineSpaces = new Set(Array.from(new Set([...mineGroup.slice(1), ...otherMineGroup.slice(1)])).filter(c => unionCells.indexOf(c) < 0));
const safeSpaces = new Set(Array.from(new Set([...mineGroup.slice(1), ...otherMineGroup.slice(1)])).filter(c => unionCells.indexOf(c) < 0));
for (let x = groupALeft.length > 0 && groupBLeft.length > 0 ? 0 : 1; x <= Math.min(unionCells.length, mineGroup[0], otherMineGroup[0]); x += 1) {
const possibleMines = getCombinations(unionCells, x);
for (let y = 0; y < possibleMines.length; y += 1) {
const minesLeftInGroupA = mineGroup[0] - x;
const minesLeftInGroupB = otherMineGroup[0] - x;
if (minesLeftInGroupA > groupALeft.length || minesLeftInGroupB > groupBLeft.length) {
continue;
}
groupALeft.forEach(n => {
if (minesLeftInGroupA === 0) {
mineSpaces.delete(n)
} else if (minesLeftInGroupA === groupALeft.length) {
safeSpaces.delete(n);
} else {
mineSpaces.delete(n);
safeSpaces.delete(n);
}
});
groupBLeft.forEach(n => {
if (minesLeftInGroupB === 0) {
mineSpaces.delete(n)
} else if (minesLeftInGroupB === groupBLeft.length) {
safeSpaces.delete(n);
} else {
mineSpaces.delete(n);
safeSpaces.delete(n);
}
});
}
}
const mines = Array.from(mineSpaces);
const safes = Array.from(safeSpaces);
if (mines.length > 0) {
mineGroups.push([mines.length, ...mines]);
}
if (safes.length > 0) {
mineGroups.push([0, ...safes]);
}
}
}
pointer += 1;
}
if (globalPositions.length > 0) {
shuffle(globalPositions);
return { pos: globalPositions[0], mine: false };
}
const bestChancePositions = mineGroups.reduce((acc, n) => n[0] / (n.length - 1) < acc[0] / (acc.length - 1) ? n : acc);
return { pos: bestChancePositions[1], mine: false };
}
(function() {
'use strict';
// Your code here...
let autoplaying = GM_getValue('status');
let loseCounter = GM_getValue('lose') || 0;
let winCounter = GM_getValue('win') || 0;
let difficulty = GM_getValue('difficulty') || 3;
let delay = GM_getValue('delay') || 1000;
const section = document.querySelector('[action="neggsweeper.phtml"] [style="padding:7px;"]');
const controlPanel = document.createElement('div');
controlPanel.innerHTML = `<div>Autoplayer</div>
<button id="start" style="margin-top: 10px;">${autoplaying ? 'Stop' : 'Start'}</button><button id="reset">Reset</button>
<div>
<select id="difficulty"><option value="1">Easy</option><option value="2">Medium</option><option value="3">Hard</option></select>
<label title="don't set this too low">Click Delay (ms)<input value="${delay}" type="number" /></label>
</div>
<div>Win ${winCounter}</div>
<div>Lose ${loseCounter}</div>`;
section.prepend(controlPanel);
controlPanel.querySelector('#start').addEventListener('click', function (evt) {
evt.stopPropagation();
evt.preventDefault();
if (autoplaying) {
evt.currentTarget.innerText = 'Start';
GM_setValue('status', false);
autoplaying = false;
} else {
evt.currentTarget.innerText = 'Stop';
GM_setValue('status', true);
autoplaying = true;
setTimeout(() => {
window.location.reload();
}, 1000);
}
});
controlPanel.querySelector('#reset').addEventListener('click', function (evt) {
evt.stopPropagation();
evt.preventDefault();
GM_setValue('lose', 0);
GM_setValue('win', 0);
});
controlPanel.querySelector('select').selectedIndex = difficulty - 1;
controlPanel.querySelector('select').addEventListener('change', function (evt) {
difficulty = Number(evt.target.value);
GM_setValue('difficulty', difficulty);
});
controlPanel.querySelector('input').addEventListener('change', function (evt) {
delay = Number(evt.target.value);
GM_setValue('delay', delay);
});
try {
if (document.querySelector('[name="game_level"]')) {
if (document.querySelector('[action="neggsweeper.phtml"]').innerText.includes('You Lose!!!')) {
GM_setValue('lose', Number(loseCounter) + 1);
} else {
GM_setValue('win', Number(winCounter) + 1);
}
document.querySelector('[name="game_level"]').selectedIndex = difficulty || 3;
setTimeout(() => {
document.querySelector('[value="Play Again!!!"]').click();
}, 300);
} else if (document.querySelector('[src="//images.neopets.com/x/gn.gif"]') && autoplaying) {
const gameboard = buildGameboard();
const { pos, mine } = scanGameboard(gameboard);
const [x, y] = pos.split(',');
document.querySelectorAll('[bgcolor="white"]')[+x + 1].querySelectorAll('td')[y].style.border = mine ? '3px solid red' : '3px solid green';
setTimeout(() => {
do_move(`${y}-${x}`, { ctrlKey: mine });
}, random_in_range(delay, delay + 500));
} else if (document.querySelector('body').innerText.includes('Internal Server Error')) {
setTimeout(() => {
window.location.reload();
}, 2000);
}
} catch (e) {
console.log(e);
}
})();