Last active
January 20, 2025 10:42
-
-
Save FWDekker/5ab26c551443bd6e4a30e965c0561c65 to your computer and use it in GitHub Desktop.
Stimulation Clicker Automator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Stimulation Clicker Automator | |
// | |
// Automatically plays the entire game Stimulation Clicker by Neal Agarwal. | |
// See https://neal.fun/stimulation-clicker/ | |
// | |
// Copy and paste the entire following code into your browser's console, and press Enter. The game will then play | |
// itself. To stop the tool, simply write the command `scaStop()` into the console. To start it again, simply use the | |
// command `scaStart()`. | |
// | |
// This tool plays all aspects of the entire game, except that it DOES NOT: | |
// * buy the final two upgrades | |
// * buy items in the item shop | |
// | |
// Emails are responded to automatically. As a result, you cannot manually check the inbox while the script is running, | |
// because the script immediately closes the inbox. To work around this, temporarily set `emailEnabled = false`. After | |
// you're done, you can once again set `emailEnabled = true`. | |
/* | |
* Settings | |
*/ | |
// Number of milliseconds between each loop of the main cycle | |
var cycleTime = 25; | |
// Number of cycles between "complex" actions | |
var slowActionSpeed = 20; | |
// Questions and answers to Duo's questions | |
var duoAnswers = { | |
"How do you say goodbye in Welsh?": "Hwyl far", | |
"Owls are commonly viewed as harbingers of:": "Death", | |
"Which language is spoken by more people?": "Mandarin", | |
"Which one of these is not a German word?": "Gemealez", | |
"In Japanese, which word represents the sound of silence?": "Shiin", | |
"The most common starting letter in English is:": "S", | |
"Which of these used to be a part of the English alphabet?": "&", | |
"Which of these is not a fictional language?": "Yurok", | |
"There's only one English word that ends in": "mt", | |
"What is the plural of octopus": "Octopi or octopuses", | |
"Owls can spot prey up to ___ away": "Half a mile", | |
"Past tense of sneak?": "Sneaked or snuck", | |
"Which of these words have French origin?": "Jury", | |
"Most common letter in English": "E", | |
"Hiraeth, deep longing for home, is": "Welsh", | |
"How many languages exist today?": "Around 7,100", | |
"Which language has about 421 words for snow?": "Scottish", | |
"Which is NOT an extinct language?": "Esperanto", | |
"What is an umlaut?": "¨", | |
"What does \"fruits de mer\" mean?": "Seafood", | |
"Which is NOT a romance language?": "Polish", | |
"Which word appears in the Oxford English Dictionary?": "All of the above", | |
"Which is NOT a word for potato?": "Potate", | |
"\"Saudade\" is a Portuguese word for": "Nostalgic longing", | |
"What is Duo's favorite word?": "Vengence", | |
"Which word rhymes with \"trough\"?": "Rough", | |
"What is an owl pellet?": "Undigested prey", | |
"In medieval Europe, what creature were owls often associated with?": "Witches", | |
"Owls have double the ___ of humans": "Vertebrae", | |
"A group of pugs is called a:": "A grumble", | |
"What is the term for a word that is the same backwards and forwards?": "Palindrome", | |
"How many degrees can an owl rotate its head?": "270 degrees", | |
"What is the only letter that doesn't appear in any U.S. state name?": "Q", | |
"In which language is 'Gesundheit' commonly used after someone sneezes?": "German", | |
"Which of these words is its own opposite?": "Dust", | |
"What's the only letter that doesn't appear in the periodic table": "J", | |
"Which of these is spelled correctly?": "Minuscule", | |
"Which of these words changes its meaning when capitalized?": "March/march", | |
}; | |
// List of email actions that should NOT be clicked | |
var mailExcludeList = ["Thank them for the opportunity", "Donate 2,000 stimulation"]; | |
// List of upgrades that should NOT be bought | |
var upgradeExcludeList = ["Subway Surfers Wormhole", "Go to the Ocean"]; | |
// Maximum price to spend on repeatable upgrades while you have less than `upgradeMaxCostThreshold` stimulation | |
var upgradeMaxCost = {"Bouncing DVD": 125, "Hydraulic Speed": 5100}; | |
// Threshold below which `upgradeMaxCost` applies | |
var upgradeMaxCostThreshold = 1000000; | |
// Threshold below which certain stocks are bought, and above which certain stocks are sold | |
var stockThreshold = {"Tesla": 550, "Bitcoin": 20000}; | |
// `true` if timing performance should be written to the console log | |
var logPerformance = false; | |
// `true` if responding to emails is enabled | |
var emailEnabled = true; | |
/* | |
* Functions | |
*/ | |
function sleep(ms) { | |
return new Promise(resolve => setTimeout(resolve, ms)); | |
} | |
function getElement(className, root = document) { | |
return root.getElementsByClassName(className)[0]; | |
} | |
function getVisibleElement(className, root = document) { | |
const element = getElement(className, root); | |
if (element === undefined || element.className.includes("hide") || element.offsetParent === null) return undefined; | |
else return element; | |
} | |
function getElements(className, root = document) { | |
return Array.from(root.getElementsByClassName(className)); | |
} | |
function parseCommaInt(text) { | |
return parseInt(text.replaceAll(",", ""), 10); | |
} | |
function getStimulation() { | |
const span = getElement("main-stat-num"); | |
if (span === undefined) return 0; | |
else return parseCommaInt(span.innerText); | |
} | |
var scaToggle = true; | |
function scaStop() { | |
scaToggle = false; | |
} | |
async function scaStart() { | |
// Cached DOM elements | |
const stimulationButton = getElement("main-btn"); | |
let hydraulicStartButton = undefined; | |
let hydraulicCollectButton = undefined; | |
let tamagotchiFeedButton = undefined; | |
let duolingoBox = undefined; | |
let duolingoComplete = false; | |
let stockSelect = undefined; | |
let stockCountSpan = undefined; | |
let stockPriceSpan = undefined; | |
let stockBuyButton = undefined; | |
let stockSellButton = undefined; | |
let stockMadeLoss = false; | |
let stockMadeLossBought = false; | |
// Shut down other instances | |
scaToggle = false; | |
await sleep(5 * cycleTime); | |
// Start | |
scaToggle = true; | |
let cycleCounter = 0; | |
let cycleStartDate = undefined; | |
if (logPerformance) cycleStartDate = performance.now(); | |
while (true) { | |
/// Preamble | |
if (!scaToggle) return; | |
cycleCounter = (cycleCounter + 1) % slowActionSpeed; | |
if (logPerformance && cycleCounter === 0) { | |
const endDate = performance.now(); | |
console.log(`Time per cycle: ${(endDate - cycleStartDate) / 10}ms.`); | |
cycleStartDate = endDate; | |
} | |
/// Income | |
// Stim | |
stimulationButton.click(); | |
// Level up | |
if (cycleCounter === 0) getVisibleElement("collect")?.click(); | |
// Hydraulic press | |
if (hydraulicStartButton === undefined) hydraulicStartButton = getVisibleElement("press-btn"); | |
hydraulicStartButton?.click(); | |
if (hydraulicCollectButton === undefined) hydraulicCollectButton = getVisibleElement("press-collect"); | |
hydraulicCollectButton?.click(); | |
// Lootbox | |
getElements("loot-box-target").forEach(it => it.click()); | |
// Powerups | |
getElements("powerup").filter(it => !it.src.includes("serene")).forEach(it => it.click()); | |
// Tamagotchi | |
if (tamagotchiFeedButton === undefined) tamagotchiFeedButton = getVisibleElement("action-btn"); | |
tamagotchiFeedButton?.click(); | |
getElements("egg-wrapper").forEach(it => it.click()); | |
// DuoLingo | |
if (duolingoBox === undefined) duolingoBox = getVisibleElement("question"); | |
if (duolingoBox !== undefined && !duolingoComplete) { | |
const question = getElement("question-text")?.innerText; | |
if (question === undefined) { | |
duolingoComplete = true; | |
} else { | |
const answer = duoAnswers[question]; | |
getElements("question-choice").filter(it => it.innerText === answer).forEach(it => it.click()); | |
} | |
} | |
// Emails | |
if (emailEnabled && cycleCounter === 0) { | |
getElement("notification")?.click(); | |
getElements("question-container").forEach(it => { | |
Array.from(it.getElementsByTagName("input")).forEach(it => it.click()); | |
}); | |
const mailActionButton = getVisibleElement("action"); | |
if (mailActionButton !== undefined && !mailExcludeList.includes(mailActionButton.innerText)) | |
mailActionButton.click(); | |
getVisibleElement("close")?.click(); | |
} | |
/// Expenses | |
// Upgrades | |
if (cycleCounter === 0) { | |
getElements("upgrade") | |
.filter(it => { | |
const stimulation = getStimulation(); | |
const name = getElement("upgrade-name", it).innerText; | |
const cost = parseCommaInt(getElement("upgrade-cost", it).innerText.slice(6)); | |
return !upgradeExcludeList.includes(name) && | |
!(stimulation < upgradeMaxCostThreshold && | |
Object.keys(upgradeMaxCost).includes(name) && | |
cost > upgradeMaxCost[name]); | |
}) | |
.forEach(it => it.click()); | |
} | |
// Stock trading | |
if (stockSelect === undefined) { | |
stockSelect = getElement("stock-select"); | |
stockPriceSpan = getElement("last-price"); | |
stockCountSpan = getElement("stock-shares"); | |
stockBuyButton = getElement("stock-buy"); | |
stockSellButton = getElement("stock-sell"); | |
} | |
if (stockSelect !== undefined) { | |
const type = (stockSelect.options.length === 4) ? "Tesla" : "Bitcoin"; | |
const threshold = stockThreshold[type]; | |
if (stockSelect.value !== type) { | |
stockSelect.value = type; | |
stockSelect.dispatchEvent(new Event("change", {bubbles: true})); | |
} | |
const shareValue = parseCommaInt(stockPriceSpan.innerText.slice(1)); | |
const shareCount = parseCommaInt(stockCountSpan.innerText); | |
const canBuy = getStimulation() >= shareValue | |
const canSell = shareCount > 0 | |
if (!stockMadeLoss && type === "Bitcoin") { | |
if (!stockMadeLossBought) { | |
if (shareValue > threshold && canBuy) { | |
stockBuyButton.click(); | |
stockMadeLossBought = true; | |
} | |
} else { | |
if (shareValue < threshold && canSell) { | |
stockSellButton.click(); | |
stockMadeLoss = true; | |
} | |
} | |
} else { | |
if (shareValue < threshold && canBuy) stockBuyButton.click(); | |
else if (shareValue > threshold && canSell) stockSellButton.click(); | |
} | |
} | |
/// /Loop | |
await sleep(cycleTime); | |
} | |
} | |
scaStart().then(() => console.log("Automator has stopped.")); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment