// ==UserScript== // @name Gooboo辅助 (v4.8.5 画廊贪婪消除版) // @license MIT // @namespace http://tampermonkey.net/ // @homepage https://greasyfork.org/zh-CN/scripts/481441-gooboo辅助 // @version 4.8.5 // @description 全自动画廊策略:修复普通消除急躁引爆问题(现会贪婪吸附全图同色凑极大团)、修复特殊方块洗牌发呆Bug、全局死局自救 // @author jasmineamber & Gemini // @match https://www.ggpop.com/gooboo/ // @icon https://www.google.com/s2/favicons?sz=64&domain=github.io // @require https://cdn.jsdelivr.net/npm/bignumber.js@9.1.2/bignumber.min.js // @require https://openuserjs.org/src/libs/sizzle/GM_config.js // @grant GM.getValue // @grant GM.setValue // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_notification // @downloadURL https://update.greasyfork.icu/scripts/565259/Gooboo%E8%BE%85%E5%8A%A9%20%28v485%20%E7%94%BB%E5%BB%8A%E8%B4%AA%E5%A9%AA%E6%B6%88%E9%99%A4%E7%89%88%29.user.js // @updateURL https://update.greasyfork.icu/scripts/565259/Gooboo%E8%BE%85%E5%8A%A9%20%28v485%20%E7%94%BB%E5%BB%8A%E8%B4%AA%E5%A9%AA%E6%B6%88%E9%99%A4%E7%89%88%29.meta.js // ==/UserScript== (async function() { 'use strict'; let gmc = new GM_config( { 'id': 'gooboo', 'css': '#gooboo_section_0 { display: flex; flex-flow: row wrap } #gooboo_section_0 .config_var { } .section_header, .section_desc { flex: 1 100% }', 'title': 'Gooboo辅助设置', 'fields': { 'ignore_skills': { 'type': 'hidden', 'default': '', 'section': ['部落', '忽略技能列表'] }, 'ignore_mdi-knife-military': { 'label': '匕首', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-medical-bag': { 'label': '衬衫', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-flare': { 'label': '守护天使', 'type': 'checkbox', 'default': true, 'labelPos': 'right' }, 'ignore_mdi-bone': { 'label': '一杯牛奶', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-octagram-outline': { 'label': '星盾', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-sword': { 'label': '长剑', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-shoe-cleat': { 'label': '靴子', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-clover': { 'label': '三叶草', 'type': 'checkbox', 'default': true, 'labelPos': 'right' }, 'ignore_mdi-stomach': { 'label': '肝脏', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-fire': { 'label': '火球', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-campfire': { 'label': '营火', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-snowflake': { 'label': '雪花', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-emoticon-devil': { 'label': '压迫者', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-laser-pointer': { 'label': '腐败的眼睛', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-shimmer': { 'label': '巫师帽', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-pentagram': { 'label': '红色杖', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-timer': { 'label': '坏了的秒表', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-pillar': { 'label': '大理石柱', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-looks': { 'label': '彩虹之杖', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-bottle-tonic-skull': { 'label': '毒素', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, 'ignore_mdi-water-opacity': { 'label': '净化泉', 'type': 'checkbox', 'default': false, 'labelPos': 'right' }, }, 'events': { 'save': function () { GM_notification({title: GM_info.script.name, text: `保存成功`, timeout: 3500}); } } }); var menu_ALL = [ ['menu_study_math', '[学校]数学-学习', '[学校]数学-学习', false, math, '1'], ['menu_study_literature', '[学校]文学-学习', '[学校]文学-学习', false, literature, '2'], ['menu_study_history', '[学校]历史-学习', '[学校]历史-学习', false, history, '3'], ['menu_exam_math', '[学校]数学-考试', '[学校]学习数学-考试', "other", exam_math, '4'], ['menu_exam_literature', '[学校]文学-考试', '[学校]文学-考试', "other", exam_literature, '5'], ['menu_exam_history', '[学校]历史-考试', '[学校]历史-考试', "other", exam_history, '6'], ['menu_auto_skill', '[部落]使用技能', '[部落]使用技能', false, auto_skill, 'a'], ['menu_auto_harvest', '[农场]循环播种', '[农场]循环播种', false, auto_harvest, 'z'], ['menu_auto_water', '[农场]自动浇水', '[农场]自动浇水', false, auto_water, 'w'], ['menu_auto_shape', '[画廊]自动形状', '[画廊]自动形状', false, auto_shape, 's'], ['menu_auto_shape_water', '[组合]自动形状+浇水', '[组合]自动形状+浇水', false, auto_shape_water, 'c'], ], menu_ID = []; registerMenuCommand(); async function registerMenuCommand() { for (let i = 0; i < menu_ID.length; i++){ GM_unregisterMenuCommand(menu_ID[i]); await sleep(100) } menu_ID[0] = GM_registerMenuCommand(`ℹ️ 设置`, function() {gmc.open()}) await sleep(100) for (let i = 0;i < menu_ALL.length; i++){ let icon = '✅' if (menu_ALL[i][3] == 'other') icon = 'ℹ️'; menu_ID[i + 1] = GM_registerMenuCommand(`${menu_ALL[i][3]?icon:'❌'} ${menu_ALL[i][1]}`, async function(){ if (!(menu_ALL[i][3] == 'other')) { menu_ALL[i][3] = !menu_ALL[i][3] await menu_switch(`${menu_ALL[i][3]}`,`${menu_ALL[i][0]}`,`${menu_ALL[i][2]}`); } await menu_ALL[i][4]() }, menu_ALL[i][5]); await sleep(100) } } async function menu_switch(menu_status, Name, Tips, Title=GM_info.script.name) { if (menu_status == 'true'){ GM_notification({title: Title, text: `已开启 ${Tips} 功能`, timeout: 3500}); }else{ GM_notification({title: Title, text: `已停止 ${Tips} 功能`, timeout: 3500}); } await registerMenuCommand(); }; function get_menu_info(menuName) { return menu_ALL.find(m => m[0] == menuName); } function get_menu_value(menuName) { return get_menu_info(menuName)[3]; } BigNumber.config({ EXPONENTIAL_AT: 1e+9 }) function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // --- 学校功能 --- async function auto_calc() { let id; let question = null id = setInterval(function() { let answer = ""; let next_question = document.querySelector(".question-text") if (next_question == null) { clearInterval(id); return } if (question === next_question.innerText) return question = next_question.innerText let input = document.querySelector("#answer-input-math"); input.value = ""; input.dispatchEvent(new Event("input")) if(question.indexOf("^") > 0){ answer = Math.pow(question.split("^")[0], question.split("^")[1]) } else if (question.startsWith("√")) { answer = Math.sqrt((eval(question.replace("√", "")))) } else if (question.indexOf("e") > 0) { let x = BigNumber(question.split(" ")[0]), y = BigNumber(question.split(" ")[2]); answer = question.indexOf(" + ") > 0 ? x.plus(y).toString() : x.minus(y).toString() } else { answer = eval(question) } input.value = answer; input.dispatchEvent(new Event("input")) let btn = [...document.querySelectorAll(".v-btn__content")].find(item=>item.innerText === "答题"); btn.click() }, 200) } function auto_writing() { let id; id = setInterval(function() { let input = document.querySelector(".answer-input input"); let text = ''; let nodes = document.querySelector(".question-text .mx-2") if (nodes == null) { clearInterval(id); return } nodes = nodes.querySelectorAll("span"); for (let i of nodes) text += i.innerText input.value = text; input.dispatchEvent(new Event('input')) }, 200) } function fill_history(years) { let id; id = setInterval(function() { let dom = [...document.querySelectorAll("span")].find(item => item.innerText === "年份 ???") if (dom == null) { clearInterval(id); return } let icon = Array.from(dom.parentNode.querySelector(".v-icon").classList).filter(x => x.startsWith("mdi-"))[0] let input = document.querySelector(".answer-input input") if (input == null){ clearInterval(id); return } input.value = years[icon]; input.dispatchEvent(new Event('input')) let btn = [...document.querySelectorAll(".v-btn__content")].find(item => item.innerText === "答题"); btn.click() }, 200) } function get_history_year() { let doms = document.querySelectorAll(".v-main__wrap .rounded"); let year = {} for (let i = 0; i < doms.length; i++) { let dom = doms[i], icon = Array.from(dom.querySelector(".v-icon").classList).filter(x => x.startsWith("mdi-"))[0] year[icon] = dom.querySelector("span").innerText.match(/\d+/g)[0] } return year } let id_study async function math(is_first=true) { let menu_name = "menu_study_math"; let menu_info = get_menu_info(menu_name) if (!get_menu_value(menu_name)) { clearInterval(id_study); return } let target = [...document.querySelectorAll(".v-card__title")].find(item => item.innerText === "数学") if (!target) { if (is_first) { alert("请解锁后再使用"); menu_info[3] = !menu_info[3]; menu_switch(`${menu_info[3]}`,`${menu_info[0]}`,`${menu_info[2]}`); } return } let btn_study = [...target.parentNode.querySelectorAll(".v-btn__content")].find(item => item.innerText === "学习") if (btn_study) { btn_study.click(); await sleep(2000); auto_calc() } clearInterval(id_study); id_study = setInterval(function(){math(false)}, 5000) } async function exam_math() { let target = [...document.querySelectorAll(".v-card__title")].find(item => item.innerText === "数学") if (!target) { alert("请解锁后再使用"); return } let ticket = document.querySelector(".mdi-ticket-account").nextElementSibling.querySelector(".v-progress-linear__content span").innerText if (ticket === "0") { alert("考试次数不足"); return } let btn_exam = [...target.parentNode.querySelectorAll(".v-btn__content")].find(item => item.innerText === "参加考试") if (btn_exam) { btn_exam.click(); await sleep(2000); auto_calc() } } async function literature(is_first=true) { let menu_name = "menu_study_literature"; let menu_info = get_menu_info(menu_name) if (!get_menu_value(menu_name)) { clearInterval(id_study); return } let target = [...document.querySelectorAll(".v-card__title")].find(item => item.innerText === "文学") if (!target) { if (is_first) { alert("请解锁后再使用"); menu_info[3] = !menu_info[3]; menu_switch(`${menu_info[3]}`,`${menu_info[0]}`,`${menu_info[2]}`); } return } let btn_study = [...target.parentNode.querySelectorAll(".v-btn__content")].find(item => item.innerText === "学习") if (btn_study) { btn_study.click(); await sleep(2000); auto_writing() } clearInterval(id_study); id_study = setInterval(function(){literature(false)}, 5000) } async function exam_literature() { let target = [...document.querySelectorAll(".v-card__title")].find(item => item.innerText === "文学") if (!target) { alert("请解锁后再使用"); return } let ticket = document.querySelector(".mdi-ticket-account").nextElementSibling.querySelector(".v-progress-linear__content span").innerText if (ticket === "0") { alert("考试次数不足"); return } let btn_exam = [...target.parentNode.querySelectorAll(".v-btn__content")].find(item => item.innerText === "参加考试") if (btn_exam) { btn_exam.click(); await sleep(2000); auto_writing() } } async function history(is_first=true) { let menu_name = "menu_study_history"; let menu_info = get_menu_info(menu_name) if (!get_menu_value(menu_name)) { clearInterval(id_study); return } let target = [...document.querySelectorAll(".v-card__title")].find(item => item.innerText === "历史") if (!target) { if (is_first) { alert("请解锁后再使用"); menu_info[3] = !menu_info[3]; menu_switch(`${menu_info[3]}`,`${menu_info[0]}`,`${menu_info[2]}`); } return } let btn_study = [...target.parentNode.querySelectorAll(".v-btn__content")].find(item => item.innerText === "学习") if (btn_study) { btn_study.click(); await sleep(2000); let btn_start = [...document.querySelectorAll(".v-btn__content")].find(item => item.innerText === "答题"); btn_start.click(); await sleep(1000); fill_history(get_history_year()) } clearInterval(id_study); id_study = setInterval(function(){history(false)}, 5000) } async function exam_history(is_first=true) { let target = [...document.querySelectorAll(".v-card__title")].find(item => item.innerText === "历史") if (!target) { alert("请解锁后再使用"); return } let ticket = document.querySelector(".mdi-ticket-account").nextElementSibling.querySelector(".v-progress-linear__content span").innerText if (ticket === "0") { alert("考试次数不足"); return } let btn_exam = [...target.parentNode.querySelectorAll(".v-btn__content")].find(item => item.innerText === "参加考试") if (btn_exam) { btn_exam.click(); await sleep(2000); let btn = [...document.querySelectorAll(".v-btn__content")].find(item=>item.innerText === "答题"); btn.click(); await sleep(1000); fill_history(get_history_year()) } } // --- 部落功能 --- let id_skill async function auto_skill() { let menu_name = "menu_auto_skill" if (!get_menu_value(menu_name)) { clearInterval(id_skill); return } let skills_info = [ {"name": "匕首", "icon": "mdi-knife-military"}, {"name": "衬衫", "icon": "mdi-medical-bag"}, {"name": "守护天使", "icon": "mdi-flare"}, {"name": "一杯牛奶", "icon": "mdi-bone"}, {"name": "星盾", "icon": "mdi-octagram-outline"}, {"name": "长剑", "icon": "mdi-sword"}, {"name": "靴子", "icon": "mdi-shoe-cleat"}, {"name": "三叶草", "icon": "mdi-clover"}, {"name": "肝脏", "icon": "mdi-stomach"}, {"name": "火球", "icon": "mdi-fire"}, {"name": "营火", "icon": "mdi-campfire"}, {"name": "雪花", "icon": "mdi-snowflake"}, {"name": "压迫者", "icon": "mdi-emoticon-devil"}, {"name": "肉盾", "icon": "mdi-octagram-outline"}, {"name": "腐败的眼睛", "icon": "mdi-laser-pointer"}, {"name": "巫师帽", "icon": "mdi-shimmer"}, {"name": "红色杖", "icon": "mdi-pentagram"}, {"name": "坏了的秒表", "icon": "mdi-timer"}, {"name": "大理石柱", "icon": "mdi-pillar"}, {"name": "彩虹之杖", "icon": "mdi-looks"}, {"name": "毒素", "icon": "mdi-bottle-tonic-skull"}, {"name": "净化泉", "icon": "mdi-water-opacity"}, ] id_skill = setInterval(async function() { let player = [...document.querySelectorAll("div")].find(item => item.innerText === "玩家"); if (player == null) return let skill_bar = player.parentNode.previousElementSibling let skills = skill_bar.querySelectorAll(".v-icon"); for (let skill of [...skills]) { if (skills_info.find(item => skill.classList.contains(item.icon) && gmc.get(`ignore_${item.icon}`))) continue skill.click() await sleep(100) } }, 1000) } // --- 农场功能 --- let id_harvest async function auto_harvest() { let menu_name = "menu_auto_harvest" if (!get_menu_value(menu_name)) { clearInterval(id_harvest); return } id_harvest = setInterval(function() { let btn_seed = document.querySelector(".mdi-seed") let btn_refresh = document.querySelector(".mdi-refresh"); if (btn_refresh == null || btn_seed == null) return btn_refresh.click() }, 1000) } let id_water async function auto_water() { let val1 = get_menu_value("menu_auto_water"); let val2 = get_menu_value("menu_auto_shape_water"); if (!val1 && !val2) { clearInterval(id_water); return } clearInterval(id_water); id_water = setInterval(function() { let icons = document.querySelectorAll('.care-icon:not(.farm-care-hidden)'); icons.forEach(icon => { let tile = icon.parentElement; if (tile) { tile.dispatchEvent(new MouseEvent('mouseenter')); tile.dispatchEvent(new MouseEvent('mouseleave')); } }); }, 1000) } // ========================================== // 画廊自动消除核心算法 (V4.8.5 贪婪消除版) // ========================================== let id_shape; const SPECIAL_TYPES = ['bomb', 'chest', 'accelerator', 'sparkles', 'dice', 'hourglass']; const getSortedShapesDesc = (counts) => { return Object.keys(counts).sort((a, b) => { if (counts[b] !== counts[a]) return counts[b] - counts[a]; return a.localeCompare(b); }); }; const getSortedShapesAsc = (counts) => { return Object.keys(counts).sort((a, b) => { if (counts[a] !== counts[b]) return counts[a] - counts[b]; return a.localeCompare(b); }); }; const moveToTarget = (store, grid, from, to, lockedCells) => { if (from.x === to.x && from.y === to.y) return false; let queue = [[from]]; let visited = new Set([`${from.x},${from.y}`]); let nextStep = null; while(queue.length > 0) { let path = queue.shift(); let curr = path[path.length - 1]; if (curr.x === to.x && curr.y === to.y) { nextStep = path[1]; break; } const dirs = [[0,1], [0,-1], [1,0], [-1,0]]; for (let d of dirs) { let nx = curr.x + d[0], ny = curr.y + d[1]; if (ny >= 0 && ny < grid.length && nx >= 0 && nx < grid[0].length) { let key = `${nx},${ny}`; if (!visited.has(key) && !lockedCells.has(key)) { visited.add(key); queue.push([...path, {x: nx, y: ny}]); } } } } if (nextStep) { if (store.getters['currency/canAfford']({gallery_motivation: 1})) { store.dispatch('gallery/switchShape', { fromX: from.x, fromY: from.y, toX: nextStep.x, toY: nextStep.y }); return true; } } return false; }; const findReachable = (grid, startSpot, lockedCells, predicate) => { let queue = [startSpot]; let visited = new Set([`${startSpot.x},${startSpot.y}`]); while (queue.length > 0) { let curr = queue.shift(); if (predicate(grid[curr.y][curr.x], curr.x, curr.y)) return curr; const dirs = [[0,1], [0,-1], [1,0], [-1,0]]; for (let d of dirs) { let nx = curr.x + d[0], ny = curr.y + d[1]; if (ny >= 0 && ny < grid.length && nx >= 0 && nx < grid[0].length) { let key = `${nx},${ny}`; if (!visited.has(key) && !lockedCells.has(key)) { visited.add(key); queue.push({x: nx, y: ny}); } } } } return null; }; const findClusterSafe = (grid, startX, startY, shapeType, lockedCells) => { let queue = [{x: startX, y: startY}]; let visited = new Set([`${startX},${startY}`]); let cluster = [{x: startX, y: startY}]; while (queue.length > 0) { let curr = queue.shift(); const dirs = [[0,1], [0,-1], [1,0], [-1,0]]; for (let d of dirs) { let nx = curr.x + d[0], ny = curr.y + d[1]; if (ny >= 0 && ny < grid.length && nx >= 0 && nx < grid[0].length) { let key = `${nx},${ny}`; if (!visited.has(key) && !lockedCells.has(key) && grid[ny][nx] === shapeType) { visited.add(key); cluster.push({x: nx, y: ny}); queue.push({x: nx, y: ny}); } } } } return cluster; }; const getSafeClusters = (grid, shapeType, lockedCells) => { let cells = []; grid.forEach((row, y) => { row.forEach((type, x) => { if (type === shapeType && !lockedCells.has(`${x},${y}`)) cells.push({x, y}); }); }); let visited = new Set(); let clusters = []; for (let c of cells) { let key = `${c.x},${c.y}`; if (!visited.has(key)) { let cluster = findClusterSafe(grid, c.x, c.y, shapeType, lockedCells); cluster.forEach(i => visited.add(`${i.x},${i.y}`)); clusters.push(cluster); } } clusters.sort((a,b) => b.length - a.length); return clusters; }; // 【核心大改】:贪婪消除逻辑,必定全图吸附凑大团后再引爆 const executeNormalElimination = (store, grid, shapeType, lockedCells, isUnlockedNormal) => { let safeClusters = getSafeClusters(grid, shapeType, lockedCells); if (safeClusters.length === 0) return false; let main = safeClusters[0]; // 找寻所有不在主星团内的散落孤儿 let isolated = []; grid.forEach((row, y) => { row.forEach((type, x) => { if (type === shapeType && !lockedCells.has(`${x},${y}`) && !main.some(m => m.x===x && m.y===y)) { isolated.push({x, y}); } }); }); // 步骤1:如果有散落孤儿,坚决优先拉取吸附它们! if (isolated.length > 0) { let neighbors = []; main.forEach(m => { [[0,1], [0,-1], [1,0], [-1,0]].forEach(d => { let nx = m.x+d[0], ny = m.y+d[1]; if (ny>=0 && ny=0 && nx 0) { // 尝试将最近的散件拉到边缘空位上 for (let nei of neighbors) { let iso = findReachable(grid, nei, lockedCells, (type, x, y) => type === shapeType && !main.some(m => m.x === x && m.y === y) && !lockedCells.has(`${x},${y}`)); if (iso) { if (moveToTarget(store, grid, iso, nei, lockedCells)) return true; // 拉取成功,此帧结束,下帧继续贪婪吸附 } } } } // 步骤2:只有在以下两种情况下才引爆: // 1. isolated.length === 0 (全图同色全部凑齐了!) // 2. 有孤儿但被死路挡住(墙)拉不过来了。 // 这时只要团块数量 >= 5,就可以享受满额收益一口气引爆。 if (main.length >= 5) { store.dispatch('gallery/clickShape', {x: main[0].x, y: main[0].y}); return true; } return false; }; async function auto_shape() { let val1 = get_menu_value("menu_auto_shape"); let val2 = get_menu_value("menu_auto_shape_water"); if (!val1 && !val2) { clearInterval(id_shape) return } clearInterval(id_shape); const getStore = () => document.querySelector('.v-application')?.__vue__?.$store; id_shape = setInterval(async function() { const store = getStore(); if (!store || !store.state.gallery || !store.state.gallery.shapeGrid) return; const grid = store.state.gallery.shapeGrid; const shapeInfo = store.state.gallery.shape; const motivation = store.getters['currency/value']('gallery_motivation'); if (motivation < 1) return; let specials = { chest: null, accelerator: null, bomb: null, sparkles: null, dice: null, hourglass: null }; let normalCounts = {}; const isUnlockedNormal = (type) => { const isSpec = shapeInfo[type]?.isSpecial || SPECIAL_TYPES.includes(type); return shapeInfo[type] && shapeInfo[type].unlocked && !isSpec; }; grid.forEach((row, y) => { row.forEach((type, x) => { if (shapeInfo[type]?.isSpecial || SPECIAL_TYPES.includes(type)) { specials[type] = {x, y}; } else if (isUnlockedNormal(type)) { normalCounts[type] = (normalCounts[type] || 0) + 1; } }); }); // 沙漏绝对锁死,作为物理墙壁 let lockedCells = new Set(); if (specials.hourglass) lockedCells.add(`${specials.hourglass.x},${specials.hourglass.y}`); const handleDeadlock = () => { if (specials.dice) { store.dispatch('gallery/clickShape', {x: specials.dice.x, y: specials.dice.y}); return true; } if (specials.hourglass) { store.dispatch('gallery/clickShape', {x: specials.hourglass.x, y: specials.hourglass.y}); return true; } if (motivation >= 35) { store.dispatch('gallery/buyShapeReroll'); return true; } return false; }; // ----------------------------------------------------- // 独立状态机 1. 闪耀 (Sparkles) // ----------------------------------------------------- if (specials.sparkles) { let sp = specials.sparkles; let cx = 4, cy = 2; if (sp.x !== cx || sp.y !== cy) { moveToTarget(store, grid, sp, {x: cx, y: cy}, lockedCells); return; } lockedCells.add(`${cx},${cy}`); let top4 = getSortedShapesDesc(normalCounts).slice(0, 4); if (top4.length < 4) { store.dispatch('gallery/clickShape', {x: cx, y: cy}); return; } let seedSpots = [{x:4, y:1}, {x:4, y:3}, {x:3, y:2}, {x:5, y:2}]; let seedsPlaced = true; for (let i=0; i type === shape && !lockedCells.has(`${x},${y}`)); if (bestCell && moveToTarget(store, grid, bestCell, spot, lockedCells)) return; } else { lockedCells.add(`${spot.x},${spot.y}`); } } if (!seedsPlaced) { store.dispatch('gallery/clickShape', {x: cx, y: cy}); return; } let allConnected = true; let wandererMoved = false; for (let i=0; i `${r.x},${r.y}`)); rootSystem.forEach(r => lockedCells.add(`${r.x},${r.y}`)); let wanderersExists = false; grid.forEach((row, y) => { row.forEach((type, x) => { if (type === shape && !rootSet.has(`${x},${y}`)) wanderersExists = true; }); }); if (wanderersExists) { allConnected = false; let rootEdges = []; rootSystem.forEach(r => { [[0,1],[0,-1],[1,0],[-1,0]].forEach(d => { let nx = r.x+d[0], ny = r.y+d[1]; if (ny>=0 && ny=0 && nx type === shape && !rootSet.has(`${x},${y}`) && !lockedCells.has(`${x},${y}`)); if (w && moveToTarget(store, grid, w, e, lockedCells)) { wandererMoved = true; break; } } } if (wandererMoved) break; } if (allConnected || (!wandererMoved && seedsPlaced)) { store.dispatch('gallery/clickShape', {x: cx, y: cy}); } return; } // ----------------------------------------------------- // 独立状态机 2. 宝箱 (Chest) // ----------------------------------------------------- if (specials.chest) { let chest = specials.chest; let cx = 4, cy = 2; if (chest.x !== cx || chest.y !== cy) { moveToTarget(store, grid, chest, {x: cx, y: cy}, lockedCells); return; } lockedCells.add(`${cx},${cy}`); let spots = [[-1,-1],[0,-1],[1,-1],[-2,0],[-1,0],[1,0],[2,0],[-1,1],[0,1],[1,1]].map(d => ({x: cx+d[0], y: cy+d[1]})); let globalUnlockedCount = Object.keys(shapeInfo).filter(k => shapeInfo[k].unlocked && !shapeInfo[k].isSpecial && !SPECIAL_TYPES.includes(k)).length; if (globalUnlockedCount >= 10) { let usedTypes = new Set(); let emptySpots = []; spots.forEach(spot => { let cell = grid[spot.y][spot.x]; if (isUnlockedNormal(cell) && !usedTypes.has(cell)) { usedTypes.add(cell); lockedCells.add(`${spot.x},${spot.y}`); } else { emptySpots.push(spot); } }); if (emptySpots.length === 0) { store.dispatch('gallery/clickShape', {x: cx, y: cy}); return; } let availableTypes = getSortedShapesDesc(normalCounts).filter(k => !usedTypes.has(k)); if (availableTypes.length > 0) { let targetSpot = emptySpots[0]; let moved = false; for (let tShape of availableTypes) { let bCell = findReachable(grid, targetSpot, lockedCells, (type, x, y) => type === tShape && !lockedCells.has(`${x},${y}`)); if (bCell && moveToTarget(store, grid, bCell, targetSpot, lockedCells)) { moved = true; break; } } if (!moved) { let validMost = getSortedShapesDesc(normalCounts).filter(k => normalCounts[k] >= 5); for (let vShape of validMost) { if (executeNormalElimination(store, grid, vShape, lockedCells, isUnlockedNormal)) return; } handleDeadlock(); } } else { let validMost = getSortedShapesDesc(normalCounts).filter(k => normalCounts[k] >= 5); for (let vShape of validMost) { if (executeNormalElimination(store, grid, vShape, lockedCells, isUnlockedNormal)) return; } handleDeadlock(); } } else { let emptySpots = []; spots.forEach(spot => { let cell = grid[spot.y][spot.x]; if (isUnlockedNormal(cell)) { lockedCells.add(`${spot.x},${spot.y}`); } else { emptySpots.push(spot); } }); if (emptySpots.length === 0) { store.dispatch('gallery/clickShape', {x: cx, y: cy}); return; } let targetSpot = emptySpots[0]; let bestCell = findReachable(grid, targetSpot, lockedCells, (type, x, y) => isUnlockedNormal(type) && !lockedCells.has(`${x},${y}`)); if (bestCell) moveToTarget(store, grid, bestCell, targetSpot, lockedCells); else handleDeadlock(); } return; } // ----------------------------------------------------- // 独立状态机 3. 加速器 (Accelerator) // ----------------------------------------------------- if (specials.accelerator) { let acc = specials.accelerator; let cx = 4, cy = 2; if (acc.x !== cx || acc.y !== cy) { moveToTarget(store, grid, acc, {x: cx, y: cy}, lockedCells); return; } lockedCells.add(`${cx},${cy}`); let spots = [[-1,-1],[0,-1],[1,-1],[-1,0],[1,0],[-1,1],[0,1],[1,1]].map(d => ({x: cx+d[0], y: cy+d[1]})); let bigBrother = getSortedShapesDesc(normalCounts)[0]; let bbCount = normalCounts[bigBrother] || 0; let filledSpots = 0; let emptySpots = []; spots.forEach(spot => { if (grid[spot.y][spot.x] === bigBrother) { lockedCells.add(`${spot.x},${spot.y}`); filledSpots++; } else { emptySpots.push(spot); } }); if (filledSpots === 8) { if (motivation >= 100) store.dispatch('gallery/clickShape', {x: cx, y: cy}); return; } let availableBB = bbCount - filledSpots; if (availableBB > 0) { let targetSpot = emptySpots[0]; let bestCell = findReachable(grid, targetSpot, lockedCells, (type, x, y) => type === bigBrother && !lockedCells.has(`${x},${y}`)); if (bestCell) { moveToTarget(store, grid, bestCell, targetSpot, lockedCells); return; } } let validLeast = getSortedShapesAsc(normalCounts) .filter(k => k !== bigBrother) .filter(k => normalCounts[k] >= 5); for (let vShape of validLeast) { if (executeNormalElimination(store, grid, vShape, lockedCells, isUnlockedNormal)) return; } handleDeadlock(); return; } // ----------------------------------------------------- // 独立状态机 4. 炸弹 (Bomb) // ----------------------------------------------------- if (specials.bomb) { let bx = specials.bomb.x, by = specials.bomb.y; lockedCells.add(`${bx},${by}`); let crossSpots = []; for (let i = 0; i < grid[0].length; i++) if(i !== bx) crossSpots.push({x: i, y: by}); for (let i = 0; i < grid.length; i++) if(i !== by) crossSpots.push({x: bx, y: i}); let emptySpots = []; crossSpots.forEach(spot => { let cell = grid[spot.y][spot.x]; if (isUnlockedNormal(cell)) { lockedCells.add(`${spot.x},${spot.y}`); } else { emptySpots.push(spot); } }); if (emptySpots.length === 0) { store.dispatch('gallery/clickShape', {x: bx, y: by}); return; } let targetSpot = emptySpots[0]; let bestCell = findReachable(grid, targetSpot, lockedCells, (type, x, y) => isUnlockedNormal(type) && !lockedCells.has(`${x},${y}`) && x !== bx && y !== by); if (bestCell) moveToTarget(store, grid, bestCell, targetSpot, lockedCells); else handleDeadlock(); return; } // ----------------------------------------------------- // 基础贪婪消除 (寻找最多数量,全图聚拢) // ----------------------------------------------------- let anyNormalEliminated = false; let validNormals = getSortedShapesDesc(normalCounts).filter(k => normalCounts[k] >= 5); for (let shape of validNormals) { // 会卡在这里不断执行拉拽,直到拉不动才会发生点爆 if (executeNormalElimination(store, grid, shape, lockedCells, isUnlockedNormal)) { anyNormalEliminated = true; break; } } if (anyNormalEliminated) return; // 死局自救 handleDeadlock(); }, 200) } async function auto_shape_water() { auto_water(); auto_shape(); } })();