我已经在这个问题上绞尽脑汁了一段时间了: 当我点击我的tic tac toe游戏的全部重置按钮时,它没有正确重置。当我在MYAPP.firstGame函数上使用console.log时,它会添加MYAPP.game对象的其他实例。如果播放器在重置时没有首先播放,则会导致计算机播放器连续多次播放。
我已经问过其他几位程序员并且无法弄清楚发生了什么。 如果你能帮助我,我将不胜感激。
以下是指向code pen的链接。
HTML:
<div class="outer-container">
<button class="hard-reset">Reset All</button>
<div class="player-one-turn">
<p></p>
</div>
<div class="player-two-turn">
<p></p>
</div>
<div class="board-container">
<div class="game-starter">
<p>Would you like to be X or O?</p>
<button class="choose-x">X</button>
<button class="choose-o">O</button>
<button class="back-button"><i class="fa fa-arrow-left"></i> Back</button>
</div>
<div class="game-choice">
<p>How do you want to play?</p>
<button class="one-player">One Player</button>
<button class="two-player">Two Player</button>
</div>
<div class="game-board">
<div class="draw-message">
<p>It was a draw..</p>
</div>
<div class="lose-message">
<p>Uh oh, you lost..</p>
</div>
<div class="win-message">
<p>You Won!!! :)</p>
</div>
<canvas id="myCanvas"></canvas>
<ul class="boxes">
</ul>
</div>
</div>
</div>
CSS:
body,
html {
width: 100%;
height: 100%;
}
li {
list-style: none;
}
.outer-container {
background: rgba(240,180,135,1);
box-shadow: inset -1px 1px 7px rgba(0,0,0,.2), inset 1px -1px 7px rgba(0,0,0,.2), 1px 12px 5px rgba(0,0,0,.4), 4px 3px 8px rgba(0,0,0,.4), 5px 10px 10px rgba(0,0,0,.2), -5px 10px 10px rgba(0,0,0,.4);
position: relative;
border-radius: 10px;
width: 540px;
height: 540px;
margin: 10% auto;
padding: 30px 0;
}
.board-container {
width: 500px;
height: 500px;
background: rgba(40,40,40,1)
-webkit-radial-gradient(center, rgba(40,80,60,1), rgba(0,20,20,.6));
background: rgba(40,40,40,1)
-moz-radial-gradient(center, rgba(40,80,60,1), rgba(0,20,20,.6));
background: rgba(40,40,40,1)
-ms-radial-gradient(center, rgba(40,80,60,1), rgba(0,20,20,.6));
background: rgba(40,40,40,1)
radial-gradient(center, rgba(40,80,60,1), rgba(0,20,20,.6));
background-size: cover;
position: relative;
top: 20px;
left: 20px;
overflow: hidden;
}
.game-board {
width: 440px;
height: 450px;
margin: 20px auto;
position: relative;
}
.boxes {
padding: 0;
}
.boxes li {
width: 33%;
height: 150px;
display: inline-block;
position: relative;
z-index: 1000;
margin: 0;
overflow: hidden;
}
li i {
font-size: 7.5rem;
text-align: center;
display: block;
width: 100%;
height: 99%;
margin-bottom: 0;
margin-left: 5px;
font-style: normal;
font-family: "Architects Daughter", "Helvetica", "sans-serif";
color: rgba(220,220,220,.7);
z-index: 500;
}
/* Canvas Drawing */
#myCanvas {
width: 100%;
height: 456px;
position: absolute;
z-index: 0;
left: 0;
top: 0;
opacity: 0;
}
/* Player/Computer prompt */
.player-one-turn {
background: rgba(0,200,200,1);
left: 15px;
}
.player-two-turn {
background: rgba(200,100,100,1);
right: 15px;
}
.player-one-turn,
.player-two-turn {
position: absolute;
top: 0;
width: 200px;
height: 50px;
z-index: -10;
color: white;
text-align: center;
}
.player-one-turn p,
.player-two-turn p {
font-size: 1.5rem;
margin-top: 10px;
}
/* reset button */
.hard-reset {
position: absolute;
top: 10px;
right: 20px;
background: none;
border: none;
font-family: 'Architects Daughter', sans-serif;
color: rgba(100,60,50,.8);
font-size: 1.2rem;
border-radius: 20px;
border: 2px dashed transparent;
}
.hard-reset:hover {
border: 2px dashed rgba(100,60,50,1);
color: rgba(100,60,50,1);
}
.hard-reset:focus {
outline: none;
}
/* Result Feedback */
span.rotate {
color: rgba(0,200,200,1);
}
i.win {
background: black;
}
.draw-message,
.lose-message,
.win-message {
background: rgba(0,0,0,.8);
width: 530px;
height: 530px;
z-index: 2000;
position: absolute;
display: none;
left: -30px;
top: -35px;
box-sizing: border-box;
margin: 0;
}
.draw-message p,
.lose-message p,
.win-message p {
color: white;
text-align: center;
position: relative;
top: 230px;
font-size: 3rem;
margin: 0;
font-family: 'Architects Daughter', sans-serif;
}
/*============================================
Game Starter
============================================*/
.game-choice,
.game-starter {
background: rgba(40,40,40,1)
-webkit-radial-gradient(center, rgba(40,80,60,1), rgba(0,20,20,.6));
background: rgba(40,40,40,1)
-moz-radial-gradient(center, rgba(40,80,60,1), rgba(0,20,20,.6));
background: rgba(40,40,40,1)
-ms-radial-gradient(center, rgba(40,80,60,1), rgba(0,20,20,.6));
background: rgba(40,40,40,1)
radial-gradient(center, rgba(40,80,60,1), rgba(0,20,20,.6));
display: block;
width: 100%;
height: 500px;
position: absolute;
top: 0px;
text-align: center;
font-family: 'Architects Daughter', Helvetica, sans-serif;
z-index: 1500;
}
.game-starter {
display: none;
}
.game-choice p,
.game-starter p {
font-size: 2.2rem;
}
.game-choice button,
.game-choice p,
.game-starter button,
.game-starter p {
color: rgba(220,220,220,1);
position: relative;
top: 100px;
margin: 10px auto;
}
.game-choice p,
.game-starter p {
max-width: 60%;
}
.game-choice button,
.game-starter button {
background: none;
border: none;
opacity: .6;
border-radius: 20px;
border: 2px solid transparent;
}
.game-choice button {
font-size: 2rem;
}
.game-starter button {
font-size: 2.8rem;
}
.game-choice button:focus,
.game-starter button:focus {
outline: none;
}
.game-choice button:hover,
.game-starter button:hover {
opacity: 1;
border: 2px dashed rgba(230,230,230,.5);
}
.game-starter button.back-button {
position: absolute;
top: 400px;
right: 200px;
font-size: 1.5rem;
border: none;
}
.game-starter .back-button:hover {
border: none;
}
/*============================
Win/Lose animation
==============================*/
@-webkit-keyframes rotating /* Safari and Chrome */ {
from {
-ms-transform: rotateY(0deg);
-moz-transform: rotateY(0deg);
-webkit-transform: rotateY(0deg);
-o-transform: rotateY(0deg);
transform: rotateY(0deg);
}
to {
-ms-transform: rotateY(360deg);
-moz-transform: rotateY(360deg);
-webkit-transform: rotateY(360deg);
-o-transform: rotateY(360deg);
transform: rotateY(360deg);
}
}
@keyframes rotating {
from {
-ms-transform: rotateY(0deg);
-moz-transform: rotateY(0deg);
-webkit-transform: rotateY(0deg);
-o-transform: rotateY(0deg);
transform: rotateY(0deg);
}
to {
-ms-transform: rotateY(360deg);
-moz-transform: rotateY(360deg);
-webkit-transform: rotateY(360deg);
-o-transform: rotateY(360deg);
transform: rotateY(360deg);
}
}
.rotate {
-webkit-animation: rotating 2s linear infinite;
-moz-animation: rotating 2s linear infinite;
-ms-animation: rotating 2s linear infinite;
-o-animation: rotating 2s linear infinite;
animation: rotating 2s linear infinite;
}
JS / jQuery:
var MYAPP = MYAPP || {};
/*=========================
Display functions
==========================*/
MYAPP.display = {
hideGameStarter: function() {
$('.game-starter').fadeOut();
},
showGameStarter: function(isTwoPlayer) {
var message;
if (isTwoPlayer) {
message = "Player 1 : Would you like X or O?"
}
else {
message = "Would you like to be X or O?";
}
window.setTimeout(function() {
$('.game-starter').fadeIn(500).children('p').text(message);
}, 700);
},
showGameChoice: function() {
$('.game-choice').fadeIn();
},
hideGameChoice: function() {
$('.game-choice').fadeOut(600);
},
showPlayerOnePrompt: function() {
var game = MYAPP.game;
if (game.secondPlayer) {
$('.player-one-turn p').text('Go Player 1!');
}
else {
$('.player-one-turn p').text('Your turn!');
}
$('.player-one-turn').animate({'top': '-45px'}, 500);
},
hidePlayerOnePrompt: function() {
$('.player-one-turn').animate({'top': '0'}, 500);
},
showPlayerTwoPrompt: function() {
var game = MYAPP.game;
if (game.secondPlayer) {
$('.player-two-turn p').text('Go Player 2!');
}
else {
$('.player-two-turn p').text('Computer\'s turn');
}
$('.player-two-turn').animate({'top': '-45px'}, 500);
},
hidePlayerTwoPrompt: function() {
$('.player-two-turn').animate({'top': '0'}, 500);
},
showDrawMessage: function() {
window.setTimeout(function() {
$('.draw-message').fadeIn(500);
}, 1500);
},
hideDrawMessage: function() {
$('.draw-message').fadeOut(1000);
},
showLoseMessage: function() {
window.setTimeout(function() {
$('.lose-message').fadeIn(500);
}, 1500);
},
hideLoseMessage: function() {
$('.lose-message').fadeOut(1000);
},
showWinMessage: function(turn) {
window.setTimeout(function() {
$('.win-message').fadeIn(500).children('p').text("Player " + turn + " wins!! :D ")
}, 1500);
},
hideWinMessage: function() {
$('.win-message').fadeOut(1000);
},
drawBoard: function() {
window.setTimeout(function() {
var c = document.getElementById("myCanvas");
var canvas = c.getContext("2d");
canvas.lineWidth = 1;
canvas.strokeStyle = "#fff";
//vertical lines
canvas.beginPath();
canvas.moveTo(100, 0);
canvas.lineTo(100, $('#myCanvas').height());
canvas.closePath();
canvas.stroke();
canvas.beginPath();
canvas.moveTo(200, 0);
canvas.lineTo(200, $('#myCanvas').height());
canvas.closePath();
canvas.stroke();
// horizontal lines
canvas.lineWidth = .5;
canvas.beginPath();
canvas.moveTo(4, 100.5);
canvas.lineTo(296, 100.5);
canvas.closePath();
canvas.stroke();
canvas.beginPath();
canvas.moveTo(4, 49.5);
canvas.lineTo(296, 49.5);
canvas.closePath();
canvas.stroke();
}, 1500);
},
resetSquares: function() {
$('.boxes').html('');
for (var i = 1; i <= 9; i++) {
var box = '<li class="' + i + '"><i class="letter"><span></span></i></li>';
$(box).appendTo($('.boxes'));
}
}
};
/*=========================
Game Logic
==========================*/
MYAPP.game = {
initialize: function() {
this.winCombos = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[1, 4, 7],
[2, 5, 8],
[3, 6, 9],
[1, 5, 9],
[7, 5, 3]
];
this.numFilledIn = 0;
this.currentBoard = {
1: '',
2: '',
3: '',
4: '',
5: '',
6: '',
7: '',
8: '',
9: ''
};
},
whoStarts: function() {
var random = Math.floor(Math.random() * 2 + 1);
return random;
},
gameSelection: function(item) {
if ($(item).text() === 'One Player') {
// returns what secondPlayer value to set
return false;
}
else {
return true;
}
},
firstGame: function() {
MYAPP.game.playerOneSymbol = $(this).text();
MYAPP.game.playerTwoSymbol = MYAPP.game.playerOneSymbol == 'X' ? 'O' : 'X';
MYAPP.game.turn = MYAPP.game.whoStarts();
MYAPP.display.hideGameStarter();
$('#myCanvas').animate({'opacity': '1'}, 500);
MYAPP.display.resetSquares();
MYAPP.game.gameInPlay = true;
MYAPP.game.play();
},
play: function() {
$('.boxes li').on('click', function() {
MYAPP.game.playerTurn(MYAPP.game, this);
});
window.setTimeout(function(){
if (MYAPP.game.turn === 1) {
MYAPP.display.showPlayerOnePrompt();
}
else if (MYAPP.game.turn === 2) {
MYAPP.display.showPlayerTwoPrompt();
}
}, 1500);
window.setTimeout(function() {
if (MYAPP.game.turn === 2 && !MYAPP.game.secondPlayer) {
MYAPP.game.computerPlay();
}
}, 1200);
},
playerTurn: function(game, square) {
var symbol = game.turn === 1 ? game.playerOneSymbol : game.playerTwoSymbol;
var box = $(square).children('i').children('span');
if (box.text() === '' && game.gameInPlay && (game.turn === 1 || (game.turn === 2 && game.secondPlayer))) {
box.text(symbol);
var number = $(square).attr('class');
game.updateSquare(game, number, symbol);
game.endTurn(symbol);
}
},
computerPlay: function() {
//test computer move suggestion
var game = MYAPP.game;
var boxNumber;
if (computerWhichMove(MYAPP.game)) {
boxNumber = computerWhichMove(MYAPP.game);
var currentBox = $('.' + boxNumber).children('i');
}
var symbol = game.playerTwoSymbol;
window.setTimeout(function() {
currentBox.children('span').text(symbol);
game.updateSquare(game, boxNumber, game.playerTwoSymbol);
game.endTurn(symbol);
}, 1000);
},
endTurn: function(symbol) {
var display = MYAPP.display;
this.numFilledIn = this.numFilledIn + 1;
if (this.gameInPlay) {
if (this.checkWin(symbol)[0]) {
if (this.secondPlayer) {
display.showWinMessage(this.turn);
}
else {
this.turn === 1 ? display.showWinMessage(this.turn) : display.showLoseMessage();
}
this.gameInPlay = false;
this.showWinningCombination();
display.hidePlayerOnePrompt();
display.hidePlayerTwoPrompt();
this.reset();
}
// stop if it is a draw
else if (this.numFilledIn >= 9) {
this.gameInPlay = false;
display.hidePlayerOnePrompt();
display.hidePlayerTwoPrompt();
display.showDrawMessage();
this.reset();
} else {
if (this.turn === 1) {
display.hidePlayerOnePrompt();
display.showPlayerTwoPrompt();
this.turn = 2;
// call computer turn if no second player
if (!this.secondPlayer) {
this.computerPlay();
}
} else if (this.turn === 2) {
display.showPlayerOnePrompt();
display.hidePlayerTwoPrompt();
this.turn = 1;
}
}
}
},
updateSquare: function(game, number, symbol) {
game.currentBoard[number] = symbol;
},
checkWin: function(symbol) {
var currentBoard = this.currentBoard;
var wins = this.winCombos;
var winningCombo = [];
var winner = wins.some(function(combination) {
var winning = true;
for (var i = 0; i < combination.length; i++) {
if (currentBoard[combination[i]] !== symbol) {
winning = false;
}
}
if (winning) {
winningCombo = combination;
}
return winning;
});
return [winner, winningCombo];
},
showWinningCombination: function() {
var symbol = this.turn === 1 ? this.playerOneSymbol : this.playerTwoSymbol;
var combo = this.checkWin(symbol)[1];
for (var i = 0; i < combo.length; i++) {
var currentBox = '.' + combo[i];
// Black box and rotating test for winning combo
$(currentBox).children('i').addClass('win').children('span').addClass('rotate');
}
},
reset: function() {
var game = MYAPP.game;
var display = MYAPP.display;
game.initialize();
window.setTimeout(function() {
display.hideDrawMessage();
display.hideLoseMessage();
display.hideWinMessage();
$('.boxes').fadeOut(500);
}, 5000);
window.setTimeout(function(){
display.resetSquares();
$('.boxes').fadeIn();
game.numFilledIn = 0;
}, 6000);
//Make sure time for next timeout is long enough
//to not cause problems after first game
window.setTimeout(function() {
game.gameInPlay = true;
game.play();
}, 6000);
},
resetGame: function() {
$('#myCanvas').css('opacity', '0');
MYAPP.display.resetSquares();
MYAPP.game.initialize();
MYAPP.game.gameInPlay = false;
MYAPP.game.playerOneSymbol = null;
MYAPP.game.playerTwoSymbol = null;
MYAPP.display.showGameChoice();
}
};
$(document).ready(function() {
MYAPP.display.drawBoard();
MYAPP.game.initialize();
$('.game-choice button').click(function() {
MYAPP.game.secondPlayer = MYAPP.game.gameSelection(this);
MYAPP.display.hideGameChoice();
MYAPP.display.showGameStarter(MYAPP.game.secondPlayer);
$('.game-starter .choose-x, .game-starter .choose-o').on('click', MYAPP.game.firstGame);
$('.back-button').on('click', function() {
MYAPP.display.hideGameStarter();
MYAPP.display.showGameChoice();
});
});
$('.hard-reset').on('click', MYAPP.game.resetGame);
});
/*================================
Computer Move Decisions
=================================*/
function computerWhichMove(game) {
var board = game.currentBoard;
var move = winOrBlockChoice(game, 'win', board)[0];
if (!move) {
move = winOrBlockChoice(game, 'block', board)[0];
}
if (!move) {
move = doubleThreatChoice(game, 'win');
}
if (!move) {
move = doubleThreatChoice(game, 'block');
}
if (!move) {
move = firstPlay(game);
}
if (!move) {
move = emptyCorner(game);
}
if (!move) {
move = emptySide(game);
}
move = (move && game.currentBoard[move]) === '' ? move : false;
return move;
}
function winOrBlockChoice(game, choiceType, board) {
if (choiceType === 'win') {
var currentSymbol = game.playerTwoSymbol;
var opponentSymbol = game.playerOneSymbol;
} else if (choiceType === 'block') {
var currentSymbol = game.playerOneSymbol;
var opponentSymbol = game.playerTwoSymbol;
} else {
return;
}
var moves = [];
game.winCombos.forEach(function(combo) {
var notFound = [];
var notPlayer = true;
for (var i = 0; i < combo.length; i++) {
if (board[combo[i]] !== currentSymbol) {
if (board[combo[i]] === opponentSymbol) {
notPlayer = false;
} else {
notFound.push(combo[i]);
}
}
}
if (notFound.length === 1 && notPlayer) {
var move = notFound[0];
moves.push(move);
}
});
return moves;
}
function doubleThreatChoice(game, choiceType) {
// use winChoice function to test a spot for double threat
var board = game.currentBoard;
var move;
if (choiceType === 'win') {
var currentSymbol = game.playerTwoSymbol;
var opponentSymbol = game.playerOneSymbol;
} else if (choiceType === 'block') {
var currentSymbol = game.playerOneSymbol;
var opponentSymbol = game.playerTwoSymbol;
}
// forced diagonal win on 4th move prevention
if (board[5] === currentSymbol && game.numFilledIn === 3) {
if ((board[1] === opponentSymbol && board[9] === opponentSymbol) || (board[3] === opponentSymbol && board[7] === opponentSymbol)) {
// Play an edge to block double threat
move = emptySide(game);
}
}
if (board[5] === opponentSymbol && game.numFilledIn === 2) {
move = diagonalSecondAttack(game);
}
if (!move) {
// clone current board;
var testBoard = $.extend({}, board);
for (var i = 1; i <= 9; i++) {
testBoard = $.extend({}, board);
if (testBoard[i] === '') {
testBoard[i] = currentSymbol;
if (winOrBlockChoice(game, choiceType, testBoard).length >= 2) {
move = i;
}
}
}
}
return move || false;
}
function diagonalSecondAttack(game) {
var board = game.currentBoard;
var comp = game.playerTwoSymbol;
var corners = [1,3,7,9];
for (var i = 0; i < corners.length; i++) {
if (board[corners[i]] === comp) {
return 10 - corners[i];
}
}
}
function firstPlay(game) {
var board = game.currentBoard;
var corners = [1, 3, 7, 9];
var move;
if (game.numFilledIn === 1) {
// player plays center
if (board[5] === game.playerOneSymbol) {
var cornerNum = Math.floor(Math.random() * 4 + 1);
move = [1, 3, 7, 9][cornerNum];
}
//player plays corner, play opposite corner
else {
for (var i = 0; i < corners.length; i++) {
if (game.currentBoard[corners[i]] === game.playerOneSymbol) {
move = 5;
}
}
}
} else if (game.numFilledIn === 0) {
var cornerNum = Math.floor(Math.random() * corners.length + 1);
move = corners[cornerNum];
}
return move ? move : false;
}
function emptyCorner(game) {
var board = game.currentBoard;
var corners = [1, 3, 7, 9];
var move;
for (var i = 0; i < corners.length; i++) {
if (board[corners[i]] === '') {
move = corners[i];
}
}
return move || false;
}
function emptySide(game) {
var sides = [2, 4, 6, 8];
for (var i = 0; i < sides.length; i++) {
if (game.currentBoard[sides[i]] === '') {
return sides[i];
}
}
return false;
}
/* End Computer Move Decisions */
答案 0 :(得分:0)
在敲击键盘后我找到了解决方案!
我所要做的就是在第一次点击方法之前添加.off
。这显然等同于现在已弃用的.unbind
方法,并将元素限制为单击一次。这stackoverflow question为我做了。我希望我能够知道真正的问题,因为这会对我的搜索有所帮助。