Modifying alignment of buttons in React components

Transforming the typical Tic Tac Toe game from the React tutorial, with an added one-player feature. Oddly, button alignment shifts out of place once a value is inserted; causing the button to drop by about 50px until the row is filled with values, after which they realign back into formation on the board. I'm puzzled as to why this occurs and would appreciate any assistance in maintaining consistent button alignment.

There's also unpredictability in the computer's logic when selecting a diagonal move; it seems simple to trap the computer into choosing either 0 or 2, where it then resorts to random selection to fill those squares. As per my understanding, the program "should..." recognize the necessity to choose these squares based on the given logic;

else if (squares[b] === 'X' && squares[b] === squares[c] && squares[a] === null){
      console.log("need O in ", a);
      return a;

I've maintained the console.log statements within the program to demonstrate how and where I faced issues while troubleshooting this problem. Any insights offered would be immensely valued. Thank you for taking the time to assist.

The alignment glitch can be viewed 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/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="640605060108491710050a0005080b0a0124524a55514a54">[email protected]</a>/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>

Aspect Ratio issues persisted despite utilizing this CSS adaptation;

#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;
}

Your continued support is greatly appreciated.

Answer №1

It's interesting that the alignment issue arises. Adding a vertical align of top to .square seems to do the trick (at least in Chrome and FireFox). Check out this link for reference

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

The logic problem stems from your javascript code specifying to only proceed with the 'best move' if it evaluates to true. If 0 is returned, it indicates not to make the best move.

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

To rectify this, consider adjusting the condition as follows:

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

"Failed to insert or update a child record in the database due to a SQL

I encountered an issue while trying to perform the following transaction: static async save(habit){ await db.beginTransaction; try { await db.execute('SELECT @habitId:=MAX(habits.habitId)+1 FROM habits'); await db.execute( ...

Using Material-UI in a project without the need for create-react-app

My objective is to utilize Material-UI without relying on create-react-app, as I feel that it abstracts too much and hinders my learning process. Unfortunately, all the instructions I have come across are centered around create-react-app. I am aiming to s ...

Does the Apps Script parser JSON.parse() incorrectly remove leading zeros from string object keys?

I am facing an issue with my Apps Script Web App where JSON data is being manipulated when I try to parse it. Specifically, keys with leading zeros are being altered (e.g "0123" becomes "123") during the JSON.parse() function call. It seems like the functi ...

The Photoswipe default user interface cannot be located

While attempting to incorporate PhotoSwipe into my website, I encountered an uncaught reference error related to The PhotoswipeUI_Default is not defined at the openPhotoSwipe function Below is the code snippet that I have been working on: <!doctyp ...

Activate the scroll accordion to automatically expand when the page loads

Incorporated within my FAQ page is an accordion feature. My desired functionality includes allowing the user to click on a link such as localhost:3000/faq#why-choose-us, which should prompt the page to smoothly scroll to the accordion element marked with t ...

Having trouble with proper functionality of OnMouseEnter in React when used on multiple divs

I am struggling with a React issue related to onMouseEnter. My goal is to expand only one card at a time, but currently, when I hover over one card, all of them expand simultaneously. Can someone please offer some guidance on how to resolve this? export ...

Utilizing browser's local storage to dynamically update text in buttons and panels

In my file, I have the following code snippet (I've removed other irrelevant code) <div data-role="panel" id="loc_panel"> <h2>Panel Header</h2> <p>info about this stop</p> </div> <!-- /panel --> < ...

Encountering an ExpressionChangedAfterItHasBeenCheckedError in Angular 6 when selecting an option from a dropdown menu

How can we fix the error mentioned below through code changes? Situation An input dropdown UI is safeguarded against unintentional value changes by a modal. However, triggering an event (such as click or focus) on the dropdown leads to the ExpressionChan ...

Navigating through the features of www.addthis.com is simple and straightforward

I am looking to integrate the addthis.com plugin into my website. Specifically, I want to pass my own data to the plugin when users click on the email link. This data should then be sent to the client. Is there a way to achieve this? ...

"Maintaining Consistency: Ensuring The First Row is Repeated on Every

When trying to save a PDF using jspdf, I encountered an issue with tables in my HTML. The tables do not have headers, but JsPDF is automatically repeating the first row of the table on every page, causing overlap. I don't want headers on every new pag ...

The issue with VueEditor in Nuxt.js is that it's throwing an error

When working on my Nuxt.js project, I integrated the vue2-editor package for writing articles with HTML. Everything functions properly when I enter text and submit it, however, upon reloading the page, I encounter an error stating "document is not defined" ...

What is the best way to play a random song when the user clicks a button?

My goal is to develop a website where users can click on an image and have a random song play from a playlist. I currently have a functioning code that activates one song, but it fails when adding multiple songs to the mix. <html> <head> ...

console fails to return a function even when it is defined

After clicking the "stopCrash" button, I want the function backAgain to be executed 2 seconds later. However, I am encountering an error in the console: Uncaught TypeError: this.backAgain is not a function. Do you have any suggestions on how to fix this i ...

Use the ng-repeat directive to display multiple items and let the user input a quantity for each item. Then, in AngularJs, gather all the form data, including the repeated items and their quantities, and

Hey there! I have a question related to AngularJs: How can I repeat pre-selected items using (ng-repeat) and allow users to enter a quantity for each item in a subsequent step, then submit all the form data? I'm wondering if adding $index to the repe ...

Modifying the name of a file upload in AngularJS

Does anyone know a way to dynamically change the file name in AngularJS? <input type="file" onchange="angular.element(this).scope().filename(this)"> In the filename method, I am attempting to change the file name but the value is not updating. How ...

Cursor hovers over button, positioned perfectly in the center

I am facing a challenge with implementing a hover function for a set of buttons on the screen. The goal is to display the mouse pointer at the center of the button upon hovering, rather than the actual position of the cursor being displayed. I have tried u ...

Creating a dynamic image binding feature in Angular 8

I am working with an object array that requires me to dynamically add an image icon based on the type of credit card. Typescript file icon: any; savedCreditCard = [ { cardExpiryDateFormat: "05/xx", cardNumberLast: "00xx", cardId: "x ...

Is NextJS 13 the Key to App Directory On-Demand Revalidation?

I am looking to implement on-demand revalidation with next13 and the app directory. While I have successfully used the app-directory to replace the need for getServerSideProps or getStaticProps, there is one specific page on our site that needs to be rebui ...

Implementing a permanent class following an incident

Is it possible to dynamically add a class to an element when an event occurs rather than based on a condition? For example, I have a td that displays a variable. <td>{{myVar}}</td>//myVar=10 When the variable changes to myVar=15, I want to ad ...

Tips for fixing a type error in javascript/cypress

While learning cypress and javascript, I encountered this type error: TypeError: _testElements.default.selectionRow is not a function I have thoroughly reviewed the documentation for cypress but cannot seem to find any errors in my code. I'm hoping ...