2

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.

Cameron
  • 135
  • 1
  • 13

1 Answers1

1

That alignment issue is odd, adding a vertical align of top to .square seems to fix it though (in Chrome and FireFox at least). https://jsbin.com/mogemogame/1/edit?html,css,output

.square {
    ...
    vertical-align: top;
}

The logic issue is because your javascript says to only make the 'best move' if the best move is truthy. If 0 is returned, you're saying to not make the best move.

if (HALbest(squares)){
   HALmove = HALbest(squares);
   squares[HALmove] = 'O';
   this.setState({squares: squares});
   return;
}

should be

if (HALbest(squares) !== null){
   HALmove = HALbest(squares);
   squares[HALmove] = 'O';
   this.setState({squares: squares});
   return;
}
pbarrasso
  • 156
  • 3
  • That worked! Thanks...did you get the same issue with the failure to select square ```0``` to block? I am not sure why it is not being selected....Thanks again for the css help. – Cameron Apr 18 '17 at 01:45
  • Excellent! That did it....I never would have thought of that, thanks for the insight. – Cameron Apr 18 '17 at 02:15
  • Glad to help, also meant to link this -- more on truthy falsy http://stackoverflow.com/questions/35642809/understanding-javascript-truthy-and-falsy – pbarrasso Apr 18 '17 at 02:21