斯诺克计分应用优化咨询:代码精简与规则限制实现
斯诺克计分应用优化与规则实现方案
Hey Josh, great start on your snooker scoring app! Let's break this down into two parts: cleaning up your repetitive code to make it smoother, and adding the rule-based restrictions you need (shot order limits and the 147 max score cap).
一、代码简洁化重构
Your original code has a lot of repeated DOM selection and event listener logic—we can fix that with a data-driven approach that centralizes all ball configurations. This makes the code shorter, easier to maintain, and simpler to extend later:
// 统一配置所有球的核心信息:选择器、分值、类型、初始状态 const ballConfigs = [ { selector: '.red', points: 1, type: 'red', remaining: 15 }, { selector: '.yellow', points: 2, type: 'colour', isPotted: false }, { selector: '.green', points: 3, type: 'colour', isPotted: false }, { selector: '.brown', points: 4, type: 'colour', isPotted: false }, { selector: '.blue', points: 5, type: 'colour', isPotted: false }, { selector: '.pink', points: 6, type: 'colour', isPotted: false }, { selector: '.black', points: 7, type: 'colour', isPotted: false } ]; // 全局状态管理 const scoreElement = document.querySelector('.score'); let currentScore = 0; let remainingReds = 15; let canPotColour = false; // 标记:打完红球后是否可击打彩球 let gameOver = false; // 通用分数更新函数 function updateScoreDisplay() { scoreElement.textContent = currentScore; // 检查是否达到满分147,触发游戏结束 if (currentScore >= 147) { endGame(); } } // 游戏结束:禁用所有球的点击 function endGame() { gameOver = true; ballConfigs.forEach(config => { const ball = document.querySelector(config.selector); ball.style.pointerEvents = 'none'; ball.style.opacity = '0.5'; // 视觉上标记为不可用 }); } // 初始化所有球的事件监听 ballConfigs.forEach(config => { const ball = document.querySelector(config.selector); ball.addEventListener('click', () => { if (gameOver) return; // 游戏结束后不再响应点击 // 处理红球击打逻辑 if (config.type === 'red') { // 红球已打完,或当前处于需打彩球的阶段,无法点击红球 if (remainingReds <= 0 || canPotColour) return; currentScore += config.points; remainingReds--; // 视觉上标记红球已击落 ball.style.opacity = '0.5'; ball.style.pointerEvents = 'none'; canPotColour = true; // 打完红球后允许击打任意彩球 updateScoreDisplay(); } // 处理彩球击打逻辑 else { // 红球未打完且未先击打红球,无法直接打彩球 if (remainingReds > 0 && !canPotColour) return; if (remainingReds > 0) { // 还有红球时,彩球击落会放回台面,只加分不标记为已击落 currentScore += config.points; canPotColour = false; // 打完彩球后回到可击打红球的状态 } else { // 红球已打完,需按分值从低到高击打彩球 const availableColours = ballConfigs .filter(c => c.type === 'colour' && !c.isPotted) .sort((a, b) => a.points - b.points); // 不是当前允许击打的彩球(最低分值未击落彩球),直接返回 if (availableColours[0].selector !== config.selector) return; // 击落彩球,标记状态并更新分数 currentScore += config.points; config.isPotted = true; ball.style.opacity = '0.5'; ball.style.pointerEvents = 'none'; // 检查是否是最后一颗黑球,触发游戏结束 if (config.points === 7 && availableColours.length === 1) { endGame(); } } updateScoreDisplay(); } }); }); // 初始化分数显示 updateScoreDisplay();
核心优化点:
- 集中配置:所有球的属性存在
ballConfigs数组里,避免重复的DOM选择和事件绑定 - 通用函数:提取
updateScoreDisplay和endGame函数,减少重复代码 - 状态集中管理:把剩余红球数、击球阶段、游戏状态等变量统一管理,逻辑更清晰
二、规则限制实现
1. 击球顺序限制
- 红球阶段:必须先击打红球,打完红球后才能选择任意彩球;彩球击打后会"放回"台面(代码中不标记为已击落,只是切换回可打红球状态)
- 彩球清台阶段:红球全部打完后,必须按**黄(2分)→绿(3分)→棕(4分)→蓝(5分)→粉(6分)→黑(7分)**的顺序击打,无法跳级选择
- 游戏终止:最后一颗黑球被击打后,所有球立即禁用点击
2. 147分最高分限制
在updateScoreDisplay函数中添加了满分判断:当分数达到147(15红15黑的120分 + 彩球总分27分)时,直接触发endGame函数,禁用所有球的操作,避免超分。
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

