Introducing Rock, Paper, Scissors Battle!

March 15, 2016

I started this project at the end of December 2015 and have recently gotten it to a good point here, releasing it in-beta at the end of February 2016. Creating a rock, paper, scissors game is an exercise/project that Codecademy has in its Javascript course. More than a year ago I had taken this course and decided to go further with the Codecademy exercise and build a pretty simple, really ugly looking, but usable RPS game on my own. At that time I had found myself getting programmed in to corners that I couldn't get out of. The code was a mess and I gave up on it.

Recently I came across that project and being that I made leveling up my Javascript one of my goals for 2015, I decided to take another crack at it, starting from scratch. My goals were the following:

SPA framework?

Nah. I deliberately chose not to use a Javascript framework. I went old-school and just used jQuery. No React, no Angular, no Ember, no Aurelia. My intent was to improve Javascript and not spend a bunch of time learning a specific framework. In fact, building this small game is really preparation for the app I plan to build using Angular. My thought process is that I build a simple app using Javascript and jQuery as a stepping stone to building a larger app using Angular. I’m taking it one step at a time.

Handlebars

No JS frameworks, but I did use Handlebars as a Javascript templating engine. I had some data for the opponents, like image, name, tagline, tendencies, and excerpt. This data was going to be presented in multiple views so instead of keeping it in the HTML I wanted to separate it into a JSON file. This way if the name or tendencies of an opponent changed I wouldn’t have to hunt it down in multiple HTML files. Instead I could update the data in one place. Since I extracted all of the data in to a separate file, it made sense to use a Handlebars template to suck in the data to the view I needed it in. I’ve become familiar with its easy to use syntax and that part went very easy. I’ve learned the ins and outs of Handlebars over the last year and plan to write a post on it in the future.

Organized, maintainable JS

I really wanted to focus on creating very organized and maintainable Javascript and I think I succeeded in that. I wanted each function to have a dedicated purpose so that functions could be re-used and have a narrow responsibility. I made sure functions and variables were named logically. I refactored when needed. I could’ve spend another few nights cleaning up the JS and fixing a few small bugs, but for the most part I know it’s in good shape because when I added features it wouldn’t take me long to figure out exactly what to do. I think that’s the true metric of maintainable code. How long does it take you to add a new feature? How many bugs does it produce? If the answers are quick and few than that means your code is organized and maintainable.

Gulp

One of the great things about this project was really getting a better understanding of Gulp. I had used it for compiling Sass and running a web server on a previous project, but on this project I made sure Gulp was doing everything I needed it to do. I essentially set up 4 tasks: sass, js, browsersync, and watch. Here’s what each task did.

‘Sass’ compiled Sass in to CSS with Sourcemaps outputting to a separate file. I made sure to output to a separate file, because the sourcemaps can get quite large and I didn’t want that bloating the size of my CSS file. The ‘Sass’ task is also compressing the outputted CSS, using Autoprefixer to add the needed vendor prefixes, all the while logging syntax errors without crashing Gulp via plumber and notifying me of those errors via beeper.

For the ‘js’ task I am simply concatenating all my 3rd party libraries and my own JS files in to one mega-file, and uglifying it. Uglifying is just compressing it but also shortening variable and function names when possible.

The ‘browsersync’ task runs browsersync, which if you’re not familiar, is a static file server with live browser reloading. It also syncs multiple browsers together. So not only does it automatically refresh my browser when I make a CSS change, but it also keeps my scroll position and clicks in sync between multiple browsers. It’s kind of cool that what I’m doing in Chrome is updated is also happening simultaneously in Firefox.

And finally the ‘watch’ task keeps an eye on my scss and js files and when it detects a change it runs the ‘sass’ and ‘js’ tasks respectively followed by browsersync refresh all my browsers.

I also have a jshint task and a image minification task that I can run when I need or want to that are not part of my ‘watch’ task as they are things I prefer to do on demand.

To get all this working I read Getting Started with Gulp by Travis Maynard. This book reminded me of books by the publisher, A Book Apart, in that it was incredible short, easily digestible, and by the end of it I felt I really understood the topic. I highly recommend it if you’re having trouble setting up the perfect Gulpfile.

Here is my whole kit and kaboodle if you’re interested in how I set up this project’s Gulp tasks:

var gulp = require('gulp'),
    sourcemaps = require('gulp-sourcemaps'), //sass sourcemaps
    sass = require('gulp-sass'), //compile sass
    autoprefixer = require('gulp-autoprefixer'), //vendor prefixes
    uglify = require('gulp-uglify'), //minify js
    jshint = require('gulp-jshint'), //jshint
    concat = require('gulp-concat'), //concatenate files
    imagemin  = require('gulp-imagemin'), //optimize images
    browsersync = require('browser-sync'),
    plumber = require('gulp-plumber'),
    beeper = require('beeper');

//notifying and reporting errors (for the sass and js tasks)
function onError(err) {
  beeper();
  console.log(err);
}
var autoprefixerOptions = {
  browsers: ['last 2 versions', '> 1%', 'Firefox ESR']
};
//do sass related stuff
gulp.task('sass', function() {
  return gulp.src('app/sass/*.scss')
    .pipe(plumber({
      errorHandler: onError    
    }))
    .pipe(sourcemaps.init())
    .pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
    .pipe(autoprefixer(autoprefixerOptions)) 
    .pipe(sourcemaps.write('maps'))
    .pipe(gulp.dest('app/build/css'))
});
//jshint as a separate tasks when I want it
gulp.task('jshint', function () {
  return gulp.src(['app/js/badGuys.js', 'app/js/app.js'])
    .pipe(jshint())
    .pipe(jshint.reporter('default'))
});
//image minification as a separate tasks when I need it
gulp.task('images', function() {
  return gulp.src('app/images/*')
    .pipe(imagemin())
    .pipe(gulp.dest('app/build/images'))
});
//js related tasks
gulp.task('js', function () {
  return gulp.src(['app/js/vendors/firebase-2.4.0.js','app/js/vendors/fastclick.js', 'app/js/vendors/jquery-2.1.4.js', 'app/js/vendors/handlebars-v4.0.5.js', 'app/js/vendors/velocity.min.js', 'app/js/vendors/velocity.ui.min.js', 'app/js/badGuys.js', 'app/js/app.js', 'app/js/leader-board.js'])
    .pipe(plumber({
      errorHandler: onError    
    }))
    .pipe(concat('app.js'))
    .pipe(uglify())
    .pipe(gulp.dest('app/build/js'));
});
//browsersync
gulp.task('browsersync', function(cb) {
    return browsersync({
      server: {
        baseDir: './app'
      }
    }, cb);
});
//watch for changes - run tasks - reload browsers
gulp.task('watch', function() {
    gulp.watch('app/sass/*.scss', ['sass', browsersync.reload]);
    gulp.watch('app/js/*.js', ['js', browsersync.reload]);
});
//default task
gulp.task('default', ['sass', 'js', 'browsersync', 'watch']);

Firebase

Firebase is mainly a real-time database hosted in the cloud with a great web interface for managing your data. Through Javascript alone you can perform CRUD (Create, Read, Update, Delete) operations. I was really impressed with how simple it was to get it working. I used it to create the leaderboard of all the highest scores.

The leader board for rock, paper, scissors battle

One of the keys though to working with Firebase was to download the Chrome extension, Vulcan which allowed me to view, edit, add, and delete records from the Firebase database directly from Chrome’s developer tools, which was super helpful. All in all, I was really proud to be able to get this working as this was something I had never done before.

LocalStorage

I recently started getting in to localStorage and even wrote an article about it. I’m using it mainly to save the user’s name, whether they chose best of 3, 5, or 7, and how many opponents they’ve defeated. Working with localStorage is very easy. The only trick is determining when to overwrite it, and when not to. For instance, if there’s nothing in localStorage, then we want to add properties, but if there is something in localStorage then we want to get those values and use them. I kept a Javascript object that was basically the game state. So I also had this localStorage object as well. So I had to make sure that I kept them in sync at all times. Easy enough, but sometimes I forgot. It added another element to testing.

I didn’t implement any fallback for older browsers, and frankly support is so good, starting at IE8, I don’t think I’m going to even test on IE7.

SVG

I used SVG for the rock, paper, and scissors icons and continue to love SVG. I started to use inline SVG for everything, which worked great. There was one issue with inline SVG’s a the JS library, fastclick.js, which eliminates the 300ms delay on iOS devices. There was one place I used the inline SVG’s inside of a label element. The label element was holding the image and the corresponding radio button was hidden from view. Clicking on the label activated the radio button. After being clicked once, the labels wouldn’t accept a second click, making it so changing your mind after making a selection was impossible. This was an accessible way to go about it, but the inline SVG’s, the radio buttons, and fastclick just couldn’t play nicely together. The only solution I could think of was taking the inline SVG out of the label and so I settled on using SVG as a background image. I went about that by using Grunticon to convert the SVG in to a data URI I could inline in to my CSS to eliminate the http request. But, I didn’t want to add the complexity of Grunticon to my Gulp flow, so instead the fastest approach was to run them through Grunticon’s complimentary web, Grumpicon. Once the SVG’s were a background image, the problem was solved.

Responsive

I thought if anyone actually plays this game, its going to be on a phone. So I made it responsive. No Bootstrap, no grid system. I kept it close to the metal, and used a lot of Flexbox for aligning items horizontally, and then stacking them vertically for smaller devices. I think this is one of the selling points of Flexbox. I also started using Autoprefixer as part of my Gulp chain to prefix for Flexbox. Flexbox is definitely the most confusing thing to write vendor prefixes for and there are a lot of prefixes. So instead of writing a Sass mixin, I elected for Autoprefixer, and man I am not looking back!

Animations

Even though I have written a few articles on CSS Animations (CSS animations aren’t that tough, CSS animation events), I kind of like using VelocityJS, a Javascript animation engine, as it’s just really easy to use and very flexible. I probably could’ve easily done all the VelocityJS animations using CSS animations, but my instinct here was to reach for Velocity.

Things I didn’t do

Unit Testing. I didn’t do any unit testing but I really want to write my first test. I plan to circle back around to this as a learning experience.

Speed. I really wanted to get 100/100 on Google’s PageSpeed Insights but I settled for 97/100 on desktop and 90/100 on mobile for now (which I’m actually very proud of). The main thing it wants me to do is Eliminate render-blocking JavaScript and CSS in above-the-fold content, which means inlining the critical, or above-the-fold CSS, in the head of the document and load the rest of the CSS at the bottom of the page. It actually wouldn’t be very difficult for me to do this because I modularized my CSS files in a way where I could grab my _header.scss and _yourName.scss and inline them in the head of the site. However, I don’t have this built in to my build process so maintaining that would pose an issue I don’t think I’m ready to put up with yet.

Images. There are 6 images: the 5 bad guys and the thumbs up icon when you win. I would like to get some original artwork and make original bad guys. I would also like to ditch the thumbs up for original artwork. Really though, I wouldn’t mind working with a designer to come up with a better overall design and logo as well.

Features to make it better. There are so many features I would like to add. If I get any action on this game, I think I’ll return to adding better features, but for now I think it is in pretty good shape for the moment.

Source code

Everything is up on Bitbucket if you’d like to check out the source code: https://bitbucket.org/richfinelli/rock-paper-scissors.

Mastering CSS: Book by Rich Finelli
Mastering CSS, the Book! Flexbox, Layout, Animations, Responsive, Retina, and more!
Mastering CSS, 2nd Edition, video course by Rich Finelli
Mastering CSS, Second Edition: 47 videos on how to make websites like a boss! Flexbox, Animations, Responsive, Retina, and more!
Back to top