Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- "use strict";
- const GRID_SIZE_ROWS = 3;
- const GRID_SIZE_COLUMNS = 3;
- class Status {
- constructor (element) {
- this.element = element;
- }
- clear() {
- while (this.element.firstChild) {
- this.element.removeChild(this.element.firstChild);
- }
- }
- appendText(text, style) {
- let textNode = document.createElement('span');
- textNode.innerText = text;
- for (let property in style) {
- textNode.style[property] = style[property];
- }
- this.element.appendChild(textNode);
- }
- appendMany(array) {
- for (let item of array) {
- this.appendText(item.text, item.style);
- }
- }
- }
- class Grid {
- constructor(element) {
- this.element = element;
- this.callbacks = [];
- this.build();
- }
- reset() {
- while (this.element.firstChild) {
- this.element.removeChild(this.element.firstChild);
- }
- this.build();
- }
- build() {
- // Create row elements
- this.rows = [];
- for (let rowIndex = 0; rowIndex < GRID_SIZE_ROWS; rowIndex++) {
- // Create the object representing a row
- let row = {
- grid: this,
- index: rowIndex,
- element: document.createElement('div'),
- cells: []
- };
- this.rows.push(row);
- // Configure the row element
- row.element.className = 'row';
- this.element.appendChild(row.element);
- // Create the cells for this row
- for (let colIndex = 0; colIndex < GRID_SIZE_COLUMNS; colIndex++) {
- let cell = {
- row: row,
- index: colIndex,
- element: document.createElement('div'),
- piece: {
- element: document.createElement('div'),
- get type() {
- let classes = this.element.className.split(' ');
- if (classes.length >= 2)
- return classes.find(i => i != 'piece');
- return 'empty';
- },
- set type(piece) {
- this.element.className = `${piece} piece`;
- }
- },
- get highlighted() {
- return this.element.className.startsWith('highlighted');
- },
- set highlighted(value) {
- if (value)
- this.element.className = 'highlighted cell';
- else
- this.element.className = 'cell';
- }
- };
- row.cells.push(cell);
- row.element.appendChild(cell.element);
- // Configure the cell element
- cell.element.className = 'cell';
- cell.element.addEventListener('click', (e) => {
- cell.row.grid.onClickCell(cell);
- });
- cell.element.appendChild(cell.piece.element);
- // Configure the piece element
- cell.piece.element.className = 'piece';
- }
- }
- }
- attachClickCellHandler(callback) {
- this.callbacks.push(callback);
- }
- onClickCell(cell) {
- for (let callback of this.callbacks)
- callback(cell);
- }
- * getLine(startX, startY, stepX, stepY) {
- let x = startX;
- let y = startY;
- while (((y >= 0) && (y < this.rows.length)) &&
- ((x >= 0) && (x < this.rows[y].cells.length)))
- {
- yield this.rows[y].cells[x];
- x += stepX;
- y += stepY;
- }
- }
- }
- class TicTacToe {
- constructor(gridNode, statusNode) {
- // Create an array of "players" to control turns
- this.players = [
- {
- piece: 'nought',
- displayText: {
- single: 'Player One',
- possessive: 'Player One\'s'
- },
- color: 'green'
- },
- {
- piece: 'cross',
- displayText: {
- single: 'Player Two',
- possessive: 'Player Two\'s'
- },
- color: 'red'
- }
- ];
- this.currentPlayerIndex = 0;
- this.finished = false;
- // Create the status text
- this.status = new Status(statusNode);
- // Create the grid, and attach a "click cell" handler
- this.grid = new Grid(gridNode);
- this.grid.attachClickCellHandler(this.onClickCell.bind(this));
- // Begin the first turn
- this.beginTurn();
- }
- get currentPlayer() {
- return this.players[this.currentPlayerIndex];
- }
- beginTurn() {
- this.status.clear();
- this.status.appendMany([
- { text: 'It is ' },
- {
- text: this.currentPlayer.displayText.possessive,
- style: {
- color: this.currentPlayer.color
- }
- },
- { text: ' turn!' }
- ]);
- }
- nextTurn() {
- // Move to the next player
- this.currentPlayerIndex += 1;
- if (this.currentPlayerIndex >= this.players.length)
- this.currentPlayerIndex = 0;
- this.beginTurn();
- }
- onClickCell(cell) {
- // Has the game been won?
- if (this.finished) {
- this.reset();
- return;
- }
- // Did the user click on a cell that was already occupied?
- if (cell.piece.type != 'empty') {
- this.status.clear();
- this.status.appendText('That space is already occupied!');
- return;
- }
- // Add their piece to the board and check for a win condition
- cell.piece.type = this.currentPlayer.piece;
- this.checkForWinCondition(cell);
- // If the game is not finished, go to the next turn
- if (!this.finished)
- this.nextTurn();
- }
- checkForWinCondition(cell) {
- // Only check lines that are worth checking
- let lines = [
- // The horizontal line
- [ 0, cell.row.index, 1, 0 ],
- // The vertical line
- [ cell.index, 0, 0, 1 ]
- ];
- // Check if either of the diagonals are worth checking
- if (cell.index == cell.row.index)
- lines.push([ 0, 0, 1, 1 ]);
- if (Math.abs(cell.index - 2) == cell.row.index)
- lines.push([ 2, 0, -1, 1 ]);
- // Go through each line
- for (let lineParams of lines) {
- // Check is this line is a win for the current player
- let line = this.grid.getLine.apply(this.grid, lineParams);
- let path = [];
- while (true) {
- // Get the next "step" in this line
- let step = line.next();
- if (step.done)
- // There are no more steps, break out of this loop
- break;
- // If this cell in the line contributes to a win, then add it to
- // the path
- if (step.value.piece.type === this.currentPlayer.piece)
- path.push(step.value);
- else
- // This line is not a win for the current player, so
- // break out of this loop
- break;
- };
- // If the line we just checked is a win, finish the game
- if (path.length == 3) {
- this.playerWonGame(path);
- break;
- }
- }
- // If none of the lines were a win, make sure the game is still winnable
- if (!this.finished)
- this.checkForUnwinnableCondition();
- }
- checkForUnwinnableCondition() {
- let allOccupied = true;
- for (let column = 0; column < GRID_SIZE_COLUMNS; column++) {
- let line = this.grid.getLine(column, 0, 0, 1);
- while (true) {
- // Get the next "step" in this line
- let step = line.next();
- if (step.done)
- // There are no more steps, break out of this loop
- break;
- if (step.value.piece.type === 'empty') {
- allOccupied = false;
- break;
- }
- };
- if (!allOccupied)
- break;
- };
- // If all spaces are occupied, and nobody has won, then
- // the game is over.
- if (allOccupied) {
- this.status.clear();
- this.status.appendText('Nobody wins!');
- this.finished = true;
- }
- }
- playerWonGame(path) {
- // Highlight the winning line
- for (let cell of path) {
- cell.highlighted = true;
- }
- // Show a victory message
- this.status.clear();
- this.status.appendMany([
- {
- text: this.currentPlayer.displayText.single,
- style: {
- color: this.currentPlayer.color
- },
- },
- { text: ' is the winner!' }
- ]);
- // Finish the game
- this.finished = true;
- }
- reset() {
- this.status.clear();
- this.grid.reset();
- this.currentPlayerIndex = 0;
- this.finished = false;
- this.beginTurn();
- }
- };
- window.addEventListener('load', (e) => {
- // Create a new TicTacToe object
- let game = new TicTacToe(
- document.querySelector('#game .grid'),
- document.querySelector('#game .status')
- );
- });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement