Building a tic tac toe game with LitElement

Using web components to build an interactive game

jevisan
CodeX
7 min readJul 23, 2021

--

In this article we're gonna build the classic tic tac toe game with the web components based library LitElement.

Project initialization

First, let's do some scaffolding. For this, I recommend using the handy dandy open-wc generator tool, that sets up a lit-element component for us. We'll type the following command in a terminal:

$ npm init @open-wc

This command will initialize the scaffolding tool to generate a Lit Element component or app. We'll see the following screen:

To create a new project, select the first option: Scaffold a new project. After that, the tool will ask us if we want to create a component or an app. In order to make a reusable component, we'll select Web Component.

Then we'll be asked if we want to add some extra features, like Linting (for code styling), testing and demoing. These are completely optional, but you may consider including at least the testing module to run automated tests in for the component.

Then the tool will asks us if we want to use typescript, which wont be the case for this project, so we hit No.

We'll proceed then to name our component. Feel free to name it as you want, I'll simply name mine: tic-tac-toe. Remember Lit requires to name it at least with two hyphen separated words like: my-component. No one-word-name is allowed.

Lastly, it will show you the component's file structure that'll be write to disk. After selecting yes the tool will ask us if we want to install dependencies. We'll select yes, with npm so we can use npm as our dependencies manager. Then, it'll finally create all the folders and files for our new component.

Project structure

The open-wc tool generates the following project structure inside the folder with the specified component name (in this case, tic-tac-toe).

./
├── tic-tac-toe/
│ ├── demo/
│ │ └── index.html
│ ├── src/
│ │ └── TicTacToe.js
│ ├── .editorconfig
│ ├── .gitignore
│ ├── custom-elements.json
│ ├── index.js
│ ├── LICENSE
│ ├── package.json
│ ├── README.md
│ ├── tic-tac-toe.js
│ └── web-dev-server.config.mjs

The most important files are:

  • package.json Contains the specifications of the npm project, along with its dependencies and scripts.
  • tic-tac-toe.js The definition of our element as an native web component.
  • demo/index.html A showcase page for our component where we can actually see it displayed.
  • src/TicTacToe.js The component per se. The file that contains the main component’s class

Game components

Now that we have a component, lets begin segmenting our little game component into smaller bits so the code base is more manageable. The main game instance will be in our TicTacToe.js file, it’ll run the game logic and contain the board. Then, we’ll have a TicTacToeCell.js to represent each of our board cells and to contain either a cross or a circle. So go ahead and create a new file and copy the contents of the first element into the second, or duplicate the first component, just don’t forget to rename it. The project structure should now look something like this:

./
├── tic-tac-toe/
│ ├── ...
│ ├── src/
│ │ ├── TicTacToe.js
│ │ └── TicTacToeCell.js
│ ├── ...

Now if we take a look at the file TicTacToe.js it should contain a lot of scaffold code that we won’t need. Feel free to delete the __increment method and the contents of all the methods. Now, let’s get coding.

Properties & template

Let’s set some properties for managing the board state and the game condition. Replace the properties method contents with the following:

Here I’ve set up a board property that will hold an array to represent the moves made in the board by each of the players. The currentPlayer property will hold the number, either 1 or 2, of the player in turn. The plays is an object that holds the number of moves made by each of the players, in case we you want to implement some kind of stats later. Finally turn holds the number of plays made for the same reason.

Then, to initialize all these variables, we use the constructor method, native to all web components:

As the game initialization takes place several times, that functionality is refactored in the initGame function, which is called once in the constructor, and then again every time the game needs to be reset. We also set up some event listeners to display confirm messages when the game reaches the condition for it to end, that is, a player victory or a tie. We’ll set the triggers for these events later.

Then, lets set up the template for the main component:

In here we only set up a div to serve as our board. However, the interesting thing here is we are using our tic-tac-toe-cell to actually render the cell’s contents, that could be either a cross or a circle. In order to accomplish that, we set the cell’s property .symbol that’s initially set up to an empty string as we don’t want it to display any symbol yet. Then, to get the game logic to work, we need to couple our data representation of our board state (in this case our this.board property) with the visual representation of each cell (each of the tic-tac-toe-cell), that’s why we also have set up a .col and a .row properties in the cells. Lastly, we need a event listener in the cell to listen when a user clicks it. The method this._handleClick will serve to that purpose:

The handleClick method will take care of setting the corresponding symbol to the clicked cell, according to the player currently in turn. It’ll also update the board array to express the latest move made, as well as the number of plays. Lastly, it will pass the turn to the next player calling to the changePlayer method. A thing to notice here is any of the actions above will be executed if the selected cell has already been selected.

Now for the actual tic-tac-toe-cell:

It consist of a simple component with the properties: row, col and symbol. It has a very simple template, we only need it to display a div with a class according to the symbol passed as a parameter. Maybe the most notable thing in this component are its styles, that are made to reflect the symbol property. To display a pretty icon-like symbol, we use plain css.

Styles & project start command

Talking of styles, lets also set a style for our board, shall we? Add the following method to our TicTacToe class:

Our tic-tac-toe component is now ready for display. Now go ahead and type the following command in your shell to visualize your project, just make sure you’re inside the project’s folder:

$ npm run start

This will serve the project’s files. Specifically, we’ll be visualizing all that is in the demo/index.html file, the showcase for our component, remember?

With a few lines of code our component has came to life!

Simple, isn’t it? Notice how it’ll even change the symbol the cell is filled with according to who is the current player. However our little tic-tac-toe game has no way to determine who has won, or if the match has ended up in a tie, nor will reset once the match has finished, lets fix that.

Game logic

First of all, we need to tell our component to check for either player victory once a move has been made. And for that we need to set some methods to check what constitutes a player victory.

Check victory

There’s three ways a player could win: if a row consist only of a player’s symbol (horizontal check); if a column consists only of that player’s symbol (vertical check); or if any of the two board’s diagonals consists only of the player’s symbol (diagonal check).

Lets create the methods that make those checks:

Not the fanciest algorithms here, but these are the three different methods for the three cases described before. Feel free to replace these with your own implementation, but this will work just fine. Finally there’s a wrapper checkVictory function to easily call the three methods when needed.

Now we need to call these every time a player makes a move. We already have a _handleClick method that triggers whenever a players clicks on a cells. Lets add the victory check to this method.

Remember the event listeners we set up in the constructor at the beginning? Those listen to a player-win and a tie event. This new implementation triggers those events. The timeout function prevents the event to be dispatched before the board actually updates with the final move, then and only then the game over confirm message can be displayed.

Conclusion

We built a simple yet fun tic-tac-toe component with lit-element. It can be packed and deployed in any web application, just like in the demo/index.html. The only thing that kept bothering me was that confirm message was really anti-esthetic so lets build a nice modal component to display the game messages in a following tutorial.

--

--

jevisan
CodeX
Writer for

Frontend developer, Python enthusiast, music lover and videogame geek