You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
5.7 KiB
179 lines
5.7 KiB
<!doctype html>
|
|
<head>
|
|
<style>
|
|
body {
|
|
font-family: 'Helvetica', sans-serif;
|
|
}
|
|
|
|
.board-cell {
|
|
border: 1px solid #666;
|
|
box-sizing: border-box;
|
|
display: inline-block;
|
|
font-size: 32px;
|
|
height: 100px;
|
|
line-height: 100px;
|
|
text-align: center;
|
|
width: 100px;
|
|
}
|
|
|
|
.board-cell .content {
|
|
display: inline-block;
|
|
vertical-align: middle;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Tic Tac Toe</h1>
|
|
<p>Current player turn: <span class="js-current-player"></span></p>
|
|
<div class="js-board"></div>
|
|
<button class="js-reset">Reset</button>
|
|
<script>
|
|
// We will spend the next 45 minutes building a single-page web app that implements a Tic-Tac-Toe game. jQuery has been included for you. We'll implement the following features in order:
|
|
|
|
// 1. Render a 3x3 board. You can hardcode some X and O values within the cell for starters.
|
|
// 2. Implement the add symbol functionality that adds a X or O into a cell whenever the player clicks on it.
|
|
// 3. Rotate between the players whenever a move is made. Update the current player display.
|
|
// 4. Check for end game conditions after each move and display the winner if any.
|
|
// horizontally, vertically, diagonally
|
|
// 5. After a winner has been determined, disable further moves on the board.
|
|
// 6. Add a button to reset the game state.
|
|
(() => {
|
|
function init() {
|
|
const DOM = {
|
|
$currentPlayer: document.querySelector('.js-current-player'),
|
|
$board: document.querySelector('.js-board'),
|
|
$resetButton: document.querySelector('.js-reset'),
|
|
};
|
|
const SIZE = 3;
|
|
function initialState() {
|
|
return {
|
|
boardModel: Array(SIZE).fill(null).map(_ => Array(SIZE).fill(null)),
|
|
players: ['X', 'O'],
|
|
currentPlayer: 0,
|
|
gameEnded: false,
|
|
turn: 0,
|
|
};
|
|
}
|
|
let state = initialState();
|
|
|
|
function renderBoard() {
|
|
DOM.$currentPlayer.textContent = state.players[state.currentPlayer];
|
|
// Assuming SIZE > 0.
|
|
DOM.$board.innerHTML = '';
|
|
for (let i = 0; i < SIZE; i++) {
|
|
const $row = document.createElement('div');
|
|
$row.classList.add('board-row');
|
|
for (let j = 0; j < SIZE; j++) {
|
|
const $cell = document.createElement('div');
|
|
$cell.classList.add('board-cell');
|
|
$cell.setAttribute('data-i', i);
|
|
$cell.setAttribute('data-j', j);
|
|
const $content = document.createElement('span');
|
|
$content.classList.add('content');
|
|
$content.textContent = state.boardModel[i][j];
|
|
$cell.appendChild($content);
|
|
$row.appendChild($cell);
|
|
}
|
|
DOM.$board.appendChild($row);
|
|
}
|
|
}
|
|
|
|
function checkWinning(board, player) {
|
|
// Check horizontal.
|
|
for (let i = 0; i < SIZE; i++) {
|
|
if (board[i].every(cell => cell === player)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Check vertical.
|
|
for (let j = 0; j < SIZE; j++) {
|
|
let verticalAllPlayer = true;
|
|
for (let i = 0; i < SIZE; i++) {
|
|
if (board[i][j] !== player) {
|
|
verticalAllPlayer = false;
|
|
break;
|
|
}
|
|
}
|
|
if (verticalAllPlayer) {
|
|
return verticalAllPlayer;
|
|
}
|
|
}
|
|
|
|
// Check diagonal South-East.
|
|
let diagonalAllPlayer = true;
|
|
for (let i = 0; i < SIZE; i++) {
|
|
if (board[i][i] !== player) {
|
|
diagonalAllPlayer = false;
|
|
break;
|
|
}
|
|
}
|
|
if (diagonalAllPlayer) {
|
|
return diagonalAllPlayer;
|
|
}
|
|
|
|
// Check diagonal North-East.
|
|
diagonalAllPlayer = true;
|
|
for (let i = SIZE - 1, j = 0; i >= 0; i--, j++) {
|
|
if (board[i][j] !== player) {
|
|
diagonalAllPlayer = false;
|
|
break;
|
|
}
|
|
}
|
|
if (diagonalAllPlayer) {
|
|
return diagonalAllPlayer;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function attachEventListeners() {
|
|
DOM.$board.addEventListener('click', (event) => {
|
|
if (state.gameEnded) {
|
|
return;
|
|
}
|
|
if (!event.target.classList.contains('board-cell')) {
|
|
return;
|
|
}
|
|
const $cell = event.target;
|
|
const i = parseInt($cell.getAttribute('data-i'), 10);
|
|
const j = parseInt($cell.getAttribute('data-j'), 10);
|
|
if (state.boardModel[i][j] !== null) {
|
|
alert('Cell has already been taken!');
|
|
return;
|
|
}
|
|
const player = state.players[state.currentPlayer];
|
|
state.boardModel[i][j] = player;
|
|
const winningMove = checkWinning(state.boardModel, player);
|
|
state.turn++;
|
|
if (!winningMove) {
|
|
state.currentPlayer = (state.currentPlayer + 1) % 2;
|
|
renderBoard();
|
|
if (state.turn === SIZE * SIZE) {
|
|
alert('It\'s a draw!');
|
|
}
|
|
} else {
|
|
renderBoard();
|
|
state.gameEnded = true;
|
|
alert(`Player ${player} wins!`);
|
|
}
|
|
});
|
|
|
|
DOM.$resetButton.addEventListener('click', () => {
|
|
if (confirm('Start a new game?')) {
|
|
state = initialState();
|
|
renderBoard();
|
|
}
|
|
});
|
|
}
|
|
|
|
renderBoard();
|
|
attachEventListeners();
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|