The Minimalist Randomizer
DSet is an app designed to help players of Dominion set up their game based on desired criteria. Through a simple interface, users can search for cards by type, name, or cost to filter down to the set of cards desired for their game.
Tools
Rails, Rspec, jQuery, AJAX, Arel, Bootstrap, Atom, Ubuntu
About Dominion
Dominion is a popular deckbuilding card game by Donald X. Vaccarino. If you are not familiar with the game, the most pertinent thing to this app is how to set up a game. During a game, players spend coins to “buy” new cards from a supply in the center of the board, made up of 10 piles of 10 identical cards called “kingdom cards”. To set up a game, players use the “randomizer deck” - a deck containing one copy of each of the 25 cards in the base game. Ten of these cards are drawn at random to reveal which 10 kingdom cards will be used for that game. In the physical game, what cards are drawn are totally random, but some randomizer apps provide users with filtering criteria to control what type of cards they want. For example, if one wanted to play a game without attack cards, or had a favorite card they wanted to include, those preferences could be used to control which 10 cards will be generated.
How It Works
Users are greeted with 10 face-down Dominion cards. Upon clicking “Generate Card Set” the cards will change into 10 random, unique Dominion cards. Clicking on any of these cards brings you to the “slot” view for that card.
From here, users will first see an input box with a list of cards under “Acceptable Cards for the Slot”. If the user provides no input, then it is assumed that any of the 25 cards is allowed to be selected for this slot. Additional input will narrow this list further.
As the user types, the cards will be arranged by categories that match against the query string. It will use the user’s input to search across multiple categories, such as name, type, and cost. A user can chain up to two comma-separated queries to further narrow down their search.
The current slot will store the user’s queries and upon returning to Home, the next time you click “Generate Card Set”, it will following the filtering criteria for each slot they have edited. Alternately, cards may be directly clicked on in the slot view to guarantee that slot will only generate that card. The same can be done if you click on one of the “Matched Filters” shown just below the input form.
Inspiration
Looking at other randomizer apps, I liked the range of features they offered, but found their interface a bit cumbersome. I wanted to make something that made use of modern search functionality. Specifically, I was inspired by Ubuntu’s Unity search and Atom’s Fuzzy Search.
When you search for something in Ubuntu, it doesn’t just search for files by name - it simultaneously runs several parallel searches across multiple categories. For Atom’s fuzzy search, files within a project could be located just by typing in a couple of letters, without requiring the whole word (such as typing “pc” to get “post_controller.rb”). I wanted to incorporate these elements into a text-based interface to create a simple and clean user interface.
The Process
My first priority was to create a searchable database of cards. After getting a simple name and cost search working, I implemented AJAX functionality to add livesearch responsiveness to the search form. It took some research, but I was ultimately able to convert some reference code I found into what I needed.
I then moved on to “multi-category searches”. A key thing I wanted to include was the ability to search across many types of categories, not just the obvious ones. I looked at the Dominion Strategy Wiki to see the terms pro users used to categorize and describe various types of cards. I used these to create the various categories my search function would search against.
As a user types their query, cards would display below under the categories that match the user’s input. Thus if someone typed ‘v’, they would get a result like this:
The top three would match by name (containing a v), the middle by type (victory), and the bottom by category (village-type cards). I realized, however, that it wasn’t entirely clear which categories were being matched, as that information was hidden from the user. As such, I built a text result system off of the search to display the specific matching terms my search picked up.
My next stage was to implement a fuzzy search. I wanted users to be able to locate what they want with the minimum number of characters. Specifically, I wanted a user to be able to input a series of queries (such as “v 3”) and use that to compose a filter chain to narrow down search results. This step proved to be the most challenging.
For my fuzzy search, I wanted the user to be able to filter results by inputting multiple, incomplete queries, which simple chaining of scopes could not do. I initially tried to find existing fuzzy search gems I could use, but none of them were quite what I wanted. I was stumped for a while until during a discussion with my mentor, I came across a very handy function: ActiveRecord.to_sql
. With this function I realized I could directly chain sql statements off each other, using grep
to remove the first part of the statement for subsequent queries. The result was this function (comments added for clarity):
The function works, though it is limited. I was only able to get it to work for two consecutive queries, though I intend to make it unlimited in the future.
My initial implementation used scopes with “LIKE” queries to get matches with partial queries. This worked well up until I deployed to Heroku. As it turns out, my method was not compatible with PostGres. To get around this, I installed the Arel gem and refactored by scopes to use Arel tables and its .match
function.
After this hurdle was cleared, the rest was a matter of creating views for the slots and home page.