JavaScript tic tac toe - beginner level - t24
Tags: javascript, webdev, beginners, tutorial
Published: 2022-09-09 08:29:15

Hi there👋🏽.

## Introduction

As a beginner the best way to level up is to actually build complete programs, I use the word "complete" loosely. I am hoping to write a series where we will build 24 applications or programs together, varying in levels from beginner to advanced.  for example : this tic tac toe mini series

for beginner level we will build just a working tac in the console, 
for intermediate we will migrate tac to the canvas(mapping a data structure to canvas objects)
for advanced we will implement minMax algorithm 

most of the projects in what I call a `t24` series will follow this three part layer. These projects were actually suppose to be part of an [ebook](https://ebook-js.netlify.app/) I was working on, to release under pay what you want, but I decided it is way easier to write articles and make it accessible for everyone, and to be fair I also want to improve my writing as I am not a native English speaker, I thought this was a perfect way, lot of writing etc.


## let's get coding 

here is my folder structure, we will update it as we go along, I do actually suggest that you put all the projects under the same folder prolly called t24.

![folder structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/be6nhytiw25aikx6xeq3.png)

if you get stuck: [repo](https://github.com/SfundoMhlungu/tictactoe/blob/main/tic.js)

### prerequisites 

I am assuming you are familiar with JavaScript fundamentals: Objects, Arrays and Functions, if you want to level up a bit I also have a series for [intermediate JS](https://dev.to/sfundomhlungu/unpacking-javascript-01-prototypes-emulating-proto-runtime--1m68)(which will be helpful as we move along the levels) you can check it out at your own leisure.

You need to be also familiar with [tic tac toe](https://en.wikipedia.org/wiki/Tic-tac-toe) or as we call it here "X's and O's"

### tic tac toe

we will use a hardcoded 3 x 3 grid to represent our game space, we will prolly make this dynamic in other levels

file structure: 

```cmd
  tic\
     tic.js

``` 
open tic.js and let's start coding

representing the board:

```js
// array of arrays
const board = [[" ", " " , " "], 
               [" ", " " , " "], 
               [" ", " " , " "], 
               
]

```

the idea here is simple since we are in the console we will `console.log` our board after every `update`.
update being whether the player or computer puts an X or O on a free space in our board, it will all make sense as we code.

we will use an object to encapsulate all of our gaming logic 

```js
const game =  {
      update : function(){},
      isgameOver: function(){},
      move : function(c){},
      possibleMoves: function(){},
      computer: function(){},
      updateBoard: function(){}

}  

```
I suggest typing along as we fill these functions rather than copying as you'll learn more.

#### updateBoard 

prolly the simplest function, all we need to do is loop over our array of arrays and log them as a tic tac toe board 
   |   |
   |   |
   |   |

  ```js
   updateBoard: function(){
        console.log("   ") // empty space before a board
        board.forEach((arr, i)=> { // looping over each array in the board
            console.log(arr.toString().replace(/,/g, "|"))
        })
    }


  ```
  the magic in update board happens here:

```js
arr.toString().replace(/,/g, "|")
```
if you understand this you good to go, basically what we are doing here is turning an array `[" ", " " , " "]` to a string and replacing every `,` with a `|` symbol, basically if we had an array `["O", "X" , "O"]` the above code will turn it to string `O|X|O`, which is what we are doing to log our board, you should end with a 3 x 3 board like this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6inz2kmzkhk5dd80nbvu.png)

this function is actually complete, next let's implement possibleMoves, the second easiest func to implement, we will worry about composing everything together later

#### possibleMoves 

```js
 possibleMoves: function(){
        const p = []
         // just looping over the board O(n*2)
        for(let i = 0; i < board.length; i++){
            for(let j = 0; j < board[0].length; j++){
               if(board[i][j] === " "){ // if we find a space without X or O add it to p array(it's a possible move)
                   p.push({row: i, col: j})
               }
            }
        }

        return p
    }

```

Our little loop takes O(n*2) time, which is not bad considering our board size, with wich we got coordinates for free spaces in an array.  
Coordinates in this case are indexes to our array of arrays e.g
`board[p.row][p.col]` is how access a space.

let's bring some of this together, by implementing a part of the update function

### update 

```js
let m = [] // place this outside our object, under board, 
           // m will hold all current possible moves



update : function(){
   this.updateBoard();
   m = this.possibleMoves();
}

```
for now the update function is just drawing the board and getting all possible moves

let's implement a simple computer function, that will pick a random move, from possible moves and place an O, we will handle turns etc later

#### computer 



```js

   let turn = true // declare turn outside we will use it to change turns later

   computer: function(){
         
          console.log(m) // see all possible moves(for debugging purposes)
          if(m.length > 0){
              let ra = Math.round(Math.random() * (m.length - 1)) // picking a random number in a range of our board len
              board[m[ra].row][m[ra].col] = "O" // using that index to pick a random coordinate
          }
          turn = true;
          this.update() // important
          console.log("ur turn")
    },

``` 

believe it or not, that's all for our computer, now let's implement the player which is responsible for starting the game via the move function, hang in there we are about to bring everything together, it will all make sense.

#### move 

now this is the complicated part especially if you are not familiar with node js, I will try to explain everything

basically we need a way to process player input and get coordinates to make a move on the board(put an X)

```js
// declare var on top 
let combo = {
    row: undefined,
    col: undefined
}


```

combo will hold user input, the idea is simple when both row and col are other values than `undefined` e.g 

```js
 combo = {
   row: 0,
   col: 0
}

```
move will put an x on 0,0. and hand over the turn to the computer function vice versa

getting user input

```js
const readline = require("readline")  // way to import a module into node(comes with node)
```

you do not need to know how the readline module works, just the part we interested in

at the bottom of everything write the following, which is responsible for waiting for user input in the console(think of it as a while true loop) but waiting for user input 

```js
readline.emitKeypressEvents(process.stdin);  
process.stdin.setRawMode(true);

// the following code is all you need to understand for now
// all it is saying is when we receive a key press event react
process.stdin.on('keypress', (str, key) => {
   // we only care about the key
  if (key.ctrl && key.name === 'c') {
     // if we receive ctrl-c we exit the "loop"
    process.exit();  // exits the program
  } else {
     // else if we get other keys and it's the players turn
      if(turn){ 
        if(combo.row){
          // if we got the coord for row fill col
            combo.col = key.name
            turn = false
            // basically we have all coordinates to make a move
            game.move(combo)  
         }else{
           // the first key we want is the row
           combo.row = key.name
         }
      }else{
          // pressing a key while it's computer's turn
          console.log("wait your turn")
      }
     
   
  }
});



```

now we can implement the move function, as we can now get user input 

#### move 

remember move will only be invoked when combo has both row and col coordinates

```js
  move : function(c){

    
        board[+c.row][+c.col] = "x"
        // turning combo back to undefined
        // so we can take coordinates for the next turn for player
        // if we don't do this we will never get new coords 
        combo.row = undefined
        combo.col = undefined
        this.update()
        setTimeout(() => {
             // after making our move we give CPU(I am tired of writing computer😂, it's cpu from now on) 
             // the turn 
            this.computer()
        }, 3000);
    },

```

if you run the program now it should hang and wait for coordinate(input numbers), then it will execute move, which will call update and CPU, vice versa

we almost done, all we need now is gameover logic 

#### gameover 

```js
  // write this function on top of everything
const allequal = arr => arr.every(v => v !== " " &&  v === arr[0]);

```

if you understand the above function, you are basically done with gameover.

what this func is basically doing is checking for same contiguous  values

e.g [1, 1, 1] it will return true, for [1, 2, 1] false,
meaning we can just put ["X", "X", "X"], and we will know X has won vice versa, all we need now is dumb logic to check all possible combos basically:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zbkdjmzkhoap4sqoypiq.png)

that is basically what the code below is doing 
first on top declare winner and gameOver vars

```js
   let winner = ""
   let gameOver = false;
```
dumb logic 
```js
 isgameOver: function(){

    if(allequal(board[0])){
                      gameOver = true;
	              winner = board[0][0]
		   
		    }
		      if(allequal(board[1])){
                      gameOver = true;
			winner = board[1][0]
	
		      }
		        if(allequal(board[2])){
                          gameOver = true;
			  winner = board[2][0]
	
			}

             if(allequal([board[0][0], board[1][0], board[2][0]])){
                        gameOver = true;
	             winner = board[0][0]
             }
              if(allequal([board[0][1], board[1][1], board[2][1]])){
                        gameOver = true;
	        winner = board[0][1]
              }
               if(allequal([board[0][2], board[1][2], board[2][2]])){
                        gameOver = true;
	         winner = board[0][2]
               }
                 if(allequal([board[0][0], board[1][1], board[2][2]])){
                        gameOver = true;
	           winner = board[0][0]
		 }
                   if(allequal([board[0][2], board[1][1], board[2][0]])){
                        gameOver = true;
	             winner = board[0][2]
		   }

  }

```


all we need to do now is bring everything together in update

#### update 

```js
  update : function(){
    this.isgameOver();  // checking if game is over
   
    if(gameOver){
      this.updateBoard()
      console.log(`Game over ${winner} won!`)
      process.exit();  // exit ("loop")
      }
     this.updateBoard();
    m = this.possibleMoves();
    // if there no possible moves anymore it's a draw
    if(m.length === 0){
      gameOver = true;
      console.log("Game over by draw")
      process.exit();
    }
    
  }


```

with that we are done with the beginner level, next article we are making it graphical and do a bit of clean up and error handling, you can do it as a challenge if you want

you can find the [repo here](https://github.com/SfundoMhlungu/tictactoe/blob/main/tic.js) for this code so far, watch, fork, star the repo all that good stuff I will be updating it as we go along, you can get the code even before the article for intermediate as I will commit as I code too 

## Conclusion 

I will try to make this series as consistent as possible, More interesting projects coming, including a mini compiler👌🧙‍♂️, yes I am going all out, teaching is the best way to learn too,.

if you would like to support me, you can buy me a coffee below, I will highly appreciate it❤❤, you certainly don't have to if you can't.

[![Buy Me A Coffee](https://cdn.buymeacoffee.com/buttons/default-black.png)](https://www.buymeacoffee.com/mhlunguskF)

with that cheers till next time, Happy Hacking!!