Building Fruit Fight

Stuart Fooks
6 min readFeb 20, 2019

--

I’m now right around halfway through a coding boot camp in Washington, DC, General Assembly. Last week, our cohort spent the week building the second major project out of four major projects that we will build during our time here. We were tasked with building a full stack web application using the MEHN stack (MongoDB/Mongoose, Express, Handlebars, and Node.js). I chose to build an app that I’m calling Fruit Fight. In this post I’ll attempt to give insight into my planning and building processes for Fruit Fight.

The Idea

The idea for Fruit Fight came from my friend and housemate, Gus Warren (github.com/GWarrenn). Gus works as a data scientist, and he had initially sent around a Google Form to some of our friends to try to answer: what is truly the best fruit. However, he expressed that the way to best collect the data around fruit preferences would actually be more similar to what Fruit Fight ended up being. When I later saw the assignment for our second project at GA, I thought Gus’ idea fit nicely with the scope and the required functionality. The app works like this: it displays 2 fruits to the user (like apple and orange), allows them to select their favorite of those two fruits, saves that user response, and then repeats. The app also has a leaderboard that displays the current ranking of all fruits based on all of the users’ selections. You can check out the deployed version of the app here:

The Architecture and Planning

Fruit Fight uses the rMVC architecture and was built using the MEHN stack, as I’ve mentioned above. I used Node.js/Express, Mongoose, and Handlebars to build out routes, models, views, and controllers for each of my resources. To begin, I considered the MVP, or Minimum Viable Product, of the project, as well as some stretch goals that I wanted to attempt if I had enough time. For the MVP, my necessary resources were Fruits and Comments. We were required have full CRUD (Create, Read, Update, and Delete) functionality for our apps, which I broke down as follows:

  • Create: Allow the user to create a Comment about the site
  • Read: Display 2 Fruits for user selection, display a leaderboard of Fruits, and display the user’s Comment after they input it
  • Update: Each user selection between 2 Fruits updates the “score” of both Fruits
  • Delete: Allow the user to delete their Comment

Beyond that MVP, I wanted to implement a Matchups resource to store the winning Fruit, losing Fruit, and the time of each user selection. The idea here was that the history of each Fruit’s ranking over time would not be lost. Additionally, my biggest stretch goal was to add some form of user authentication to allow for a “personal leaderboard” of a user’s favorite fruits in addition to the global leaderboard.

Some Code

Instead of writing an overview on all aspects of my code, I’ll focus on going more in-depth on the one piece of code that challenged me the most. Having said that, I did learn a lot throughout this project about full stack applications and about RESTful Routing, as well as interacting with a database — so there was a ton of value throughout the project. The one piece of code that I found most challenging would be my method for updating the scores of both fruits that a user picked between. That method can be viewed below:

As dictated by the first line of code, the above image shows the path that the method takes if there is not currently a user logged in. I successfully added user authentication after I wrote this portion of the method, and I then inserted the if statement for the “personal leaderboard” portion of the method.

The method first makes a new Matchup document — like a new row in a SQL database — with the Fruit that the the user selects, the other (losing) Fruit, and an automatically generated timestamp. So far — easy enough. Where the method became difficult for me was when I started dealing with asynchronous Mongoose methods, and understanding their parameters, their return values, and their scope. Much of the method is nested further and further into .then Promises, which makes it difficult to understand where exactly you are in the process. A lot of debugging took place just to understand the flow of the Mongoose methods.

After creating the Matchup document, the method searches the Fruit documents for the Fruit with an id equal to id1, which will always be the winning Fruit. I then stored that Fruit’s score in a variable, score1. The process then repeats with the second Fruit, the losing Fruit.

Once I had the scores of both Fruits going into the Matchup, I called my function, elo, on both score1 and score2. As the name implies, elo uses the Elo Rating System to generate a new score for each fruit. The Elo Rating System assigns values to victories and losses based on the scores of both opponents coming into the matchup. For example, consider a game where two players, player1 and player2, enter a competition with scores of 1000 and 800 respectively. The value of player1 winning would be less than the value of player2 winning, because player1 is expected to win. That math is shown below — the K value, 32, is a decision I made. K determines how much a single game can affect the scores.

After calculating new scores for both Fruits with the elo function, I again searched for those fruits in the database, and updated their scores. And finally the method sends a response to the browser to redirect to /fruit, which starts the process over again. When the user clicks “View Leaderboard” it populates a leaderboard of Fruits in descending order by their scores. Method complete!

Some Thoughts and Lessons

This project was a great learning experience for me both in terms of my technical skills and soft skills. Our instructor said something to our cohort after we completed the projects that really struck me. He said that when he first started programming, he would come to a problem and just keep hammering down the same path, solving one problem and hitting another. He said he had to teach himself to recognize when he was doing that, and perhaps take a step back and consider that there might be a different solution that didn’t require so much hammering through problems.

After getting my MVP working, I decided to implement user authentication and a personal user leaderboard. It was difficult, and at the point of writing this post, it’s about 90% working. When you look at the “user is logged in” path of the same method discussed above, it becomes pretty clear that I was hastily writing code to try to solve problem after problem.

There are two loops to check if the user has already voted on either Fruit, two newly generated Fruit scores, and four if statements (two off screen) to handle different paths based on if the user has already voted on Fruit 1, on Fruit 2, on both Fruits, or on neither Fruit.

Having let things digest, I plan to return to this code to not only get things working 100% of the time, but also to seek out a better solution. Perhaps there is a built-in Mongoose method that handles some of things I wrote Vanilla JS for. I’m definitely now more aware of the “hammering” problem, and feel I can recognize when I am starting to fall victim to it.

All in all, another fantastic learning experience at GA. You can view my full repository here: github.com/stufooks/fruit-fight. I’d be happy to answer any questions, and welcome feedback on all aspects of the project.

--

--