diff --git a/react-fb-tictactoe/src/TicTacToe/TicTacToe.css b/react-fb-tictactoe/src/TicTacToe/TicTacToe.css index 0aa20d69..5198d2b7 100644 --- a/react-fb-tictactoe/src/TicTacToe/TicTacToe.css +++ b/react-fb-tictactoe/src/TicTacToe/TicTacToe.css @@ -48,3 +48,17 @@ .game-info { margin-left: 20px; } + + +.current-move { + font-weight: bold; +} + + +ul { + list-style: none; +} + +.win { + background-color: PaleGreen; +} diff --git a/react-fb-tictactoe/src/TicTacToe/index.js b/react-fb-tictactoe/src/TicTacToe/index.js index 56944d57..b420e416 100644 --- a/react-fb-tictactoe/src/TicTacToe/index.js +++ b/react-fb-tictactoe/src/TicTacToe/index.js @@ -9,57 +9,133 @@ import './TicTacToe.css' * with * class Square extends Component */ -class Square extends Component { - render() { - return ( - - ); +function Square(props) { + let btnClass = 'square'; + if(props.highlight) { + btnClass += ' win'; } + return ( + + ); } -class Board extends Component { - renderSquare(i) { - return ; - } - render() { - const status = 'Next player: X'; +class Board extends React.Component { + renderSquare(i) { + const highlight = this.props.winner && this.props.winner.includes(i); return ( -
-
{status}
-
- {this.renderSquare(0)} - {this.renderSquare(1)} - {this.renderSquare(2)} -
-
- {this.renderSquare(3)} - {this.renderSquare(4)} - {this.renderSquare(5)} -
-
- {this.renderSquare(6)} - {this.renderSquare(7)} - {this.renderSquare(8)} -
-
+ this.props.onClick(i)} + highlight={highlight} + /> ); } + + render() { + let rows = []; + for(let i = 0; i < 3; i++) { + let columns = []; + for(let j = 0; j < 3; j++) { + columns.push(this.renderSquare(i * 3 + j)) + } + rows.push(
{columns}
) + } + return (
{rows}
); + } } + class Game extends Component { + constructor() { + super(); + this.state = { + history: [{ + squares: Array(9).fill(null), + index: null, + }], + stepNumber: 0, + xIsNext: true, + reversedOrder: false, + }; + } + + handleClick(i) { + const history = this.state.history.slice(0, this.state.stepNumber + 1); + const current = history[history.length - 1]; + const squares = current.squares.slice(); + if (calculateWinner(squares) || squares[i]) { + return; + } + squares[i] = this.state.xIsNext ? 'X' : 'O'; + this.setState({ + history: history.concat([{ + squares: squares, + index: i, + }]), + stepNumber: history.length, + xIsNext: !this.state.xIsNext, + }); + } + + handleSort() { + this.setState({ + reversedOrder: !this.state.reversedOrder, + }) + } + + + jumpTo(step) { + this.setState({ + stepNumber: step, + xIsNext: (step % 2) === 0, + }); + } + render() { + const history = this.state.history; + const current = history[this.state.stepNumber]; + const winner = calculateWinner(current.squares); + + + const moves = history.map((step, move) => { + const desc = move ? + move + '. Move ' + getCoordinates(step.index) : + 'Game start'; + const currentMoveClass = step == current ? 'current-move' : null; + return ( +
  • + this.jumpTo(move)}>{desc} +
  • + ); + }); + + + let status; + if (winner) { + status = 'Winner: ' + current.squares[winner[0]]; + } else { + status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O'); + } + return (
    - + this.handleClick(i)} + winner={winner} + />
    -
    {/* status */}
    -
      {/* todo */}
    +
    {status}
    +
      + {this.state.reversedOrder ? moves.reverse() : moves} +
    +
    ); @@ -67,3 +143,31 @@ class Game extends Component { } export default Game; + + +function getCoordinates(index) { + let x = index % 3; + let y = Math.floor(index / 3); + return `(${x}, ${y})`; +} + + +function calculateWinner(squares) { + 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], + ]; + for (let i = 0; i < lines.length; i++) { + const [a, b, c] = lines[i]; + if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { + return lines[i]; + } + } + return null; +}