This is an adaptation of the Tic Tac Toe game from the React tutorial, with the addition of a one player option. For some reason the button alignment drops out of line once there is a value added; the button drops about 50px until the rest of the row holds values, then they all align back into the board as before. I don't understand why this is happening and would appreciate any help possible at keeping the buttons all in line.
There is also inconsistent performance from the game logic for the computer to select a
diagonal option; it is fairly easy to position the computer to have to choose either 0
or 2
and then the computer has to rely on the random choice function to occupy those
squares. My understanding is that the program should ("should...") recognize the need to
choose those squares due to this logic;
else if (squares[b] === 'X' && squares[b] === squares[c] && squares[a] === null){
console.log("need O in ", a);
return a;
I left the console.log
statements in the program to show how and where I was
trouble shooting this problem. Any insights would be greatly appreciated. Thank you for
your time.
The problem with the alignment can be seen on JS Bin
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Tic Tac Toe</title>
<script src="https://unpkg.com/react@latest/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@latest/dist/react-dom.js"></script>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id='root'></div>
<script type='text/babel' >
var numberXs = 0;
var 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],
];
function Square(props){
return(
<button className="square" onClick={() => props.onClick()}>{props.value}</button>
);
}
class Board extends React.Component{
renderSquare(i){
return <Square value={this.props.squares[i]} onClick={() => this.props.onClick(i)} />
}
render(){
return(
<div className="board" key={this.props.key}>
<div className="row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
}
}
class Game extends React.Component{
defaultState = {
onePlayerGame: true,
squares: Array(9).fill(null),
xIsNext: true
};
constructor(props){
super(props);
this.onePlayer = this.onePlayer.bind(this);
this.twoPlayer = this.twoPlayer.bind(this);
this.handleMove = this.handleMove.bind(this);
this.gameOver = this.gameOver.bind(this);
this.state = this.defaultState;
}
onePlayer(){
this.setState({onePlayerGame: true});
return(
document.getElementById('onePlayer').style.color = "red",
document.getElementById('twoPlayer').style.color = "black"
);
}
twoPlayer(){
this.setState({ onePlayerGame: false});
return(
document.getElementById('onePlayer').style.color= "black",
document.getElementById('twoPlayer').style.color = "red"
);
}
handleMove(i){
var HALturn = true,
HALmove;
const squares = this.state.squares.slice();
if (checkWinner(squares) || squares[i]){
return;
}
if (this.state.onePlayerGame){
squares[i] = 'X';
numberXs += 1;
this.setState({squares:squares});
if (numberXs >= 5){
return;
}
if (HALbest(squares)){
HALmove = HALbest(squares);
squares[HALmove] = 'O';
this.setState({squares: squares});
return;
} else {
while (HALturn) {
HALmove = Math.floor(Math.random() * squares.length);
if (squares[HALmove] === null){
squares[HALmove] = 'O';
this.setState({
squares: squares
});
return;
HALturn = false;
} else {
console.log("Additional random selection");
}
}
}
}
else {
squares[i] = this.state.xIsNext ? 'X':'O';
this.setState({
squares: squares,
xIsNext: !this.state.xIsNext
});
}
}
gameOver(){
this.setState({...this.defaultState});
numberXs = 0;
}
render(){
var winner = checkWinner(this.state.squares);
let status, endGame;
if (winner) {
status = "Winner is " + winner;
} else if (!winner && numberXs === 5){
status = "No Winner. Reset to play again.";
} else {
status = "Next Move: " + (this.state.xIsNext ? 'X': 'O');
}
return(
<div id="main">
<div>
<button className="gameSelect" id="onePlayer" value={this.state.onePlayerGame} onClick={() => this.onePlayer()} >One Player</button>
<button className="gameSelect" id="twoPlayer"value={this.state.onePlayerGame} onClick={() => this.twoPlayer()} >Two Players</button>
</div>
<Board
squares={this.state.squares}
onClick={(i) => this.handleMove(i)} />
<div id="status">{status}</div>
<button className="gameSelect" id="resetButton" onClick={() => this.gameOver()}>Reset Game</button>
</div>
);
}
}
function checkWinner(squares){
for (var i = 0; i < lines.length; i++){
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]){
return squares[a];
}
}
return null;
}
function HALbest(squares){
for (var i = 0; i < lines.length; i++){
const [a, b, c] = lines[i];
if (squares[a] === 'X' && squares[a] === squares[c] && squares[b] === null){
console.log("need O in ", b);
return b;
} else if (squares[b] === 'X' && squares[b] === squares[c] && squares[a] === null){
console.log("need O in ", a);
return a;
} else if ( squares[a] === 'X' && squares[a] === squares[b] && squares[c] === null ){
console.log("need O in ", c);
return c;
}
}
return null;
}
ReactDOM.render(
<Game />,
document.getElementById('root')
)
</script>
</body>
</html>
With this CSS;
#main {
width: 80%;
margin: auto;
color: blue;
text-align: center;
}
.gameSelect {
width: 30%;
text-align: center;
background-color: white;
border: 1px solid transparent;
margin: 5px 0 5px 0;
}
.board {
display: inline;
}
#onePlayer {
color: red;
}
#status {
margin: 25px 0 5px 0;
}
.row {
margin: auto;
height: 100px;
}
.square {
width: 100px;
height: 100px;
background-color: white;
font-size: 20px;
margin: 0;
display: inline;
}
Thanks again for the help.