AI Gaming Quick Start

New to the site and want to get to grips with how it works as quickly as possible? This page will show you how to develop a simple Noughts & Crosses (TIC-TAC-TOE) bot.

Play your first game in just a few steps

In another browser tab to this one, create an account by registering on the site and logging in. Then go to our Online Code Editor by choosing the Editor option from the menu at the top of the page.

mainMenu.png

When you are at our Online Code Editor, make sure that it is set up to play Noughts & Crosses by setting the options that display at the top of the Editor Window.

editorGameOptions.png
  • Set the Game Type to Noughts & Crosses
  • Set the Opponent to housebot-practise
  • Set the Game Style to 301 which has a stake of 0 meaning you can play as much as you like with no risk

With these options set, you can now click the Run button at the top right of the Online Code Editor window and you will already be playing your first game on the site.

runButtonGear.png

You will see your game of Noughts & Crosses play in a pop-up window like the one in the screenshot below:

visualiserNAC.png

When your game has finished playing, click the Close button on the Game Visualiser to close it and return the Online Code Editor.

Improving your Noughts & Crosses code

It's very simple to start improving your Noughts & Crosses code. The code we have given you doesn't make any intelligent decisions so you probably didn't win your first game. Not to worry. Let's make some changes to the code to improve your chances.

The first tactic in Noughts & Crosses is to take the centre square if you can, so, let's look at updating our code to do that.

The middle part of the Online Code Editor window contains the code of your game playing bot and we have given you some template code to get you started.

Everything happens in the calculateMove() function. Scroll down the code in the Editor until you find the calculateMove() function. It will be somewhere around line 29.

calculateMoveRandomGuesserCode.png

Just these few lines are everything needed to play the game. We'll explain each line of the code next but first, let's improve how our game playing bot performs.

We aren't playing very well because we are randomly guessing where to move next. Let's improve this by adding a new line of code in the randomGuesser() function that is just below the calculateMove() function.

Replace the code on the first line of the randomGuesser() function

Replace

move = randint(0,8) #Random guess

With

move = 4 #Guess center first

This will mean that, if it can, your bot will make its move in the centre square. Your code should look like the following:

randomGuesserStep1Code.png

We'll explain how this works later if it all looks new to you, but for now, click the Run button again to play another game of Noughts & Crosses with your newly updated code.

Did you win? Maybe you did, maybe not. There's still a lot of random guessing going on, but, you've improved your bot by improving your first move. Run the bot a few more times to see how many games you win against our housebot-practise.

Understanding each line of the code - calculateMove()

As we said, the calculateMove() is the main function of your game playing bot. This is where all of the code that controls your bot goes. Let's work through all of the lines of the calculateMove function.

def calculateMove(gameState):

The first line of the function receives the gameState variable as input. This is a JSON string that contains all of the information about the current state of the game. We can access all of the information very easily and it is this information that we will use to decide what move we want to make.

Here is a look at what the gameState variable for Noughts & Crosses contains:

{
  'Board': [' ', 'X', 'O', ' ', 'X', ' ', ' ', ' ', 'O'], 
  'IsMover': True, 
  'Role': 'O', 
  'ResponseDeadline': 1524598894935, 
  'MyScore': 0, 
  'OppScore': 0, 
  'GameStatus': 'RUNNING', 
  'GameId': 1833392, 
  'OpponentId': 'housebot-practise'
}

The good news is that you are only really concerned with the Board string. Everything else in gameState is useful, but not necessary for us to improve our Noughts & Crosses code.

The first line of our functions creates a variable to hold the move that we will make. This whole function is about calculating our next move and returning it to the game playing engine. We calculate one move every time our calculateMove() function is called.

The move we return for Noughts & Crosses is a very simple JSON string with one key value pair. The easiest way to create this JSON string is to create a Python dictionary:

ret = dict()

Once we have a variable to hold the move that we will return to the game playing engine, we figure out what move we want to make. Our Noughts & Crosses board is a grid of 9 squares and we need to decide which square we want to put our mark in.

igyaog.png

This playing board is represented in the Board key value pair of our gameState JSON string. Here is an example of a Board that is part way through playing a game. You can see how the elements of the Board relate to the positions of the markers on the board image.

  'Board': [' ', 'X', 'O', ' ', 'X', ' ', ' ', ' ', 'O'],
boardNAC.png

To choose our first move, the next line of our calculateMove() function uses a small function that we have written called randomGuesser()

pos = randomGuesser(gameState)

This is the function we updated earlier.

def randomGuesser(gamestate): #Randomly guesses a position
    move = 4 #Guess center first
    while(gamestate["Board"][move] != " "): #If taken...
        move = randint(0,8) #... keep random guessing until found one
    return move
  • It takes a copy of the gameState variable as its input.
  • It sets a move variable to be position 4 which is the center square on our Noughts & Crosses board as you can see in the image of our board above.
  • Then it enters a while loop to keep checking to see if this move position already has a marker in it
  • If it does, it randomly guesses another possible move location on the board by choosing a random number from 0 to 8
  • The while loop will keep checking to see if the randomly guessed move location has a marker in it
  • When a move location is guessed that does not already have a marker in it, the loop will end and the function will return the move.

Now that we have chosen our move we can return it to the game playing engine, but first, we have chosen to print the move in order to show how the game is progressing.

    print('My move is '+str(pos)+' OpponentId: '+gameState['OpponentId'])

This print statement is not necessary. It is just a way of debugging or showing progress as our code runs. The output is shown in the third column of our Online Code Editor Window.

oceOutput.png

Next, we record our chosen move in the ret dictionary variable that we created in the first line of our calculateMove() function.

ret['Position'] = pos

This line of code creates a new key in the dictionary named Position and assigns the value that we returned from the randomGuesser() function to it.

This dictionary looks like the following JSON string:

ret = {'Position': 4}

And finally, we return the move that we want to make.

return ret

This returns the move to the game playing engine that will make sure it is a valid move. If it is it will update the state of the game passing the new gameState to your opponent who will calculate and make their next move. This will cause the gameState to be updated and the new gameState will then be sent back to you for you to calculate and play your next move.

This process keeps repeating until a winning move is made or no more moves can be made and the game is a draw

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License