https://www.sourcecodeexamples.net/2020/09/javascript-tic-tac-toe-game.html
在本教程中,我们将学习如何开发高级 JavaScript井字游戏。在井字棋是很常见的游戏,是很容易玩。游戏规则简单而广为人知。
在本教程中,我们将开发基于 Web 的 Tic Tac Toe 游戏。在这个游戏中,提示用户选择网格中的九个方格之一。玩家选择的网格然后由玩家的相应标志显示。第一个玩家标记X,第二个玩家标记O。
创建一个名为JavaScript-Tic-Tac-Toe的文件夹作为项目工作区,我们将在此文件夹中创建所有项目文件。
让我们创建index.html并向其添加以下代码:
Document
让我们创建名为script.js的 JavaScript 文件,并向其中添加以下 JavaScript 代码:
const X_CLASS = 'x'
const CIRCLE_CLASS = 'circle'
const WINNING_COMBINATIONS = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
]
const cellElements = document.querySelectorAll('[data-cell]')
const board = document.getElementById('board')
const winningMessageElement = document.getElementById('winningMessage')
const restartButton = document.getElementById('restartButton')
const winningMessageTextElement = document.querySelector('[data-winning-message-text]')
let circleTurn
startGame()
restartButton.addEventListener('click', startGame)
function startGame() {
circleTurn = false
cellElements.forEach(cell => {
cell.classList.remove(X_CLASS)
cell.classList.remove(CIRCLE_CLASS)
cell.removeEventListener('click', handleClick)
cell.addEventListener('click', handleClick, {once: true})
})
setBoardHoverClass()
winningMessageElement.classList.remove('show')
}
function handleClick(e) {
const cell = e.target
const currentClass = circleTurn ? CIRCLE_CLASS : X_CLASS
placeMark(cell, currentClass)
if (checkWin(currentClass)) {
endGame(false)
} else if (isDraw()) {
endGame(true)
} else {
swapTurns()
setBoardHoverClass()
}
}
function endGame(draw) {
if (draw) {
winningMessageTextElement.innerText = 'Draw!'
} else {
winningMessageTextElement.innerText = `${circleTurn ? "O's" : "X's"} Wins!`
}
winningMessageElement.classList.add('show')
}
function isDraw() {
return [...cellElements].every(cell => {
return cell.classList.contains(X_CLASS) || cell.classList.contains(CIRCLE_CLASS)
})
}
function placeMark(cell, currentClass) {
cell.classList.add(currentClass)
}
function swapTurns() {
circleTurn = !circleTurn
}
function setBoardHoverClass() {
board.classList.remove(X_CLASS)
board.classList.remove(CIRCLE_CLASS)
if (circleTurn) {
board.classList.add(CIRCLE_CLASS)
} else {
board.classList.add(X_CLASS)
}
}
function checkWin(currentClass) {
return WINNING_COMBINATIONS.some(combination => {
return combination.every(index => {
return cellElements[index].classList.contains(currentClass)
})
})
}
让我们创建一个名为style.css的 CSS 文件,并向其中添加以下 CSS 代码:
*, *::after, *::before {
box-sizing: border-box;
}
:root {
--cell-size: 100px;
--mark-size: calc(var(--cell-size) * .9);
}
body {
margin: 0;
}
.board {
width: 100vw;
height: 100vh;
display: grid;
justify-content: center;
align-content: center;
justify-items: center;
align-items: center;
grid-template-columns: repeat(3, auto)
}
.cell {
width: var(--cell-size);
height: var(--cell-size);
border: 1px solid black;
display: flex;
justify-content: center;
align-items: center;
position: relative;
cursor: pointer;
}
.cell:first-child,
.cell:nth-child(2),
.cell:nth-child(3) {
border-top: none;
}
.cell:nth-child(3n + 1) {
border-left: none;
}
.cell:nth-child(3n + 3) {
border-right: none;
}
.cell:last-child,
.cell:nth-child(8),
.cell:nth-child(7) {
border-bottom: none;
}
.cell.x,
.cell.circle {
cursor: not-allowed;
}
.cell.x::before,
.cell.x::after,
.cell.circle::before {
background-color: black;
}
.board.x .cell:not(.x):not(.circle):hover::before,
.board.x .cell:not(.x):not(.circle):hover::after,
.board.circle .cell:not(.x):not(.circle):hover::before {
background-color: lightgrey;
}
.cell.x::before,
.cell.x::after,
.board.x .cell:not(.x):not(.circle):hover::before,
.board.x .cell:not(.x):not(.circle):hover::after {
content: '';
position: absolute;
width: calc(var(--mark-size) * .15);
height: var(--mark-size);
}
.cell.x::before,
.board.x .cell:not(.x):not(.circle):hover::before {
transform: rotate(45deg);
}
.cell.x::after,
.board.x .cell:not(.x):not(.circle):hover::after {
transform: rotate(-45deg);
}
.cell.circle::before,
.cell.circle::after,
.board.circle .cell:not(.x):not(.circle):hover::before,
.board.circle .cell:not(.x):not(.circle):hover::after {
content: '';
position: absolute;
border-radius: 50%;
}
.cell.circle::before,
.board.circle .cell:not(.x):not(.circle):hover::before {
width: var(--mark-size);
height: var(--mark-size);
}
.cell.circle::after,
.board.circle .cell:not(.x):not(.circle):hover::after {
width: calc(var(--mark-size) * .7);
height: calc(var(--mark-size) * .7);
background-color: white;
}
.winning-message {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .9);
justify-content: center;
align-items: center;
color: white;
font-size: 5rem;
flex-direction: column;
}
.winning-message button {
font-size: 3rem;
background-color: white;
border: 1px solid black;
padding: .25em .5em;
cursor: pointer;
}
.winning-message button:hover {
background-color: black;
color: white;
border-color: white;
}
.winning-message.show {
display: flex;
}
让我们在浏览器中打开index.html文件,您将能够看到以下屏幕: