斯诺克计分应用优化:代码精简与规则限制实现咨询
斯诺克计分应用优化与规则实现方案
嘿,我帮你梳理下怎么优化这段代码,同时实现斯诺克的核心计分规则——这俩问题其实可以结合起来解决,让代码既简洁易维护,又完全符合规则要求。
一、先把代码改得简洁流畅
你原来的代码重复了大量的元素选择和事件绑定逻辑,这完全可以用数据驱动的方式重构,把重复代码抽成统一的逻辑:
优化思路
- 用一个数组统一存储所有球的配置信息(类名、分值、初始数量),后续要加/改球的信息直接改数组就行,不用重复写代码
- 循环这个数组,一次性完成元素选择、事件绑定,减少冗余
基础优化后的代码
// 统一管理所有球的配置:类名、对应分值、初始数量 const ballConfigs = [ { className: 'red', points: 1, count: 15 }, { className: 'yellow', points: 2, count: 1 }, { className: 'green', points: 3, count: 1 }, { className: 'brown', points: 4, count: 1 }, { className: 'blue', points: 5, count: 1 }, { className: 'pink', points: 6, count: 1 }, { className: 'black', points: 7, count: 1 } ]; const scoreElement = document.querySelector('.score'); let scoreVal = 0; scoreElement.textContent = scoreVal; // 循环绑定事件,统一处理击球逻辑 ballConfigs.forEach(config => { const ballElement = document.querySelector(`.${config.className}`); ballElement.addEventListener('click', () => { // 这里先预留规则判断的位置,后面再补 scoreVal += config.points; scoreElement.textContent = scoreVal; }); });
这样一来,原来十几行的重复代码直接缩成了几行,可读性和维护性都提升了不少。
二、实现击球限制与147分规则约束
接下来要加入斯诺克的核心规则,我们需要几个状态变量来跟踪游戏的当前阶段,然后在点击事件里做规则判断:
需要跟踪的状态
remainingReds: 剩余红球数(初始15)canPotColour: 是否允许击打彩球(初始false,开局必须先打红球)clearedReds: 是否已经清完所有红球(初始false)remainingColours: 红球清台后剩余的彩球集合(初始包含所有彩球)- 额外加个最高分检测:当分数达到147时,直接禁用所有球的点击
完整规则实现代码
// 统一管理所有球的配置:类名、对应分值、初始数量 const ballConfigs = [ { className: 'red', points: 1, count: 15 }, { className: 'yellow', points: 2, count: 1 }, { className: 'green', points: 3, count: 1 }, { className: 'brown', points: 4, count: 1 }, { className: 'blue', points: 5, count: 1 }, { className: 'pink', points: 6, count: 1 }, { className: 'black', points: 7, count: 1 } ]; const scoreElement = document.querySelector('.score'); let scoreVal = 0; scoreElement.textContent = scoreVal; // 游戏状态变量,跟踪当前阶段 let remainingReds = 15; let canPotColour = false; let clearedReds = false; const remainingColours = new Set(['yellow', 'green', 'brown', 'blue', 'pink', 'black']); // 检查是否达到最高分147 const isMaxScoreReached = () => scoreVal === 147; // 禁用所有球的点击,并给视觉反馈 const disableAllBalls = () => { ballConfigs.forEach(config => { const ball = document.querySelector(`.${config.className}`); ball.style.pointerEvents = 'none'; ball.style.opacity = '0.5'; // 变灰标记已禁用 }); }; // 统一处理击球逻辑 const handleBallClick = (config) => { // 先判断是否已经达到最高分,直接返回 if (isMaxScoreReached()) return; // 分阶段处理游戏逻辑 if (!clearedReds) { // 红球未清台阶段:红球→彩球→红球循环 if (config.className === 'red') { // 击打红球的条件:还有剩余红球,且当前不能打彩球(即刚打完彩球后要回到打红球) if (remainingReds > 0 && !canPotColour) { scoreVal += config.points; remainingReds--; canPotColour = true; // 打完红球后允许打彩球 // 如果红球打完了,切换到彩球阶段 if (remainingReds === 0) { clearedReds = true; } scoreElement.textContent = scoreVal; // 这里可以加红球剩余数量的显示,比如页面上加个.red-count元素: // document.querySelector('.red-count').textContent = remainingReds; } } else { // 击打彩球的条件:刚打完红球(canPotColour为true),且彩球还在台上 if (canPotColour && config.count > 0) { scoreVal += config.points; canPotColour = false; // 打完彩球后必须回到打红球(如果还有红球) scoreElement.textContent = scoreVal; } } } else { // 红球已清台阶段:只能打剩余的彩球,打一个少一个 if (remainingColours.has(config.className)) { scoreVal += config.points; remainingColours.delete(config.className); scoreElement.textContent = scoreVal; // 如果最后一颗黑球被击落,直接禁用所有球 if (config.className === 'black' && remainingColours.size === 0) { disableAllBalls(); } } } // 每次击球后检查是否达到最高分,达到则禁用所有球 if (isMaxScoreReached()) { disableAllBalls(); } }; // 循环绑定所有球的点击事件 ballConfigs.forEach(config => { const ballElement = document.querySelector(`.${config.className}`); ballElement.addEventListener('click', () => handleBallClick(config)); });
规则说明
- 击球顺序约束:开局必须先打红球,打完红球才能打任意彩球,打完彩球回到打红球,直到15颗红球全部清台
- 彩球阶段约束:红球清台后,只能打剩余的彩球,每颗彩球只能打一次,最后一颗黑球打完后所有球禁用
- 最高分约束:只要分数达到147,立即禁用所有球的点击,不管当前处于哪个阶段
这样改造后,代码既简洁,又完全符合你描述的斯诺克计分规则啦。
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

