DEV Community

Cover image for Let's build an AI Tic-Tac-Toe in React within 1 hour
Sadeedpv🥇
Sadeedpv🥇

Posted on

Let's build an AI Tic-Tac-Toe in React within 1 hour

Hello, Let's build an AI tic-tac-toe today in React. Just a small note before we start, we'll not be using any Mini-Max AI algorithms to build the game. Instead, we are going to use the tictactoeAPI. Here is the link to the game and the Github repo. It will be easier for you to follow the tutorial if you have some basic knowledge of React.

First of all, let's create the react-app with the command
npx-create-react-app tictactoe

Delete the boilerplate code and you should see a blank page when you run the command npm start. Our folder structure will be quite straightforward with a components folder inside the src folder. Our index.js file should look like this:

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <>
     <Header />
     <AI/>

  </>
);

Enter fullscreen mode Exit fullscreen mode

Now our task is to create these two components. Let's start with the Header file which is really simple.

import React from 'react'

function Header() {
  return (
    <div style={{
        display:'flex',
        alignItems:'cemter',
        justifyContent:'center',
        padding:'10px',
        marginTop:'1.4em',
        fontWeight:'650',
        fontSize:'1.4rem'
    }}>Let's Play Tac-Tac-Toe 👍</div>
  )
}

export default Header
Enter fullscreen mode Exit fullscreen mode

The AI component is the complicated one, but let's break it down.

import React from 'react'
import axios from 'axios'

function AI(){
return(
     <div style={{
        display:'flex',
        flexDirection:'column',        
        alignItems:'center',
        justifyContent:'center'
    }}>

    </div>
)
}
Enter fullscreen mode Exit fullscreen mode

Here we have an outer div to place all its contents in the center of the webpage. Then, we are going to build a reset button.

   <div style={{
        display:'flex',
        alignItems:'center',
        flexDirection:'row',
        flexWrap:'wrap',
        justifyContent:'center',
        marginTop:'-16px'
    }}>
      {* Reset button *}
      <button 
        style={{
            color:'red'
        }}>Reset</button>

    </div>

Enter fullscreen mode Exit fullscreen mode

Then, we need to declare some states

    const [winner, setwinner] = React.useState('No winner');
    const [undo, setundo] = React.useState(null);

    const [board, setboard] = React.useState({
        '0': '-',
        '1': '-',
        '2': '-',
        '3': '-',
        '4': '-',
        '5': '-',
        '6': '-',
        '7': '-',
        '8': '-'
    })

    const [turn, setturn] = React.useState('X');
Enter fullscreen mode Exit fullscreen mode

We need to tell the user whose turn is and who won the game, so:

    <div>{turn} turn!</div>

    <div>{ winner && winner !== 'No winner' ? (`${winner} won!`):'No one won!'}    
    </div>
Enter fullscreen mode Exit fullscreen mode

Let's add some styling:

    <div style={{
        fontSize:'25px',
        padding:'3px',
        fontWeight:'650'
    }}>{turn} turn!</div>

    <div style={{
        display:'flex',
        alignItems:'center',
        justifyContent:'center',
        fontSize:'25px',
        color:'green'
    }}>{ winner && winner !== 'No winner' ? (`${winner} won!`):'No one won!'}    
    </div>
Enter fullscreen mode Exit fullscreen mode

It's time to create the table component

    <table>
        <tbody>
        <tr>
            <td onClick={() =>{
                handleEvent(0);
            }}> {board['0']} </td>
            <td onClick={() =>{
                handleEvent(1)
            }}> { board['1']} </td>
            <td onClick={() =>{
                handleEvent(2)
            }}> {board['2']} </td>
        </tr>
        <tr>
            <td onClick={() =>{
                handleEvent(3)
            }}> {board['3']} </td>
            <td onClick={() =>{
                handleEvent(4)
            }}> {board['4']} </td>
            <td onClick={() =>{
                handleEvent(5)
            }}> {board['5']} </td>
        </tr>
        <tr>
            <td onClick={() =>{
                handleEvent(6)
            }}> {board['6']} </td>
            <td onClick={() =>{
                handleEvent(7)
            }}> {board['7']} </td>
            <td onClick={() =>{
                handleEvent(8)
            }}> {board['8']} </td>
        </tr>
        </tbody>
    </table>
Enter fullscreen mode Exit fullscreen mode

Let's add some more styling:

table{
    background-color: white;
    border: 2px solid #1b1b32;

}

td{
    border: 2px solid #1b1b32;
    padding: 40px;
    align-items: center;
}

td:hover{
    background-color: azure;
}

@media (max-width:485px){
    td{
        padding: 25px;
    }
}
Enter fullscreen mode Exit fullscreen mode

Our website should look like this:

Ai tic-tac-toe

Now, it's time to handle the logic of the game. Right now, we have an onClick event handler on every td element. So let's create that function.

   function handleEvent(e){
        setundo(e);
        setboard(prevstate => ({...prevstate, [e]: 'O'}))
    }
Enter fullscreen mode Exit fullscreen mode

Right now the user could play O on every square. So we need to add a condition to make sure that the user does not play O on the same square already played. So let's rewrite the function as:

   function handleEvent(e){
        if (board[e] === '-' && winner === 'No winner'){
            setundo(e);
            setboard(prevstate => ({...prevstate, [e]: 'O'}))

        }
    }
Enter fullscreen mode Exit fullscreen mode
    // Check for winners

    React.useEffect(() =>{
        if (board['0'] === board['1'] && board['1'] === board['2'] && board['2'] !== '-'){
            setwinner(board['0'])
        }
        else if(board['3'] === board['4'] && board['4'] === board['5'] && board['5'] !== '-'){
            setwinner(board['3'])
        }
        else if(board['6'] === board['7'] && board['7'] === board['8'] && board['8'] !== '-'){
            setwinner(board['6'])
        }
        else if(board['0'] === board['3'] && board['3'] === board['6'] && board['6'] !== '-'){
            setwinner(board['0'])
        }
        else if(board['1'] === board['4'] && board['4'] === board['7'] && board['7'] !== '-'){
            setwinner(board['1'])
        }
        else if(board['2'] === board['5'] && board['5'] === board['8'] && board['8'] !== '-'){
            setwinner(board['2'])
        }
        else if(board['0'] === board['4'] && board['4'] === board['8'] && board['8'] !== '-'){
            setwinner(board['0'])
        }
        else if(board['2'] === board['4'] && board['4'] === board['6'] && board['6'] !== '-'){
            setwinner(board['2'])
        }
    }, [board])
Enter fullscreen mode Exit fullscreen mode

The above part is to check whether anyone has won the game whenever the state of the board changes.
Remember the button we made(Reset button). Let's add logic to that as well.

        <button 
        onClick={() =>{
            setwinner('No winner');
            setboard({
                '0': '-',
                '1': '-',
                '2': '-',
                '3': '-',
                '4': '-',
                '5': '-',
                '6': '-',
                '7': '-',
                '8': '-'
            });
            setundo(null);
        }}>Reset</button>
Enter fullscreen mode Exit fullscreen mode

Now comes the API part of the game. I recommend you to go through the API documentation to get a more clear idea of what's happening. So, this time, we are going to create another useEffect.

    React.useEffect(() =>{

        var game = [board[0], board[1], board[2], board[3], board[4], board[5], board[6], board[7], board[8]];
        game = game.join('');


    const options = {
        method: 'GET',
        url: `https://stujo-tic-tac-toe-stujo-v1.p.rapidapi.com/${game}/X`,
        headers: {
        'X-RapidAPI-Key': #your api key,
        'X-RapidAPI-Host': #rapidapi host
        }
    };

    axios.request(options).then(function (response) {
        if (winner === 'No winner'){
            setboard(prevstate => ({...prevstate, [response.data.recommendation]: 'X'}))

        }
    }).catch(function (error) {
        console.error(error);
    });

    }, [undo])
Enter fullscreen mode Exit fullscreen mode

If you look at the API documentation, you could see that we need to pass in the current state of the board in the URL.

API documentation

And that's it!! Did you enjoy the tutorial? Write your suggestions in the comment section.

Top comments (0)