Scrabbling with Javascript

Valerie W. McCarthy
7 min readFeb 2, 2017

Most of us know how to play Scrabble. We build words on a 15x15 board pulling from a hand of 7 tiles which have been randomly selected from a pool of 100 tiles. All words placed on the board must build on an existing word. Each letter has a different point value and the goal is to accumulate the most points before all the tiles run out.

Scrabble: the ultimate friends and family game

All of this is pretty straightforward, assuming you have a decent vocabulary in the native language being played. So I thought it would be no big deal to build a Scrabble game, one of my favorite childhood games, in the three days we were were given to build a game of our choice. We would use javascript and jQuery with API calls to a 3rd party dictionary to confirm word submissions. Piece of cake? Umm, not really. It turns out building a Scrabble application is quite a bit harder than playing the physical board game. Following are some of the hurdles I and my pair partner had to overcome.

Board Grid

Grids are grids, right? Wrong. We had to figure out how to render 225 squares, any one square that could be accessed at anytime. We opted to create a board based upon nested arrays. For each row, we create an empty array and then push in 15 arrays. Into each of these nested arrays, we render a div that most importantly contains a unique identifier we use to play the game.

//in Board.js
buildBoardGrid(row, col){
let square = null
let boardArray = []
for(var tempRow = 0; tempRow < row; tempRow++){
boardArray.push([])
boardArray[tempRow].push(new Array(col)) for(var tempCol = 0; tempCol < col; tempCol++){
square = new Square(tempRow, tempCol)
boardArray[tempRow][tempCol] = square.render()
}
}
return boardArray
}
//In Square.js
render(){
return `<div id="${this.tempRow}_${this.tempCol}" class="square" ></div>`
}

So the third square of the fourth row has an id of “3_2,” matching its place in the array. To simplify our code, we opted to keep the actual array position and not the true row or column number. We also started the array from the upper left corner and built from there. So ID’s are in the form of “{row}_{column}” (and not an x/y coordinate).

Keeping Track of the Tiles

When you play scrabble with a physical board, it is pretty straightforward to keep track of tiles. They are either in the tile pool, one of the players’ hands, or on the board. When a tile is being moved from one place to another, or even when a player decides to back track and start over, we have stock on the tiles. This is a very different situation in the virtual world. To address this, we created stacks. Stacks, stacks, stacks. These stacks keep track of tiles in a move and are cleared after the end of a turn.

var boardStack = [] // tiles placed on the board
var handStack = [] // tiles removed from the hand (needs to be separate from the boardStack because sometimes a tile does not make it to the board
var wordStack = [] // all tiles making up word being played (need to be separate from boardStack because includes letters already on board)

Each stack is an array of arrays. The array elements of each stack have different properties based upon the desired use case. These stacks allow us to properly locate letters on the board, evaluate newly built words, and enable the player to cancel a move if she so desires.

API Calls in an Asynchronous Environment

We first had to find a 3rd party API who would grant us access from a client-side API call and be in a format we did not need to tediously parse. We went through three before landing on a fourth: WordsAPI, which turned out to be relatively user-friendly via an account on Mashape. We thought we were set when we called the WordsAPI with a query on “hello” and could get an object back in JSON format, or when we sent a call with mumble jumble like “wshtn” and get an error returned. But no. We were stung by the asynchronous nature of Javascript.

Upon a player submitting a word, we call an onFinishTurn method that calls the API and follows different scenarios based upon the success or failure of the API call. Due to asynchronous javascript, the scenarios are being played out before the results of the API call are returned. Disaster.

Hello promises! Yes, we needed to find a way to wait for the return of the API call to proceed with finishing the turn. Enter promises, but in a special way due to the jQuery ajax call being utilized to reach the API. As of jQuery 1.5 (dark ages at this point), jQuery XHR objects returned by $.ajax() implement the Promise interface, giving them all the properties, methods, and behavior of a Promise (so we do not need to make a separate new Promise call). These methods take one or more function arguments that are called when the $.ajax() request terminates. The most relevant for us, where we needed both a success and a failure scenario, is the .then() method, which incorporates the functionality of the .done() method [equivalent construct to the success callback option] and the .fail() method [equivalent construct to the error callback option]. In addition to the .then() method, we need a .catch(), which serves as a handler that is to be called when the deferred object [from the .then() call] is rejected. An example of the code implementation is as follows:

onFinishTurn(){$('.finish').click((event) => {
this.wordCheck().then((response) =>
// success scenario:
// code to calculate points, draw new
// tiles and change turns
}).catch((e) => {
// failure scenario:
// code to reject word, return tiles and lose turn
})
})
}
wordCheck(){
return $.ajax({
url: `${URL}${word}`,
headers: {'X-Mashape-Key': wordsApiKey},
success : function(data, textStatus, XMLHttpRequest){
},
error: function(XMLHttpRequest, textStatus, errorThrown){
}
})
}

The above sounds obvious now but it was a big stumbling block for us along the way.

Game Logic

We thought once we got the game built and had a way to check for a valid word, we would be free sailing. But when you actually get down to implementing the rules of the game, the code gets complex very quickly. Basically, we have to check for many different use cases.

  • 100 tiles. Official scrabble has 100 tiles, each with a letter and a value. We could hard code these tiles but that is no fun. So each time a new game is started, we set var tileArray to an empty array and then call the tileMaker method which loops through an array of 26 objects. Each object includes a letter, value and number of occurrences. For each letter, we loop through that letter according to the number of occurrences and for each occurrence, we push a new tile into the tileArray
  • A word must build on another word. We initially set var crossTile to false. As a word is being built (tiles added to board), we check to see if the newly placed letter is adjacent to an existing letter. If so, we set the var crossTile to true. When we call onFinishTurn, we check to see if crossTile is true. If false, tiles are returned and player loses turn.
  • Words read left to right or up to down. When placing tiles, you can only place to the right or below of an existing letter. “Backward” squares on the grid are not enabled.
  • Points are accrued based on point values of each tile used. When we push a tile into the wordStack, we include the value of the tile in the array. Calculating the word value then becomes pretty simple as we loop through that index value of each item in the wordStack array.

Features to come:

We got an MVP out in the three days we were given to build the game. But we have more important features we plan on building in the coming weeks.

  • Supporting words built in any order. Right now our stacks depend on words being built in order. But we know in reality, we don’t always place a letter in order it appears in the word. When spelling “hello” around the letter “o,” we should be able to place the two “l’s” and then the “e” and then the “h” (assuming the final word reads left to right). To implement this functionality, we need to change the way our wordStack is built. Instead of building it when tiles are placed on the board, we need to build it after the word is completed.
  • Handle adjacencies. Currently we only provide credit for the base word built. We do not account for adjacencies, which we know is one of the super fun parts of Scrabble. To do this, upon finishing a turn, we need to loop through each letter of our wordStack and check for adjacent letters and then build additional wordStacks to validate and account for.
  • Showing tile point values on the board. We track tile values and award points based on the tile value and the values are included in the div rendering, but we do not show these values on tiles. To do this, we need to make some changes to our CSS parameters without blowing up the grid and while keeping the full game visible on a normal sized screen. Stretch goal here is to make the game completely responsive.
  • Implementing Premium Squares on the board. Right now all board squares offer 1x the value of the letter placed on it. Real scrabble has 2x and 3x squares, once again providing more excitement and strategy to the game. To implement this, we need to add another element into each square on the grid that keeps track of the multiple value of that square. This multiple then would be applied to the wordStack upon finishing a valid turn.

Happy Scrabbling!

--

--