我正在进行扫雷游戏。浏览器说“过多的递归”。你能否看到代码并纠正我犯错误的部分?
<?php
/*
Basic PHP code for generating a table 8x8 with every button a unique id
*/
$tableDesign = "<table>";
$id = 1;
for($i=0; $i<8; $i++){
$tableDesign .= "<tr>";
for($j=0; $j<8; $j++){
$tableDesign .= "<td><button id='num_$id' onclick='get_button(this.id)'></button></td>";
$id++;
}
$tableDesign .= "</tr>";
}
$tableDesign .= "</table>";
?>
<!DOCTYPE html>
<html>
<head>
<title>Mine Sweeper Ver. 0.1</title>
<style type="text/css">
table {
border-collapse: collapse;
}
td {
width: 60px;
height: 60px;
border: 1px solid #ddd;
}
button {
width: 60px;
height: 60px;
cursor: pointer;
}
img {
width: 50px;
height: 50px;
}
.table{
float: left;
width: 550px;
}
.switch_command {
padding-top: 40px;
font-size: 22px;
font-family: sans-serif;
float: left;
}
</style>
</head>
<body>
<div class="table">
<?php echo $tableDesign; ?>
</div>
<div class="switch_command">
Switch to protect from Expload: <input id="check" type="checkbox">
</div>
<script type="text/javascript">
var randomMinesLocation = new Array(); // this array will contain the id number part of the mines
var mineProtecition = new Array(); // this array will contain the mine with flags to not be abled to open when the player clicks on it
var emptyCells = new Array(); // this array will contain the empty cells opened to prevent the recursion going through the same cell again
// this while loop is responsible for generating 10 different mines in random from 1 to 64
while (randomMinesLocation.length < 10) {
index = randomMinesLocation.length;
match = false;
position = Math.floor((Math.random() * 64) + 1);
for (i = 0; i < randomMinesLocation.length; i++) {
if(position == randomMinesLocation[i])
match = true;
}
if(!match) randomMinesLocation[index] = position;
}
// I made this check function to prevent to equal id to be contained to the openedEmptyCells array which will be used to open the cells in cooperation with another function
function checkForEqualVal(valToTest, openedEmptyCells) {
match = false;
if (openedEmptyCells.length == 0) {
openedEmptyCells.push(valToTest);
} else {
for (i = 0; i < openedEmptyCells.length; i++){
if(openedEmptyCells[i][1] == valToTest[1]) {
match = true;
break;
}
}
if (!match) {
openedEmptyCells.push(valToTest);
}
}
return openedEmptyCells;
}
function logic_game(idNumericPart, randomMinesLocation, openedEmptyCells = []) {
match = false;
numberOfMinesNear = 0;
canditateCells = [];
for (j = -1; j <= 1; j++) {
if (idNumericPart <= 8 && j == 1 || idNumericPart > 56 && j == -1 ) continue;
for(k= -1; k <= 1; k++){
if(((idNumericPart - 1) % 8 == 0 && k == -1) || (idNumericPart % 8 == 0 && k == 1))
continue;
idNumericPartOfNear = idNumericPart - (j * 8) + k;
if(j != 0 && k == 0 || j == 0 && k != 0 || j != 0 && k != 0)
canditateCells.push(idNumericPartOfNear);
for(i=0; i<10; i++){
if(idNumericPart == randomMinesLocation[i]){
match = true;
break;
}
else if(idNumericPartOfNear == randomMinesLocation[i])
numberOfMinesNear++;
}
}
}
if(match)
openedEmptyCells = checkForEqualVal([true, idNumericPart], openedEmptyCells);
else if(numberOfMinesNear > 0){
openedEmptyCells = checkForEqualVal([numberOfMinesNear, idNumericPart], openedEmptyCells);
}
else{
openedEmptyCells = checkForEqualVal([false, idNumericPart], openedEmptyCells);
for(i=0; i<canditateCells.length; i++){
matchedBtw = false;
for(j=0; j<emptyCells.length; j++){
if(canditateCells[i] == emptyCells[j])
matchedBtw = true;
break;
}
if(!matchedBtw)
openedEmptyCells = logic_game(canditateCells[i], randomMinesLocation, openedEmptyCells);
}
}
return openedEmptyCells;
}
function printToScreen(value, idNumericPart){
if(typeof(value) === 'boolean' && value){
document.getElementById("num_" + idNumericPart).parentElement.innerHTML = '<img src="mine.png">';
}
else if(typeof(value) === 'number'){
document.getElementById("num_" + idNumericPart).parentElement.style.backgroundColor = '#eee';
document.getElementById("num_" + idNumericPart).parentElement.innerHTML = value;
}
else{
document.getElementById("num_" + idNumericPart).parentElement.style.backgroundColor = '#eee';
document.getElementById("num_" + idNumericPart).parentElement.innerHTML = '';
}
}
function get_button(id){
idNumericPart = id.substring(4);
checkValue = document.getElementById("check").checked;
matchCheckForMineProtect = false;
if(checkValue){
for(i=0; i<mineProtecition.length; i++){
if(mineProtecition[i] == idNumericPart){
matchCheckForMineProtect = true;
break;
}
}
if(matchCheckForMineProtect){
mineProtecition.splice(i, 1);
valueToBeSubstitude = '<button id="' + id + '" onclick="get_button(this.id)"></button>';
}
else{
mineProtecition.push(idNumericPart);
valueToBeSubstitude = '<button id="' + id + '" style="background: url(' + 'flag.png' + ') no-repeat; background-size: 40px 40px;" onclick="get_button(this.id)"></button>';
}
document.getElementById(id).parentElement.innerHTML = valueToBeSubstitude;
}
else {
matchProtected = false;
for(i=0; i<mineProtecition.length; i++){
if(mineProtecition[i] == idNumericPart){
matchProtected = true;
break;
}
}
if(!matchProtected){
result = logic_game(idNumericPart, randomMinesLocation);
for(i=0; i<result.length; i++){
printToScreen(result[i][0], result[i][1]);
}
}
}
}
</script>
</body>
</html>
答案 0 :(得分:1)
用java脚本替换你的代码中的php(参见下面的修改代码)并将console.log添加到方法调用中,允许定位无限递归循环的位置,在控制台中给出一个输出: / p>
> logic_game(15)
minesweeper.html:116 randomMinesLocation
minesweeper.html:117 (10) [10, 56, 36, 33, 3, 14, 24, 54, 2, 13]
minesweeper.html:118 openedEmptyCells
minesweeper.html:119 [Array(2)]
minesweeper.html:115 logic_game(24)
minesweeper.html:116 randomMinesLocation
minesweeper.html:117 (10) [10, 56, 36, 33, 3, 14, 24, 54, 2, 13]
minesweeper.html:118 openedEmptyCells
minesweeper.html:119 (2) [Array(2), Array(2)]
minesweeper.html:115 logic_game(15)
minesweeper.html:116 randomMinesLocation
minesweeper.html:117 (10) [10, 56, 36, 33, 3, 14, 24, 54, 2, 13]
minesweeper.html:118 openedEmptyCells
minesweeper.html:119 (3) [Array(2), Array(2), Array(2)]
minesweeper.html:115 logic_game(24)
minesweeper.html:116 randomMinesLocation
minesweeper.html:117 (10) [10, 56, 36, 33, 3, 14, 24, 54, 2, 13]
minesweeper.html:118 openedEmptyCells
minesweeper.html:119 (3) [Array(2), Array(2), Array(2)]
我可以看到对id 15的logic_game调用,名为l logic_game,id为24,后者又将logic_game称为id 15,继续作为无限递归循环。
要回答问题本身,这不是一个正确的修复,修复是一个id堆栈:
var idStack = [];
对于对logic_game函数的每次递归调用,检查堆栈以确保该函数尚未被调用,并且如果它已从函数返回以结束无限递归。
function logic_game(idNumericPart,randomMinesLocation, openedEmptyCells = []){//阻止无限递归循环;如果 (idStack.indexOf(idNumericPart)&gt; -1){ console.log('无限循环检测'); 返回; }
如果尚未调用id,则将其添加到堆栈并继续执行该功能。
//将id推送到idStack,这样我们就可以判断这个函数是否输入了 无限递归循环;
idStack.push(idNumericPart);
最后,清除每个按钮单击时的idStack,以便仅跟踪每个递归循环的ID。
function get_button(id){ // clear the button ID stack so we can track recursive calls per button click to assure that will only be one call per id per button game; idStack = [];
我们现在已经阻止了问题中提出的问题,解决了递归过多的问题。但是,您会看到代码中还有其他逻辑错误需要修复:
minesweeper.html:180 Uncaught TypeError:无法读取属性 'parentElement'为null at printToScreen(minesweeper.html:180) at get_button(minesweeper.html:233) 在HTMLButtonElement.onclick(minesweeper.html:1)
这是修改后的代码,用javascript替换php,现在可以在浏览器中本地运行而不会崩溃:
<!DOCTYPE html>
<html>
<head>
<title>Mine Sweeper Ver. 0.1</title>
<style type="text/css">
table {
border-collapse: collapse;
}
td {
width: 60px;
height: 60px;
border: 1px solid #ddd;
}
button {
width: 60px;
height: 60px;
cursor: pointer;
}
img {
width: 50px;
height: 50px;
}
.table{
float: left;
width: 550px;
}
.switch_command {
padding-top: 40px;
font-size: 22px;
font-family: sans-serif;
float: left;
}
</style>
<script>
function getTableDesign()
{
var tableDesign = "<table>";
var id = 1;
for(var i=0; i<8; i++){
tableDesign += "<tr>";
for(var j=0; j<8; j++){
tableDesign += "<td><button id='num_" + id + "' onclick='get_button(this.id)'></button></td>";
id++;
}
tableDesign += "</tr>";
}
tableDesign += "</table>";
return tableDesign;
}
</script>
<script
src="https://code.jquery.com/jquery-2.2.4.min.js"
integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
crossorigin="anonymous"></script>
<script>
$(document).ready(function() {
$('#tblTableDesign').html(getTableDesign());
});
</script>
</head>
<body>
<div class="table" id="tblTableDesign">
</div>
<div class="switch_command">
Switch to protect from Expload: <input id="check" type="checkbox">
</div>
<script type="text/javascript">
var randomMinesLocation = new Array(); // this array will contain the id number part of the mines
var mineProtecition = new Array(); // this array will contain the mine with flags to not be abled to open when the player clicks on it
var emptyCells = new Array(); // this array will contain the empty cells opened to prevent the recursion going through the same cell again
var idStack =[];
// this while loop is responsible for generating 10 different mines in random from 1 to 64
while (randomMinesLocation.length < 10) {
index = randomMinesLocation.length;
match = false;
position = Math.floor((Math.random() * 64) + 1);
for (i = 0; i < randomMinesLocation.length; i++) {
if(position == randomMinesLocation[i])
match = true;
}
if(!match) randomMinesLocation[index] = position;
}
// I made this check function to prevent to equal id to be contained to the openedEmptyCells array which will be used to open the cells in cooperation with another function
function checkForEqualVal(valToTest, openedEmptyCells) {
match = false;
if (openedEmptyCells.length == 0) {
openedEmptyCells.push(valToTest);
} else {
for (i = 0; i < openedEmptyCells.length; i++){
if(openedEmptyCells[i][1] == valToTest[1]) {
match = true;
break;
}
}
if (!match) {
openedEmptyCells.push(valToTest);
}
}
return openedEmptyCells;
}
function logic_game(idNumericPart, randomMinesLocation, openedEmptyCells = []) {
// prevent infinite recursion loop;
if (idStack.indexOf(idNumericPart)>-1) {
console.log('infinite loop detected');
return;
}
// push the id to the idStack so we can tell if this function enters an infinite recursion loop;
idStack.push(idNumericPart);
console.log('logic_game('+ idNumericPart +')');
console.log('randomMinesLocation');
console.log(randomMinesLocation);
console.log('openedEmptyCells');
console.log(openedEmptyCells);
match = false;
numberOfMinesNear = 0;
canditateCells = [];
for (j = -1; j <= 1; j++) {
if (idNumericPart <= 8 && j == 1 || idNumericPart > 56 && j == -1 ) continue;
for(k= -1; k <= 1; k++){
if(((idNumericPart - 1) % 8 == 0 && k == -1) || (idNumericPart % 8 == 0 && k == 1))
continue;
idNumericPartOfNear = idNumericPart - (j * 8) + k;
if(j != 0 && k == 0 || j == 0 && k != 0 || j != 0 && k != 0)
canditateCells.push(idNumericPartOfNear);
for(i=0; i<10; i++){
if(idNumericPart == randomMinesLocation[i]){
match = true;
break;
}
else if(idNumericPartOfNear == randomMinesLocation[i])
numberOfMinesNear++;
}
}
}
if(match)
openedEmptyCells = checkForEqualVal([true, idNumericPart], openedEmptyCells);
else if(numberOfMinesNear > 0){
openedEmptyCells = checkForEqualVal([numberOfMinesNear, idNumericPart], openedEmptyCells);
}
else{
openedEmptyCells = checkForEqualVal([false, idNumericPart], openedEmptyCells);
for(i=0; i<canditateCells.length; i++){
matchedBtw = false;
for(j=0; j<emptyCells.length; j++){
if(canditateCells[i] == emptyCells[j])
matchedBtw = true;
break;
}
if(!matchedBtw)
openedEmptyCells = logic_game(canditateCells[i], randomMinesLocation, openedEmptyCells);
}
}
return openedEmptyCells;
}
function printToScreen(value, idNumericPart){
if(typeof(value) === 'boolean' && value){
document.getElementById("num_" + idNumericPart).parentElement.innerHTML = '<b>Boom</b>';
}
else if(typeof(value) === 'number'){
document.getElementById("num_" + idNumericPart).parentElement.style.backgroundColor = '#eee';
document.getElementById("num_" + idNumericPart).parentElement.innerHTML = value;
}
else{
document.getElementById("num_" + idNumericPart).parentElement.style.backgroundColor = '#eee';
document.getElementById("num_" + idNumericPart).parentElement.innerHTML = '';
}
}
function get_button(id){
// clear the button ID stack so we can track recursive calls per button click to assure that will only be one call per id per button game;
idStack = [];
console.log('get_button('+ id + ')');
idNumericPart = id.substring(4);
checkValue = document.getElementById("check").checked;
matchCheckForMineProtect = false;
if(checkValue){
for(i=0; i<mineProtecition.length; i++){
if(mineProtecition[i] == idNumericPart){
matchCheckForMineProtect = true;
break;
}
}
if(matchCheckForMineProtect){
mineProtecition.splice(i, 1);
valueToBeSubstitude = '<button id="' + id + '" onclick="get_button(this.id)"></button>';
}
else{
mineProtecition.push(idNumericPart);
valueToBeSubstitude = '<button id="' + id + '" style="background: url(' + 'flag.png' + ') no-repeat; background-size: 40px 40px;" onclick="get_button(this.id)"></button>';
}
document.getElementById(id).parentElement.innerHTML = valueToBeSubstitude;
}
else {
matchProtected = false;
for(i=0; i<mineProtecition.length; i++){
if(mineProtecition[i] == idNumericPart){
matchProtected = true;
break;
}
}
if(!matchProtected){
result = logic_game(idNumericPart, randomMinesLocation);
for(i=0; i<result.length; i++){
printToScreen(result[i][0], result[i][1]);
}
}
}
}
</script>
</body>
</html>