如何解决我的 TicTacToe 中的这些意外效果是否只是我不知道的 Javascript 计时方面?
我制作了一款运行良好的井字游戏。不过,我正在努力解决两件事。
- 在我选择我的之后,对手在“DumbAI”中的动作立即显示。当我施加
setTimeout()
并且 AI 对手获胜时,残局序列不会触发。不过,当我获胜时它会起作用。
最后的顺序是,当任何人连续得到 3 个时,警报应该闪烁,获胜的 3 个方格会突出显示,并且事件侦听器被移除,因此无法进行更多标记。
相反,代码让我切换到活跃玩家。如果主动玩家连续获得 3 个,则残局序列会触发。
所有这些功能都在同一个块中。通过在对手的移动上放置 setTimeout()
,是否跳过了残局序列?
- 同样,当我将残局序列分解为单独的块时,会出现另一个问题。
当我将残局序列从方块中取出并获胜时,代码会闪烁警报并突出显示空格,但它也会允许 AI 对手进行额外的移动。
通过将残局序列从块中取出,计算机是否在代码中移动得太快,允许对手在启动残局序列之前轮到他?
script.js:
var ONE_CLASS
var TWO_CLASS
const btn = document.querySelector('#PlayerOneSymbol');
btn.onclick = function () {
const XOs = document.querySelectorAll('input[name="choice"]');
for (const XO of XOs) {
if (XO.checked) {
ONE_CLASS = XO.value
TWO_CLASS = XO.value == 'X' ? 'O' : 'X'
break;
}
}
alert("First Move Belongs to " + ONE_CLASS + ". Select Player Two.");
};
var playerTwoIdentity
const btn2 = document.querySelector('#PlayerTwoChoice');
btn2.onclick = function () {
const Opponents = document.querySelectorAll('input[name="choice2"]');
for (const Opponent of Opponents) {
if (Opponent.checked) {
playerTwoIdentity = Opponent.value
break;
}
}
alert("Your Opponent is " + playerTwoIdentity + ". Start New Game.")
};
let playerOneTurn
function swapTurns() {
playerOneTurn = !playerOneTurn
};
const winningTrios = [
[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,7],[2,5,2]
]
restartBtn.addEventListener('click',startGame);
function startGame() {
if (ONE_CLASS == undefined || playerTwoIdentity == undefined) {return alert ("Make sure players are defined")}
console.log("player 1 = " + ONE_CLASS + ",player 2 = " + playerTwoIdentity)
drawBoard();
playerOneTurn = true;
}
const arrayfromBoxes = Array.from(document.getElementsByClassName('box'));
const stylingOfBoxes = document.querySelectorAll('.box');
function drawBoard() {
console.log(stylingOfBoxes)
for (let i = 0; i < stylingOfBoxes.length; i++) {
stylingOfBoxes[i].addEventListener('click',boxmarked,{once: true});}
stylingOfBoxes.forEach(gridBox => {
gridBox.classList.remove(ONE_CLASS)
gridBox.classList.remove(TWO_CLASS)
gridBox.classList.remove('winner')
gridBox.innerHTML = ""
})
}
function boxmarked(e) {
const index = arrayfromBoxes.indexOf(e.target)
// how to consolidate? maybe I just let ONE_CLASS mark and then if the AI or player
// or do it even earlier and link it with playerTurn?
if(playerOneTurn) {
arrayfromBoxes[index].classList.add(ONE_CLASS)
e.target.innerHTML = ONE_CLASS
} else {
arrayfromBoxes[index].classList.add(TWO_CLASS)
e.target.innerHTML = TWO_CLASS
}
// if (playerhasWon()) {
// declareWinner()
// return
// }
// if (emptySpaceRemains() == false) {
// declareTie()
// return
// }
hasGameEnded()
swapTurns()
// eliminate repetition -
if(playerTwoIdentity === "Dumb AI") {
var dumbAIArray = arrayfromBoxes.reduce((dumbAIArray,box,idx) => {
if (box.innerHTML === "") {
dumbAIArray.push(idx);
}
return dumbAIArray;
},[]);
let dumbAIpicked = dumbAIArray[Math.floor(dumbAIArray.length * (Math.random()))]
arrayfromBoxes[dumbAIpicked].classList.add(TWO_CLASS)
arrayfromBoxes[dumbAIpicked].innerHTML = TWO_CLASS
// why does Timeoutfunction prevent opponent sequence?
// setTimeout(() => {arrayfromBoxes[dumbAIpicked].classList.add(TWO_CLASS)},500);
// setTimeout(() => {arrayfromBoxes[dumbAIpicked].innerHTML = TWO_CLASS},500);
// if (playerhasWon()) {
// declareWinner()
// return
// }
// if (emptySpaceRemains() == false) {
// declareTie()
// return
// }
hasGameEnded()
swapTurns()
} else { console.log("Human")
}
}
function hasGameEnded() {
// fix declareWinner() appears before the added classes bc alert happens quicker than redraw
// I also cannot pull these out because then the opponent move fires and shows
// could have something to do with timing of in-block code
if (playerhasWon()) {
declareWinner()
return
}
if (emptySpaceRemains() == false) {
declareTie()
return
}
}
function checkClass() {
if(playerOneTurn) {
return ONE_CLASS
} else {
return TWO_CLASS
};}
function emptySpaceRemains() {
var innerHTMLempty = (insidebox) => insidebox.innerHTML===""
console.log(arrayfromBoxes.some(innerHTMLempty))
return (arrayfromBoxes.some(innerHTMLempty))
}
function declareTie() {
setTimeout(alert ("TIE GAME"),1000)}
function playerhasWon() {
var indexOfSelected = arrayfromBoxes.reduce((indexOfSelected,idx) => {
if (box.classList[1] === checkClass()) {
indexOfSelected.push(idx);
}
return indexOfSelected;
},[]);
const winningThreeIndexes = winningTrios
.map(trio => trio.filter(i => indexOfSelected.includes(i)))
.filter(i => i.length === 3);
console.log(winningThreeIndexes)
console.log(winningThreeIndexes.length)
if (winningThreeIndexes.length === 1) {winningThreeIndexes[0].map((index) => {arrayfromBoxes[index].className += ' winner'})}
var isThereAWinner =
winningTrios.some(trio => {return trio.every(i => indexOfSelected.includes(i))});
console.log({isThereAWinner});
return isThereAWinner
}
function declareWinner() {
setTimeout(alert (checkClass() + " WINS"),1000);
for (let i=0; i < stylingOfBoxes.length; i++) {
stylingOfBoxes[i].removeEventListener('click',{once: true});}
}
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Tic Tac Toe</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1 id="playtext">Let's Play</h1>
<div class="radioContainer">
<div id="playerOne">
<h3>Player One</h3>
<form>
<input type="radio" name="choice" value="X"> X<br>
<input type="radio" name="choice" value="O"> O<br>
<input type="button" id="PlayerOneSymbol" value="Confirm">
</form>
</div>
<div id="playerTwo">
<h3>Player Two</h3>
<form>
<input type="radio" name="choice2" value="Human"> Human<br>
<input type="radio" name="choice2" value="Dumb AI"> Dumb AI<br>
<input type="radio" name="choice2" value="Smart AI"> Smart AI<br>
<input type="button" id="PlayerTwoChoice" value="Confirm">
</form>
</div>
</div>
<div class="buttonHolder">
<div class="buttonWrapper">
<button id="restartBtn">Start New Game</button>
</div>
</div>
<div class="gameboard">
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
style.css:
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
}
#playtext {
text-align: center;
padding: 10px;
}
.buttonHolder {
height: 60px;
width: 100%;
float: left;
position: relative;
background-color: purple;
}
.buttonWrapper {
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%,-50%);
transform: translate(-50%,-50%);
}
.container {
background-color: purple;
justify-content: center;
/* display: flex;
flex-wrap: wrap; */
width: 400px;
height: 600px;
}
#gameboard {
border-top:10px;
border-bottom: 4px;
border-bottom-color: black;
background-color: chartreuse;
}
.box {
background-color: yellow;
width: 125px;
height: 125px;
float: left;
width: 33.33%;
}
button:hover {
cursor: pointer;
transform: translateY(-2px);
}
.winner {
background-color: black;
}
.X {
content: 'X';
font-size: 135px;
}
.O {
content: 'O';
font-size: 135px;
}
#spacer {
height: 10px;
width: 100%;
background-color: purple;
padding: 10px;
}
#playerOne {
background-color: blanchedalmond;
padding: 5px;
height: 110px;
float: left;
width: 50%;
}
#playerTwo {
background-color: mintcream;
padding: 5px;
height: 110px;
float: left;
width: 50%;
}
解决方法
一位朋友帮我弄清楚了#1 中发生的事情——我想。他指出 JS 是异步的。我有三个功能:
- 对手在空格中放置标记。
- 评估棋盘以查看对手是否获胜。
- 如果没有,它会切换回合并让玩家选择一个空间。
- 如果是,则结束游戏并阻止选择空格
我希望 (1) 延迟时,(2) 和 (3) 不会触发,直到 (1) 触发。
但实际上 (1) 被延迟了,所以 (2) 无论如何都会跑,并没有看到对手获胜,所以 (3) 让玩家选择一个空间。
所以为了解决这个问题,我在所有 3 个函数上都设置了超时时间:
setTimeout(() => {
let dumbAIpicked = dumbAIArray[Math.floor(dumbAIArray.length * (Math.random()))]
arrayfromBoxes[dumbAIpicked].classList.add(TWO_CLASS)
arrayfromBoxes[dumbAIpicked].innerHTML = TWO_CLASS
if (playerhasWon()) {
declareWinner()
return
}
if (emptySpaceRemains() == false) {
declareTie()
return
}
// hasGameEnded()
swapTurns()
``},500);
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。