Practical CoffeeScript: Making a Tic-Tac-Toe Game

Share this article

CoffeeScript is a tiny little language that compiles to JavaScript. There is no interpretation at runtime since you write CoffeeScript, compile it to JavaScript and use the resulting JavaScript files for your app. You can use any JavaScript library (e.g. jQuery) from within CoffeeScript, just by using its features with the appropriate CoffeeScript syntax. CoffeeScript can be used both for writing JavaScript on the front-end and JavaScript on the back-end.

So Why CoffeeScript?

Less Code

According to the Little Book on CoffeeScript, CoffeeScript’s syntax reduces the amount of characters you need to type to get your JS working by around 33% to 50%. I will be presenting a simple Tic-Tac-Toe game created using CoffeeScript (you probably already guessed this from the title) which in its raw CoffeeScript format contains 4963 characters, whereas the compiled JavaScript code contains 7669 characters. That is a difference of 2706 characters or 36%!

Faster Development Time

Because you write shorter, less error-prone (e.g. variables are auto-scoped, meaning you can’t accidentally overwrite globals by omitting var) you can finish your projects quicker. CoffeeScript’s terse syntax also makes for more readable code, and ultimately code which is easier to maintain.

Getting Started

In this article, we will be building a simple Tic-tac-toe game with CoffeeScript and jQuery. If you want to read up on the syntax before examining a practical case, I suggest my Accelerate Your JavaScript Development with CoffeeScript article here at SitePoint. This also details how to install CoffeeScript via npm (the Node Package manager).

As ever, all of the code from this tutorial is available on GitHub and a demo is available on CodePen or at the end of the tutorial.

The most common CoffeeScript commands you will be using are:

coffee -c fileName will compile the CoffeeScript file to a file with the same name but with a .js extension (CoffeeScript files typically have .coffee extension).

coffee -cw fileName will watch for changes in a file (whenever you save the file) and compile it.

coffee -cw folderName/ will watch for changes to all .coffee files in the folder and compile them in the same directory when there are any changes.

Finally, it is handy to compile CoffeeScript from a folder with .coffee files to a folder containing only .js files.

coffee -o js/ -cw /coffee will watch for changes in all .coffee files located in the coffee folder and place the output (JavaScript) in the js folder.

If you are not into terminals, you can use a tool with a GUI to handle your CoffeeScript files. For instance, you can try Prepros on a free unlimited trial (although you have to buy it if you like it). The image below shows some of the options it provides:

Screen shot of Prepros

You can see that Prepros does all the work for you—it sets up watchers so your .coffee files will be compiled to JS, it allows you to use Uglify JS which will minify/compress your code, it can automatically mangle variables and it supports Iced CoffeeScript. Prepros can also be used for CSS preprocessors such as Less and Sass and template engines like Jade.

The Game

Let’s start with the markup:

<div class="wrapper">
  <header class="text-center">
    <h1>Tic Tac Toe</h1>
  </header>

  <div id="board"></div>
  <div class="alerts welcome"></div>
  <div class="notifications"></div>

  <form action="" method="POST">
    ...
  </form>
</div>

<script src="jquery.min.js"></script>
<script src="logic/app.js"></script>

The game’s interface consists of the following:

  • A header which briefly describes the game
  • A div element with the id of board which is where the 3×3 squares will be located
  • A div element with a class of alerts which is where the game status will be shown
  • A div element with a class of notifications which will show who is playing X and O, along with the general player statistics.
  • A form which will be displayed only when the game loads and will prompt the players to enter their names.

In accordance with best practice, both jQuery and the script that makes our app tick are loaded before the closing body tag.

The Styling

Using CSS, we can make the nine squares involved appear in a 3×3 grid by floating each square and clearing every 4th one.

.square:nth-of-type(3n + 1) {
  clear: both;
}

We can also add a different color to the squares depending on whether they have the class x or o (which is added using JavaScript).

.square.x {
  color: crimson;
}

.square.o {
  color: #3997ff;
}

CoffeeScript in Action

For reference, you can find the main CoffeeScript file here.

You can see our Tic-Tac-Toe app starts with $ ->, this is equivalent to the shorthand for jQuery’s function that executes code when the DOM is ready: $(function() { ... });.

CoffeeScript does not rely on semicolons and braces but on indentation. -> tells CoffeeScript that you are defining a function so you can start the body of the function on the next line and indent the body with two spaces.

Next, we create an object called Tic which itself contains an object called data. You can see that braces or commas are not obligatory when creating objects, as long as you indent the properties correctly.

$ ->
  Tic =
    data:
      turns: 0
      x: {}
      o: {}
      gameOver: false

The turns property will hold the total number of turns taken in the game. We can check whether it holds an even or uneven number and in that way determine whether it is the turn of X or O.

The x and o properties are objects and will contain data relating to the number of X’s or O’s on the three axes that are important for the game: horizontal, vertical and diagonal. They will be updated on every move through the checkEnd method to represent the distribution of X and O on the board. The checkEnd method will then call checkWin to determine if there is a winner.

After that we have a method inside the Tic object that will get everything up and running:

initialize: ->
  @data.gameOver = false
  @.setPlayerNames()
  @.retrieveStats()
  @.assignRoles()
  @.prepareBoard()
  @.updateNotifications()
  @.addListeners()

Notice the use of @ which compiles to the JavaScript keyword this. As illustrated in the first property of initialize, you can skip the dot after the @ keyword when setting or calling a property or method.

By giving the methods sensible names, we have a fair idea of what they are doing:

  • setPlayerNames stores the values entered by users in the inputs into the data object.
  • retrieveStats retrieves the player’s statistics from localStorage and sets them up in the data object.
  • assignRoles determines who is playing X and who is playing O.
  • prepareBoard hides the form, removes any notifications, empties the board and fills it with nine empty squares.
  • updateNotifications updates the UI with information about who is playing X and who is playing O, as well as the player’s statistics.
  • addListeners attaches the event listeners, so that we can respond to players making a move.

Diving Deeper

Let’s look at a couple of these methods in more detail.

prepareBoard: ->
  ...
  $("<div>", {class: "square"}).appendTo("#board") for square in [0..8]

Here we iterate nine times and add nine divs with a class of square to the empty board in order to populate it. This demonstrates how CoffeeScript lets you write one-line loops and declare the body of the loop before writing the condition itself.

updateNotifications: ->
  $(".notifications").empty().show()
  @.addNotification "#{@data.player1} is playing #{@data.rolep1}"
  ...

CoffeeScript allows for string interpolation which increases readability and reduces complexity and code length. You can add a #{} within any string and insert any variable or return value from a function call within the braces.

addNotification: (msg) ->
  $(".notifications").append($("<p>", text: msg));

The addNotification method exemplifies how you define parameters in CoffeeScript. You write them before the arrow (->):

You can provide default values for parameters similar to PHP:

addNotification: (msg = "I am a message") ->

When a function with a default parameter is compiled, it is converted to:

if (msg == null) { msg = "I am a message"; }

Finally, let’s turn to the addListeners method:

addListeners: ->
  $(".square").click ->
    if Tic.data.gameOver is no and not $(@).text().length
      if Tic.data.turns % 2 is 0 then $(@).html("X").addClass("x moved")
      else if Tic.data.turns % 2 isnt 0 then $(@).html("O").addClass("o moved")
      ...

Here we see that CoffeeScript offers additional keywords to represent truthy and falsy values such as no, yes, off and on. Additionally, !==, ===, &&, ! can be represented using isnt, is , and and not accordingly.

You can make readable single line conditionals using if ... then ... else ... syntax.

The Mechanics of the Game

The workhorse method checkEnd checks if there is a winner every time a player makes a move. It does this by iterating over the board and counting the squares that belong to X and O. It first checks the diagonal axes, then the vertical, then the horizontal.

checkEnd : ->
  @.data.x = {}
  @.data.o = {}

  #diagonal check
  diagonals = [[0,4,8], [2,4,6]]
  for diagonal in diagonals
    for col in diagonal
      @.checkField(col, 'diagonal')
    @.checkWin()
    @.emptyStorageVar('diagonal')
  for row in [0..2]
    start = row * 3
    end = (row * 3) + 2
    middle = (row * 3) + 1

    #vertical check
    @checkField(start, 'start')
    @checkField(middle, 'middle')
    @checkField(end, 'end')
    @checkWin()

    #horizontal check
    for column in [start..end]
      @checkField(column, 'horizontal')
    @checkWin()
    @emptyStorageVar('horizontal')

As you can see, this makes use of another handy CoffeeScript feature—ranges.

for row in [0..2]

This will loop three times, setting row equal to 0, 1 and 2 in this order. Alternatively, [0...2] (an exclusive range) would result in just two iterations, setting row equal to 0 and 1.

In the horizontal check we see again how indentation is crucial in determining what is part of the loop and what is outside of the loop—only the checkField call is inside the inner loop.

This is what checkField looks like:

checkField: (field, storageVar) ->
  if $(".square").eq(field).hasClass("x")
    if @.data.x[storageVar]? then @.data.x[storageVar]++ else @.data.x[storageVar] = 1
    else if $(".square").eq(field).hasClass("o")
      if @.data.o[storageVar]? then @.data.o[storageVar]++ else @.data.o[storageVar] = 1

This method demonstrates the use of the ? keyword, which when inserted next to a variable in a conditional, compiles to:

if (typeof someVariable !== "undefined" && someVariable  !== null) {

Which is obviously quite handy.

What the checkField method does is add one to the appropriate axis of the x or o property depending on the class name of the square which was clicked. The class name is added when a user clicks on an empty board square in the addListeners method.

This brings us on to the checkWin method, which is used to check if one of the players has won the game:

checkWin: ->
    for key,value of @.data.x
      if value >= 3
        localStorage.x++
        @showAlert "#{@.getPlayerName("X")} wins"
        @data.gameOver = true
        @addToScore("X")
    for key,value of @.data.o
      if value >= 3
        localStorage.o++
        @showAlert "#{@.getPlayerName("O")} wins"
        @data.gameOver = true
        @addToScore("O")

In CoffeeScript you can use for ... in array to loop over array values and for key,value of object to loop over the properties of an object. checkWin utilizes this to check all the properties inside the x and o objects. If any of them holds a number greater than or equal to three, then we have a winner and the game should end. In such a case, we call the addToScore method which persists the results of the players through localStorage.

A Word about Local Storage

LocalStorage is part of the Web Storage specification and has a pretty good browser support. It allows you to store data (similar to cookies) on the user’s machine and access it whenever you want.

You can access the API in several ways, for example just as you would to the properties of a regular object:

//fetch item
localStorage.myProperty 

// set item
localStorage.myProperty = 123

Local storage always saves strings so if you want to store an object or an array you would have to use JSON.stringify when storing the array/object and JSON.parse when retrieving it.

Our addToScore method utilizes this fact:

addToScore: (winningParty) ->
  ...
  if winningParty is "none"
    @.showAlert "The game was a tie"
  else
    ...
    localStorage[@data.player1] = JSON.stringify @data.p1stats

It also demonstrates how you can omit parentheses in CoffeeScript (JSON.stringify), although that is recommended for the outermost function calls only.

Next we have a couple of utility methods. We use emptyStorageVar to clear the contents of a particular horizontal row or diagonal. This is necessary because there are two diagonals on the board and inside our chekEnd method we use the same data property for both diagonals. Therefore, we have to clear the property before checking the second diagonal. The same goes for the horizontal rows.

emptyStorageVar: (storageVar) ->
    @.data.x[storageVar] = null
    @.data.o[storageVar] = null

Getting the Player Names

When the form with the names of the players is submitted at the beginning of a game, we can prevent its default action and handle the submission using JavaScript. We check if there is an empty name or if both names are the same and display a friendly alert if so. Otherwise, we start the game by calling Tic.initialize().

$("form").on "submit", (evt) ->
  evt.preventDefault()
  $inputs = $("input[type='text']")
  namesNotEntered = $inputs.filter(->
    return @.value.trim() isnt ""
  ).length isnt 2
  namesIndentical = $inputs[0].value is $inputs[1].value
  if namesNotEntered then Tic.showAlert("Player names cannot be empty")
  else if namesIndentical then Tic.showAlert("Player names cannot be identical")
  else Tic.initialize()

The final line uses event delegation to have any element with the class play-again respond to a click. Event delegation is necessary, as this element is only added to a page once a game has finished. It is not present when the DOM is first rendered.

$("body").on("click", ".play-again", -> Tic.initialize())

Putting it all Together

And that’s it. In less than 150 lines of CoffeeScript we have a working game. Don’t forget, you can download the code from this tutorial from GitHub.

See the Pen Tic-Tac-Toe by SitePoint (@SitePoint) on CodePen.

Conclusion

I hope that this tutorial has solidified your knowledge of CoffeeScript and has shown you how jQuery and CoffeeScript can work together. There are many things that you can do to improve the game. For example you could add an option to make the board different than its standard 3×3 dimensions. You could implement some simple AI so that players can play against the machine, or you could implement bombs in the game, e.g. by adding a random X or O on a random game move while the players are battling for glory.

Frequently Asked Questions (FAQs) about CoffeeScript and Tic Tac Toe Game

How can I start with CoffeeScript for creating a Tic Tac Toe game?

To start with CoffeeScript for creating a Tic Tac Toe game, you first need to have a basic understanding of CoffeeScript. CoffeeScript is a little language that compiles into JavaScript. It provides better syntax avoiding the quirky parts of JavaScript, still retaining the flexibility and beauty of the language. You can start by learning the basics of CoffeeScript from the official website or other online resources. Once you have a basic understanding, you can start coding your Tic Tac Toe game. You can use any text editor for writing your code and then compile it into JavaScript using the CoffeeScript compiler.

What are the basic components of a Tic Tac Toe game in CoffeeScript?

The basic components of a Tic Tac Toe game in CoffeeScript are similar to any other programming language. They include the game board, the players, and the game logic. The game board is a 3×3 grid where the players place their marks. The players are usually two, and they take turns to place their marks on the game board. The game logic includes the rules of the game, such as how a player wins, what happens when the game is a draw, and so on.

How can I create a game board for Tic Tac Toe in CoffeeScript?

Creating a game board for Tic Tac Toe in CoffeeScript involves defining a 3×3 matrix. This can be done using an array of arrays. Each inner array represents a row on the game board, and each element in the inner array represents a cell on the game board. Initially, all cells are empty. When a player makes a move, the corresponding cell in the matrix is updated with the player’s mark.

How can I handle player moves in CoffeeScript?

Handling player moves in CoffeeScript involves updating the game board and checking if the game has been won. When a player makes a move, you need to update the corresponding cell in the game board with the player’s mark. Then, you need to check if the player has won the game. This can be done by checking all possible winning combinations of cells.

How can I check if a player has won the game in CoffeeScript?

Checking if a player has won the game in CoffeeScript involves checking all possible winning combinations of cells. There are 8 possible winning combinations: 3 rows, 3 columns, and 2 diagonals. You can check each combination to see if all cells in the combination have the same mark, which is the mark of the current player. If so, the player has won the game.

How can I handle a draw in CoffeeScript?

Handling a draw in CoffeeScript involves checking if all cells on the game board have been marked and no player has won the game. If all cells have been marked and no player has won, the game is a draw. You can check this after each player’s move.

How can I compile my CoffeeScript code into JavaScript?

Compiling your CoffeeScript code into JavaScript can be done using the CoffeeScript compiler. You can install the compiler using npm, the Node.js package manager. Once installed, you can compile your CoffeeScript code into JavaScript using the coffee command followed by the -c option and the name of your CoffeeScript file.

Can I use jQuery with CoffeeScript?

Yes, you can use jQuery with CoffeeScript. CoffeeScript compiles into JavaScript, so you can use any JavaScript library with it, including jQuery. You can use jQuery to manipulate the DOM, handle events, create animations, and more.

How can I debug my CoffeeScript code?

Debugging your CoffeeScript code can be done using the same tools you use to debug JavaScript. Most modern browsers come with built-in developer tools that include a JavaScript debugger. You can use this debugger to step through your code, inspect variables, and more. Note that you will be debugging the compiled JavaScript code, not the original CoffeeScript code.

Where can I learn more about CoffeeScript?

You can learn more about CoffeeScript from the official website, which includes a detailed guide, a reference to the language syntax, and examples. There are also many online tutorials and courses available on websites like Codecademy, Udemy, and Coursera. Additionally, you can find many open-source CoffeeScript projects on GitHub to learn from real-world code.

Ivan DimovIvan Dimov
View Author

Ivan is a student of IT, a freelance web developer and a tech writer. He deals with both front-end and back-end stuff. Whenever he is not in front of an Internet-enabled device he is probably reading a book or travelling.

CoffeeScriptgame programmingjameshjQuerylocal storage
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week