链式反应网页源码
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>链式反应</title> <link rel="stylesheet" href="https://unpkg.com/bootstrap-icons@1.11.3/font/bootstrap-icons.css"> <!-- 本地存储工具类 - rotaryChainLocalStorageUtil.js 功能:封装游戏配置、历史记录、关卡进度的本地存储操作 --> <script src="https://ltda.wdfiles.com/local--code/ry-rotating-localstorageutil/1"></script> <!-- 引入Vue 2 --> <script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script> <!-- 链式反应-装置组件 --> <script src="https://ltda.wdfiles.com/local--code/ry-rotating-device/1"></script> <!-- pako压缩库 --> <script src="https://ltda.wdfiles.com/local--code/ry-rotating-pako/1"></script> <!-- fonts-style对应的style会在图标库字体加载完成之后被删除 --> <style id="fonts-style"> .bi::before, [class^="bi-"]::before, [class*=" bi-"]::before { content: '☒' !important; font-family: unset !important; } </style> <style> [v-cloak] { display: none !important; /* vue2未挂载时隐藏元素 */ } </style> <style> /* 页面css */ @import url("https://ltda.wikidot.com/ry-rotating-device/code/2"); </style> </head> <body> <div id="app" v-cloak style="display: flex;flex-direction: column;justify-content: flex-start;align-items: center;align-content: center;"> <!-- minScreenDimension这里“+20”和“+120”是因为getMinDimension方法中“-20”和“-120” --> <div style="display: flex;flex-direction: column;justify-content: flex-start;align-items: center;align-content: center;" :style="{width: (minScreenDimension+20) + 'px', height: (minScreenDimension+120) + 'px'}"> <!-- 顶部UI --> <div class="ui-container" :style="{width: minScreenDimension + 'px'}"> <div class="ui-section"> <i class="bi bi-lightning-charge-fill"></i> <span>{{ actionPoints }}</span> </div> <div class="ui-section"> <i class="bi bi-circle-fill"></i> <span>× {{ zeroRotationDevicesCount + 1 }}</span> </div> <div class="ui-section"> <i class="bi bi-star-fill"></i> <span>{{ operationLog.score }}</span> </div> <div class="ui-section"> <i class="bi bi-hash"></i> <span>{{ operationLog.time }}</span> </div> </div> <!-- 游戏主体、内容遮罩 --> <div class="devices-container-wrapper" :style="{width: minScreenDimension + 'px', height: minScreenDimension + 'px'}"> <!-- 带坐标的网格容器 --> <div class="grid-with-coordinates" v-if="devicesGrid.length > 0"> <!-- 网格 --> <div class="coordinates-and-grid"> <!-- 装置网格 --> <div class="devices-container"> <div class="device-row" v-for="(row, rowIndex) in devicesGrid" :key="rowIndex"> <template v-for="(device, colIndex) in row"> <div :key="rowIndex+'_'+colIndex" style="position: relative;"> <rotating-device :ref="'device_' + rowIndex + '_' + colIndex" :width="deviceSize" :height="deviceSize" :max-rotation-times="device.maxRotationTimes" :speed="speed" :flash-enabled="flashEnabled" :init-rotation-angle="device.initRotationAngle" :title="'('+rowIndex+', '+colIndex+')'" :style="{ pointerEvents: isDeviceClickable ? 'auto' : 'none', border: (currentClick.rowIndex !== null && currentClick.colIndex !== null && currentClick.rowIndex===rowIndex && currentClick.colIndex===colIndex) ? 'solid 1px red' : '' }" :class="[device.t ? 'specialDevice' : '']" @rotated="handleDeviceRotated(rowIndex, colIndex, $event)" @reset-color="resetDeviceColor(rowIndex, colIndex)"> </rotating-device> <div style="position: absolute;z-index: -1;left: 0;right: 0;top: 0;bottom: 0;" :style="isInChainReaction ? {} : assistPredictStyle[rowIndex + '_' + colIndex]"> </div> </div> </template> </div> <div style="position: absolute;top: 0px;right: 0px;bottom: 0px;left: 0px;z-index: -2;pointer-events: none;display: flex;flex-direction: column;justify-content: center;align-items: center;color:#aaa;font-weight: bold;"> <!-- 执行历史记录状态提示 --> <div v-show="isExecutingHistory" :style="{'font-size': minScreenDimension / 18 + 'px'}"> 执行历史记录中... {{ currentExecutionStep }}/{{ totalExecutionSteps }} </div> <!-- 游戏当前状态:运行/备战 --> <div :style="{ 'font-size': minScreenDimension / 12 + 'px', 'color': gameState === '运行' ? '#22C55E' : '#EF4444' }"> {{ gameState }} </div> <!-- 游戏当前分数 --> <div :style="{'font-size': minScreenDimension / 6 + 'px'}"> {{ operationLog.score }} </div> </div> </div> </div> </div> <div v-else> 正在生成装置网格... </div> </div> <div v-show="!autoSaveHistory" style="font-size: 12px;color: red;"> 自动保存暂不可用,重置即可重新开启 </div> <!-- 底部UI --> <div class="ui-container" :style="{width: minScreenDimension + 'px'}"> <div class="ui-section"> <button @click="toggleGameState" :disabled="isExecutingHistory || isInChainReaction"> 切换状态 </button> </div> <div class="ui-section"> <button @click="undoLastOperation" :disabled="!canUndo || isExecutingHistory || isInChainReaction"> 撤回 </button> </div> <div class="ui-section"> <button @click="regenerateGrid">重置</button> </div> <div class="ui-section"> <button @click="showSettings" :disabled="isExecutingHistory || isInChainReaction">设置</button> </div> </div> </div> <!-- 设置面板 --> <div v-show="settingsDialog" class="overlay-container settings-overlay" @click="(e) => { if (e.target === e.currentTarget) settingsDialog = false; }"> <div class="panel settings-panel"> <!-- 面板头部:返回按钮 + 标题 + 分割线 --> <div class="panel__header"> <i class="bi bi-arrow-left" @click="settingsDialog = false;"></i> <div> <h3 class="panel__title">系统设置</h3> <div class="panel__divider"></div> </div> <!-- 占位,保持头部布局对称 --> <i></i> </div> <!-- 面板内容区:滚动容器 --> <div class="panel__content"> <div class="settings-li"> <button class="settings-panel__btn settings-panel__btn--action" @click="instructionsDialog = true;"> <i class="bi bi-book"></i> <div>游戏说明</div> </button> </div> <div class="settings-li"> <button class="settings-panel__btn settings-panel__btn--action" @click="predictStoreDialog = true;" :disabled="isExecutingHistory"> <i class="bi bi-star-fill"></i>今日评级 </button> </div> <div class="settings-li"> <button class="settings-panel__btn settings-panel__btn--action" @click="executeRecordDialog = true;" :disabled="isExecutingHistory"> <i class="bi bi-play-circle"></i>录入操作 </button> </div> <div class="settings-li"> <button class="settings-panel__btn settings-panel__btn--action" @click="logOperationDetails" :disabled="isExecutingHistory"> <i class="bi bi-list-check"></i> <div>操作日志</div> </button> </div> <div class="settings-li"> <button class="settings-panel__btn settings-panel__btn--action" @click="showHistoryRecords" :disabled="isExecutingHistory"> <i class="bi bi-clock-history"></i>历史记录 </button> </div> <div class="settings-li"> <button class="settings-panel__btn settings-panel__btn--action" @click="importExportRecordDialog = true;" :disabled="isExecutingHistory"> <i class="bi bi-download"></i> <div>导入/导出记录</div> </button> </div> <div class="settings-li"> <button class="settings-panel__btn settings-panel__btn--action" @click="assistPredictDialog = true;" :disabled="isExecutingHistory"> <i class="bi bi-lightbulb"></i> <div>辅助预测</div> </button> </div> <div class="settings-li"> <button class="settings-panel__btn settings-panel__btn--action" @click="toggleSpeed"> <i class="bi bi-speedometer"></i>切至{{speed===0.5?'2倍速':'1倍速'}} </button> </div> <div class="settings-li"> <button class="settings-panel__btn settings-panel__btn--action" @click="toggleFlashEnabled"> <i class="bi bi-eye"></i>{{flashEnabled?'关闭闪烁动画':'开启闪烁动画'}} </button> </div> </div> </div> </div> <!-- 游戏说明面板 --> <div v-show="instructionsDialog" class="overlay-container instructions-overlay" @click="(e) => { if (e.target === e.currentTarget) instructionsDialog = false; }"> <div class="panel instructions-panel"> <!-- 面板头部:返回按钮 + 标题 + 分割线 --> <div class="panel__header"> <i class="bi bi-arrow-left" @click="instructionsDialog = false;"></i> <div> <h3 class="panel__title">游戏说明</h3> <div class="panel__divider"></div> </div> <!-- 关闭所有遮罩 --> <i class="bi bi-x-circle" style="cursor: pointer;" @click="instructionsDialog = false;settingsDialog = false;"></i> </div> <!-- 面板内容区:滚动容器 --> <div class="panel__content"> <div class="instructions-content"> <!-- 核心图标说明(新增,同步顶部UI) --> <div class="instructions-section"> <div class="section-heading">一、核心图标说明</div> <div class="section-list" style="display: flex; flex-wrap: wrap; gap: 15px; padding-left: 0;"> <div style="display: flex; align-items: center;"> <i class="bi bi-lightning-charge-fill" style="color: #2196F3; margin-right: 0.5em;"></i> <span>行动点数:每次点击消耗1点,初始10点</span> </div> <div style="display: flex; align-items: center;"> <i class="bi bi-circle-fill" style="color: #f44336; margin-right: 0.5em;"></i> <span>耗尽装置数:变红装置越多,得分倍数越高</span> </div> <div style="display: flex; align-items: center;"> <i class="bi bi-star-fill" style="color: #FFC107; margin-right: 0.5em;"></i> <span>当前分数:旋转装置可累积,连锁反应得分更高</span> </div> <div style="display: flex; align-items: center;"> <i class="bi bi-hash" style="color: #4CAF50; margin-right: 0.5em;"></i> <span>随机种子:相同种子可生成完全一致的装置网格</span> </div> </div> </div> <!-- 游戏规则(细化描述+示例) --> <div class="instructions-section"> <div class="section-heading">二、核心游戏规则</div> <ul class="section-list"> <li>1. 装置操作:点击网格中的装置,装置会顺时针旋转90度。 </li> <li>2. 行动点限制:每次点击消耗1点行动点,点数为0时无法点击;点击「重置」可恢复10点并生成新网格。</li> <li>3. 装置旋转次数: <ul style="padding-left: 2rem; margin-top: 4px;"> <li>有限次数:装置上显示数字,代表剩余旋转次数,数字为0时装置变红、不可再转;</li> <li>无限次数:无数字显示的装置,可无限次旋转。</li> </ul> </li> <li>4. 两种游戏状态(点击底部「切换状态」按钮切换): <ul style="padding-left: 2rem; margin-top: 4px;"> <li>备战状态:仅当前点击的装置旋转,不触发相邻装置连锁反应(适合规划布局);</li> <li>运行状态:点击后,相邻装置角度匹配时自动连锁旋转(如当前装置朝上、上方装置朝下)。</li> </ul> </li> <li>5. 得分规则(倍数=1+耗尽装置数): <ul style="padding-left: 2rem; margin-top: 4px;"> <li>基础得分:点击旋转后该装置未变0,得1×倍数分;</li> <li>高额得分:点击旋转后该装置变0(耗尽次数),得10×倍数分;</li> <li>连锁得分:运行状态下,每连锁旋转1个装置,额外得1×倍数分。</li> </ul> </li> </ul> </div> <!-- 功能操作(补充细节+关联图标) --> <div class="instructions-section"> <div class="section-heading">三、实用功能操作</div> <ul class="section-list"> <li>1. 状态切换:底部「切换状态」按钮,在“备战/运行”间切换(连锁反应中不可点击); <br> 提示:规划初期建议用“备战”调整布局,调整完成后切“运行”触发连锁。 </li> <li>2. 撤回操作:底部「撤回」按钮,可回到上一步操作(最多保留最近10步,连锁/历史执行中不可用)。</li> <li>3. 重置网格:底部「重置」按钮,生成新装置网格,同时恢复10点行动点、清空当前分数。</li> <li>4. 历史记录(设置面板中「历史记录」按钮 <i class="bi bi-clock-history" style="color: #4CAF50;"></i>): <ul style="padding-left: 2rem; margin-top: 4px;"> <li>运行历史:选择历史记录,自动复现操作流程(适合学习高分策略);</li> <li>挑战种子:可生成与历史记录完全一致的网格,重试优化操作。</li> </ul> </li> <li>5. 速度调节:设置面板中「切至X倍速」按钮 <i class="bi bi-speedometer" style="color: #dc3545;"></i>, 可切换1倍速(0.5秒/旋转)或2倍速(0.25秒/旋转),不影响实际得分。 </li> <li>6. 辅助预测(设置面板中「辅助预测」按钮 <i class="bi bi-lightbulb" style="color: #2ecc71;"></i>): <ul style="padding-left: 2rem; margin-top: 4px;"> <li>分数优先:推荐单次点击得分最高的装置(绿色提示圈);</li> <li>乘数优先:推荐能快速增加耗尽装置数的装置(红色提示圈);</li> <li>提示:辅助仅提供单步参考,长期策略需结合自身判断。</li> </ul> </li> </ul> </div> <!-- 常见问题(新增,解决高频疑问) --> <div class="instructions-section"> <div class="section-heading">四、常见问题解答</div> <ul class="section-list"> <li>Q1:为什么有的装置点击后没反应? <br> A:可能是行动点耗尽、装置已变红(次数为0),或处于连锁反应/历史执行状态。 </li> <li>Q2:如何让连锁反应触发更多装置? <br> A:用“备战”状态调整相邻装置角度(如让横向装置朝左/右、纵向装置朝上/下),再切“运行”点击。 </li> <li>Q3:历史记录会保存多久? <br> A:保存在浏览器本地,清除浏览器缓存前不会丢失;同一种子保留前3名得分,全局保留前20名高分。 </li> </ul> </div> </div> </div> </div> </div> <!-- 录入操作面板 --> <div v-show="executeRecordDialog" class="overlay-container history-overlay" @click="(e) => { if (e.target === e.currentTarget) executeRecordDialog = false; }"> <div class="panel instructions-panel"> <!-- 面板头部 --> <div class="panel__header"> <i class="bi bi-arrow-left" @click="executeRecordDialog = false;"></i> <div> <h3 class="panel__title">录入操作</h3> <div class="panel__divider"></div> </div> <!-- 关闭所有遮罩 --> <i class="bi bi-x-circle" style="cursor: pointer;" @click="executeRecordDialog = false;settingsDialog = false;"></i> </div> <!-- 面板内容区 --> <div class="panel__content"> <!-- 自定义JSON输入框 --> <div class="custom-json-input"> <textarea class="json-textarea" placeholder='{"time":20241001,"records":["1_0_0"],"score":0,"date":1622505600000}' v-model="customRecordJson" :disabled="isExecutingHistory"></textarea> </div> <!-- 功能按钮组 --> <div class="custom-json-buttons"> <button @click="executeCustomRecord" :disabled="isExecutingHistory">运行此操作</button> <button class="challenge-btn" @click="challengeCustomRecord(customRecordJson)" :disabled="isExecutingHistory">挑战相同种子</button> </div> <!-- 自定义JSON输入框 --> <div class="custom-json-input"> <textarea class="json-textarea" placeholder='请输入种子(仅支持数字)' v-model="customRecordSeed" :disabled="isExecutingHistory"></textarea> </div> <!-- 功能按钮组 --> <div class="custom-json-buttons"> <button class="challenge-btn" @click="challengeCustomRecord(JSON.stringify({time: parseInt(customRecordSeed)}))" :disabled="isExecutingHistory">挑战种子</button> </div> </div> </div> </div> <!-- 今日评级面板 --> <div v-show="predictStoreDialog" class="overlay-container predict-store-overlay" @click="(e) => { if (e.target === e.currentTarget) predictStoreDialog = false; }"> <div class="panel predict-store-panel"> <!-- 面板头部 --> <div class="panel__header"> <i class="bi bi-arrow-left" @click="predictStoreDialog = false;"></i> <div> <h3 class="panel__title">今日评级</h3> <div class="panel__divider"></div> </div> <!-- 关闭所有遮罩 --> <i class="bi bi-x-circle" style="cursor: pointer;" @click="predictStoreDialog = false;settingsDialog = false;"></i> </div> <!-- 面板内容区 --> <div class="panel__content"> <!-- 2.1 今日种子提示(新增,让用户明确当前评级的种子) --> <div style="background-color: #f8f9fa; padding: 12px 15px; border-radius: 8px; border-left: 4px solid #4CAF50;"> <div style="display: flex; align-items: center; gap: 8px; font-size: 14px; color: #333;"> <i class="bi bi-hash" style="color: #4CAF50; font-size: 16px;"></i> <span><strong>当前评级种子</strong>:{{ getTodaySeed() }}</span> </div> <div style="font-size: 12px; color: #666; margin-top: 4px;"> 提示:仅基于今日种子生成的网格进行评级,每日种子唯一 </div> </div> <!-- 2.2 评级操作与结果区(核心模块,居中突出) --> <div style="display: flex; flex-direction: column; align-items: center; gap: 1rem;margin-top: 1rem;"> <!-- 评级按钮(优化样式,增加交互反馈) --> <button @click="handlePredictStore" :disabled="isExecutingHistory || predictProgress.isActive || predictStoreStar !== 0" style="padding: 6px 15px; font-size: 16px; font-weight: 600; border-radius: 8px; background-color: #2196F3; color: white; border: none; transition: all 0.3s; display: flex; align-items: center; gap: 8px;" :style="{ backgroundColor: (isExecutingHistory || predictProgress.isActive || predictStoreStar !== 0) ? '#cccccc' : '#2196F3', boxShadow: (isExecutingHistory || predictProgress.isActive || predictStoreStar !== 0) ? 'none' : '0 4px 12px rgba(33, 150, 243, 0.2)' }" onmouseover="!this.disabled && (this.style.backgroundColor='#0b7dda', this.style.transform='translateY(-2px)')" onmouseout="!this.disabled && (this.style.backgroundColor='#2196F3', this.style.transform='translateY(0)')"> <i class="bi bi-star-half" style="font-size: 18px;"></i> 生成今日评级 </button> <!-- 进度条容器(仅计算中显示) --> <div v-show="predictProgress.isActive && predictStoreStar === 0" style="width: 100%; margin-top: 1rem;"> <!-- 步骤文本提示 --> <div style="font-size: 12px; color: #666; text-align: center; margin-bottom: 4px;"> {{ predictProgress.stepText }} </div> </div> <!-- 星级结果展示(放大字体,增加星级图标,突出视觉) --> <div v-show="predictStoreStar !== 0" style="display: flex; flex-direction: column; align-items: center; gap: 1rem;"> <div style="font-size: 15px; color: #666;">今日评级结果</div> <div style="display: flex; align-items: center;"> <!-- 星级图标(动态显示,与评级结果联动) --> <div style="display: flex; gap: 0.25rem;"> <i class="bi bi-star-fill" style="color: #FFC107; font-size: 1rem;" v-for="(star, idx) in Math.min(predictStoreStar, 5)" :key="idx"></i> <!-- 10星特殊处理(显示2组5星) --> <template v-if="predictStoreStar === 10"> <i class="bi bi-star-fill" style="color: #FFC107; font-size: 1rem;" v-for="idx in 5" :key="idx+5"></i> </template> </div> </div> <!-- 结果说明(未评级时提示,已评级时显示分数范围) --> <div style="font-size: 12px; color: #666; text-align: center;"> <span v-if="predictStoreStar === 0">点击上方按钮生成评级</span> <span v-else> 对应分数范围:{{ getStarScoreRange(predictStoreStar) }} </span> </div> </div> <div v-show="predictStoreStar === 0" style="font-size: 12px; color: #666; text-align: center;"> <span>点击上方按钮生成评级</span> </div> </div> <!-- 2.3 注意事项说明区(优化排版,增加分隔线) --> <div style="padding-top: 15px; border-top: 1px solid #eee; margin-top: 1rem;"> <div style="font-size: 14px; font-weight: 600; color: #333; margin-bottom: 8px; display: flex; align-items: center; gap: 6px;"> <i class="bi bi-info-circle" style="color: #ff9800;"></i> 评级规则与说明 </div> <div style="font-size: 12px; color: #666; line-height: 1.8; padding-left: 22px;"> <!-- 新增:先说明“预测逻辑”,让用户理解评级依据 --> <p>1. 评级核心逻辑:基于今日种子生成独立网格,通过多轮智能预测(模拟最优点击策略【伪】)计算“参考最高得分”,最终按得分匹配星级,结果反映网格的潜力上限。</p> <!-- 优化:补充分数区间的“衔接关系”,消除“≤5万是1星,>5万是否算2星”的歧义 --> <p>2. 星级判定标准(按预测最高分划分):</p> <ul style="padding-left: 20px; margin: 8px 0; list-style-type: disc;"> <li>1星:0 ~ 50,000分(基础潜力)</li> <li>2星:50,001 ~ 70,000分(中等潜力)</li> <li>3星:70,001 ~ 100,000分(良好潜力)</li> <li>4星:100,001 ~ 150,000分(优秀潜力)</li> <li>5星:150,001 ~ 200,000分(极佳潜力)</li> <li>10星:>200,000分(满分评级,网格潜力天花板)</li> </ul> <!-- 优化:补充“种子时效性”的原因,增加用户理解 --> <p>3. 评级时效性:每日仅支持“当日种子”评级(种子由系统按日期自动生成,每日唯一),历史日期的种子无法回溯计算,建议每日尽早完成评级。</p> <!-- 优化:强化“理论与实际的差异”,给出实用提示 --> <p>4. 结果参考价值:评级是“理论最优得分”的参考,实际游戏中得分会受您的操作策略(如点击顺序、连锁反应触发时机)影响,建议结合“辅助预测”功能提升实际得分。</p> <!-- 优化:将“卡顿提示”改为“体验说明+应对建议”,降低用户焦虑 --> <p>5. 计算体验说明:预测过程中若出现短暂卡顿,是因系统在多轮迭代计算最优路径(网格尺寸越大、迭代轮次越多,耗时略长)</p> <ul style="padding-left: 20px; margin: 6px 0; list-style-type: disc; font-size: 11px;"> <li>计算时避免频繁切换页面或操作其他功能;</li> <li>若卡顿超过30秒,或影响到了浏览器正常运行,可关闭或刷新页面。</li> </ul> <p style="font-size: 11px; color: #888; margin-top: 6px;">注:算法优化已在规划中,未来将进一步提升计算速度与准确性。</p> </div> </div> </div> </div> </div> <!-- 操作日志面板 --> <div v-show="operationLogDialog" class="overlay-container history-overlay" @click="(e) => { if (e.target === e.currentTarget) operationLogDialog = false; }"> <div class="panel history-panel"> <!-- 面板头部:返回按钮 + 标题 + 分割线 --> <div class="panel__header"> <i class="bi bi-arrow-left" @click="operationLogDialog = false;"></i> <div> <h3 class="panel__title">当前操作日志</h3> <div class="panel__divider"></div> </div> <!-- 关闭所有遮罩 --> <i class="bi bi-x-circle" style="cursor: pointer;" @click="operationLogDialog = false;settingsDialog = false;"></i> </div> <!-- 面板内容区:滚动容器 --> <div class="panel__content"> <!-- 无操作日志提示 --> <div class="no-history" v-if="operationLog.records.length === 0"> 暂无操作记录 </div> <!-- 有操作日志时显示详情 --> <div v-else> <!-- 日志基础信息 --> <div class="history-item" style="margin-bottom: 15px;"> <div class="history-item-header"> <span>当前游戏日志</span> <span><i class="bi bi-star-fill" style="color: #FFC107; margin-right: 0.5em;"></i>{{ operationLog.score }}</span> </div> <div class="history-item-date"> 日期: {{ formatDate(operationLog.date) }} | <i class="bi bi-hash" style="color: #4CAF50;"></i>随机种子: {{ operationLog.time }} </div> <!-- 操作记录列表 --> <div class="operation-records" style="margin: 10px 0;"> <p style="margin: 0 0 5px; font-size: 14px;">操作序列【第x行第y列表示为(x, y)】:</p> <div v-for="(record, idx) in operationLog.records" :key="idx"> {{ idx + 1 }}. {{ formatOperationRecord(record) }} </div> </div> <!-- 完整JSON数据 --> <p style="margin: 10px 0 5px; font-size: 14px;"> 完整数据【复制内容后,可以在“录入操作”中粘贴后执行】:</p> <textarea class="json-textarea" :value="JSON.stringify(operationLog)" readonly></textarea> <!-- 功能按钮组 --> <div class="history-item-btns" style="margin-top: 15px;"> <button @click="executeHistory(operationLog)" :disabled="isExecutingHistory">运行该日志</button> <button class="challenge-btn" @click="createChallengeWithSeed(operationLog.time)" :disabled="isExecutingHistory">挑战相同种子</button> </div> </div> </div> </div> </div> </div> <!-- 历史记录面板 --> <div v-show="historyDialog" class="overlay-container history-overlay" @click="(e) => { if (e.target === e.currentTarget) historyDialog = false; }"> <div class="panel history-panel"> <!-- 面板头部:返回按钮 + 标题 + 分割线 --> <div class="panel__header"> <i class="bi bi-arrow-left" @click="historyDialog = false;"></i> <div> <h3 class="panel__title">历史记录</h3> <div class="panel__divider"></div> </div> <!-- 关闭所有遮罩 --> <i class="bi bi-x-circle" style="cursor: pointer;" @click="historyDialog = false;settingsDialog = false;"></i> </div> <!-- 面板内容区:滚动容器 --> <div class="panel__content"> <!-- 清空所有记录按钮 --> <button class="clear-history-btn" @click="clearAllHistoryRecords" :disabled="isExecutingHistory"> 清空所有历史记录 </button> <!-- 种子搜索区域 --> <div class="history-search"> <input type="text" placeholder="输入随机种子搜索记录" v-model="historySearchSeed" :disabled="isExecutingHistory"> <div> <button class="search-btn" @click="searchHistoryBySeed" :disabled="isExecutingHistory">搜索</button> <button class="search-btn" @click="historySearchSeed = ''+getTodaySeed();searchHistoryBySeed();" :disabled="isExecutingHistory">今日记录</button> <button class="delete-btn" @click="historySearchSeed = '';searchHistoryBySeed();" :disabled="isExecutingHistory">清除</button> </div> </div> <div class="search-results-info"> 显示 {{filteredHistoryRecords.length}} 条记录(共 {{totalHistoryRecords}} 条) </div> <!-- 历史记录列表 --> <ul class="history-list" v-if="filteredHistoryRecords.length > 0"> <li class="history-item" v-for="(record, index) in filteredHistoryRecords" :key="index" :data-seed="record.time"> <div class="history-item-header"> <span>记录 #{{index + 1}}</span> <span><i class="bi bi-star-fill" style="color: #FFC107; margin-right: 0.5em;"></i>{{record.score}}</span> </div> <div class="history-item-date"> 日期: {{formatDate(record.date)}} | <i class="bi bi-hash" style="color: #4CAF50;"></i>随机种子: {{record.time}} </div> <textarea class="json-textarea" :value="JSON.stringify(record)" readonly></textarea> <div class="history-item-btns"> <button @click="executeHistory(record)" :disabled="isExecutingHistory">运行该记录</button> <button class="challenge-btn" @click="createChallengeWithSeed(record.time)" :disabled="isExecutingHistory">挑战相同种子</button> <button class="delete-btn" @click="deleteHistoryRecord(index, record.$index)" :disabled="isExecutingHistory">删除</button> </div> </li> </ul> <div class="no-history" v-else> 暂无历史记录 </div> </div> </div> </div> <!-- 导入/导出记录面板 --> <div v-show="importExportRecordDialog" class="overlay-container history-overlay" @click="(e) => { if (e.target === e.currentTarget) importExportRecordDialog = false; }"> <div class="panel instructions-panel"> <!-- 面板头部:返回按钮 + 标题 + 分割线 --> <div class="panel__header"> <i class="bi bi-arrow-left" @click="importExportRecordDialog = false;"></i> <div> <h3 class="panel__title">导入/导出记录</h3> <div class="panel__divider"></div> </div> <!-- 关闭所有遮罩 --> <i class="bi bi-x-circle" style="cursor: pointer;" @click="importExportRecordDialog = false;settingsDialog = false;"></i> </div> <!-- 面板内容区:文本框 + 按钮 --> <div class="panel__content" style="padding: 20px;"> <!-- 固定高度带滚动条的textarea --> <textarea class="json-textarea" placeholder="导入:粘贴数据后点击「导入记录」按钮; 导出:点击「导出记录」按钮后复制文本框中的数据" v-model="importExportText" :disabled="isExecutingHistory" style="height: 200px;resize: none;overflow-y: auto;margin-bottom: 15px;"></textarea> <!-- 第一个按钮:导入记录(空事件) --> <button class="settings-panel__btn settings-panel__btn--action" @click="handleImportRecord" :disabled="isExecutingHistory" style="width: 100%; margin-bottom: 10px; padding: 8px 0;"> <i class="bi bi-upload"></i> 导入记录 </button> <!-- 第二个按钮:导出记录(空事件) --> <button class="settings-panel__btn settings-panel__btn--action" @click="handleExportRecord" :disabled="isExecutingHistory" style="width: 100%; padding: 8px 0;"> <i class="bi bi-download"></i> 导出记录 </button> </div> </div> </div> <!-- 辅助预测面板 --> <div v-show="assistPredictDialog" class="overlay-container history-overlay" @click="(e) => { if (e.target === e.currentTarget) assistPredictDialog = false; }"> <div class="panel instructions-panel"> <!-- 面板头部:返回按钮 + 标题 + 分割线 --> <div class="panel__header"> <i class="bi bi-arrow-left" @click="assistPredictDialog = false;"></i> <div> <h3 class="panel__title">辅助预测设置</h3> <div class="panel__divider"></div> </div> <!-- 关闭所有遮罩 --> <i class="bi bi-x-circle" style="cursor: pointer;" @click="assistPredictDialog = false;settingsDialog = false;"></i> </div> <!-- 面板内容区:表单布局 --> <div class="panel__content"> <!-- 1. 是否启用辅助 --> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px;"> <!-- 开关 --> <div style="display: flex; align-items: center; gap: 8px;"> <span style="font-size: 13px; color: #ff4949;" :style="{color: assistEnabled ? '#999' : '#ff4949'}">关闭辅助</span> <div style="width: 50px; height: 24px; border-radius: 12px; background-color: #eee; cursor: pointer; position: relative; transition: background-color 0.3s;" :style="{backgroundColor: assistEnabled ? '#13ce66' : '#ff4949'}" @click="assistEnabled = !assistEnabled"> <div style="width: 20px; height: 20px; border-radius: 50%; background-color: white; position: absolute; top: 2px; left: 2px; transition: left 0.3s;" :style="{left: assistEnabled ? '28px' : '2px'}"></div> </div> <span style="font-size: 13px; color: #13ce66;" :style="{color: assistEnabled ? '#13ce66' : '#999'}">启用辅助</span> </div> </div> <!-- 2. 选择优先级 --> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px;"> <!-- 开关 --> <div style="display: flex; align-items: center; gap: 8px;"> <span style="font-size: 13px; color: #ff4949;" :style="{color: selectedScore ? '#999' : '#ff4949'}">乘数优先</span> <div style="width: 50px; height: 24px; border-radius: 12px; background-color: #eee; cursor: pointer; position: relative; transition: background-color 0.3s;" :style="{backgroundColor: selectedScore ? '#13ce66' : '#ff4949'}" @click="selectedScore = !selectedScore"> <div style="width: 20px; height: 20px; border-radius: 50%; background-color: white; position: absolute; top: 2px; left: 2px; transition: left 0.3s;" :style="{left: selectedScore ? '28px' : '2px'}"></div> </div> <span style="font-size: 13px; color: #13ce66;" :style="{color: selectedScore ? '#13ce66' : '#999'}">分数优先</span> </div> </div> <!-- 3. 开启/关闭全局预测 --> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px;"> <!-- 开关 --> <div style="display: flex; align-items: center; gap: 8px;"> <span style="font-size: 13px; color: #ff4949;" :style="{color: assistPredictGlobal ? '#999' : '#ff4949'}">关闭全局预测</span> <div style="width: 50px; height: 24px; border-radius: 12px; background-color: #eee; cursor: pointer; position: relative; transition: background-color 0.3s;" :style="{backgroundColor: assistPredictGlobal ? '#13ce66' : '#ff4949'}" @click="assistPredictGlobal = !assistPredictGlobal"> <div style="width: 20px; height: 20px; border-radius: 50%; background-color: white; position: absolute; top: 2px; left: 2px; transition: left 0.3s;" :style="{left: assistPredictGlobal ? '28px' : '2px'}"></div> </div> <span style="font-size: 13px; color: #13ce66;" :style="{color: assistPredictGlobal ? '#13ce66' : '#999'}">启用全局预测</span> </div> </div> <!-- 注意事项说明区域 --> <div style="margin-top: 30px; padding-top: 15px; border-top: 1px solid #eee;"> <div style="font-size: 14px; font-weight: 600; color: #333; margin-bottom: 8px;"> 注意事项 </div> <div style="font-size: 12px; color: #666; line-height: 1.5;"> 1. 辅助预测仅基于当前网格状态计算<span style="font-weight: bold;">单步最优解</span>,不考虑后续多步操作的连锁收益,长期策略需结合玩家自主判断,结果仅供参考;<br> 2. 优先级选择(分数优先/乘数优先)仅影响预测推荐的排序规则,不改变实际游戏得分逻辑(乘数=预测耗尽装置数,分数=预测总分);<br> 3. 启用辅助后,仅筛选并显示前20个最优推荐位置(未指明具体排名);<br> 4. 全局预测功能:会将所有位置按排名的前20%、前40%、前60%、前80%、前100%分别置为红色、黄色、绿色、蓝色、灰色;<br> 5. 辅助配置(含启用状态、优先级选择)会自动保存到本地存储,刷新页面、重新打开游戏后仍保持当前设置,无需重复调整;<br> 6. 行动点数耗尽(≤0)、装置已耗尽旋转次数(显示红色),或处于连锁反应、历史记录执行状态时,辅助推荐会自动隐藏或暂停,待状态恢复后重新计算。 </div> </div> </div> </div> </div> </div> </div> <script> // 监听 bootstrap-icons 字体加载状态 if (document.fonts) { // 检查字体是否已加载 document.fonts.check('1em "bootstrap-icons"') ? markAsLoaded() : document.fonts.load('1em "bootstrap-icons"').then(markAsLoaded); } else { // 兼容不支持 FontFaceSet 的浏览器(延迟 500ms 强制移除占位符) setTimeout(markAsLoaded, 500); } // 移除占位符样式的函数 function markAsLoaded() { document.getElementById('fonts-style').remove(); } let URL = window.location.href; window.key = URL.split('=')[1] ? URL.split('=')[1] : null; // 主应用 new Vue({ el: '#app', data() { return { // 屏幕和布局相关配置 minScreenDimension: 1000, // 游戏容器的最大尺寸限制(取屏幕宽高最小值) sizeMin: 16, // 网格尺寸最小值最小值(行数和列数的最小值) sizeMax: 24, // 网格尺寸尺寸上限(行数和列数的最大值) speed: 0.5, // 装置旋转速度 flashEnabled: true, // 开启/关闭闪烁动画 // 游戏配置参数 infinityProbability: 0.33, // 装置有无穷旋转次数的概率(33%) numMin: 4, // 装置有限旋转次数的最小值 numMax: 16, // 装置有限旋转次数的最大值 // 游戏状态数据 devicesGrid: [], // 存储所有装置的二维数组,每个元素代表一个装置的配置 actionPoints: 10, // 玩家当前的行动点数,每次点击装置消耗1点 zeroRotationDevicesCount: 0, // 记录已耗尽旋转次数的装置数量 gameState: '运行', // 游戏当前状态('运行'或'备战') operationLog: { // 记录游戏操作日志的数据对象 time: null, // 随机种子(基于日期生成) records: [], // 存储操作记录的数组,格式为"状态_行_列" score: 0, // 当前游戏的总分数 date: null // 游戏开始的时间戳 }, currentClick: { rowIndex: null, colIndex: null, }, // 当前最后一次点击的坐标 autoSaveHistory: true, // 是否自动保存(执行历史记录时将值变成false,重置时变成true) // 连锁反应相关状态 chainReactionTimerId: -1, // 连锁反应定时器ID,-1表示无活跃定时器 // 撤回功能 operationSnapshotStack: [], // 操作快照栈,存储每次点击前的游戏状态 canUndo: false, // 是否可撤回(控制按钮禁用状态) isUndoing: false, // 标记是否正在执行撤回操作(避免并发问题) // 设置面板 settingsDialog: false, // 游戏说明面板 instructionsDialog: false, // 今日评级面板 isCalculatingRating: false, // 今日评级计算锁(防止并发) predictStoreStar: 0, // 今日评级结果(默认0星) predictStoreDialog: false, // 原有面板显示控制变量 // 今日评级进度条相关状态 predictProgress: { currentStep: 0, // 当前步骤(从0开始) totalSteps: 0, // 总步骤数(初始为0,计算前初始化) isActive: false, // 进度条是否显示(计算中为true) stepText: "准备计算..." // 步骤文本提示(如“预测第1轮/共5轮”) }, // 录入操作面板 executeRecordDialog: false, // 自定义JSON输入值 customRecordJson: '', customRecordSeed: '', // 操作日志面板 operationLogDialog: false, // 历史记录面板 -- begin historyDialog: false, // 历史记录面板显示控制 historySearchSeed: '', // 历史记录搜索输入 filteredHistoryRecords: [], // 过滤后的历史记录 totalHistoryRecords: 0, // 总历史记录数 // 历史记录执行相关状态 isExecutingHistory: false, // 是否正在执行历史记录 currentExecutionStep: 0, // 当前执行到历史记录的第几步 totalExecutionSteps: 0, // 历史记录的总步骤数 executionQueue: [], // 待执行的历史操作队列 executionTimer: null, // 历史记录执行的定时器ID // 历史记录面板 -- end // 导入/导出记录面板 importExportRecordDialog: false, // 面板显示控制 importExportText: '', // textarea绑定值 // 辅助预测面板 --begin assistPredictDialog: false, // 辅助预测面板显示控制 assistEnabled: false, // 是否启用辅助(开关绑定值) selectedScore: true, // 选中的优先级(复选框绑定值,数组存value) assistPredictGlobal: false, // 是否全局预测 assistPredictStyle: {}, // 预测面板的样式二维数组(和网格一一对应) // 辅助预测面板 --end } }, watch: { // 监听自定义JSON输入变化,实时调整高度 customRecordJson() { this.$nextTick(() => { this._adjustTextareaHeight('.custom-json-input .json-textarea'); }); }, customRecordSeed() { this.$nextTick(() => { this._adjustTextareaHeight('.custom-json-input .json-textarea'); }); }, // 监听辅助功能 assistContent() { this.assistEnabled ? this.startAssist() : this.stopAssist(); }, // 监听辅助启用状态变化 assistEnabled(newVal) { // 状态变了就保存 this.saveSettingsToLocalStorage(); }, // 监听辅助优先级变化 selectedScore(newVal) { // 状态变了就保存 this.saveSettingsToLocalStorage(); }, // 监听辅助全局启用状态变化 assistPredictGlobal(newVal) { // 状态变了就保存 this.saveSettingsToLocalStorage(); } }, computed: { // 计算装置尺寸 deviceSize() { if (this.devicesGrid.length === 0) return 40; const size = this.minScreenDimension / this.devicesGrid.length / 0.9; return Math.max(10, Math.min(80, size)); }, // 格式化装置高度(用于坐标显示) formattedDeviceHeight() { return Math.floor(this.deviceSize * 10 * 0.85) / 10; }, // 格式化装置宽度(用于坐标显示) formattedDeviceWidth() { return Math.floor(this.deviceSize * 10 * 0.85) / 10; }, // 判断是否可以进行自动点击 canAutoClick() { return this.devicesGrid.length > 0; }, // 判断装置是否可点击 isDeviceClickable() { return !(this.isExecutingHistory || this.isInChainReaction || this.actionPoints <= 0); }, // 判断是否正在连锁反应中 isInChainReaction() { return this.chainReactionTimerId !== -1; }, // 当assistEnabled、selectedScore、assistPredictGlobal发生改变时,该变量改变,从而触发监听器中的方法 assistContent() { return { isDeviceClickable: this.isDeviceClickable, assistEnabled: this.assistEnabled, selectedScore: this.selectedScore, assistPredictGlobal: this.assistPredictGlobal } } }, created() { // 1. 从本地存储加载配置(优先读取,无配置则用默认) this.loadSettingsFromLocalStorage(); // 初始化屏幕尺寸 this.minScreenDimension = this.getMinDimension(); window.addEventListener('resize', () => { this.minScreenDimension = this.getMinDimension(); }); // 生成初始装置网格 this.generateDevicesGrid(); }, methods: { // 1.本地存储操作 --begin /** * 从本地存储加载配置信息 */ loadSettingsFromLocalStorage() { const savedSettings = localStorageUtil.loadGameSettings(); this.speed = savedSettings.speed; this.flashEnabled = savedSettings.flashEnabled; this.assistEnabled = savedSettings.assistEnabled; this.selectedScore = savedSettings.selectedScore; this.assistPredictGlobal = savedSettings.assistPredictGlobal; }, /** * 将当前配置信息保存到本地存储 */ saveSettingsToLocalStorage() { const settings = { speed: this.speed, flashEnabled: this.flashEnabled, assistEnabled: this.assistEnabled, // 辅助启用状态 selectedScore: this.selectedScore, // 辅助优先级 assistPredictGlobal: this.assistPredictGlobal, // 辅助全局启用状态 }; localStorageUtil.saveGameSettings(settings); }, // 1.本地存储操作 --end // 2.屏幕与布局适配 --begin // 获取屏幕最小尺寸 getMinDimension() { return Math.min(window.innerWidth - 20, window.innerHeight - 120); }, /** * 自动调整 textarea 高度以适配内容(无滚动条全显示) * @param {String} textareaSelector - textarea 的选择器(如 '.json-textarea') */ _adjustTextareaHeight(textareaSelector) { // 获取所有目标 textarea 元素(历史记录列表中可能有多个) const textareas = document.querySelectorAll(textareaSelector); textareas.forEach(textarea => { // 1. 先重置高度为最小高度,避免计算偏差 textarea.style.height = 40; // 2. 计算文本实际高度(scrollHeight 包含所有内容高度,不包括滚动条) const contentHeight = textarea.scrollHeight; // 3. 限制高度大于minHeight const minHeight = parseInt(textarea.style.minHeight) || 40; const finalHeight = Math.max(contentHeight, minHeight); // 4. 设置最终高度 textarea.style.height = `${finalHeight}px`; }); }, // 2.屏幕与布局适配 --end // 3.游戏基础状态控制 --begin // 切换游戏状态 toggleGameState() { if (!this.isInChainReaction) { this.gameState = this.gameState === '备战' ? '运行' : '备战'; } }, // 切换游戏运行速度 toggleSpeed() { // 1. 切换速度 this.speed = this.speed === 0.5 ? 0.25 : 0.5; // 2. 同步保存到本地存储 this.saveSettingsToLocalStorage(); }, toggleFlashEnabled() { this.flashEnabled = !this.flashEnabled; this.saveSettingsToLocalStorage(); }, // 打开设置面板 showSettings() { this.settingsDialog = true; }, // 格式化日期 formatDate(timestamp) { if (!timestamp) return ''; const date = new Date(timestamp); return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0') }-${date.getDate().toString().padStart(2, '0') } ${date.getHours().toString().padStart(2, '0') }:${date.getMinutes().toString().padStart(2, '0') }`; }, // 3.游戏基础状态控制 --end // 4.历史记录与日志管理 --begin // 查看历史记录 showHistoryRecords() { this.searchHistoryBySeed(); this.historyDialog = true; }, // 搜索历史记录(根据种子) searchHistoryBySeed() { const historyRecords = localStorageUtil.getHistoryRecords(); this.totalHistoryRecords = historyRecords.length; if (!this.historySearchSeed.trim()) { this.filteredHistoryRecords = historyRecords; return; } this.filteredHistoryRecords = historyRecords.filter(record => record.time.toString().includes(this.historySearchSeed.trim()) ); // 搜索结果更新后,重新调整 textarea 高度 this.$nextTick(() => { this._adjustTextareaHeight('.json-textarea'); }); }, // 删除指定的历史记录 deleteHistoryRecord(index, $index) { if (confirm('确定要删除这条历史记录吗?此操作不可恢复。')) { this.filteredHistoryRecords.splice(index, 1); localStorageUtil.deleteHistoryRecord($index); // 删除记录后,重新调整剩余 textarea 高度 this.$nextTick(() => { this._adjustTextareaHeight('.json-textarea'); }); } }, // 清空所有历史记录 clearAllHistoryRecords() { if (confirm('确定要删除所有历史记录吗?此操作不可恢复。')) { localStorageUtil.clearAllHistoryRecords(); this.searchHistoryBySeed(); } }, // 打开操作日志面板 logOperationDetails() { // 直接显示操作日志面板,无需拼接弹窗 this.operationLogDialog = true; // 面板显示后调整 JSON 文本框高度 this.$nextTick(() => { this._adjustTextareaHeight('.json-textarea'); }); }, /** * 格式化操作记录(如 "1_0_0" → "运行-坐标(1,1)") * @param {string} record 原始操作记录(格式:状态_行_列) * @returns {string} 格式化后的字符串 */ formatOperationRecord(record) { const [stateCode, rowIndex, colIndex] = record.split('_').map(Number); const stateText = stateCode === 1 ? '运行' : '备战'; // 坐标转换为「人类习惯的1开始计数」 const humanRow = rowIndex + 1; const humanCol = colIndex + 1; return `${stateText}-坐标(${humanRow}, ${humanCol})`; }, // 4.历史记录与日志管理 --end // 5.历史记录执行逻辑 --begin // 执行历史记录 executeHistory(record) { if (this.historyDialog) { // 关闭历史记录面板和设置面板 this.historyDialog = false; this.settingsDialog = false; } else if (this.executeRecordDialog) { // 关闭录入操作面板和设置面板 this.executeRecordDialog = false; this.settingsDialog = false; } else if (this.operationLogDialog) { // 关闭操作日志面板和设置面板 this.operationLogDialog = false; this.settingsDialog = false; } // 停止连锁反应 if (this.isInChainReaction) { clearInterval(this.chainReactionTimerId); this.chainReactionTimerId = -1; } // 初始化执行状态 this.isExecutingHistory = true; this.executionQueue = [...record.records]; this.totalExecutionSteps = this.executionQueue.length; this.currentExecutionStep = 0; // 使用相同的随机种子生成相同的网格 this.operationLog = { time: record.time, date: record.date || Date.now(), records: [], score: 0 }; this.actionPoints = 10; this.zeroRotationDevicesCount = 0; // 生成与历史记录相同的网格 this.devicesGrid = []; this.$nextTick(() => { this.devicesGrid = this._generateRandom2DArray({ sizeMin: this.sizeMin, sizeMax: this.sizeMax, infinityProbability: this.infinityProbability, numMin: this.numMin, numMax: this.numMax, seed: this.operationLog.time }); // 网格生成后开始执行操作 this.$nextTick(() => { this.autoSaveHistory = false; this._executeNextStep(); }); }); }, // 执行下一步操作 _executeNextStep() { if (!this.isExecutingHistory || this.executionQueue.length === 0) { this._finishExecution(); return; } // 等待连锁反应结束 if (this.isInChainReaction) { setTimeout(() => this._executeNextStep(), 100); return; } // 执行下一步操作 const operation = this.executionQueue.shift(); this.currentExecutionStep++; const [stateCode, rowIndex, colIndex] = operation.split('_').map(Number); this.gameState = stateCode === 1 ? '运行' : '备战'; // 执行点击操作 const deviceRef = this._getDeviceRef(rowIndex, colIndex); if (deviceRef && deviceRef.remainingRotationTimes > 0) { deviceRef.rotate90Degrees('history'); this.executionTimer = setTimeout(() => this._executeNextStep(), 1000); } else { this._executeNextStep(); } }, // 完成历史记录执行 _finishExecution() { if (this.executionTimer) { clearTimeout(this.executionTimer); this.executionTimer = null; } this.isExecutingHistory = false; this.currentExecutionStep = 0; this.totalExecutionSteps = 0; this.executionQueue = []; }, /** * 运行自定义录入操作 */ executeCustomRecord() { // 校验输入不为空 if (!this.customRecordJson.trim()) { alert('请输入符合格式的 operationLog JSON 字符串'); return; } try { // 解析JSON字符串 const customRecord = JSON.parse(this.customRecordJson); // 校验必填字段 if (!customRecord.time || !customRecord.records || !Array.isArray(customRecord .records)) { throw new Error('JSON格式错误!需包含 "time"(随机种子)和 "records"(操作记录数组)字段'); } // 关闭录入操作面板 this.executeRecordDialog = false; this.settingsDialog = false; // 复用历史记录的执行逻辑 this.executeHistory(customRecord); } catch (e) { alert('操作失败:' + e.message); } }, /** * 挑战自定义录入操作 */ challengeCustomRecord(customRecordJson) { // 校验输入不为空 if (!customRecordJson.trim()) { alert('请输入符合格式的 operationLog JSON 字符串'); return; } try { // 解析JSON字符串 const customRecord = JSON.parse(customRecordJson); // 挑战仅需校验种子字段 if (!customRecord.time && customRecord.time !== 0) { throw new Error('JSON格式错误!需包含 "time"(随机种子)字段'); } // 关闭录入操作面板 this.executeRecordDialog = false; this.settingsDialog = false; // 复用历史记录的挑战逻辑 this.createChallengeWithSeed(customRecord.time); } catch (e) { alert('挑战失败:' + e.message); } }, // 使用指定种子创建挑战(生成相同网格但不执行历史操作) createChallengeWithSeed(seed) { if (this.historyDialog) { // 关闭历史记录面板和设置面板 this.historyDialog = false; this.settingsDialog = false; } else if (this.executeRecordDialog) { // 关闭录入操作面板和设置面板 this.executeRecordDialog = false; this.settingsDialog = false; } else if (this.operationLogDialog) { // 关闭操作日志面板和设置面板 this.operationLogDialog = false; this.settingsDialog = false; } // 停止连锁反应 if (this.isInChainReaction) { clearInterval(this.chainReactionTimerId); this.chainReactionTimerId = -1; } // 停止历史执行 if (this.isExecutingHistory) this._finishExecution(); // 初始化日志 this.operationLog = { time: seed, date: Date.now(), records: [], score: 0 }; // 重置游戏状态 this.actionPoints = 10; this.zeroRotationDevicesCount = 0; this.gameState = '运行'; // 清空快照栈和可撤回状态 this.operationSnapshotStack = []; this.canUndo = false; // 重置网格时,清除最后点击标记 this.currentClick = { rowIndex: null, colIndex: null }; // 生成网格 this.devicesGrid = []; this.$nextTick(() => { this.devicesGrid = this._generateRandom2DArray({ sizeMin: this.sizeMin, sizeMax: this.sizeMax, infinityProbability: this.infinityProbability, numMin: this.numMin, numMax: this.numMax, seed: this.operationLog.time }); this.$nextTick(() => { this.startAssist(); }); }); }, // 5.历史记录执行逻辑 --end // 6.网格与装置核心逻辑 --begin // 获取今日种子 getTodaySeed() { const today = new Date(); return (today.getFullYear() * 10000 + (today.getMonth() + 1) * 100 + today.getDate()); }, /** * 生成装置网格(支持自定义初始状态) * @param {Array} [devicesGrid] 自定义装置二维数组(可选) * @param {number} [actionPoints=10] 自定义剩余行动数(可选,默认10) * @param {number} [zeroRotationDevicesCount=0] 自定义耗尽装置数(可选,默认0) * @param {number} [score=0] 自定义初始分数(可选,默认0) * @param {number} [seed] 自定义随机种子(可选,仅当未传入devicesGrid时生效) */ generateDevicesGrid(devicesGrid, actionPoints = 10, zeroRotationDevicesCount = 0, score = 0, seed) { // 初始化操作日志(优先使用传入的种子,否则生成基于当天的种子) const currentSeed = seed || this.getTodaySeed(); // 当前种子 this.operationLog = { time: currentSeed, date: Date.now(), records: [], score: score // 初始分数:优先使用传入值 }; // 重置游戏状态(优先使用传入的参数) this.actionPoints = actionPoints; this.zeroRotationDevicesCount = zeroRotationDevicesCount; this.gameState = '运行'; // 默认状态为“运行” // 判断是否使用传入的自定义网格 if (devicesGrid && Array.isArray(devicesGrid) && devicesGrid.length > 0) { // 校验传入的网格格式是否合法(二维数组且每行元素为对象) const isGridValid = devicesGrid.every(row => Array.isArray(row) && row.every(device => device && typeof device === 'object' && 'maxRotationTimes' in device && 'initRotationAngle' in device ) ); if (isGridValid) { this.devicesGrid = devicesGrid.map(row => row.map(device => ({ ...device })) // 深拷贝传入的网格,避免外部修改影响内部状态 ); return; // 直接使用自定义网格,终止后续随机生成逻辑 } } // 未传入有效网格时,按原逻辑随机生成(使用传入的seed或默认种子) this.devicesGrid = this._generateRandom2DArray({ sizeMin: this.sizeMin, sizeMax: this.sizeMax, infinityProbability: this.infinityProbability, numMin: this.numMin, numMax: this.numMax, seed: currentSeed }); }, // 重新生成网格(重置功能) regenerateGrid() { // 1. 清理连锁反应与历史执行 if (this.isExecutingHistory) this._finishExecution(); if (this.isInChainReaction) this._stopChainReaction(); // 2. 重置快照栈与可撤回状态 this.operationSnapshotStack = []; this.canUndo = false; this.currentClick = { rowIndex: null, colIndex: null }; // 生成随机网格 this.devicesGrid = []; this.$nextTick(() => { this.generateDevicesGrid() this.$nextTick(() => { this.autoSaveHistory = true; this.startAssist() }); }); }, /** * 特殊初始化方法:种子为特定值时,手动设置固定网格 * @returns {Array} 手动定义的装置网格(二维数组) */ specialInitGrid(seed, devicesGrid) { if (seed === 20251001) { devicesGrid = [[{"maxRotationTimes":1,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":1,"initRotationAngle":270,"currentRotationAngle":90,"t":true},{"maxRotationTimes":3,"initRotationAngle":90,"currentRotationAngle":0,"t":true},{"maxRotationTimes":3,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":2,"initRotationAngle":180,"currentRotationAngle":0,"t":true},{"maxRotationTimes":2,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":1,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":2,"initRotationAngle":180,"currentRotationAngle":0,"t":true},{"maxRotationTimes":1,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":3,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":5,"initRotationAngle":270,"currentRotationAngle":90,"t":true},{"maxRotationTimes":5,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":8,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":6,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":5,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":6,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":5,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":4,"initRotationAngle":0,"currentRotationAngle":180,"t":true},{"maxRotationTimes":6,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":7,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":5,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":4,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":3,"initRotationAngle":270,"currentRotationAngle":270}],[{"maxRotationTimes":2,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":3,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":4,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":2,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":6,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":5,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":2,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":5,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":4,"initRotationAngle":180,"currentRotationAngle":270},{"maxRotationTimes":9,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":7,"initRotationAngle":180,"currentRotationAngle":270,"t":true},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":11,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":9,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":8,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":8,"initRotationAngle":0,"currentRotationAngle":180,"t":true},{"maxRotationTimes":8,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":9,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":9,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":7,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":1,"initRotationAngle":0,"currentRotationAngle":0,"t":true}],[{"maxRotationTimes":3,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":5,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":4,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":8,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":9,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":5,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":8,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":7,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":0},{"maxRotationTimes":15,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":13,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":13,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":11,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":11,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":15,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":15,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":13,"initRotationAngle":0,"currentRotationAngle":180},{"maxRotationTimes":6,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":2,"initRotationAngle":0,"currentRotationAngle":0}],[{"maxRotationTimes":5,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":8,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":7,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":8,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":9,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":9,"initRotationAngle":270,"currentRotationAngle":0,"t":true},{"maxRotationTimes":7,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":6,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":9,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":15,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":14,"initRotationAngle":270,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":15,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":14,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":13,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":12,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":9,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":13,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":13,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":8,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":4,"initRotationAngle":180,"currentRotationAngle":180}],[{"maxRotationTimes":5,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":9,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":8,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":8,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":7,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":7,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":14,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":12,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":16,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":13,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":270,"t":true},{"maxRotationTimes":16,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":16,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":15,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":13,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":12,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":12,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":8,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":3,"initRotationAngle":0,"currentRotationAngle":180}],[{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":270,"t":true},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":9,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":6,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":1,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":2,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":270,"t":true},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":14,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":16,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":12,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":13,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true}, {"maxRotationTimes":13,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":8,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":9,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":7,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":2,"initRotationAngle":90,"currentRotationAngle":90}],[{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":15,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":13,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":7,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":6,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":12,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":13,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":15,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":16,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":12,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":12,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":11,"initRotationAngle":0,"currentRotationAngle":90},{"maxRotationTimes":13,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":14,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":9,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":11,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":11,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":2,"initRotationAngle":90,"currentRotationAngle":90}],[{"maxRotationTimes":5,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":12,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":12,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":13,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":8,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":8,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":14,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":17,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":14,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":12,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":14,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":16,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":15,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":12,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":8,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":2,"initRotationAngle":90,"currentRotationAngle":90}],[{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":12,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":8,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":7,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":15,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":16,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":15,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":17,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":16,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":15,"initRotationAngle":0,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":14,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":9,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":5,"initRotationAngle":180,"currentRotationAngle":180}],[{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":270,"t":true},{"maxRotationTimes":9,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":9,"initRotationAngle":180,"currentRotationAngle":0},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":15,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":16,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":14,"initRotationAngle":90,"currentRotationAngle":270},{"maxRotationTimes":17,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":18,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":18,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":18,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":14,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":19,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":14,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":11,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":13,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":9,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":9,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":4,"initRotationAngle":270,"currentRotationAngle":270}],[{"maxRotationTimes":7,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":7,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":20,"initRotationAngle":270,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":15,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":15,"initRotationAngle":180,"currentRotationAngle":90},{"maxRotationTimes":11,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":12,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":11,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":6,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":4,"initRotationAngle":180,"currentRotationAngle":180,"t":true}],[{"maxRotationTimes":7,"initRotationAngle":90,"currentRotationAngle":270},{"maxRotationTimes":11,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":12,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":14,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":16,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":16,"initRotationAngle":0,"currentRotationAngle":0}, {"maxRotationTimes":17,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":15,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":13,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":15,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":19,"initRotationAngle":90,"currentRotationAngle":0},{"maxRotationTimes":27,"initRotationAngle":90,"currentRotationAngle":270},{"maxRotationTimes":20,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":20,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":22,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":21,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":19,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":15,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":16,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":13,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":12,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":6,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":5,"initRotationAngle":180,"currentRotationAngle":180}],[{"maxRotationTimes":7,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":14,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":17,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":17,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":13,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":14,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":15,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":17,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":24,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":24,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":20,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":20,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":19,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":19,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":15,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":8,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":4,"initRotationAngle":270,"currentRotationAngle":90}],[{"maxRotationTimes":5,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":4,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":3,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":14,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":15,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":15,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":14,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":16,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":15,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":20,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":21,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":13,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":14,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":15,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":3,"initRotationAngle":0,"currentRotationAngle":0}],[{"maxRotationTimes":4,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":3,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":2,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":12,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":11,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":13,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":17,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":13,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":12,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":14,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":14,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":13,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":15,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":14,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":11,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":5,"initRotationAngle":180,"currentRotationAngle":180}],[{"maxRotationTimes":5,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":4,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":9,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":13,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":14,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":13,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":14,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":16,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":180,"t":true},{"maxRotationTimes":12,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":15,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":15,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":11,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":12,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":12,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":9,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":5,"initRotationAngle":180,"currentRotationAngle":180}],[{"maxRotationTimes":4,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":11,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":9,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":9,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":11,"initRotationAngle":0,"currentRotationAngle":180},{"maxRotationTimes":11,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":9,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":13,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":13,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":12,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":18,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":17,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":14,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":15,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":13,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":11,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":13,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":6,"initRotationAngle":0,"currentRotationAngle":90}, {"maxRotationTimes":4,"initRotationAngle":270,"currentRotationAngle":270}],[{"maxRotationTimes":5,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":12,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":9,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":9,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":18,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":22,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":9,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":9,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":9,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":6,"initRotationAngle":270,"currentRotationAngle":270,"t":true},{"maxRotationTimes":2,"initRotationAngle":90,"currentRotationAngle":90}],[{"maxRotationTimes":4,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":13,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":11,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":11,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":11,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":14,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":7,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":6,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":6,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":12,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":17,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":4,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":4,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":4,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":7,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":9,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":11,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":11,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":8,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":4,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":2,"initRotationAngle":90,"currentRotationAngle":90}],[{"maxRotationTimes":3,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":12,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":9,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":13,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":13,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":11,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":10,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":11,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":11,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":11,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":9,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":12,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":9,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":6,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":6,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":6,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":9,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":6,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":6,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":4,"initRotationAngle":270,"currentRotationAngle":270}],[{"maxRotationTimes":2,"initRotationAngle":180,"currentRotationAngle":270},{"maxRotationTimes":8,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":8,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":9,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":11,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":10,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":10,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":12,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":12,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":11,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":9,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":8,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":8,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":8,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":6,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":8,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":4,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":5,"initRotationAngle":180,"currentRotationAngle":270},{"maxRotationTimes":5,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":270},{"maxRotationTimes":6,"initRotationAngle":270,"currentRotationAngle":180,"t":true},{"maxRotationTimes":3,"initRotationAngle":0,"currentRotationAngle":90}],[{"maxRotationTimes":3,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":5,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":4,"initRotationAngle":180,"currentRotationAngle":180,"t":true},{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":5,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":4,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":7,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":5,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":7,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":5,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":8,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":6,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":9,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":9,"initRotationAngle":0,"currentRotationAngle":90},{"maxRotationTimes":6,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":9,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":5,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":5,"initRotationAngle":180,"currentRotationAngle":180},{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":5,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":2,"initRotationAngle":0,"currentRotationAngle":0,"t":true}],[{"maxRotationTimes":2,"initRotationAngle":90,"currentRotationAngle":270},{"maxRotationTimes":3,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":2,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":5,"initRotationAngle":90,"currentRotationAngle":90,"t":true},{"maxRotationTimes":5,"initRotationAngle":270,"currentRotationAngle":180},{"maxRotationTimes":3,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":4,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":3,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":3,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":3,"initRotationAngle":0,"currentRotationAngle":180,"t":true},{"maxRotationTimes":3,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":4,"initRotationAngle":0,"currentRotationAngle":0},{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":180},{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":90},{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":180},{"maxRotationTimes":5,"initRotationAngle":90,"currentRotationAngle":180,"t":true},{"maxRotationTimes":6,"initRotationAngle":0,"currentRotationAngle":0,"t":true},{"maxRotationTimes":5,"initRotationAngle":90,"currentRotationAngle":180},{"maxRotationTimes":4,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":4,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":6,"initRotationAngle":90,"currentRotationAngle":180},{"maxRotationTimes":4,"initRotationAngle":270,"currentRotationAngle":270},{"maxRotationTimes":4,"initRotationAngle":270,"currentRotationAngle":270}]]; } return devicesGrid; }, // 生成随机的二维装置数组 _generateRandom2DArray(options) { // 是否属于特殊种子 const isSpecial = options.seed === 20251001 const { sizeMin = 10, sizeMax = 20, infinityProbability = 0.3, numMin = 3, numMax = 10, seed = this.getTodaySeed() } = !isSpecial ? options : { ...options, sizeMin: 23, sizeMax: 23, numMin: 10, numMax: 20, seed: options.seed }; // 随机数生成器 const rand = this._mulberry32(seed); const size = Math.floor(rand() * (sizeMax - sizeMin + 1)) + sizeMin; let result = []; const initialRotationAngles = [0, 90, 180, 270]; // 生成二维数组 for (let i = 0; i < size; i++) { const row = []; for (let j = 0; j < size; j++) { // 随机决定是否为无限次旋转 const maxRotationTimes = rand() < infinityProbability ? Infinity : Math.floor(rand() * (numMax - numMin + 1)) + numMin; // 随机初始旋转角度 const initRotationAngle = initialRotationAngles[ Math.floor(rand() * initialRotationAngles.length) ]; row.push({ maxRotationTimes, initRotationAngle, currentRotationAngle: initRotationAngle }); } result.push(row); } if (isSpecial) result = this.specialInitGrid(seed, result) return result; }, // 随机数生成算法 _mulberry32(seed) { return function () { seed += 0x6D2B79F5; let t = seed; t = Math.imul(t ^ t >>> 15, t | 1); t ^= t + Math.imul(t ^ t >>> 7, t | 61); return ((t ^ t >>> 14) >>> 0) / 4294967296; }; }, // 获取装置引用 _getDeviceRef(rowIndex, colIndex) { const refs = this.$refs[`device_${rowIndex}_${colIndex}`]; return refs && Array.isArray(refs) ? refs[0] : null; }, // 6.网格与装置核心逻辑 --end // 7.连锁反应与得分计算 --begin // 处理装置旋转事件 handleDeviceRotated(rowIndex, colIndex, eventData) { const { oldDevice, triggerSource, becameZero, remainingRotations } = eventData; // 仅当玩家主动点击/自动点击时,在操作前生成快照(排除连锁反应自动触发和历史执行) if (['click', 'auto'].includes(triggerSource) && !this.isExecutingHistory && !this .isInChainReaction && !this.isUndoing) { const snapshot = this._createGameSnapshot(rowIndex, colIndex, oldDevice); this.operationSnapshotStack.push(snapshot); // 更新可撤回状态(有快照则可撤回) this.canUndo = this.operationSnapshotStack.length > 0; // 暂时停止预测 this.stopAssist(); } // 更新已耗尽装置计数 if (becameZero) { this.zeroRotationDevicesCount++; } // 计算分数 if (['click', 'auto', 'history'].includes(triggerSource)) { this.currentClick = { rowIndex: rowIndex, colIndex: colIndex } const multiplier = 1 + this.zeroRotationDevicesCount; this.operationLog.score += remainingRotations === 0 ? 10 * multiplier : 1 * multiplier; } // 处理不同触发来源的逻辑 switch (triggerSource) { case 'click': case 'auto': case 'history': // 记录操作日志 const stateCode = this.gameState === '备战' ? 0 : 1; this.operationLog.records.push(`${stateCode}_${rowIndex}_${colIndex}`); // 消耗行动点 - 这是触发颜色重置的关键位置 const previousActionPoints = this.actionPoints; this.actionPoints--; // 当行动点数减少时,重置所有装置颜色为原蓝色 if (previousActionPoints > this.actionPoints) { this._resetAllDeviceColors(); } // 反应状态下触发连锁反应 if (this.gameState === '运行' && !this.isInChainReaction) { // 记录当前触发旋转的装置坐标集合 this._startChainReaction(rowIndex, colIndex, oldDevice); } break; case 'force': break; } }, // 开始连锁反应 _startChainReaction(i, j, oldDevice) { // 预测点击后的结果 const currentState = { devicesGrid: this.devicesGrid.map((row, r) => row.map((device, c) => { if (i === r && c === j) { // 触发连锁反应的装置需要用oldDevice的角度和剩余次数 return { maxRotationTimes: device.maxRotationTimes, initRotationAngle: device.initRotationAngle, currentRotationAngle: oldDevice.currentRotationAngle, remainingRotationTimes: oldDevice.remainingRotationTimes, }; } else { let deviceRef = this._getDeviceRef(r, c); return { maxRotationTimes: device.maxRotationTimes, initRotationAngle: device.initRotationAngle, currentRotationAngle: deviceRef.currentRotationAngle, remainingRotationTimes: deviceRef.remainingRotationTimes, }; } }) ), actionPoints: this.actionPoints + 1, // 如果oldDevice的剩余次数是1,则zeroRotationDevicesCount需要+1 // (因为oldDevice执行完旋转后,剩余次数会变成0,此时装置消耗总数+1) zeroRotationDevicesCount: this.zeroRotationDevicesCount + (oldDevice .remainingRotationTimes === 1 ? 1 : 0), score: 0, // 此处分数错误,但不会影响操作 }; const predictedState = this._predictChainReaction(currentState, i, j); // 计时器的次数 let num = 1; // 设置连锁反应定时器 this.chainReactionTimerId = setInterval(() => { if (num < predictedState.rotationBatches.length) { const currentRotationRowColArray = predictedState.rotationBatches[ num]; // 本轮连锁反应的旋转装置坐标集合 // 汇总待旋转的旋转装置列表 // 存储待旋转的装置的rowIndex_colIndex,主要用于去重判断 let pendingRotationCoordsSet = new Set(); // 存储待旋转的装置组件列表 let deviceRefList = []; currentRotationRowColArray.forEach((rowColIndex) => { const rowColArray = rowColIndex.split('_'); const rowIndex = parseInt(rowColArray[0]); const colIndex = parseInt(rowColArray[1]); const rotatingDevice = this._getDeviceRef(rowIndex, colIndex); if (!rotatingDevice) return; deviceRefList.push(rotatingDevice); }); // 没有待处理装置时结束连锁反应 if (deviceRefList.length === 0) { this._stopChainReaction(); return; } // 计算并添加连锁反应分数 const multiplier = 1 + this.zeroRotationDevicesCount; this.operationLog.score += deviceRefList.length * multiplier; // 处理当前批次的旋转 deviceRefList.forEach(deviceRef => { if (deviceRef) { deviceRef.forceRotate('force'); } }); num++; } else { this._stopChainReaction(); return; } }, this.speed * 1000); }, // 停止连锁反应的定时器 _stopChainReaction() { clearInterval(this.chainReactionTimerId); this.chainReactionTimerId = -1; // 继续执行历史记录(如果正在执行) if (this.isExecutingHistory) { this._executeNextStep(); } else { // 不是执行记录,则保存记录并预测 this.saveHistoryRecord(); this.startAssist(); } }, // 重置指定装置的颜色为原蓝色 resetDeviceColor(rowIndex, colIndex) { const deviceRef = this._getDeviceRef(rowIndex, colIndex); if (deviceRef) { deviceRef.resetToNormalColor(); } }, // 重置所有装置的颜色为原蓝色 _resetAllDeviceColors() { this.devicesGrid.forEach((row, rowIndex) => { row.forEach((_, colIndex) => this.resetDeviceColor(rowIndex, colIndex)); }); }, // 7.连锁反应与得分计算 --end // 8.撤回与状态快照 --begin /** * 生成游戏状态快照(深拷贝,避免状态引用污染) * @returns {Object} 包含所有关键状态的快照对象 */ _createGameSnapshot(rowIndex, colIndex, oldDevice) { // 1. 从组件实例中读取装置状态(旋转角度、剩余旋转次数) const devicesGridSnapshot = []; for (let i = 0; i < this.devicesGrid.length; i++) { const row = []; for (let j = 0; j < this.devicesGrid[i].length; j++) { const deviceRef = this._getDeviceRef(i, j); if (i === rowIndex && j === colIndex) { row.push({ maxRotationTimes: this.devicesGrid[i][j] .maxRotationTimes, // 基础配置仍来自devicesGrid initRotationAngle: this.devicesGrid[i][j].initRotationAngle, currentRotationAngle: oldDevice .currentRotationAngle, // 此时组件已执行完旋转操作,所以快照中需要保留执行前的数据 remainingRotationTimes: oldDevice .remainingRotationTimes, // 此时组件已执行完旋转操作,所以快照中需要保留执行前的数据 t: this.devicesGrid[i][j].t, }); } else { row.push({ maxRotationTimes: this.devicesGrid[i][j] .maxRotationTimes, // 基础配置仍来自devicesGrid initRotationAngle: this.devicesGrid[i][j].initRotationAngle, currentRotationAngle: deviceRef.currentRotationAngle, // 存储组件当前数据 remainingRotationTimes: deviceRef .remainingRotationTimes, // 存储组件当前数据 t: this.devicesGrid[i][j].t, }); } } devicesGridSnapshot.push(row); } // 2. 深拷贝操作日志 const operationLogSnapshot = JSON.parse(JSON.stringify(this.operationLog)); // 3. 返回完整快照 return { devicesGrid: devicesGridSnapshot, actionPoints: this.actionPoints, zeroRotationDevicesCount: this.zeroRotationDevicesCount, operationLog: operationLogSnapshot, currentClick: { ...this.currentClick }, timestamp: Date.now() }; }, // 撤回上一步操作(恢复到最近一次点击前的状态) undoLastOperation() { // 撤回边界校验:无快照/连锁反应中/执行历史中,禁止撤回 if (this.operationSnapshotStack.length === 0 || this.isInChainReaction || this .isExecutingHistory) { this.canUndo = this.operationSnapshotStack.length > 0; return; } // 撤回前停止连锁反应 if (this.isInChainReaction) { this._stopChainReaction(); } this.isUndoing = true; // 标记正在撤回,避免并发操作 try { // 1. 弹出最近的快照(无需清空网格,直接覆盖状态) const lastSnapshot = this.operationSnapshotStack.pop(); // 2. 恢复基础状态(分数、行动点等) this.actionPoints = lastSnapshot.actionPoints; this.zeroRotationDevicesCount = lastSnapshot.zeroRotationDevicesCount; this.operationLog = JSON.parse(JSON.stringify(lastSnapshot.operationLog)); this.currentClick = { ...lastSnapshot.currentClick }; // 3. 恢复网格结构(若尺寸不变,直接更新;若尺寸变化,才需要重新渲染) this.devicesGrid = JSON.parse(JSON.stringify(lastSnapshot.devicesGrid)); // 4. 等待网格更新后,同步组件状态(仅需一次 $nextTick) this.$nextTick(() => { for (let rowIndex = 0; rowIndex < this.devicesGrid.length; rowIndex++) { for (let colIndex = 0; colIndex < this.devicesGrid[rowIndex] .length; colIndex++) { const deviceRef = this._getDeviceRef(rowIndex, colIndex); const snapshotDevice = lastSnapshot.devicesGrid[rowIndex][colIndex]; if (deviceRef && snapshotDevice) { // 恢复组件的旋转角度和剩余次数(直接操作组件实例) deviceRef.currentRotationAngle = snapshotDevice .currentRotationAngle; deviceRef.remainingRotationTimes = snapshotDevice .remainingRotationTimes; // 恢复颜色并强制更新(确保视图同步) deviceRef.resetToNormalColor(); deviceRef.$forceUpdate(); // 兼容非响应式属性的组件 } } } // 5. 更新可撤回状态 this.canUndo = this.operationSnapshotStack.length > 0; this.startAssist(); }); } catch (error) { alert("撤回失败,请重试"); } finally { this.isUndoing = false; // 解除撤回标记 } }, /** * 保存记录到本地存储 * 规则: * 1. 每个随机种子的得分前三名必然保留 * 2. 所有种子的全部记录中,分数排名前20名必然保留 * 最终历史记录数量可能超过20条 */ saveHistoryRecord() { if (this.operationLog.records.length === 0) return; if (this.autoSaveHistory) localStorageUtil.saveHistoryRecord(this.operationLog); // 清理快照栈(保留最近10次) const MAX_SNAPSHOTS = 10; if (this.operationSnapshotStack.length > MAX_SNAPSHOTS) { this.operationSnapshotStack = this.operationSnapshotStack.slice(-MAX_SNAPSHOTS); this.canUndo = this.operationSnapshotStack.length > 0; } }, /** * 导入记录按钮点击事件(空实现,后续可扩展) */ handleImportRecord() { if(!this.importExportText) { alert('请输入想导入的数据!') return; } if(!window.key) { alert('请先登录!') return; } let result = localStorageUtil.mergeCiphertextToHistory(this.importExportText); if(!result) alert('请核对导入的数据!仅可导入自己账号登录时点击“导出记录”生成的数据!') else { alert('导入成功!') this.importExportText = ''; } }, /** * 导出记录按钮点击事件(空实现,后续可扩展) */ handleExportRecord() { if(!window.key) { alert('请先登录!') return; } this.importExportText = localStorageUtil.exportStorageData(); if(!this.importExportText) alert('为什么没有数据捏?也许是还没有数据吧') else alert('数据已导出!复制上方文本后,在另一个浏览器中点击“导入记录”即可!ps:另一个浏览器也要记得登录本人账号哦!') }, // 8.撤回与状态快照 --end // 9.预测与辅助计算 --begin /** * 预测点击指定装置后,连锁反应结束的最终状态 * @param {Object} initialState 初始状态数据 * @param {Array} initialState.devicesGrid 初始装置网格(二维数组) * @param {number} initialState.actionPoints 初始行动点数 * @param {number} initialState.zeroRotationDevicesCount 初始耗尽装置数 * @param {number} initialState.score 初始分数 * @param {number} rowIndex 触发装置的行坐标 * @param {number} colIndex 触发装置的列坐标 * @returns {Object} 连锁反应结束后的最终状态 */ _predictChainReaction(initialState, rowIndex, colIndex) { // 1. 深拷贝初始状态,避免污染原数据 const state = { devicesGrid: this._deepCloneWithInfinity(initialState.devicesGrid), actionPoints: initialState.actionPoints, zeroRotationDevicesCount: initialState.zeroRotationDevicesCount, score: initialState.score }; // 用于记录每一批次的队列(连锁反应的批次历史) const rotationBatches = []; const size = state.devicesGrid.length; if (size === 0) { return { ...state, rotationBatches: [], }; } // 2. 检查初始条件(是否可点击) const maxRowIndex = size - 1; const maxColIndex = state.devicesGrid[0].length - 1; if ( state.actionPoints <= 0 || rowIndex < 0 || rowIndex > maxRowIndex || colIndex < 0 || colIndex > maxColIndex || state.devicesGrid[rowIndex][colIndex].remainingRotationTimes <= 0 ) { // 不可点击时直接返回初始状态 return { ...state, rotationBatches: [] // 无连锁反应,队列历史为空 }; } // 3. 模拟首次点击(消耗行动点+旋转装置) let firstDevice = state.devicesGrid[rowIndex][colIndex]; state.actionPoints--; // 消耗1点行动点 // 计算首次旋转得分 const firstRotationScore = firstDevice.remainingRotationTimes === 1 ? 10 * (1 + state .zeroRotationDevicesCount) : 1 * (1 + state.zeroRotationDevicesCount); state.score += firstRotationScore; // 更新装置状态(旋转90度+减少剩余次数) firstDevice.currentRotationAngle = (firstDevice.currentRotationAngle + 90) % 360; firstDevice.remainingRotationTimes--; // 检查是否变为耗尽状态 if (firstDevice.remainingRotationTimes === 0) { state.zeroRotationDevicesCount++; } // 4. 模拟连锁反应(使用队列处理批量旋转) const rotationQueue = new Set(); // 存储待旋转装置坐标(格式:"row_col") rotationQueue.add(`${rowIndex}_${colIndex}`); // 初始加入首次点击的装置 // 朝上朝左的方法 const isTopFunc = (angle) => [0, 90].includes(angle); const isLeftFunc = (angle) => [270, 0].includes(angle); // 循环处理连锁反应,直到无新装置需要旋转 while (rotationQueue.size > 0) { const currentBatch = Array.from(rotationQueue); // 记录当前批次的队列(转换为坐标数组格式,如 [[r1,c1], [r2,c2]]) rotationBatches.push(currentBatch); rotationQueue.clear(); // 清空队列,准备接收新的待旋转装置 // 处理当前批次的所有装置旋转 currentBatch.forEach(coord => { const [r, c] = coord.split('_').map(Number); const device = state.devicesGrid[r][c]; // 检查装置是否还能旋转(防御性判断) // if (device.remainingRotationTimes <= 0) return; // 根据装置当前朝向,检查相邻装置是否需要旋转 const angle = device.currentRotationAngle % 360; const isTop = isTopFunc(angle); // 朝上状态 const isLeft = isLeftFunc(angle); // 朝左状态 // 检查上方装置(当前朝上时,上方装置需朝下) if (isTop && r > 0) { const topDevice = state.devicesGrid[r - 1][c]; const topAngle = topDevice.currentRotationAngle % 360; if (!isTopFunc(topAngle) && topDevice.remainingRotationTimes > 0) { const key = `${r - 1}_${c}`; if (!rotationQueue.has(key)) rotationQueue.add(key); } } // 检查下方装置(当前朝下时,下方装置需朝上) if (!isTop && r < size - 1) { const bottomDevice = state.devicesGrid[r + 1][c]; const bottomAngle = bottomDevice.currentRotationAngle % 360; if (isTopFunc(bottomAngle) && bottomDevice.remainingRotationTimes > 0) { const key = `${r + 1}_${c}`; if (!rotationQueue.has(key)) rotationQueue.add(key); } } // 检查左侧装置(当前朝左时,左侧装置需朝右) if (isLeft && c > 0) { const leftDevice = state.devicesGrid[r][c - 1]; const leftAngle = leftDevice.currentRotationAngle % 360; if (!isLeftFunc(leftAngle) && leftDevice.remainingRotationTimes > 0) { const key = `${r}_${c - 1}`; if (!rotationQueue.has(key)) rotationQueue.add(key); } } // 检查右侧装置(当前朝右时,右侧装置需朝左) if (!isLeft && c < size - 1) { const rightDevice = state.devicesGrid[r][c + 1]; const rightAngle = rightDevice.currentRotationAngle % 360; if (isLeftFunc(rightAngle) && rightDevice.remainingRotationTimes > 0) { const key = `${r}_${c + 1}`; if (!rotationQueue.has(key)) rotationQueue.add(key); } } }); // 计算当前批次的连锁得分 const chainScore = rotationQueue.size * (1 + state.zeroRotationDevicesCount); state.score += chainScore; // 执行当前批次的旋转(更新角度和剩余次数) rotationQueue.forEach(coord => { const [r, c] = coord.split('_').map(Number); const device = state.devicesGrid[r][c]; // 旋转90度 device.currentRotationAngle = (device.currentRotationAngle + 90) % 360; // 减少剩余次数 if (device.remainingRotationTimes !== Infinity) { device.remainingRotationTimes--; // 检查是否变为耗尽状态 if (device.remainingRotationTimes === 0) { state.zeroRotationDevicesCount++; } } }); } // 5. 返回连锁反应结束后的最终状态 return { devicesGrid: state.devicesGrid, actionPoints: state.actionPoints, zeroRotationDevicesCount: state.zeroRotationDevicesCount, score: state.score, rotationBatches: rotationBatches // 新增:所有批次的队列历史 }; }, /** * 深克隆函数,高效保留 Infinity 类型 * @param {any} value 要克隆的值 * @returns {any} 克隆后的新值 */ _deepCloneWithInfinity(value) { // 基本类型直接返回(含null,因为typeof null === 'object') if (value === null || typeof value !== 'object') { // 对于Infinity直接返回,其他基本类型也会直接返回 return value; } // 处理数组(使用Array.from提高效率) if (Array.isArray(value)) { return Array.from(value, item => this._deepCloneWithInfinity(item)); } // 处理对象(使用Object.create保持原型,Object.keys避免原型链遍历) const cloned = Object.create(Object.getPrototypeOf(value)); const keys = Object.keys(value); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const val = value[key]; // 快速判断并处理Infinity,其他值递归克隆 cloned[key] = val === Infinity ? Infinity : this._deepCloneWithInfinity(val); } return cloned; }, _calculateDevicesResult(currentStateArray) { var time = new Date().getTime() // 全局存储所有状态的数据 const allData = []; // 遍历currentStateArray中的每个状态 currentStateArray.forEach(item => { let currentState = item.state // 遍历当前状态的所有装置位置(使用独立状态的网格尺寸,不依赖this.devicesGrid) const gridRows = currentState.devicesGrid.length; if (gridRows === 0) return; const gridCols = currentState.devicesGrid[0].length; // 遍历当前状态的所有装置位置 for (let i = 0; i < gridRows; i++) { for (let j = 0; j < gridCols; j++) { const device = currentState.devicesGrid[i][j]; // 跳过已耗尽的装置(无需预测点击) if (device.remainingRotationTimes <= 0) continue; // 预测点击后的结果 const predictedState = this._predictChainReaction(currentState, i, j); const index = `${i}_${j}`; // 汇总数据 allData.push({ index: item.index ? `${item.index},${index}` : index, score: predictedState.score, zeroCount: predictedState.zeroRotationDevicesCount, state: predictedState, parentState: currentState // 保留父状态,便于迭代 }); } } }); return allData; }, // 开启辅助,计算当前状态下的最优解 startAssist() { if (!this.assistEnabled || !this.isDeviceClickable) { this.stopAssist(); return; } // 获取当前游戏状态 const currentState = { devicesGrid: this.devicesGrid.map((row, r) => row.map((device, c) => { const deviceRef = this._getDeviceRef(r, c); return { maxRotationTimes: device.maxRotationTimes, initRotationAngle: device.initRotationAngle, currentRotationAngle: deviceRef ? deviceRef .currentRotationAngle : 0, remainingRotationTimes: deviceRef ? deviceRef .remainingRotationTimes : 0, }; }) ), actionPoints: this.actionPoints, zeroRotationDevicesCount: this.zeroRotationDevicesCount, score: this.operationLog.score }; // [{index, score, zeroCount, state}] let mergedAndUnique = this._calculateDevicesResult([{ state: currentState }]); if (this.selectedScore) mergedAndUnique.sort((a, b) => b.score === a.score ? b.zeroCount - a.zeroCount : b .score - a .score) else mergedAndUnique.sort((a, b) => b.zeroCount === a.zeroCount ? b.score - a.score : b .zeroCount - a.zeroCount) this.assistPredictStyle = {} if (this.assistPredictGlobal) { // 全局预测 const length = mergedAndUnique.length; // 1. 定义5份分级颜色(同色系渐变:蓝→青→黄→橙→红,视觉递进明显) const hueGradeColors = [ [255, 107, 107], // 层级1:红色 (#FF6B6B) [255, 209, 102], // 层级2:黄色 (#FFD166) [6, 214, 160], // 层级3:绿色 (#06D6A0) [17, 138, 178], // 层级4:蓝色 (#118AB2) [188, 188, 188] // 层级5:紫色 (#9B5DE5) ]; mergedAndUnique.forEach((item, index) => { // 2. 计算当前索引的“占总长度比例”(0 ~ 1) const ratio = index / length; // 3. 按比例分5级:0~20%→0号色、20%~40%→1号色、…、80%~100%→4号色 // Math.min(..., 4) 限制最大等级为4,避免超出颜色数组索引(0~4) const colorIndex = Math.min(Math.floor(ratio * 5), 4); // 4. 获取当前分级对应的颜色(解构RGB值) const [r, g, b] = hueGradeColors[colorIndex]; // 5. 背景色使用分级颜色,透明度可固定(如0.8,确保不完全遮挡下层) this.assistPredictStyle[item.index] = { position: 'absolute', 'width': '80%', 'height': '80%', 'border-radius': '50%', 'top': '50%', 'left': '50%', 'transform': 'translate(-50%, -50%)', 'z-index': '-1', 'filter': 'blur(0.1rem)', 'background': `radial-gradient(circle at center, rgb(${r}, ${g}, ${b}) 40%, rgba(${r}, ${g}, ${b}, 0.6) 50%, rgba(${r}, ${g}, ${b}, 0) 90%)` }; }); } else { // 非全局预测 let color = this.selectedScore ? '19, 206, 102' : '255, 73, 73'; mergedAndUnique = mergedAndUnique.slice(0, 20) mergedAndUnique.forEach(item => { this.assistPredictStyle[item.index] = { position: 'absolute', 'width': '80%', 'height': '80%', 'border-radius': '50%', 'top': '50%', 'left': '50%', 'transform': 'translate(-50%, -50%)', 'z-index': '-1', 'filter': 'blur(0.1rem)', 'background': `radial-gradient(circle at center, rgb(${color}) 40%, rgba(${color}, 0.6) 50%, rgba(${color}, 0) 90%)` } }) } }, stopAssist() { this.assistPredictStyle = {}; }, // 9.预测与辅助计算 --end /** * 根据星级获取对应分数范围(用于评级结果说明) * @param {number} star - 当前星级 * @returns {string} 分数范围文本 */ getStarScoreRange(star) { switch (star) { case 1: return "≤50,000分"; case 2: return "50,001 ~ 70,000分"; case 3: return "70,001 ~ 100,000分"; case 4: return "100,001 ~ 150,000分"; case 5: return "150,001 ~ 200,000分"; case 10: return ">200,000分(满分评级)"; default: return "暂无数据"; } }, /** * 今日评级核心方法:基于今日种子生成独立网格,通过多轮预测计算最高评级 * 逻辑步骤: * 1. 生成今日种子对应的独立二维数组(与现有devicesGrid完全隔离) * 2. 第一轮预测:计算初始网格所有点击位置的结果,筛选前10高分+前10高乘数 * 3. 多轮迭代:对第一轮筛选结果中的每个位置,再次预测后续点击结果,累积最优解 * 4. 基于最终最优分数匹配评级规则,更新今日评级结果 */ handlePredictStore() { // 禁用重复点击(防止并发计算) if (this.isCalculatingRating) return; this.isCalculatingRating = true; this.predictStoreStar = 0; // 重置评级结果(避免旧数据干扰) this.predictProgress = { currentStep: 0, totalSteps: 10, // 总步骤=迭代轮次+初始步骤 isActive: true, stepText: "初始化今日网格..." }; try { // -------------------------- 步骤1:生成今日种子的独立二维数组(与devicesGrid完全隔离) -------------------------- let todaySeed = this.getTodaySeed(); // 获取今日种子 // 1.1 生成独立的随机二维数组(复用现有随机生成逻辑,但不关联this.devicesGrid) const independentGrid = this._generateRandom2DArray({ sizeMin: this.sizeMin, sizeMax: this.sizeMax, infinityProbability: this.infinityProbability, numMin: this.numMin, numMax: this.numMax, seed: todaySeed }); // 1.2 为独立网格补充「剩余旋转次数」属性(模拟初始状态,与组件实例隔离) const initialIndependentState = { devicesGrid: independentGrid.map(row => row.map(device => ({ ...device, // 初始化剩余旋转次数:无限次为Infinity,有限次为maxRotationTimes remainingRotationTimes: device.maxRotationTimes === Infinity ? Infinity : device.maxRotationTimes, // 初始旋转角度与配置一致 currentRotationAngle: device.initRotationAngle })) ), actionPoints: 10, // 初始行动点(与游戏默认一致) zeroRotationDevicesCount: 0, // 初始耗尽装置数(0) score: 0 // 初始分数(0) }; this.predictProgress.totalSteps = initialIndependentState.actionPoints; this.predictProgress.currentStep += 1; this.predictProgress.stepText = `预测中...(${this.predictProgress.currentStep}/${this.predictProgress.totalSteps})`; // 需要截取的数组区间配置 // 格式:[start, end, maxCount] // - start/end支持数字(索引)、百分比字符串(如'10%') // - maxCount可选,限制当前区间最多截取的条数(默认无限制) // 例如:[[0, 10], ['10%', '20%', 10]] const numArray = [ [0, 5], ['10%', '15%', 1], ['15%', '20%', 1], ['20%', '25%', 1], ['25%', '30%', 1], ['30%', '35%', 1], ['35%', '40%', 1], ['40%', '45%', 1], ['45%', '50%', 1], ['50%', '55%', 1], ['55%', '60%', 1], ]; // 按numArray区间截取数组的工具函数(支持百分比、负索引和数量限制) const spliceNumArrayFunc = (numArray, array) => { const len = array.length; return numArray.flatMap(([start, end, maxCount]) => { // 1. 转换start为索引(支持百分比和负索引) let safeStart; if (typeof start === 'string' && start.endsWith('%')) { // 百分比转换:'10%' -> 0.1 * 数组长度(向下取整) safeStart = Math.floor(len * (parseFloat(start) / 100)); } else { // 数字索引(支持负索引) safeStart = start < 0 ? len + start : start; } // 2. 转换end为索引(支持百分比和负索引) let safeEnd; if (typeof end === 'string' && end.endsWith('%')) { // 百分比转换:'20%' -> 0.2 * 数组长度(向下取整) safeEnd = Math.floor(len * (parseFloat(end) / 100)); } else { // 数字索引(负索引end需要+1,例如-1表示最后一个元素) safeEnd = end < 0 ? len + end + 1 : end; } // 3. 确保索引在有效范围内 const finalStart = Math.max(0, safeStart); let finalEnd = Math.min(len, safeEnd); // 4. 应用maxCount限制(如果设置了最大数量) if (maxCount !== undefined) { const currentLength = finalEnd - finalStart; if (currentLength > maxCount) { finalEnd = finalStart + maxCount; // 超出时截断到maxCount } } // 5. 处理start >= end的情况(返回空数组) if (finalStart >= finalEnd) return []; return array.slice(finalStart, finalEnd); }); }; setTimeout(() => { // -------------------------- 步骤2:第一轮预测:筛选初始网格的指定区间高分+指定区间高乘数 -------------------------- // 2.1 计算初始网格所有点击位置的结果(调用_calculateDevicesResult,传入独立状态) const firstRoundAllResults = this._calculateDevicesResult([{ state: initialIndependentState, index: '' } // index空字符串:标记初始状态 ]); // 2.2 筛选规则1:按分数降序,取numArray指定区间的结果(高分优先) const sortedByScore = [...firstRoundAllResults].sort((a, b) => b.score - a .score); const topNumByScore = spliceNumArrayFunc(numArray, sortedByScore); const sortedByMultiplier = sortedByScore.sort((a, b) => b.zeroCount - a .zeroCount); const topNumByMultiplier = spliceNumArrayFunc(numArray, sortedByMultiplier); // 2.4 第一轮筛选结果:合并指定区间高分+指定区间高乘数,去重(避免同一位置重复进入迭代) const firstRoundFiltered = Array.from( new Map([...topNumByScore, ...topNumByMultiplier].map(item => [item .index, item ])).values() ); // -------------------------- 步骤3:多轮迭代预测:基于第一轮结果,递归计算后续最优点击 -------------------------- // 3.1 定义迭代函数:接收当前状态数组,返回下一轮预测结果 const iteratePredict = (currentStateArray) => { let nextRoundResults = []; // 遍历当前状态数组中的每个状态 currentStateArray.forEach(stateItem => { // 计算当前状态下所有点击位置的结果 const currentRoundResults = this ._calculateDevicesResult([stateItem]); // 筛选规则:指定区间高分 + 指定区间高乘数(与第一轮一致) const sortedByScore = [...currentRoundResults].sort((a, b) => b.score - a.score); const topNumScore = spliceNumArrayFunc(numArray, sortedByScore); const sortedByMulti = sortedByScore.sort((a, b) => b .zeroCount - a.zeroCount); const topNumMulti = spliceNumArrayFunc(numArray, sortedByMulti); // 合并当前状态的筛选结果,补充「父级索引」(便于追溯点击路径) const merged = [...topNumScore, ...topNumMulti].map( item => ({ ...item })); nextRoundResults = [...nextRoundResults, ...merged]; }); // 去重:避免同一状态+同一位置的重复计算 const currentRoundResults = Array.from( new Map(nextRoundResults.map(item => [`${item.index}`, item ])).values() ); // 再次筛选 const sortedByScore = [...currentRoundResults].sort((a, b) => b .score - a.score); const topNumScore = spliceNumArrayFunc(numArray, sortedByScore); const sortedByMulti = sortedByScore.sort((a, b) => b.zeroCount - a .zeroCount); const topNumMulti = spliceNumArrayFunc(numArray, sortedByMulti); // 合并当前状态的筛选结果,补充「父级索引」(便于追溯点击路径) const merged = [...topNumScore, ...topNumMulti].map(item => ({ ...item })); return merged; }; // 3.2 执行多轮迭代 let currentIterationStates = firstRoundFiltered; // 初始迭代状态:第一轮筛选结果 let iterationRounds = initialIndependentState .actionPoints; // 迭代轮次(建议3-5轮,平衡精准度与性能) let currentRound = 1; // 当前迭代轮次(从1开始,与原逻辑一致) // 异步迭代函数 const asyncIterate = () => { if (currentRound >= iterationRounds) { // 迭代结束,执行步骤4(计算最终分数) calculateFinalScore(); return; } // 1. 执行本轮迭代计算 currentIterationStates = iteratePredict(currentIterationStates); // 2. 更新进度条(此时主线程空闲,Vue 能实时渲染) this.predictProgress.currentStep += 1; this.predictProgress.stepText = `预测中...(${this.predictProgress.currentStep}/${this.predictProgress.totalSteps})`; // 3. 延迟 0ms 执行下一轮(让出主线程,允许浏览器重绘) currentRound += 1; setTimeout(asyncIterate, 0); }; // 新增:计算最终分数的独立函数(迭代结束后执行) const calculateFinalScore = () => { const allResults = currentIterationStates; allResults.sort((a, b) => b.score - a.score); const maxScore = allResults[0] ? allResults[0].score : 0; const maxScorePath = allResults[0] ? allResults[0].index : ''; // 匹配评级规则 if (maxScore > 200000) { this.predictStoreStar = 10; // 满分评级 } else if (maxScore > 150000) { this.predictStoreStar = 5; } else if (maxScore > 100000) { this.predictStoreStar = 4; } else if (maxScore > 70000) { this.predictStoreStar = 3; } else if (maxScore > 50000) { this.predictStoreStar = 2; } else if (maxScore > 0) { this.predictStoreStar = 1; } else { this.predictStoreStar = 0; // 无有效分数(异常情况) } // 进度条更新为 100% this.predictProgress.currentStep = this.predictProgress.totalSteps; this.predictProgress.stepText = `计算完成(${this.predictProgress.totalSteps}/${this.predictProgress.totalSteps})`; }; // 启动异步迭代 asyncIterate(); }, 0) } catch (error) { // 异常处理:避免计算失败导致界面卡死 alert('评级计算出错,请稍后重试'); this.predictStoreStar = 0; } finally { // 恢复点击状态(无论成功/失败,都允许再次点击) this.isCalculatingRating = false; } }, } }); </script> </body> </html>





