// ==UserScript== // @name Neopets Pyramids Autoplayer // @namespace GreaseMonkey // @version 1.1 // @description Auto-plays Neopets Pyramids // @match *://www.neopets.com/games/pyramids/* // @author @willnjohnson // @grant none // @run-at document-end // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/576510/Neopets%20Pyramids%20Autoplayer.user.js // @updateURL https://update.greasyfork.icu/scripts/576510/Neopets%20Pyramids%20Autoplayer.meta.js // ==/UserScript== (async () => { // Made the main IIFE async to allow awaiting checkForErrorMessage const DELAY_GO_BACK = () => Math.floor(Math.random() * (1800 - 1050 + 1)) + 1050; const DELAY_RELOAD_PAGE = () => Math.floor(Math.random() * (1500 - 1000 + 1)) + 1000; const getElement = (selector, context = document) => { try { return context.querySelector(selector); } catch (e) { return null; } }; const SELECTORS = { errorMessageDiv: "div.errorMessage b", continuePlayingButton: "form[action='pyramids.phtml'] input[type='submit'][value='Continue Playing']", }; async function goBack() { await new Promise(resolve => setTimeout(resolve, DELAY_GO_BACK())); window.history.back(); } async function reloadPage() { await new Promise(resolve => setTimeout(resolve, DELAY_RELOAD_PAGE())); window.location.replace("https://www.neopets.com/games/pyramids/pyramids.phtml"); } async function checkForErrorMessage() { const errorBoldText = getElement(SELECTORS.errorMessageDiv); if (errorBoldText && errorBoldText.textContent.includes("Error: ") && errorBoldText.closest("div.errorMessage").textContent.includes("You have been directed to this page from the wrong place!")) { await goBack(); return true; } return false; // No error found or handled } if (await checkForErrorMessage()) { return; // Stop script execution if an error was found and handled } const R = /\/(\d+)_\w+\.gif/; const hl = el => { if(el){el.style.border='2px solid magenta'; el.style.boxSizing='border-box'; el.style.boxShadow='magenta 0 0 12px 3px';} }; const clickHl = (el,min=500,max=750) => { if(!el) return; if(["input[value='Play Pyramids Again!']", "a[href*='pyramids.phtml?action=collect']"].some(s=>el.matches?.(s))) { setTimeout(()=>el.click(),Math.random()*(max-min)+min); return; } if(el.tagName==='A'){const i=el.querySelector('img'); if(i){hl(i); setTimeout(()=>el.click(),Math.random()*(max-min)+min); return;}} hl(el); setTimeout(()=>el.click(),Math.random()*(max-min)+min); }; const faceRank = () => { for(const i of document.querySelectorAll("img[src*='mcards/']")){ if(i.src.includes("backs")||i.src.includes("empty")) continue; if(i.parentElement?.tagName==='A' && i.parentElement.href.includes("action=play")) continue; const m=R.exec(i.src); if(m) return +m[1]; } }; const playable = () => [...document.querySelectorAll("a[href*='action=play&position=']")] .map(a=>{ const p=/position=(\d+)/.exec(a.href), i=a.querySelector("img[src*='mcards/']"), r=i&&R.exec(i.src); return p&&r?{pos:+p[1],rank:+r[1],el:a}:null; }).filter(Boolean); const adj = r => [r>2?r-1:14, r<14?r+1:2]; const maxChain = (face,cards) => { const valid = cards.filter(c => { const [l,h] = adj(face); return c.rank===l||c.rank===h; }); if(!valid.length) return 0; return 1+Math.max(...valid.map(c => maxChain(c.rank, cards.filter(x=>x.pos!==c.pos)))); }; // Keep track if any action was taken let actionTaken = false; if(document.querySelector("input[value='Play Pyramids Again!']")) { clickHl(document.querySelector("input[value='Play Pyramids Again!']"),700,800); actionTaken = true; } if(!actionTaken && document.querySelector("a[href*='pyramids.phtml?action=collect']")) { clickHl(document.querySelector("a[href*='pyramids.phtml?action=collect']"),700,800); actionTaken = true; } // Handle "Continue Playing" screen if (!actionTaken && document.querySelector(SELECTORS.continuePlayingButton)) { clickHl(document.querySelector(SELECTORS.continuePlayingButton), 700, 800); actionTaken = true; } const face=faceRank(); if(!actionTaken && !face) { // If no other action and no face card found, it might be an invalid state. // This could happen if the game hasn't loaded properly, or is between states. // We will let the final reload handle this for robustness. } if(!actionTaken && face) { // Only proceed with game logic if no prior action was taken const plays=playable(), [left,right]=adj(face); const valid=plays.filter(c=>c.rank===left||c.rank===right); if(valid.length){ let best=valid[0],bestScore=0; for(const m of valid){ // CORRECTED: changed 'c' to 'x' in filter for maxChain const score=maxChain(m.rank, plays.filter(x=>x.pos!==m.pos)); if(score>bestScore){bestScore=score; best=m;} } clickHl(best.el,500,1500); actionTaken = true; } if(!actionTaken){ // Only try to draw if no other moves were found const draw=document.querySelector("a[href='pyramids.phtml?action=draw']"); if(draw) { clickHl(draw.querySelector('img')||draw,550,600); actionTaken = true; } } } if (!actionTaken) { await reloadPage(); } })();