Building Fantasy Sumo: How It Started and How It’s Going
Eight months ago I completed a 15 week, Full Stack Software Engineering Bootcamp. It was challenging. It was edifying. It was fun!
For my final project which I would be doing by myself I decided to create the world’s first Fantasy Sumo Wrestling app. Couldn’t be too hard, right?
Wrong. Turns out very hard. Especially if all you’ve done are some color by number assignments and little journal single page applications (no offense intended, loved the bootcamp curriculum, my cohort-mates and all of our projects, definitely wasn’t prepared for this though).
I wanted to take a moment to walk through the inspiration, the issues and the technology in this project and hopefully hash out the rest of the way.
My friends and I have watched Grand Sumo tournaments for almost 5 years now. The sport is easy to follow (first person to touch the ground with anything but the soles of their feet or get pushed out of the ring loses), the format is quick and exciting to watch (videos of the matches are condensed to roughly 30 minutes) and the competitors are full of character and charm. Here’s the final day’s highlights of the most recent tournament in March 2021:
After a couple years of watching we had developed our favorite wrestlers (Takayasu). I had an idea to make it more interesting. What if each of us picked teams of wrestlers and earned points for their victories or special prizes that they win? My close friend and spreadsheet enthusiast created a Google Doc that contained pertinent info about each wrestler and also tabulated the points for each team in our fantasy sumo league. Now each of us had a handful of wrestlers that we could cheer on. Friends that had otherwise never watched the sport could easily get involved and soon were curious (what’s the diaper called? what’s the referee saying? how many pounds is 150 kg?).
With my bootcamp coming to its final 3 week module, I’d decided that turning Fantasy Sumo into a full stack app was the perfect project for me. Data about the wrestlers and the matches is available on a few sites, I was now comfortable using React as well as Ruby on Rails as an API and most importantly I was very motivated to make this a reality. This is something that I want for not only my friends and I to use but anyone else that wants to participate. Unfortunately, I would come to find out that it wouldn’t be a small thing to accomplish.
I started by initializing a RESTful JSON API using Ruby on Rails. Sure this was all I was familiar with at this point but also Rails afforded me the opportunity to get my backend up quickly and securely.
Figure 1 is a map of the schema I created before I coded anything. This was also used to prove to my instructors that my idea was feasible and to communicate how I was going to model the data. There has been some slight adjustment but the general structure is still the same. Users and Leagues are connected in a many-to-many relationship by way of Teams. Teams and Wrestlers are also connected through a many-to-many relationship in that Teams have many Wrestlers and Wrestlers belong to many different Teams. Matches are used to determine the points of Users in respective Leagues and belong to two Wrestlers at a time.
My API serves my frontend JSON objects full of data about users, teams, leagues, and wrestlers once user is authenticated by signing up or logging in. Rails controllers made it fairly simple to rig up authentication and authorization in my app and once a user is granted access my frontend sends a GET request for any other pertinent data it needs.
With my backend set up I felt pretty good about my progress. That is until I came across my first obstacle.
First Challenge: Web Scraping with Nokogiri
When I was first conceptualizing this project I came across a github repo for an API with several endpoints for Sumo Wrestling data including wrestler information as well as tournament and match results. In the readme the author of this repo wrote that they hoped someday someone would use this for exactly what I wanted to do: a Fantasy Sumo Wrestling app. I closed the tab thinking that I had found where my data would come from. Upon diving back in I found that if I’d ventured a little further down the page I would have seen that it also said that this API was not ready yet. My schema was laid out and ready to go but I had no data to fill it with! Or at least no data that was neatly curated for me.
Fortunately there is a wonderful site that punctually updates sumo information. So my first week was spent learning about how to use Nokogiri. Nokogiri is an open source library that makes it easier to parse HTML and XML within Ruby. It is a pretty inflexible solution in that whenever an element on the aforementioned page is changed or named something differently than I’ve referenced it my scrape_results_page function will probably break. But it is my solution for the time being until I figure out how to gracefully maintain a sumo data API.
My function takes an argument of a URL and the tournament that the matches are happening in. Then in line 12 it identifies the table of match data on the page and gets rid of the header row in line 16.
The rest of the function parses each element in each row of the table, finds or creates a Wrestler record from the name on the page, determines the result of the match and the amount of points resulting and finally adds those points to every team in which the winning wrestler is a member. Phew.
This is something that I would love to optimize. It is pretty ugly; there’s gotta be a more effective solution for this. But with only 3 weeks to complete the project and this pretty substantial roadblock bypassed I felt like I had to just push on and save this for another day (once I get the app up and running).
I followed along with this thorough blog on web scraping in Ruby using Nokogiri. Very easy to follow, I highly recommend it.
Frontend State Management: Discovering MobX
My first week was devoted to scaffolding my backend and figuring out how to scrape HTML and my friend’s spreadsheets into my database (great blog on importing spreadsheets fwiw). The bootcamp urged me to use a technology I hadn’t used before (aside from Nokogiri) so the next week I learned about Redux and implemented it into my frontend. Probably has something to do with the short amount of time I learned it in and the pressure of finishing this project before the end of my bootcamp but it really wasn’t working for me. All the reducers and root reducers and actions, so many different parts for a beginner like me who had just been making fetches straight from his top level component. A sweet friend told me about MobX, showed me some of his code and I was very down for it.
MobX is a state management system that’s central aim is to “make sure that everything that can be derived from the application state, will be derived. Automatically.” For a library called “React” it’s unfortunate that the rendered components and state are antithetical to the namesake. When utilizing MobX you provide a central store to hold your application’s state and wrap any components that you want to be sensitive to that state with an observer function. Here’s an example of how I’m using it.
This is (a portion of) the store for my app.
At the top I define my state for my currently logged in user. MobX gives us a function called ‘makeAutoObservable’ that allows the properties of our state to be trackable, observable. The properties themselves are denoted as “observable”, their setters are “actions” and their getters “computed”.
The remainder of my Store class is actions and computeds that I can call from my components to verify or change my application’s state. In a lot of my actions I have a property called “retrievingData”. This is helpful for when data might not be ready yet and you want to show to the user that data is being loaded or updated.
Here at the bottom I export the store so that I can wrap my entire app at the top level with the ‘StoreProvider’ and my individual components can access the store with the ‘useStore’ function.
This is an example of an observed component in an app using MobX for state management. While it does limit using other higher order components, it makes your app more reactive and insures that your components will respond and respect the most current version of your app’s state.
The MobX documentation is very well written. Here’s a quick tutorial if you’d like more info on what MobX can do. I know that I’m probably just skimming the surface of its capabilities.
Latest Challenge: Implementing a Live Draft
At the end of my bootcamp, I did not have a fully functioning version of the app I aspired to create. My eyes were bigger than my stomach. But what I did have was the foundation for something that I truly wanted to turn into a reality. I had something with which I could keep learning, building and maintaining as I began my job search.
And that brings me to what I’ve been currently working on to implement into my app: a live draft event where users in a league can start a draft and sequentially add wrestlers to their teams.
The most recent addition to my app has been Action Cable. Very briefly, Action Cable is the native web socket integration for Ruby on Rails. It allows you access to all of your domain models through Active Record or whatever ORM you choose. Here’s an excellent blog with more info on web sockets and how to implement them, specifically in a Rails backend/React frontend environment.
This has been an enormous challenge for me. I have called on all of the skills and knowledge that I acquired in my bootcamp, continue to deepen my understanding and learn new things to make this app even better. For example I know that I need to tighten my grasp on distributed locks to make this draft work. My goal is to have a test version of the app deployed by the next Grand Sumo tournament which starts the second week of May.
If you’re interested in learning more about Sumo, the app or even just little ‘ol me I am attaching where you can contact me. Would love to talk about any of it! Also below is the Fantasy Sumo repo on Github if you’d like to see more of where I’m at.