Perlessence

Set Game in JS with YUI

February 10, 2008 23:13

I grew up on a game called Set. For those of you too lazy to click, it's a pattern recognition card game. You start by laying out 12 cards. Each card has symbols with four attributes: shape (squiggles, pills, diamonds), color (red, purple, green), fill (solid, empty, semi-filled), and count (1, 2 or 3). The goal of the game is to make the most Sets of three cards. Here's where it gets tricky. Three cards constitute a set if, for each attribute, all the cards are either all the same or all different. See the end of the post for examples.

I used several elements of YUI to build this game. The Dom and Event utility classes were used for basic element manipulation and for attaching event listeners. I used the Button class for creating the playing cards. Each card on the playing field is a Button instance of type checkbox, since a card can toggle between selected and not. I used CSS in combination with the classes that YUI attaches to each button on certain events to change the border of the card to indicate it is selected. I used the focus property (and its yui-button-focus class) to highlight a button in the Find Third method.

The code went through many iterations, in a sort of agile development way. I only wrote code as I needed it, but I also did think ahead in the architecture and planned out the methods I knew I'd need. I found that it was easier--at least on such a small project--to code for functionality first and then go back and optimize as I learned what was and wasn't being reused. My rule of thumb, seconded by dormando in his Ops Mantras post, is that the second time I find myself writing the same/similar code, it's time to refactor it out.

The images for the cards were stolen (for testing purposes only!) from the Set official site and are still named according to their convention, which is a simple integer.gif (e.g., 81.gif). At first I have an array of objects hardcoded to the attribute values of each image this.cards[81] = {shape: 'squiggle', color : 'green', ... } but I found that I could dynamically create this array because of the way the cards were numbered. Rather than store each property as a string, I store an integer that maps to a global attributes array (e.g. shape : 0 corresponds to a squiggle) and translate for the few times that I need to show pretty text. Long-term, I plan on renaming the images to match the index values of the corresponding attributes (e.g., 3012.gif would be shape 3, color 0, count 1, fill 2). And even more long-term, it would be fun to play with PHP's image functions to dynamically generate the images.

The biggest challenge was actually finishing this project. If you view the JS source, you'll see I still have features I want to implement, but the game is fully functional with a couple of little addons. It was a nice challenge in algorithm design; as that's not my strength, I don't doubt it can be optimized further. Still, I'm happy with the way it turned out and hope you have fun with it.

Play game and view source code

Valid set:
- Color : same (all red)
- Shape : different (one pill, one diamond, one squiggle)
- Fill : different (one empty, one solid, one semi-filled)
- Count : different (one, two, three shapes)
1 empty red pill 3 solid red diamonds 2 semi red squiggles
Invalid set
- Color : FAIL (two are green, one is purple)
- Shape : FAIL (two are diamonds, one is a pill)
- Fill : FAIL (two are empty, one is semi-filled)
- Count : same (all two shapes)
2 semi green diamonds 2 empty green pills 2 empty purple diamonds
© Probably a million other people from whom I've subconsciously stolen this layout. Also, Karen Ziv (me). Don't steal my stuff.