使用JS开发配对卡牌游戏时遭遇无限循环问题求助
配对卡牌游戏:实现卡牌内容随机分配(每个内容使用两次)
我正在开发一款基于JavaScript的配对卡牌游戏,需求为:将卡牌内容随机分配,每个内容被使用两次后从可用列表中移除。尝试用for或while循环实现该逻辑时,始终出现无限循环问题。查阅资料得知可创建包含0-4的对象并使用Object.keys方法,但作为JavaScript新手,希望获得相关技术协助。
原代码
CSS
*{ box-sizing: border-box; } .clear { clear: both; } section { border: 1px solid black; height: 200px; width: 50%; float: left; }
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Card Game</title> <link rel="stylesheet" href="css/normalize.css"> <link rel="stylesheet" href="css/style.css"> </head> <body> <h1>Match-The-Cards</h1> <h2 id = "results"></h2> <section id="one" class="one"></section> <section id="two" class="one"></section> <section id="three"class="one"></section> <section id="four" class="one"></section> <section id="five" class="one"></section> <section id="six" class="one"></section> <section id="seven"class="one"></section> <section id="eight"class="one"></section> <section id="nine" class="one"></section> <section id="ten" class="one"></section> <script type="text/javascript" src="js/main.js"></script> </body> </html>
JavaScript
const theFlipSide = { cardOne: "🚕", cardTwo: "🗽", cardThree: "🌃", cardFour: "🍕", cardFive: "🍎" } console.log(theFlipSide); const eachSection = Object.values(theFlipSide) console.log(eachSection); eachSectionReturn() function eachSectionReturn() { let items let randomNumber = Math.floor(Math.random() * eachSection.length ) let theIndex = [0,1,2,3,4] let randomEachSection = [] console.log(randomNumber, 'Random number'); console.log(theIndex, 'The index'); console.log(randomEachSection, 'random each section'); { if (theIndex.find((e) => e == randomNumber) != undefined){ randomEachSection.push(eachSection[randomNumber] ) theIndex = theIndex.filter((e) => e != randomNumber) console.log(theIndex,'filtered'); console.log(randomNumber, 'w/n if'); //return eachSection[Math.floor(Math.random() * eachSection.length)] // allow that item to be outputted twice and then remove it from list //_ => Math.random() - 0.5); } } document.querySelectorAll('.one').forEach(section => section.addEventListener('click', theGame)) function theGame(e) { let theSquares = eachSectionReturn() e.target.innerText = (theSquares) } }
问题分析
原代码存在几个核心问题:
eachSectionReturn函数每次调用都会重新初始化索引数组theIndex,无法持久跟踪哪些卡牌已经被使用了两次- 未实现「每个内容使用两次」的逻辑,仅做了单次索引过滤
- 函数未正确返回卡牌内容,导致点击后显示
undefined - 事件监听被嵌套在函数内部,会重复绑定触发逻辑
修改后的完整代码
CSS(优化交互样式)
* { box-sizing: border-box; } .clear { clear: both; } section { border: 1px solid black; height: 200px; width: 50%; float: left; display: flex; align-items: center; justify-content: center; font-size: 4rem; cursor: pointer; user-select: none; }
HTML(结构不变)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Card Game</title> <link rel="stylesheet" href="css/normalize.css"> <link rel="stylesheet" href="css/style.css"> </head> <body> <h1>Match-The-Cards</h1> <h2 id="results"></h2> <section id="one" class="one"></section> <section id="two" class="one"></section> <section id="three" class="one"></section> <section id="four" class="one"></section> <section id="five" class="one"></section> <section id="six" class="one"></section> <section id="seven" class="one"></section> <section id="eight" class="one"></section> <section id="nine" class="one"></section> <section id="ten" class="one"></section> <script type="text/javascript" src="js/main.js"></script> </body> </html>
JavaScript(核心逻辑优化)
// 卡牌内容集合 const cardContents = { cardOne: "🚕", cardTwo: "🗽", cardThree: "🌃", cardFour: "🍕", cardFive: "🍎" }; // 生成每个内容出现两次的数组,满足配对需求 let cardPool = []; Object.values(cardContents).forEach(content => { cardPool.push(content, content); }); // Fisher-Yates洗牌算法:保证随机分布均匀 function shuffleArray(arr) { for (let i = arr.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [arr[i], arr[j]] = [arr[j], arr[i]]; } return arr; } // 初始化游戏 function initGame() { const shuffledCards = shuffleArray(cardPool); const sections = document.querySelectorAll('.one'); sections.forEach((section, index) => { // 用dataset存储卡牌内容,点击前不显示 section.dataset.cardContent = shuffledCards[index]; section.addEventListener('click', handleCardClick); }); } // 卡牌点击事件逻辑 function handleCardClick(e) { const section = e.target; // 点击后显示卡牌内容 section.innerText = section.dataset.cardContent; // 可在此扩展配对逻辑:记录已点击卡牌、判断是否匹配、游戏胜利条件等 } // 启动游戏 initGame();
关键逻辑说明
- 卡牌池生成:通过
Object.values获取所有卡牌内容,循环将每个内容添加两次到数组,天然满足「每个内容使用两次」的需求 - 洗牌算法:使用Fisher-Yates算法打乱数组,相比简单的
sort(() => Math.random() - 0.5)随机性更均匀,避免排序偏差 - 数据存储与事件绑定:初始化时给每个区块绑定点击事件,用
dataset存储对应卡牌内容,点击时直接读取显示,无需反复随机取值 - 避免无限循环:预先生成包含所有重复内容的数组,洗牌后直接分配,从根源解决循环逻辑错误
内容的提问来源于stack exchange,提问作者techCARE_
相关产品推荐
相关产品推荐

