如何重置井字游戏板?

时间:2020-10-25 14:02:41

标签: javascript html tic-tac-toe

如何在JavaScript中重置井字游戏板?我尝试通过单击按钮来调用intializeBoardView(),但是它在我不需要的旧木板旁边创建了第二个木板。我还尝试过从DOM中删除该元素,然后调用intializeBoardView()。这似乎有效,但是它也删除了我想要保留的分数。谁能解释为什么?我什至没有从html中删除得分div?!

HTML:

<button id="btn">Reset</button>

JavaScript:

class TicTacToe {

  constructor() {

  }

  initializeBoardView() {
    const div = document.getElementById('game')
    const boardSize = gridSize * cellSize
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
    svg.setAttribute('width', boardSize + (margin * 2))
    svg.setAttribute('height', boardSize + (margin * 2))
    div.appendChild(svg)
    const board = document.createElementNS('http://www.w3.org/2000/svg', 'g')
    board.setAttribute('transform', 'translate(' + margin + ',' + margin + ')')
    svg.appendChild(board)
    const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
    rect.setAttribute('width', boardSize)
    rect.setAttribute('height', boardSize)
    rect.setAttribute('fill', 'white')
    board.appendChild(rect)
    
    for (let i = 1; i < gridSize; i++) {
      let line1 = createSVGLine([cellSize * i, 0], [cellSize * i, boardSize])
      let line2 = createSVGLine([0, cellSize * i], [boardSize, cellSize * i])
      line1.setAttribute('class', 'grid')
      board.appendChild(line1)
      line2.setAttribute('class', 'grid')
      board.appendChild(line2)
    }
    return board
  }
}

let myGame = new TicTacToe()
let restartBtn = document.getElementById("btn")
restartBtn.addEventListener("click", () => {
  let svgEl = document.querySelector("svg")
  svgEl.remove()
  myGame = new TicTacToe()
}); 

2 个答案:

答案 0 :(得分:1)

const gridSize = 3;
const cellSize = 100;
const margin = 30;
let xScore = 0;
let oScore = 0;

// Helper function for creating a straight line in SVG, given its coordinates.
function createSVGLine([x1, y1], [x2, y2]) {
  const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
  line.setAttribute('x1', x1);
  line.setAttribute('y1', y1);
  line.setAttribute('x2', x2);
  line.setAttribute('y2', y2);
  return line;
}

// Helper function to create a cross symbol from 2 SVG lines, given its intended size. The cross is centered on [0, 0].
function createSVGCross(size) {
  const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
  const line1 = createSVGLine([-size, -size], [size, size]);
  const line2 = createSVGLine([-size, size], [size, -size]);
  g.appendChild(line1);
  g.appendChild(line2);
  return g;
}

class TicTacToe {
  constructor() {
    this.init();
  }

  init() {
    // 2-dimensional array to store the ID of the player whose symbol is in each grid cell. Initially all are null.
    this.boardState = new Array(gridSize)
      .fill(null)
      .map((column) => new Array(gridSize).fill(null));
    // Which player's turn it is. 1 = cross, 2 = circle.
    this.turn = 1;
    this.gameOver = false;
    this.filledSquares = 0;
    this.boardView = this.initializeBoardView();
    this.displayMessage('X Turn');
    this.displayXScore();
    this.displayOScore();
  }

  // Set up the basic SVG for the board, including the grid lines.
  initializeBoardView() {
    const div = document.getElementById('game');
    div.textContent = '';
    const boardSize = gridSize * cellSize;
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('width', boardSize + margin * 2);
    svg.setAttribute('height', boardSize + margin * 2);
    div.appendChild(svg);
    const board = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    board.setAttribute('transform', 'translate(' + margin + ',' + margin + ')');
    svg.appendChild(board);
    const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
    rect.setAttribute('width', boardSize);
    rect.setAttribute('height', boardSize);
    rect.setAttribute('fill', 'white');
    board.appendChild(rect);

    for (let i = 1; i < gridSize; i++) {
      let line1 = createSVGLine([cellSize * i, 0], [cellSize * i, boardSize]);
      let line2 = createSVGLine([0, cellSize * i], [boardSize, cellSize * i]);
      line1.setAttribute('class', 'grid');
      board.appendChild(line1);
      line2.setAttribute('class', 'grid');
      board.appendChild(line2);
    }

    board.addEventListener('click', (event) => {
      this.handleTurn(event);
    });

    return board;
  }

  // Core game logic: deal with a click on the square [gridX, gridY].
  handleTurn(event) {
    // Figure out which grid square was clicked on in this turn, and then handle that turn.
    const gridX = Math.floor((event.pageX - margin) / cellSize);
    const gridY = Math.floor((event.pageY - margin) / cellSize);
    // If the game is already over, or this square already contains a symbol, do nothing.
    if (this.gameOver || this.boardState[gridX][gridY] !== null) {
      return;
    }
    // Otherwise, add the current player's symbol to this square.
    const symbol = this.createPlayerSymbol(gridX, gridY);
    this.boardView.appendChild(symbol);
    this.boardState[gridX][gridY] = this.turn;
    this.filledSquares++;
    // Check whether this symbol means that the current player has won, or the game has ended in a draw.
    if (this.playerHasWon()) {
      this.gameOver = true;
      if (this.turn === 1) {
        this.displayMessage('X won!');
        xScore++;
        console.log(xScore);
        this.displayXScore(`${xScore}`);
      } else {
        this.displayMessage('O won!');
        oScore++;
        console.log(oScore);
        this.displayOScore(`${oScore}`);
      }
    } else if (this.filledSquares === gridSize * gridSize) {
      this.gameOver = true;
      this.displayMessage('Draw!');
    } else {
      // It's the other player's turn.
      this.changeTurn();
    }
  }

  changeTurn() {
    this.turn = (this.turn % 2) + 1;
    if (this.turn === 1) {
      this.displayMessage('X turn');
    } else {
      this.displayMessage('O turn');
    }
  }

  // Create the SVG for the symbol corresponding to the current player (1 = cross, 2 = circle).
  createPlayerSymbol(gridX, gridY) {
    const offset = cellSize / 2;
    const symbolSize = cellSize / 5;
    const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute(
      'transform',
      'translate(' + (gridX * cellSize + offset) + ',' + (gridY * cellSize + offset) + ')',
    );
    g.setAttribute('class', 'player' + this.turn);
    if (this.turn === 1) {
      const cross = createSVGCross(symbolSize);
      g.appendChild(cross);
    } else {
      const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
      circle.setAttribute('r', symbolSize);
      g.appendChild(circle);
    }
    return g;
  }

  // Returns true if there is any row, column, or diagonal where every cell matches the current player ID.
  playerHasWon() {
    // Check rows
    if (
      this.boardState[0].some((cell, i) => this.boardState.every((row) => row[i] === this.turn))
    ) {
      return true;
    }
    // Check columns
    if (this.boardState.some((column) => column.every((cell) => cell === this.turn))) {
      return true;
    }
    // Check the main diagonal
    if (this.boardState.every((column, i) => this.boardState[i][i] === this.turn)) {
      return true;
    }
    // Check the other diagonal
    if (this.boardState.every((column, i) => this.boardState[i][gridSize - 1 - i] === this.turn)) {
      return true;
    }

    return false;
  }

  displayMessage(message) {
    document.getElementById('info').textContent = message;
  }

  displayXScore(currentXScore) {
    document.getElementById('x-score').textContent = currentXScore;
  }

  displayOScore(currentOScore) {
    document.getElementById('o-score').textContent = currentOScore;
  }
}

let myGame = new TicTacToe();
let restartBtn = document.getElementById('btn');
restartBtn.addEventListener('click', () => {
  myGame.init();
});
  • 我在initializeBoardView函数中添加了click事件的侦听器
  • 我更改了构造函数,以创建一个负责执行所有设置和重放功能的初始化函数
  • 重构一点

现在让我看看是否可行,因为它对我有用works

答案 1 :(得分:0)

每次调用initializeBoardView时,您都必须删除svg并创建一个新的svg

我们可以通过添加:

div.innerHTML=""

添加子对象之前

这是initializeBoardView的完整代码

initializeBoardView() {
    const div = document.getElementById('game')
    const boardSize = gridSize * cellSize    
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
    svg.setAttribute('width', boardSize + (margin * 2))
    svg.setAttribute('height', boardSize + (margin * 2))
    div.innerHTML=""
    div.appendChild(svg)
    const board = document.createElementNS('http://www.w3.org/2000/svg', 'g')
    board.setAttribute('transform', 'translate(' + margin + ',' + margin + ')')
    svg.appendChild(board)
    const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
    rect.setAttribute('width', boardSize)
    rect.setAttribute('height', boardSize)
    rect.setAttribute('fill', 'white')
    board.appendChild(rect)
    
    for (let i = 1; i < gridSize; i++) {
    let line1 = createSVGLine([cellSize * i, 0], [cellSize * i, boardSize])
    let line2 = createSVGLine([0, cellSize * i], [boardSize, cellSize * i])
    line1.setAttribute('class', 'grid')
    board.appendChild(line1)
    line2.setAttribute('class', 'grid')
    board.appendChild(line2)
    }
    return board
}