Oh Ember, my Ember
Final projects are done. My partner and I created a version of Battleship using an Ember.JS front-end, which communicated with a Rails 5.0 API as the back-end. Rails persisted any and all data, did a lot of the computational heavy lifting (not terribly heavy, admittedly), and returned the results which Ember then rendered. It was an interesting experience working with this JS framework, and here are my thoughts.
Firstly, it’s important for me to keep in mind that everything we accomplished, we did so with a bit under two weeks of Ember knowledge. We were thrown into final projects after, I believe, four days of Ember labs and lecture, and then it was essentially sink-or-swim. This had interesting results. Firstly, almost everything we needed to do in order to complete the lab required independent research. Returning values based upon conditionals to the template? That’s a helper function. Cause bindings to refresh when the model updates? Likely you’re going to need to make sure that a computed function knows what part of the model it’s watching before it realizes changes have occurred. All things we know now which, at the time, devoured hours before we understood them.
We also had the supremely fun challenge of creating a functioning AI (we’re calling her Esmeralda, because… why not.) The logic essentially goes: the AI takes a random pot-shot, then the next round looks at the board to see the results. If it was a miss, then that board value changed from a 0 to a 1, and it knows to shoot randomly again (only at other 0's.) If it changed to a 2, then it knows that a ship was spotted, and it shoots again at an adjacent location. If subsequent to this, there are two or more 2’s on the board, it knows it’s found the axis along which the ship lies, and to continue along it. If while doing so it hits water before the ship is sunk, then go back to the first hit and continue the other way. When the 2’s suddenly change to 3’s, the ship is sunk and go back to taking random shots.
There were some edge cases which we were in the process of fixing when we ran out of time and had to begin our demo to the rest of the school. For instance, when two ships are adjacent to one another, it could understandably think that the ship was along a certain axis when it had actually just hit two different ships along each of their axes once.
Then, say we fix this, and it subsequently starts moving along a second axis. The next round, there will be three hits, a central one and two others branching off in different directions. Which direction does it choose? It would need to recognize this dilemma, parse the two directions as different arrays of choices, choose one, and then do the single-axis logic on it until it turns into 3’s, then go back along the other axis. It would also need to continue watching out for getting hemmed in by misses, lest it choose a direction that leads to another dead end.
Not terribly convoluted: we simply ran out of time toward the end. However, it was a very interesting process thinking through all the types of scenarios the AI could run into during a game.
We also created a random ship generating algorithm to make replay more fun (rather than seeding the game with the same ships each time.) The algorithm choose an initial coordinate, then made certain there wasn’t already a ship there. It then selected 1–4 other coordinates (for ship lengths ranging from 2–5), made sure that A) that length didn’t intersect any other ships, and B) all coordinates were valid on a 10x10 grid running from 0–9.
I’m actually rather proud of this code. It chooses randomly both whether to build vertically or horizontally, and then in the positive or negative direction. It’s modular and, while it could clearly be refactored (we’re forced to go back and forth between integers and strings far too often, for one thing, and the per-ship-coordinate generation should be its own function), it’s neat and effective nonetheless.
When we first began designing the game, we anticipated a user experience with sign-in, saved games able to be resumed, multi-player experience using Action Cable and Ember Cable… right. This was a framework we were dropped into from a great height. Even getting a single-player game up and running was a continual challenge. People sitting near me soon learned that when frustrated I have the language of a sailor who just stubbed their toe on the railing.
Compared to Rails, Ember.JS felt like it fought us at every turn. We were continually presented with situations where we knew exactly what we wanted to do and how but were unable to, usually because Ember prevented us from doing so by convention (not even the most simple of logic in templates, difficulties tracking the values of parameters in routes, et cetera.) Which I can accept, but finding the appropriate and Ember-accepted (meaning: actually friggin’ works) solution to that problem would take hours, if not longer. Meanwhile, the same problem in Rails would have taken us quite literally minutes. It could be frustrating at times.
One last note: see how the player boards aren’t even properly aligned and centered? See how the styling is barely existent? This is what happens when you’re still debugging logic two minutes before demoing to a school full of inquisitive, perceptive minds. You make sacrifices. Triage, I think they call it.