Pain Point: PKMN

There are 718 Pokémon. I can hardly remember the types of the 6 that I use throughout the course of the game, let alone the other 700+.

Whenever I’m looking for a weekend project the first thing I consider “is there a pain point I have at the moment, and how can I fix it?”. This simple question ensures that I’m building tools that solve problems... that solve my problems at least.

I’ve been, very slowly, playing through the latest Pokémon game on the 3DS. The basic idea is that each monster has a type (fire, water, grass, etc.) and each one is weak/strong against another: rock, paper, scissors style. The issue is that the types are not obvious from the name or graphic of the monster you're fighting. I have consistently gone to the net to quickly search for a Pokémon by name, scroll down some ridiculous Wiki style page to find their type, and then (because my brain doesn’t care) go find out which type is good against it! Nope. Too hard. Pain point.

I spent a few hours this weekend seeing how I could quickly fix this with a web app. Turns out it's about 4 hours. It involved finding Pokémon data in a usable format (was hoping for JSON but I managed to find some open source CSV instead) and then code a page to interact with it in an easy and mobile friendly (oh so important!) way. I built an AngularJS app which uses a JSON database storing information about every single Pokémon and their weaknesses.

SOLUTION: PKMN-TYPÉCAST

When you load up the app it lists a few Pokémon and allows you to search for the monster you’re battling. When you find the correct one you see a list of all the types that are VERY EFFECTIVE, NOT VERY EFFECTIVE or IMMUNE. Now I can, at a glance, work out exactly what move I should be using against my enemy.

Building quickly was important so I stuck with the tools that I’m currently using most. I’ve got a command line node script (./bin/generate-json), which takes JSON versions of CSV data found from another Github project veekun/pokedex, and maps it to my needs. Using many map and filter methods it generates a large JSON file which contains a list of every Pokémon, their type and their strengths, immunities, and weaknesses.

There is some complication when a Pokémon has more than one type, which this script handles by filtering out duplicate and conflicting types.

Once I had the data taken care of AngularJS was a dream to code the user facing app. There are, realistically, only two functions to get this entire app working. First, it loads the JSON database using $resource so that I have an Array of JS objects, each representing a Pokémon.

var Pokemon = $resource('data/db.json', {}, {
    get: { method:'GET', isArray: true }
});
var pokemons = Pokemon.get(function (pkmn) {
    $scope.pokemons = pkmn.slice(0,9);
});

This $resource function makes an HTTP request to the provided URL and maps the returned JSON Array to standard JS objects. Generally $resource is used to run queries against the URL (send POST/GET variables like ?limit=10) for searching and filtering, however I only need a single set of data so I decided load the entire database in one hit. Not the most efficient use of RAM, but there are only 700 records and it runs fine on the devices I own. The pokemons variable is actually a promise, returned from Pokemon.get, so I can’t use it immediately as it doesn’t actually have data — it will only have it once the request is completed. You can pass get() a callback to run with the data as soon as it has arrived, which I’ve used to pre-fill some initial Pokémon to the screen.

The second part of functionality allows the app to watch the search field in the browser and filter the visible Pokémon to match:

$scope.$watch('pokemonSearch', function (newValue, oldValue) {
    if (!newValue || newValue.length < 3) {
        return;
    }
    var pokemon = newValue.toLowerCase();
    $scope.pokemons = pokemons.filter(function (item) {
        return item.identifier.indexOf(pokemon.toLowerCase()) === 0;
    });
});

$watch is a special method that will invoke a provided function every time the $scope variable pokemonSearch is changed. This scope variable ($scope.pokemonSearch) is binded to the form through an attribute added to the HTML: ng-model="pokemonSearch”. This attribute tells AngularJS to map the value of this input field directly to $scope.pokemonSearch. What that means is: if you type into the input field in your browser $scope.pokemonSearch will update to match the user typed string and if you change $scope.pokemonSearch in JS your input value will update to match the JS value. This is what AngularJS calls two-way binding and is a large reason why the framework is so powerful.

The method provided to $watch does the filtering of Pokémon. If the newValue, that is the string which a user typed into the form input, is over a certain size I use filter() against the list of all Pokémons to return only those which start with newValue. This data is set to another two-way binding variable $scope.pokemons so that the list of Pokémon will automatically update in your browser!

This time, instead of a form input, I’ve used ng-repeat to bind the data directly to plain old HTML elements:

<li ng-repeat="pokemon in pokemons">

The ng-repeat directive takes an Array and repeats the element for each record. The current index becomes a local variable as named (pokemon) which you can use to display data for your users.

<h2 class="pokemon-name">{{pokemon.identifier}}</h2>
<h4 class="pokemon-types">
    Type
    <span ng-repeat="type in pokemon.types">
        {{type.identifier}}
    </span>
</h4>

Here you can see how {{pokemon}}, which represents a single object within the $scope.pokemons array, is used to display it’s details. AngularJS will automatically replace {{pokemon.indentifier}} with the value in the pokemon object every time it changes. I’ve used plain HTML to represent the data I want from each Pokemon. Now, any time that $scope.pokemons changes this list of <li> elements will automatically be updated and redrawn with the DOM to show the current data.

In short: the user’s input search field is tied to $scope.pokemonSearch. Anytime that changes $scope.pokemons is updated to only have Pokémon which match the search. Anytime $scope.pokemons is changed the Pokémon <li>s are redrawn for the user. That’s it. We have an app.

I’m hosting the whole thing on a github.io project page, because it’s completely static with the database stored as JSON file.

I’ve managed to solve this, albeit pointless pain point, with a few hours coding. It’s not a bad introduction to AngularJS for anyone who is interested in seeing the power of two-way binding. This same project could have been built with jQuery (or no framework at all) but it would have taken considerably more time, especially when mapping data from a form to JS to search and then back to HTML via template.

Time to get back into Pokémon. I want to move on to Zelda already.