What can you learn from playing a game? It turns out: A LOT!
At Ruby Hack Night this month, we took on the Ruby Warrior game kata, which sort of sounds like a waste of time, but was a great opportunity to practice pair programming, the TDD workflow, Ruby idioms and to get our hands dirty with Artificial Intelligence in preparation for next month. Thirty-one of us suited up in plate mail, ready for battle, and by the time the dust settled, we had accomplished what we came out to do. This is a story of glory and sacrifice.
It is standard procedure in RHN workshops to work in pairs. It not only benefits the newcomers, but allows us all to get going faster since we need only half of the laptops to be set up – that and we can heal each other on the battlefield. So it was in 15 pairs we took on the kata.
We began by reviewing the project repo [Ruby Warrior project on Github] and discussing the goals of our solutions:
- Pass the level
- Improve our score (finish the level faster and more completely)
- Do it all with the simplest code possible
Our approach was founded in the TDD workflow. This made sense to us because the rubywarrior
application is much like an RSpec
test suite. By this we mean:
- we run them to determine if our tests pass using the current solution, and
- at each level,
rubywarrior
adds new requirements, much like implementing updated tests.
This offered the option to do “red-green” development, which includes an opportunity to add a refactoring step into the workflow. For those needing to brush-up on the workflow, read my blog entry “Refactoring in Context”.
The workflow begins with the “red” step – to prove the existing code fails the (updated) tests. Then, a discussion of strategy (based on requirement derived from the README
and evidence collected from the failed output). This often results in pseudo-code laying out the strategy in a structure. But on higher levels this becomes a process of exploration: Too many teams experienced their warriors pacing back and forth endlessly, or attack anything and everything that moved (poor captives!) Ok, back to the drawing board.
Once a “green” is obtained (by passing the level) refactoring becomes an important part of the solution. Each pair grappled with simplifying the code by extracting methods, defining new classes (which on some teams included monkey-patching), and generally trying to simplify the play_turn
method.
Being that we are implementing AI, the play_turn
method will suffer from spaghetti logic. Many of our pairs spent time to adjust the logic into uncomplicated, parallel clauses. To do this, they grappled with the three main decisions (when solving the first seven or so levels):
- What’s in front of me?
- Am I healthy?
- Am I taking damage?
Exercise left to the reader: Build a solution based on truth tables using the above as inputs!
By focussing on parallel tests, the warriors were able to extract each spaghetti string from the others, and along with the liberal use of well-named methods, produce readable, simple code. Unfortunately, much like real life, it was not long before the cleanliness got destroyed by the introduction of new “curveball” requirements.
The team working on the screen at the front were able to pass level 6 in the time we had – this included saving the captive against the back wall. From the looks of things around the room, most of the other pairs were on similar tracks to victory.
So with our newfound respect for the complexity of AI code, and the benefits of TDD workflows, we take on the final session of Captain’s Mistress on November 25, 2015 at The Score in Toronto. If you want to meet the smartest, most dedicated bunch of devs on the continent, meet us at Ruby Hack Night.
– David