The Trials And Tribulations Of 10KB
JavaScript is a fun language. It lets you build almost anything you can imagine. For some reason, I thought it would be fun to build a simulator for the card game War. I started hacking away on the idea in my spare time. It was also a good excuse to learn object-oriented JavaScript to make my card game simulator flexible and modular. After a few weekends I had something that worked (screenshot below). Unfortunately after that life got busy and my war simulator just sat there collecting virtual dust for more than a year.
Fast forward to July 29th when the 10K Apart contest is launched challenging developers to build compelling apps that are 10 kilobytes or less. This would be a fun way to refine my stale war game simulator as well as test my JavaScript coding-foo to squeeze it all in to 10 kilobytes.
Pure HTML Cards
One of my first challenges was re-doing the cards. Initially I constructed a single-image sprite containing all of the cards. JavaScript would manipulate the class of a div which would determine how to position the image for the proper card to be displayed. This method led me to discover something Internet Explorer actually gets right with CSS and the other browsers fall short. This image of all of the cards weighed in at 36KB alone. Ouch!
My solution was to use unicode characters for the different suites and letters and numbers for the card values. Child elements of the card held the symbols and I positioned them absolutely within the card element. The final touch was to rotate one of the child elements 180 degrees using the transform CSS property so it would display upside down just like a real card. My image-less approach was just a fraction of the size of using images for cards. But something was still missing.
Playing cards have decorative designs in the middle. It would be too complicated to completely replicate these designs so I came up with the idea to use a Twitter avatar as a background. Thanks to the awesome service provided by http://tweetimag.es/ their RESTful api let me grab any public Twitter users avatar by constructing a simple URL. Browsers provide an easy way to add new CSS rules to a stylesheet via JavaScript so I could dynamically change the background image of a card based on which Twitter names the user provided. This is where the idea for making my War game simulator revolve around Twitter users was born.
Viral Sharing
A bigger advantage to hooking into Twitter is it makes each game more personal for the player. Pitting two people in a card game battle makes the experience more meaningful then a bunch of plain, virtual playing cards flipping back and forth for a couple hundred of turns. This also has the potential for a viral aspect to my app since I already have atleast two Twitter names that would be interested in the results. In order to share a game I needed to create a way to read in the number of players, their twitter name, and their deck at the start of the game. Using a simple string of characters that represented this data attached to the end of the URL allowed me to do this. All of the values in the deck could be stored as a number or letter since I had unique first letters.
Card | Encoded Equivalent |
---|---|
0, 1, 2 … 10 | 0, 1, 2 … 10 |
Jack | j |
Queen | q |
King | k |
Ace | a |
Hearts | h |
Diamonds | d |
Spades | s |
Clubs | c |
Therefore, the Ace of Spades could be represented as ‘as’, the 10 of Diamonds would be ’10d’ and so on. A full game looks like this: p1-kingkool68:adqhkc2d5d10s3h3d5hjs8dqc9c8h2s9d6c7c9skd10d8c7s7d2c4s|p2-naudebynature:jc6d10cjdkh5c4h6sqdks2has6h4d7h4cac10h9hah3s8s5s3cjhqs
A Heap of Data
One of my favorite parts of developing this app was creating a way for keeping track of all the different data that results from a single game. For each war I store the turn it occurred on as well as keep track of which player won the war. After each turn I store a copy of the players deck for further analysis after the game concludes. I originally wanted to include a line graph showing the number of cards a player had in their deck as the game progressed. Building that feature was pretty simple thanks to the Google Charts API but in the end it had to be cut in order to get below the 10KB limit.
Squeezing Down the Bits
I was a little ambitious with the functionality that when I was ready to start compressing my code down I was shocked to see my idea bloated to 33KB. That’s more than 3 times the maximum size. A serious slimming down was in order. Pre-populat lists of Twitter users that would make for interesting battles: cut. Charting capabilities mentioned earlier: gone. A brief blurb explaining the game and who I was: removed. Title tags: discarded. Every little bit I could trim down would help. After all this I was down to about 17KB and along way to go to get under 10KB.
Dean Edwards took JavaScript compression to another level with his /packer/ tool. He came up with away to reduce the size of JavaScript by removing white space, stripping comments, and replacing variables with shorter, less-coherent replacements. CleanCSS.com does pretty much the same thing but for CSS. Such optimizations include replacing font-weight:bold; with font-weight:700; color:#ff0000; with color:red; and so forth. These packing tricks helped get me to 13KB but I was running out of ways to optimize further.
My next plan was to modify my HTML. IDs like ‘prepare-for-war’ became a single letter, <strong> elements were replaced with deprecated <b> tags, and the closing </body> and </html> tags were axed since they had no affect on my page. Sidenote: Google chops off the closing </body> and </html> tags which for a site that serves up that volume of pages results in a savings of a couple million dollars in bandwidth bills every year.</end sidenote>
At this point I was so close to coming under 10KB but yet so far. It was time for one more drastic technique. I noticed how well the JavaScript /packer/ tool worked so I decided to combine my HTML and CSS into a single line and use JavaScript to write it out to the page as it was loading. This way I could combine it with my main JavaScript code for the most efficient compression possible. After 3 hours of toiling in vain, I reached the point where my app was under the magic limit. Ten kilobytes is equal to 10,240 bytes. My code weighed in at 10,236 bytes; a mere 4 bytes to spare!
Lessons Learned
Even after spending hours tediously compressing and optimizing my code, I still had a lot of fun. My biggest challenge wasn’t squeezing everything down, or getting it all to work just right. Rather my biggest challenge was myself and when to determine it was finished. After each piece of functionality I coded I wound up thinking of two more things to make it even better. Once it was submitted to the contest gallery, my app got some luke-warm attention but I’m used to that now. Personal projects I toil over never get the amount of attention I always think they will. I’m anxious to see who wins the 10K Apart contest and whether my War app gets any mention at all from the judges.
But in the end that doesn’t matter. What matters is I got my idea out of my head and into a working state where people could actually try it out, no matter how trivial of an idea it is. And that is what makes the web such a great platform for an individual like myself.
So, did you learn anything useful for work doing all this? Does what you do require efficiency and speed versus just consistency and content?
Everything I do is applicable for work in some way or another. Pet projects like this keep me on my toes.
Efficency and consistency is important because many times I need to make changes. If I lay out my code in a weird way that is harder to change later down the line, I pay for it in having to rewrite a lot of it. I’ve learned it’s easier to build modularity in upfront for time savings down the line. The easier code is to change, the easier my job becomes.