Project #3: Part 1 – Words with Computer Assistance

Update: A Words With Friends update has broken compatibility. Get the updated script here.

Project #3: Part 1 – Words with Computer Assistance

Requirements:

Get Greasemonkey here (Firefox)

Get Tampermonkey here (Chrome)

Download the completed Part 1 script here
Update: A Words With Friends update has broken compatibility. Get the updated script here.

Another Greasemonkey project, this time something more substantial. If you’ve no experience with it yet, I suggest looking over my earlier project Project #2: Automating web forms with Greasemonkey. This time we’ll be making a helper for the zynga/facebook game Words With Friends (a scrabble clone).

What it will do:

  • Have a convenient interface built around the actual game board.
  • Read the letter tiles from the game.
  • Provide move suggestions to the player in a convenient way.

Mapping the gameboard into our script

As in project 2, we’ll be using the inspect element function of the webbrowser to identify the elements we want to work with:

inspect1

A little digging (pictured) determines the tile shown is determined by the background image of class wwf-letter_tile. Unfortunately instead of the image being a url named something convenient like “letter_t.png”, it’s encoded in base64 format (a text representation of the binary of the image). This means to identify each of the letters, we’re going to have to do something similar to project 1 and get a listing of all the different possible image strings and manually map them to their corresponding letter.

First I test working with the tiles.

This script should change any tile to appear like an A tile. The problem is it doesn’t work. Not in the web browser’s console, and not in greasemonkey.

I look a little more closely at the facebook page, and find that the words with friends game is actually inside an iframe, and its contents don’t come from facebook.com, but zyngawithfriends.com. Easiest way to fix this is changing my @include url:

I go to the frame directly in firefox like so:
frame1

Now the code works from the console but still doesn’t work in greasemonkey. The reason is the page loads dynamically, and our greasemonkey script is executing before the tile class even exists.

Now it works. At this point I realize that “wwf-letter_tile” class is used more than just for the tiles in your hand, but for the dictionary tiles and strangely a bunch of non-visible tiles behind the board. Easily fixed by first selecting the parent div:
var x = document.getElementById("wwf-letters").getElementsByClassName("wwf-letter_tile")

Now that we know we can work with the tiles, it’s time to work on identifying them. Since there should be only about 26 tiles it doesn’t seem worth building any scripts to help with mapping the image strings to letters. So at this point I play a game between myself in different accounts in firefox and chrome, gathering tile image strings by inspecting them and copying out the background-image style.

After doing all this work, I discover something troubling. The image strings from firefox don’t match those from chrome. I can’t find anything much about this other a reference that postprocessing could give different results, or 4 year old posts about compatibility issues with one browser or the other. So either compatibility issues, or the browser is generating or altering the images at some point. Perhaps the answer is in the javascript for the game (app.4fbc25a7c8a73178d09b.js), if it is, I can’t find it. After running it through a ‘beautifier’ it’s 40k+ lines of incomprehensible code.

Regardless, testing on a Mac gave the same string for chrome as it did on windows, so we should only need 2 sets of tile images. I will later want to detect the tiles on the board however, which appear white when they’ve just been played, and the blank tiles are missing a score. That means the game has 79 possible tiles. Grabbing them manually for each browser would be a lot of work. So as I build the next part of the script which will get the tiles on the game board, I’ll add in a function to detect if a tile has been seen before and if not a popup will ask what it is. This way it should be trivial to map the tiles to letters just by playing the game.

Here is the near completed function to get the game board tiles:

The comments hopefully explain most of it. Essentially the game area is entirely made up of positioned background images in the CSS. The first part of this function is about retrieving the CSS and turning it into arrays of images and coordinates. The next part is about identifying the images and putting them into a 2d array called grid. Finally if unknown images are found a function is called to make a popup asking the user to identify the image. In a lot of languages there are good ways of halting while waiting for user input, if javascript were one of those, we could put our function for input in the middle of the loop that identifies tiles. Unfortunately it’s not, which means making things a bit recursive. See the following functions that get the user to identify images:

Above there are some linked functions. The top createDivPopup function does what it says and creates a popup div in the middle of the screen with an X button to close it but otherwise blank. It’s general enough to be useful for other things and may make an appearance in another script. The askTilePopup function gives parameters to createDivPopup to display an image from the bgTiles array that was created previously in the getBoardTiles function. index is incremented and passed as a parameter to track where in the array it is up to. In the middle of this function a savefunction is created to be used with the form’s submit. Greasemonkey has to do this with addEventListener instead of onsubmit on the form because the greasemonkey code can’t be accessed from within the page. See this code in action in this video:

Users of the program should never actually get to see these popups as I’ve used them to map all possible tiles and then hardcoded them into the script. Still if a different web browser is used or zynga changes the images, this script will still be functional.

User Interface

Now the script needs a way for the user to run it. You might have noticed in the video the button below the game area. That was from this button creating function. I also fixed the problem with the script running before the page is loaded fully with this function:

In spite of all the code there isn’t much to this button, the code largely concerns styling and positioning the button in the right spot on the page relative to the game board. One thing I haven’t pointed out before is this useful line of code:

It injects the element we’ve defined (checkButton) into the page right after an element from the page we’ve selected (appFooter). A good way to add something to a page with greasemonkey.

Solving the game

Now there is the difficult task of solving a scrabble game. In part 2 I intend to do that, for now I’ve put together a quick solution of sending the board and tile configurations to an existing website that generates solutions, opened in new window/tab. (This could be done inside an iframe on the same page as the game but I think it would be rude to use someone’s site without actually sending someone there). I am using http://www.lexicalwordfinder.com for the solutions. Here is the function that converts our data to a format the site can use and sends it:

Here is a video of the completed script in action:


That’s it for part 1, I’ll pick this up at a later date to add in a javascript solver and interface. Get the finished part 1 script here, or copy it from the completed code below:

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">