// ==UserScript==
// @name Snow Wars Helper
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Helps with snow wars
// @author Shiba
// @match https://www.grundos.cafe/games/snowwars/*
// @grant none
// ==/UserScript==
(function () {
'use strict';
const BOARD_WIDTH = 8;
const cellNumberRegex = /submitSnowWarsAttack\(event,\s*'(\d+)'\)/;
const OBJECTS = [
{
name: 'SNOWMAN',
gifs: ['2_1.gif', '2_2.gif', '2_3.gif', '2_4.gif'],
offsets: [[0, 1, 8, 9]],
},
{
name: 'CANNON',
gifs: ['5_1.gif', '5_2.gif'],
offsets: [[0, 8]],
},
{
name: 'SNOWBALL',
gifs: ['6_1.gif', '6_2.gif'],
offsets: [[0, 8]],
},
{
name: 'SLED',
gifs: ['7_1.gif', '7_2.gif', '7_3.gif'],
offsets: [[0, 1, 2]],
},
{
name: 'CASTLE',
gifs: ['8_1.gif', '8_2.gif', '8_3.gif', '8_4.gif'],
offsets: [[0, 1, 8, 9]],
},
];
main();
function main() {
const clickableAnchors = getClickableAnchors();
if (!clickableAnchors.length) return;
const squares = getTopBoardSquares();
const partialCell = findPartialTarget(squares, clickableAnchors);
if (partialCell !== null) {
highlightPartial(partialCell, squares);
return;
}
highlightHeatmap(clickableAnchors, squares);
}
function getClickableAnchors() {
const anchors = document.querySelectorAll('.snowwars-board-container a[href="#"]');
return Array.from(anchors)
.map((a) => {
const m = (a.getAttribute('onclick') || '').match(cellNumberRegex);
return m ? { anchor: a, cell: parseInt(m[1], 10) } : null;
})
.filter(Boolean);
}
function getTopBoardSquares() {
const top = document.querySelector('.snowwars-board-container.flex.flex-column.margin-auto');
if (!top) return [];
const squares = [];
top.querySelectorAll('.flex').forEach((row) => {
Array.from(row.querySelectorAll('.snowwars-spot'))
.filter((s) => !s.classList.contains('snowwars-axis'))
.forEach((s) => {
const bg = s.style.backgroundImage || '';
const hasAnchor = !!s.querySelector('a[href="#"]');
if (!bg && !hasAnchor) return;
s.style.backgroundColor = '';
squares.push(s);
});
});
return squares;
}
function findPartialTarget(squares, clickableAnchors) {
for (const { gifs, offsets } of OBJECTS) {
const shape = offsets[0];
const foundCount = shape.filter((off, i) =>
squares.some((s) => (s.style.backgroundImage || '').includes(gifs[i])),
).length;
if (foundCount === shape.length) continue;
for (let i = 0; i < gifs.length; i++) {
const idx = squares.findIndex((s) => (s.style.backgroundImage || '').includes(gifs[i]));
if (idx === -1) continue;
const anchorIdx = idx - shape[i];
const shapeCells = shape.map((off) => anchorIdx + off);
const missing = findMissingCell(shapeCells, squares, gifs);
if (missing !== null && clickableAnchors.some((c) => c.cell === missing)) {
return missing;
}
}
}
return null;
}
function findMissingCell(indices, squares, objectGifs) {
for (const i of indices) {
if (i < 0 || i >= squares.length) continue;
const bg = squares[i].style.backgroundImage || '';
if (!objectGifs.some((g) => bg.includes(g))) return i;
}
return null;
}
function highlightPartial(cell, squares) {
squares[cell].style.backgroundColor = 'rgba(255,0,0,1)';
}
function isSunk(obj, squares) {
return obj.gifs.every((gif) =>
squares.some((sq) => (sq.style.backgroundImage || '').includes(gif)),
);
}
function highlightHeatmap(clickableAnchors, squares) {
const remainingObjects = OBJECTS.filter((o) => !isSunk(o, squares));
if (remainingObjects.length === 0) {
return;
}
const N = squares.length;
const heat = new Array(N).fill(0);
const unknown = new Set(clickableAnchors.map((c) => c.cell));
const isUnknown = (i) => unknown.has(i);
const isHit = (i) => {
const bg = squares[i].style.backgroundImage || '';
return OBJECTS.some((o) => o.gifs.some((g) => bg.includes(g)));
};
for (const { gifs, offsets } of remainingObjects) {
const shape = offsets[0];
const deltas = shape.map((o) => ({
off: o,
dr: Math.floor(o / BOARD_WIDTH),
dc: o % BOARD_WIDTH,
}));
for (let anchor = 0; anchor < N; anchor++) {
let fits = true;
for (const { off, dr, dc } of deltas) {
const idx = anchor + off;
if (idx < 0 || idx >= N) {
fits = false;
break;
}
const r0 = Math.floor(anchor / BOARD_WIDTH),
c0 = anchor % BOARD_WIDTH;
const r1 = Math.floor(idx / BOARD_WIDTH),
c1 = idx % BOARD_WIDTH;
if (r1 - r0 !== dr || c1 - c0 !== dc) {
fits = false;
break;
}
if (!(isHit(idx) || isUnknown(idx))) {
fits = false;
break;
}
if (isHit(idx)) {
const bg = squares[idx].style.backgroundImage;
if (!gifs.some((g) => bg.includes(g))) {
fits = false;
break;
}
}
}
if (!fits) continue;
deltas.forEach(({ off }) => {
const i = anchor + off;
if (isUnknown(i)) heat[i]++;
});
}
}
const maxH = Math.max(...heat);
if (maxH === 0) {
return;
}
clickableAnchors.forEach(({ cell }) => {
const score = heat[cell];
if (score === 0) return;
const alpha = 0.2 + 0.6 * (score / maxH);
squares[cell].style.backgroundColor = `rgba(255,0,0,${alpha})`;
});
}
})();