Instantly share code, notes, and snippets.
Created
January 7, 2025 05:03
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save lucacastelnuovo/590bc5732a8c10d1e014a1dbd8e82f49 to your computer and use it in GitHub Desktop.
Cookie Clicker - Game State Modifer
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
/* Constants */ | |
const ENCODING_MARKER = "!END!"; | |
const ENCODING_MARKER_ENCODED = "%21END%21"; | |
const COOKIE_PATTERN = /\d+;\d+;\d+;0;\d+;0;0;/; | |
const TIMESTAMP_PATTERN = /;\d{13};/; | |
/* Error Helper */ | |
const createError = (message, type = "GeneralError") => { | |
const error = new Error(message); | |
error.name = "GameStateError"; | |
error.type = type; | |
return error; | |
}; | |
/** | |
* Decode an encoded game state string | |
* @param {string} encoded - The encoded game state | |
* @returns {string} The decoded game state | |
*/ | |
const decode = (encoded) => { | |
if (!encoded) { | |
throw createError("Empty game state provided", "ValidationError"); | |
} | |
if (!encoded.endsWith(ENCODING_MARKER_ENCODED)) { | |
throw createError( | |
"Invalid game state format: missing end marker", | |
"ValidationError", | |
); | |
} | |
try { | |
let result = decodeURIComponent(encoded); | |
result = result.replace(ENCODING_MARKER, ""); | |
result = atob(result); | |
if (!result.startsWith("2.052||")) { | |
throw createError( | |
"Invalid game state version or format", | |
"ValidationError", | |
); | |
} | |
return result; | |
} catch (error) { | |
if (error.name === "GameStateError") throw error; | |
throw createError( | |
`Failed to decode game state: ${error.message}`, | |
"DecodingError", | |
); | |
} | |
}; | |
/** | |
* Encode a game state string | |
* @param {string} decoded - The decoded game state | |
* @returns {string} The encoded game state | |
*/ | |
const encode = (decoded) => { | |
if (!decoded) { | |
throw createError("Empty game state provided", "ValidationError"); | |
} | |
try { | |
return btoa(decoded) + ENCODING_MARKER_ENCODED; | |
} catch (error) { | |
throw createError( | |
`Failed to encode game state: ${error.message}`, | |
"EncodingError", | |
); | |
} | |
}; | |
/** | |
* Extract a template from a game state | |
* @param {string} state - The game state to extract from | |
* @returns {string} The template state | |
*/ | |
const extractBaseState = (state) => { | |
if (!COOKIE_PATTERN.test(state)) { | |
throw createError("Invalid cookie pattern in state", "ValidationError"); | |
} | |
try { | |
return state | |
.replace(COOKIE_PATTERN, "COOKIES;COOKIES;COOKIES;0;COOKIES;0;0;") | |
.replace(TIMESTAMP_PATTERN, ";TIMESTAMP;"); | |
} catch (error) { | |
throw createError( | |
`Failed to extract base state: ${error.message}`, | |
"ExtractionError", | |
); | |
} | |
}; | |
/** | |
* Modify a game state with new cookie values | |
* @param {string} state - The game state | |
* @param {number} amount - The new cookie amount | |
* @returns {string} The modified encoded game state | |
*/ | |
const modifyState = (state, amount) => { | |
if (typeof amount !== "number" || amount < 0) { | |
throw createError("Invalid cookie amount", "ValidationError"); | |
} | |
try { | |
return extractBaseState(state) | |
.replace(/TIMESTAMP/, Date.now()) | |
.replace(/COOKIES/g, amount); | |
} catch (error) { | |
throw createError( | |
`Failed to modify state: ${error.message}`, | |
"ModificationError", | |
); | |
} | |
}; | |
/** | |
* Get current cookie count from a game state | |
* @param {string} state - The game state | |
* @returns {number} The current cookie count | |
*/ | |
const getCookieCount = (state) => { | |
try { | |
const match = state.match(COOKIE_PATTERN); | |
if (!match) { | |
throw createError( | |
"Could not find cookie count in state", | |
"ExtractionError", | |
); | |
} | |
return parseInt(match[0].split(";")[0]); | |
} catch (error) { | |
throw createError( | |
`Failed to get cookie count: ${error.message}`, | |
"ExtractionError", | |
); | |
} | |
}; | |
try { | |
const args = process.argv.slice(2); | |
const usage = "Usage: node cookie.js <exported-save> <amount-to-add>"; | |
if (args.length !== 2) { | |
console.error(usage); | |
process.exit(1); | |
} | |
const [encodedState, amountStr] = args; | |
const amount = parseInt(amountStr); | |
if (isNaN(amount)) { | |
console.error("Error: Amount must be a number"); | |
console.error(usage); | |
process.exit(1); | |
} | |
/* Decode provided state */ | |
const currentState = decode(encodedState); | |
const currentCookies = getCookieCount(currentState); | |
/* Add specified amount of cookies */ | |
const newState = modifyState(currentState, currentCookies + amount); | |
const newEncodedState = encode(newState); | |
const newCookies = getCookieCount(newState); | |
/* Output */ | |
console.log("\n=== Cookie State Modifier ==="); | |
console.log(`Previous Amount: ${currentCookies}`); | |
console.log(`Added Amount: ${amount}`); | |
console.log(`New Cookies: ${newCookies}`); | |
console.log("-".repeat(40)); | |
console.log(newEncodedState); | |
console.log("=".repeat(40)); | |
} catch (error) { | |
console.error(`Error: ${error.message}`); | |
process.exit(1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment