Reactive Tic-Tac-Toe











up vote
-1
down vote

favorite












I'm trying to learn React and, as most other people, I decided to start from the React tutorial where a TicTacToe game is built .



I tried to rewrite the example by myself and one thing that I did differently was to separate the model of the Tic-tac-toe game from the React component, a style that I think is closer to a real world program.



I'm not exactly sure If I'm managing the state of the component in the correct way.



More in detail at the moment my idea has been to create a TicTacToeGame class with a mark method.



class TicTacToeGame {
constructor(board = new Array(9).fill(undefined), currentPlayer = 0) {
this.board = board;
this.currentPlayer = currentPlayer;
}

mark(cell) {
if (this.board[cell] !== undefined || this.hasWinner())
return;
const nextBoard = this.board.slice();
nextBoard[cell] = this.currentPlayer;
const nextPlayer = this.getNextPlayer();
const nextGame = new TicTacToeGame(nextBoard, nextPlayer);
return nextGame;
}

getNextPlayer() {
return (this.currentPlayer + 1) % 2;
}

hasWinner() {
return this.getWinner() !== undefined;
}

getWinner() {
function getWinnerAux(i0, i1, i2) {
if (that.board[i0] === that.board[i1] &&
that.board[i0] === that.board[i2])
return that.board[i0];
return undefined;
}
const that = this;
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
let winner;
for (let [i0, i1, i2] of lines) {
winner = getWinnerAux(i0, i1, i2);
if (winner !== undefined)
break;
}
return winner;
}

}


What this method does is to create a new TicTacToeGame object every time a mark action takes place and the action is valid.



Every time a click event happens on the game board and a new TicTacToeGame is created, the game is added to the history of the games.



class TicTacToe extends Component {
constructor(props) {
super(props);
const ticTacToeGame = new TicTacToeGame();
this.state = { history: [ticTacToeGame] };

this.onBoardCellClick = this.onBoardCellClick.bind(this);
this.onHistoryCellClick = this.onHistoryCellClick.bind(this);
}

render() {
return (
<div>
<h2>Tic tac toe</h2>
<div className="tic-tac-toe-board">
{this.getCurrentGame().board.map((item, i) => (
<TicTacToeCell key={'cell' + i} status={this.getSymbol(item)} onClick={() => this.onBoardCellClick(i)}></TicTacToeCell>
))}
</div>
{this.getCurrentGame().hasWinner() ? 'Winner: ' : 'Next player: '} {this.getSymbol(this.getCurrentGame().currentPlayer)}
<h3>History</h3>
<div className="tic-tac-toe-history">
{this.state.history.map((item, i) => (
<button key={'board' + i} onClick={() => this.onHistoryCellClick(i)}>#{i}</button>
))}
</div>
</div>
);
}

onHistoryCellClick(boardId) {
const history = this.state.history.slice(0, boardId + 1);
this.setState({ history });
}

onBoardCellClick(cell) {
const newGame = this.getCurrentGame().mark(cell);
if (!newGame)
return;
this.state.history.push(newGame);
this.setState(this.state);
}

last(a) {
return a[a.length - 1];
}

getCurrentGame() {
return this.last(this.state.history);
}

getSymbol(playerCode) {
switch (playerCode) {
case 0: return 'X';
case 1: return 'O';
default: return ' ';
}
}
}


A few questions about what I'm doing:




  • Is the idea of creating models like this correct? I am still learning React and I may be missing something but I think even with more complex state management libraries like Redux this approach should make sense.


  • Is the idea of creating immutable models correct? I think this is not always possible, hence I think sometimes a different approach may be needed. Is there any good alternative I could use?


  • I'm also not sure about directly modifying history (this.state.history.push(newGame);). Is this ok in React?


  • In this case I think that there should be also a class/object to manage the games history. Do you agree?



Any other comment is of course appreciated.










share|improve this question




























    up vote
    -1
    down vote

    favorite












    I'm trying to learn React and, as most other people, I decided to start from the React tutorial where a TicTacToe game is built .



    I tried to rewrite the example by myself and one thing that I did differently was to separate the model of the Tic-tac-toe game from the React component, a style that I think is closer to a real world program.



    I'm not exactly sure If I'm managing the state of the component in the correct way.



    More in detail at the moment my idea has been to create a TicTacToeGame class with a mark method.



    class TicTacToeGame {
    constructor(board = new Array(9).fill(undefined), currentPlayer = 0) {
    this.board = board;
    this.currentPlayer = currentPlayer;
    }

    mark(cell) {
    if (this.board[cell] !== undefined || this.hasWinner())
    return;
    const nextBoard = this.board.slice();
    nextBoard[cell] = this.currentPlayer;
    const nextPlayer = this.getNextPlayer();
    const nextGame = new TicTacToeGame(nextBoard, nextPlayer);
    return nextGame;
    }

    getNextPlayer() {
    return (this.currentPlayer + 1) % 2;
    }

    hasWinner() {
    return this.getWinner() !== undefined;
    }

    getWinner() {
    function getWinnerAux(i0, i1, i2) {
    if (that.board[i0] === that.board[i1] &&
    that.board[i0] === that.board[i2])
    return that.board[i0];
    return undefined;
    }
    const that = this;
    const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6]
    ];
    let winner;
    for (let [i0, i1, i2] of lines) {
    winner = getWinnerAux(i0, i1, i2);
    if (winner !== undefined)
    break;
    }
    return winner;
    }

    }


    What this method does is to create a new TicTacToeGame object every time a mark action takes place and the action is valid.



    Every time a click event happens on the game board and a new TicTacToeGame is created, the game is added to the history of the games.



    class TicTacToe extends Component {
    constructor(props) {
    super(props);
    const ticTacToeGame = new TicTacToeGame();
    this.state = { history: [ticTacToeGame] };

    this.onBoardCellClick = this.onBoardCellClick.bind(this);
    this.onHistoryCellClick = this.onHistoryCellClick.bind(this);
    }

    render() {
    return (
    <div>
    <h2>Tic tac toe</h2>
    <div className="tic-tac-toe-board">
    {this.getCurrentGame().board.map((item, i) => (
    <TicTacToeCell key={'cell' + i} status={this.getSymbol(item)} onClick={() => this.onBoardCellClick(i)}></TicTacToeCell>
    ))}
    </div>
    {this.getCurrentGame().hasWinner() ? 'Winner: ' : 'Next player: '} {this.getSymbol(this.getCurrentGame().currentPlayer)}
    <h3>History</h3>
    <div className="tic-tac-toe-history">
    {this.state.history.map((item, i) => (
    <button key={'board' + i} onClick={() => this.onHistoryCellClick(i)}>#{i}</button>
    ))}
    </div>
    </div>
    );
    }

    onHistoryCellClick(boardId) {
    const history = this.state.history.slice(0, boardId + 1);
    this.setState({ history });
    }

    onBoardCellClick(cell) {
    const newGame = this.getCurrentGame().mark(cell);
    if (!newGame)
    return;
    this.state.history.push(newGame);
    this.setState(this.state);
    }

    last(a) {
    return a[a.length - 1];
    }

    getCurrentGame() {
    return this.last(this.state.history);
    }

    getSymbol(playerCode) {
    switch (playerCode) {
    case 0: return 'X';
    case 1: return 'O';
    default: return ' ';
    }
    }
    }


    A few questions about what I'm doing:




    • Is the idea of creating models like this correct? I am still learning React and I may be missing something but I think even with more complex state management libraries like Redux this approach should make sense.


    • Is the idea of creating immutable models correct? I think this is not always possible, hence I think sometimes a different approach may be needed. Is there any good alternative I could use?


    • I'm also not sure about directly modifying history (this.state.history.push(newGame);). Is this ok in React?


    • In this case I think that there should be also a class/object to manage the games history. Do you agree?



    Any other comment is of course appreciated.










    share|improve this question


























      up vote
      -1
      down vote

      favorite









      up vote
      -1
      down vote

      favorite











      I'm trying to learn React and, as most other people, I decided to start from the React tutorial where a TicTacToe game is built .



      I tried to rewrite the example by myself and one thing that I did differently was to separate the model of the Tic-tac-toe game from the React component, a style that I think is closer to a real world program.



      I'm not exactly sure If I'm managing the state of the component in the correct way.



      More in detail at the moment my idea has been to create a TicTacToeGame class with a mark method.



      class TicTacToeGame {
      constructor(board = new Array(9).fill(undefined), currentPlayer = 0) {
      this.board = board;
      this.currentPlayer = currentPlayer;
      }

      mark(cell) {
      if (this.board[cell] !== undefined || this.hasWinner())
      return;
      const nextBoard = this.board.slice();
      nextBoard[cell] = this.currentPlayer;
      const nextPlayer = this.getNextPlayer();
      const nextGame = new TicTacToeGame(nextBoard, nextPlayer);
      return nextGame;
      }

      getNextPlayer() {
      return (this.currentPlayer + 1) % 2;
      }

      hasWinner() {
      return this.getWinner() !== undefined;
      }

      getWinner() {
      function getWinnerAux(i0, i1, i2) {
      if (that.board[i0] === that.board[i1] &&
      that.board[i0] === that.board[i2])
      return that.board[i0];
      return undefined;
      }
      const that = this;
      const lines = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6]
      ];
      let winner;
      for (let [i0, i1, i2] of lines) {
      winner = getWinnerAux(i0, i1, i2);
      if (winner !== undefined)
      break;
      }
      return winner;
      }

      }


      What this method does is to create a new TicTacToeGame object every time a mark action takes place and the action is valid.



      Every time a click event happens on the game board and a new TicTacToeGame is created, the game is added to the history of the games.



      class TicTacToe extends Component {
      constructor(props) {
      super(props);
      const ticTacToeGame = new TicTacToeGame();
      this.state = { history: [ticTacToeGame] };

      this.onBoardCellClick = this.onBoardCellClick.bind(this);
      this.onHistoryCellClick = this.onHistoryCellClick.bind(this);
      }

      render() {
      return (
      <div>
      <h2>Tic tac toe</h2>
      <div className="tic-tac-toe-board">
      {this.getCurrentGame().board.map((item, i) => (
      <TicTacToeCell key={'cell' + i} status={this.getSymbol(item)} onClick={() => this.onBoardCellClick(i)}></TicTacToeCell>
      ))}
      </div>
      {this.getCurrentGame().hasWinner() ? 'Winner: ' : 'Next player: '} {this.getSymbol(this.getCurrentGame().currentPlayer)}
      <h3>History</h3>
      <div className="tic-tac-toe-history">
      {this.state.history.map((item, i) => (
      <button key={'board' + i} onClick={() => this.onHistoryCellClick(i)}>#{i}</button>
      ))}
      </div>
      </div>
      );
      }

      onHistoryCellClick(boardId) {
      const history = this.state.history.slice(0, boardId + 1);
      this.setState({ history });
      }

      onBoardCellClick(cell) {
      const newGame = this.getCurrentGame().mark(cell);
      if (!newGame)
      return;
      this.state.history.push(newGame);
      this.setState(this.state);
      }

      last(a) {
      return a[a.length - 1];
      }

      getCurrentGame() {
      return this.last(this.state.history);
      }

      getSymbol(playerCode) {
      switch (playerCode) {
      case 0: return 'X';
      case 1: return 'O';
      default: return ' ';
      }
      }
      }


      A few questions about what I'm doing:




      • Is the idea of creating models like this correct? I am still learning React and I may be missing something but I think even with more complex state management libraries like Redux this approach should make sense.


      • Is the idea of creating immutable models correct? I think this is not always possible, hence I think sometimes a different approach may be needed. Is there any good alternative I could use?


      • I'm also not sure about directly modifying history (this.state.history.push(newGame);). Is this ok in React?


      • In this case I think that there should be also a class/object to manage the games history. Do you agree?



      Any other comment is of course appreciated.










      share|improve this question















      I'm trying to learn React and, as most other people, I decided to start from the React tutorial where a TicTacToe game is built .



      I tried to rewrite the example by myself and one thing that I did differently was to separate the model of the Tic-tac-toe game from the React component, a style that I think is closer to a real world program.



      I'm not exactly sure If I'm managing the state of the component in the correct way.



      More in detail at the moment my idea has been to create a TicTacToeGame class with a mark method.



      class TicTacToeGame {
      constructor(board = new Array(9).fill(undefined), currentPlayer = 0) {
      this.board = board;
      this.currentPlayer = currentPlayer;
      }

      mark(cell) {
      if (this.board[cell] !== undefined || this.hasWinner())
      return;
      const nextBoard = this.board.slice();
      nextBoard[cell] = this.currentPlayer;
      const nextPlayer = this.getNextPlayer();
      const nextGame = new TicTacToeGame(nextBoard, nextPlayer);
      return nextGame;
      }

      getNextPlayer() {
      return (this.currentPlayer + 1) % 2;
      }

      hasWinner() {
      return this.getWinner() !== undefined;
      }

      getWinner() {
      function getWinnerAux(i0, i1, i2) {
      if (that.board[i0] === that.board[i1] &&
      that.board[i0] === that.board[i2])
      return that.board[i0];
      return undefined;
      }
      const that = this;
      const lines = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6]
      ];
      let winner;
      for (let [i0, i1, i2] of lines) {
      winner = getWinnerAux(i0, i1, i2);
      if (winner !== undefined)
      break;
      }
      return winner;
      }

      }


      What this method does is to create a new TicTacToeGame object every time a mark action takes place and the action is valid.



      Every time a click event happens on the game board and a new TicTacToeGame is created, the game is added to the history of the games.



      class TicTacToe extends Component {
      constructor(props) {
      super(props);
      const ticTacToeGame = new TicTacToeGame();
      this.state = { history: [ticTacToeGame] };

      this.onBoardCellClick = this.onBoardCellClick.bind(this);
      this.onHistoryCellClick = this.onHistoryCellClick.bind(this);
      }

      render() {
      return (
      <div>
      <h2>Tic tac toe</h2>
      <div className="tic-tac-toe-board">
      {this.getCurrentGame().board.map((item, i) => (
      <TicTacToeCell key={'cell' + i} status={this.getSymbol(item)} onClick={() => this.onBoardCellClick(i)}></TicTacToeCell>
      ))}
      </div>
      {this.getCurrentGame().hasWinner() ? 'Winner: ' : 'Next player: '} {this.getSymbol(this.getCurrentGame().currentPlayer)}
      <h3>History</h3>
      <div className="tic-tac-toe-history">
      {this.state.history.map((item, i) => (
      <button key={'board' + i} onClick={() => this.onHistoryCellClick(i)}>#{i}</button>
      ))}
      </div>
      </div>
      );
      }

      onHistoryCellClick(boardId) {
      const history = this.state.history.slice(0, boardId + 1);
      this.setState({ history });
      }

      onBoardCellClick(cell) {
      const newGame = this.getCurrentGame().mark(cell);
      if (!newGame)
      return;
      this.state.history.push(newGame);
      this.setState(this.state);
      }

      last(a) {
      return a[a.length - 1];
      }

      getCurrentGame() {
      return this.last(this.state.history);
      }

      getSymbol(playerCode) {
      switch (playerCode) {
      case 0: return 'X';
      case 1: return 'O';
      default: return ' ';
      }
      }
      }


      A few questions about what I'm doing:




      • Is the idea of creating models like this correct? I am still learning React and I may be missing something but I think even with more complex state management libraries like Redux this approach should make sense.


      • Is the idea of creating immutable models correct? I think this is not always possible, hence I think sometimes a different approach may be needed. Is there any good alternative I could use?


      • I'm also not sure about directly modifying history (this.state.history.push(newGame);). Is this ok in React?


      • In this case I think that there should be also a class/object to manage the games history. Do you agree?



      Any other comment is of course appreciated.







      object-oriented tic-tac-toe react.js jsx






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 2 days ago









      200_success

      127k15148411




      127k15148411










      asked Nov 19 at 16:50









      heapOverflow

      1174




      1174



























          active

          oldest

          votes











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














           

          draft saved


          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f207990%2freactive-tic-tac-toe%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown






























          active

          oldest

          votes













          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes
















           

          draft saved


          draft discarded



















































           


          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f207990%2freactive-tic-tac-toe%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Morgemoulin

          Scott Moir

          Souastre