]> git.cworth.org Git - lmno.games/blob - tictactoe/tictactoe.jsx
Provide any downloaded deps/*.js files to git clone during deployment
[lmno.games] / tictactoe / tictactoe.jsx
1 function Square(props) {
2   return (
3     <button className="square" onClick={props.onClick}>
4       {props.value}
5     </button>
6   );
7 }
8
9 class Board extends React.Component {
10   renderSquare(i) {
11     return (
12       <Square
13         value={this.props.squares[i]}
14         onClick={() => this.props.onClick(i)}
15       />
16     );
17   }
18
19   render() {
20     return (
21       <div>
22         <div className="board-row">
23           {this.renderSquare(0)}
24           {this.renderSquare(1)}
25           {this.renderSquare(2)}
26         </div>
27         <div className="board-row">
28           {this.renderSquare(3)}
29           {this.renderSquare(4)}
30           {this.renderSquare(5)}
31         </div>
32         <div className="board-row">
33           {this.renderSquare(6)}
34           {this.renderSquare(7)}
35           {this.renderSquare(8)}
36         </div>
37       </div>
38     );
39   }
40 }
41
42 class Game extends React.Component {
43   constructor(props) {
44     super(props);
45     this.state = {
46       history: [
47         {
48           squares: Array(9).fill(null)
49         }
50       ],
51       stepNumber: 0,
52       xIsNext: true
53     };
54   }
55
56   handleClick(i) {
57     const history = this.state.history.slice(0, this.state.stepNumber + 1);
58     const current = history[history.length - 1];
59     const squares = current.squares.slice();
60     if (calculateWinner(squares) || squares[i]) {
61       return;
62     }
63     squares[i] = this.state.xIsNext ? "X" : "O";
64     this.setState({
65       history: history.concat([
66         {
67           squares: squares
68         }
69       ]),
70       stepNumber: history.length,
71       xIsNext: !this.state.xIsNext
72     });
73   }
74
75   jumpTo(step) {
76     this.setState({
77       stepNumber: step,
78       xIsNext: (step % 2) === 0
79     });
80   }
81
82   render() {
83     const history = this.state.history;
84     const current = history[this.state.stepNumber];
85     const winner = calculateWinner(current.squares);
86
87     const moves = history.map((step, move) => {
88       const desc = move ?
89         'Go to move #' + move :
90         'Go to game start';
91       return (
92         <li key={move}>
93           <button onClick={() => this.jumpTo(move)}>{desc}</button>
94         </li>
95       );
96     });
97
98     let status;
99     if (winner) {
100       status = "Winner: " + winner;
101     } else {
102       status = "Next player: " + (this.state.xIsNext ? "X" : "O");
103     }
104
105     return (
106       <div className="game">
107         <div className="game-board">
108           <Board
109             squares={current.squares}
110             onClick={i => this.handleClick(i)}
111           />
112         </div>
113         <div className="game-info">
114           <div>{status}</div>
115           <ol>{moves}</ol>
116         </div>
117       </div>
118     );
119   }
120 }
121
122 // ========================================
123
124 ReactDOM.render(<Game />, document.getElementById("tictactoe"));
125
126 function calculateWinner(squares) {
127   const lines = [
128     [0, 1, 2],
129     [3, 4, 5],
130     [6, 7, 8],
131     [0, 3, 6],
132     [1, 4, 7],
133     [2, 5, 8],
134     [0, 4, 8],
135     [2, 4, 6]
136   ];
137   for (let i = 0; i < lines.length; i++) {
138     const [a, b, c] = lines[i];
139     if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
140       return squares[a];
141     }
142   }
143   return null;
144 }