|
2025-11-09 18:16
조회: 62,450
추천: 87
7주년 카드 메모리 게임 자동화 (11/16 스크립트 멈추던 문제 해결)인벤에 글을 처음 써보는 거라 가독성이 조금 떨어질 수도 있습니다. 요즘 카드 메모리 게임이 너무 귀찮아서, 직접 자동화 스크립트를 만들어봤어요. 예전에 플레이크 자동 스크립트를 써보신 분이라면 쉽게 따라하실 수 있을 겁니다. 사용 방법 (마이크로소프트 엣지 기준)
제가 테스트할 때는 별다른 버그나 문제는 없었습니다. 혹시 사용 중 오류나 비정상 동작이 발견되면 댓글로 알려주시면 감사하겠습니다. 수정사항 1. 새로운 게임 시작시 웹페이지가 멈추는 문제가 있어서 코드를 수정했습니다.(2025/11/12) 2. 새로운 게임 시작시 카드가 늦게 생성되면 카드가 없다고 판단해서 게임을 멈춰버리는 문제가 있었습니다. 그래서 한번 실패하더라도 재시도하는 로직을 넣어서 스크립트가 멈춰버리는 문제를 해결했습니다. (2025/11/16) (function () { if (window.__lostArkCardHelperInstalled) return; window.__lostArkCardHelperInstalled = true; (function installCardMemoCore() { if (window.__cardMemoInstalled) return; window.__cardMemoInstalled = true; window.__cardMemo = window.__cardMemo || {}; window.__cardDone = window.__cardDone || []; const API_PATH = "/Promotion/Card/GetCard251105"; (function hookXHR() { const OriginalXHR = window.XMLHttpRequest; function WrappedXHR() { const xhr = new OriginalXHR(); let method = null; let url = null; let body = null; const origOpen = xhr.open; xhr.open = function (m, u) { method = m; url = u; return origOpen.apply(xhr, arguments); }; const origSend = xhr.send; xhr.send = function (data) { body = data; xhr.addEventListener("load", function () { try { if (!url || !method) return; const fullUrl = new URL(url, location.href); if (!fullUrl.pathname.endsWith(API_PATH)) return; if (method.toUpperCase() !== "POST") return; let indexStr = null; try { if (typeof body === "string") { const m = body.match(/(?:^|&)index=([^&]+)/); if (m) indexStr = decodeURIComponent(m[1]); } else if (body instanceof FormData || body instanceof URLSearchParams) { indexStr = body.get("index"); } } catch (e) {} try { const text = xhr.responseText; if (!text) return; const json = JSON.parse(text); if (!json) return; if (json.img != null) { const idx = indexStr != null ? Number(indexStr) : null; if (idx != null && !Number.isNaN(idx) && idx >= 0 && idx < 18) { const imgUrl = new URL(json.img, "https://cdn-lostark.game.onstove.com").href; window.__cardMemo[idx] = imgUrl; } } if (json.isMatch && Array.isArray(json.index)) { window.__cardDone = [...(window.__cardDone || []), ...json.index]; } if (json.complete) { window.__cardDone = []; window.__cardMemo = {}; } } catch (e) {} } catch (e) {} }); return origSend.apply(xhr, arguments); }; return xhr; } WrappedXHR.prototype = OriginalXHR.prototype; window.XMLHttpRequest = WrappedXHR; if (!window.__xhrRestoreInstalled) { window.__xhrRestoreInstalled = true; window.addEventListener("beforeunload", function () { try { window.XMLHttpRequest = OriginalXHR; } catch (e) {} }); } })(); })(); (function installAutoPlayer() { if (window.__lostArkAutoPlayerInstalled) return; window.__lostArkAutoPlayerInstalled = true; if (!window.__cardMemo) window.__cardMemo = {}; if (!window.__cardDone) window.__cardDone = []; window.__cardBusy = false; window.__cardAutoPlayRunning = false; window.__cardHasPlayedOnce = window.__cardHasPlayedOnce || false; function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); } function getCardElement(index) { return document.querySelector(`section#grid button.card[data-idx="${index}"]`); } function isBoardReady() { return !!document.querySelector('section#grid button.card[data-idx="0"]'); } function isStateEmpty() { const memo = window.__cardMemo || {}; const done = window.__cardDone || []; return Object.keys(memo).length === 0 && done.length === 0; } function isClickable(el) { if (!el) return false; if (el.disabled) return false; const rect = el.getBoundingClientRect(); if (rect.width === 0 || rect.height === 0) return false; return true; } async function waitForBoardReadyFull(timeout = 5000) { const start = Date.now(); while (Date.now() - start < timeout) { const all = document.querySelectorAll('section#grid button.card[data-idx]'); if (all && all.length >= 18) return true; await sleep(100); } return false; } async function ensureBoardReady() { if (!await waitForBoardReadyFull(5000)) return false; return true; } async function clickWhenReady(index, timeout = 4000) { const start = Date.now(); while (Date.now() - start < timeout) { const el = getCardElement(index); if (isClickable(el)) { el.click(); return true; } await sleep(50); } return false; } function findMatchedPairFromMemory() { const memo = window.__cardMemo || {}; const done = new Set(window.__cardDone || []); const map = new Map(); for (let i = 0; i < 18; i++) { const img = memo[i]; if (!img) continue; if (done.has(i)) continue; if (!map.has(img)) map.set(img, []); map.get(img).push(i); } for (const [img, idxs] of map.entries()) { if (idxs.length >= 2) return idxs.slice(0, 2); } return null; } function findOneUnknownCard(exceptIdx = null) { const memo = window.__cardMemo || {}; const done = new Set(window.__cardDone || []); for (let i = 0; i < 18; i++) { if (i === exceptIdx) continue; if (done.has(i)) continue; if (!memo[i]) return i; } return null; } function findAnyUnmatchedCard(exceptIdx = null) { const done = new Set(window.__cardDone || []); for (let i = 0; i < 18; i++) { if (i === exceptIdx) continue; if (done.has(i)) continue; return i; } return null; } function findSameImageCard(imgUrl, exceptIdx) { const memo = window.__cardMemo || {}; const done = new Set(window.__cardDone || []); for (let i = 0; i < 18; i++) { if (i === exceptIdx) continue; if (done.has(i)) continue; if (memo[i] === imgUrl) return i; } return null; } function isGameComplete() { const done = new Set(window.__cardDone || []); return done.size >= 18; } async function playOneStep() { if (window.__cardBusy) return true; if (!(await ensureBoardReady())) return false; if (isGameComplete()) return false; window.__cardBusy = true; try { let pair = findMatchedPairFromMemory(); if (pair) { const ok1 = await clickWhenReady(pair[0]); if (!ok1) return false; await sleep(600); const ok2 = await clickWhenReady(pair[1]); if (!ok2) return false; await sleep(1200); return true; } let firstIndex = findOneUnknownCard(); if (firstIndex == null) { firstIndex = findAnyUnmatchedCard(); if (firstIndex == null) return false; } const okFirst = await clickWhenReady(firstIndex); if (!okFirst) return false; await sleep(600); const imgFirst = (window.__cardMemo || {})[firstIndex]; let secondIndex = null; if (imgFirst) secondIndex = findSameImageCard(imgFirst, firstIndex); if (secondIndex == null) { secondIndex = findOneUnknownCard(firstIndex); if (secondIndex == null) secondIndex = findAnyUnmatchedCard(firstIndex); } if (secondIndex != null) { const okSecond = await clickWhenReady(secondIndex); if (!okSecond) return false; await sleep(1200); } return true; } finally { window.__cardBusy = false; } } async function autoPlayAll() { if (window.__cardAutoPlayRunning) return; window.__cardAutoPlayRunning = true; window.__cardHasPlayedOnce = true; while (window.__cardAutoPlayRunning) { const moved = await playOneStep(); if (!moved || isGameComplete()) break; } window.__cardAutoPlayRunning = false; } function stopAutoPlay() { window.__cardAutoPlayRunning = false; } window.autoPlayAll = autoPlayAll; window.stopAutoPlay = stopAutoPlay; if (!window.__watchInstalled) { window.__watchInstalled = true; (async function watchForNewGame() { let pendingToken = null; while (true) { if (window.__cardHasPlayedOnce && !window.__cardAutoPlayRunning && isBoardReady() && isStateEmpty() && !pendingToken) { const token = {}; pendingToken = token; await sleep(3000); if (pendingToken === token && window.__cardHasPlayedOnce && !window.__cardAutoPlayRunning && isBoardReady() && isStateEmpty()) { autoPlayAll(); } if (pendingToken === token) pendingToken = null; } await sleep(1000); } })(); } autoPlayAll(); })(); })();
|





