Game Logic -React/NodeJS/PostgreSQL

-Reading…a fun game for kids-

Brian Kubes
9 min readFeb 4, 2021

Video games have been the arch nemesis of parents since I was a kid in the 80s. Not being one of the lucky ones to own a Nintendo, my friends would invite me over to play Super Mario Brothers; that’s if I could convince my mother that we would only play for a maximum of one hour. To this day parents are still struggling with this paradigm.

Nintendo is a thing of the past but video games most definitely are here to stay. I can’t say for sure if there’s a direct correlation between gaming and reading for fun, but according to Common Sense Media, the amount of elementary kids who hardly ever read for fun has almost tripled in the last 30 years, which is a statistic that should not be overlooked. How can we capitalize on this scenario where kids are completely immersed in gaming and turn it into something worthwhile, maybe even something that the parents can get behind?

This is exactly what Graig Peterson and Darwin Johnson set out to do with their application called Story Squad. The game starts with the user (child) selecting an avatar, choosing a story to read, then being prompted to read the first 20 pages.

Then the user is encouraged to accomplish two tasks: (1) draw an illustration of anything that captivated them from the story, (2) write their very own side quest (sub plot) based on the characters they just learned about. The user will then upload a picture of both tasks and wait to be paired up with a teammate of similar skill level. As a team, they will be prompted to look at each others’ work to determine the lineup of their best submissions to give them the best chance against their opponent. The competition gets started via team 1s submissions vs team 2s submissions called faceoffs. These four faceoffs are voted on by outside users to determine who wins the entire matchup for the week. The game is reset weekly with a new set of pages to read and a brand new team/squad.

Here is the detailed version of gameplay, to give more understanding behind the codebase. The pairing of the teams is handled by a data science algorithm utilizing Google Cloud Vision API to pair up similar skill levels based on five criteria: story length, average word length, amount of quotes used for dialogue, unique word count and the amount of adjectives used. Each team of two users is then put into a squad consisting of two teams (team 1 vs team 2).

Within each squad, team 1 will wager 200 points amongst their four submissions signifying which illustrations/side quests have the best chance during competition time. Team 2 will do the same. This point share (wager) determines the order in which the faceoff will happen i.e. [(team 1, illustration A, 70 points) vs (team 2, illustration A, 65 points)], [(team 1, illustration B, 30 points) vs (team 2, illustration B, 45points)], [(team 1, story A, 65 points) vs (team 2, story A, 70 points)], [(team 1, story B, 35 points) vs (team 2, story B, 30 points)].

Each faceoff will be voted on three times by outside users (from different squads), in order to determine which team gets all of the points wagered on that particular faceoff. In order for a team to win the entire squad matchup, they must receive 201 points or more from the four faceoffs. The users who are part of the winning team for the matchup will go on the leaderboard which will also showcase their individual point standing. Regardless if the user is on the winning team, they will still retain their winning points from the faceoffs which can be used to unlock other fun features.

I came into this project partially finished, with five web and three data science engineers. One of our main goals was to create a reset to allow the game to be played week after week. With this limited information we didn’t really know where to start. The only approach we could think of was to look through the code and see if anything looked out of place and then try to play the game to see how far we get. After methodically making user changes on the app then comparing it with the database, I realized that one of the faceoffs wasn’t receiving any votes. During gameplay, for example squad ‘A’ needs to place a total of 12 votes on squad ‘B’, so that each faceoff receives three votes. When I logged in as 4 different users and placed 3 votes each (total of 12), I noticed that some of the faceoffs were receiving too many votes because one faceoff was not receiving any. Below is the broken code with my annotations.

Now that we had found the flaw in the code we needed to come up with some solutions.

(1) Declare that squad ‘A’ would vote on squad ‘B’, squad ‘B’ would vote on squad ‘C’, …until the last squad which would in turn vote on squad ‘A’. Within each squad, determine a voting pattern for each of the four users so that each faceoff gets exactly three votes and no user votes twice on the same faceoff i.e. faceoff indices [(0, 1, 2), (1, 2, 3), (2, 3, 0), (3, 0, 1)].

(2) Declare the same circular voting assignment per squad as option #1. Instead of determining the voting pattern for each user, we would check the length of votes cast per faceoffID, then serve up the faceoff with the least amount of votes, but only if the user hadn’t voted on it already.

(3) Allow any user to vote on any faceoff as long as the user had not voted already on that faceoff and that the user was not a part of that particular squad. This gets a little tricky because the voting assignment needs to follow strict rules in order to not end up with the last squad voting on its own faceoffs. For example, if you have four squads and everyone has voted except for the very last squad, you must have available 12 voting slots on faceoffs that are only within squads 1, 2, and 3.

It was obvious that the first option would be the easiest to implement but the fact that the voting sequence was predetermined left us all a bit unsatisfied. To add another layer of complication, during our stakeholder meeting there was talk about potentially ‘saving’ votes if the third vote for that faceoff would be insignificant due to the fact that two votes had already determined the winner. The reason for ‘saving’ votes comes down to the current structure of the game which requires a multiple of four users, with a minimum of eight so that each squad will have another squad to vote on, therefore we may need to supply bots if we are short on users. The ‘saved’ votes from users could potentially be used as the bot’s votes. With this new information for a long-term plan, we decided to work on both option #2 (which I took over) and option #3.

My first goal was to find out how users were currently being directed to vote on faceoffs because I only had seed data for squad ‘A’ and squad ‘B’; consequently they were forced to vote on each other. After adding two more squads to the seed data (16 users), I realized that they were always paired up ascendingly in groups of eight (squad ‘A’ votes on squad ‘B’ and vice versa, squad ‘C’ votes on squad ‘D’ and vice versa). The only problem with this system is it requires a multiple of eight users, making it much more challenging to fill the quota of users with bots.

To change the squad voting assignment to be circular I needed to know the number of squads that had been created for the current week. Below you can see the database query and endpoint created in the back end.

Then I could use this information to accurately set the voting assignment in the front end, shown below.

The next step was to find a way for four users from squad ‘A’ to vote 3 times each, making sure that the same user didn’t vote twice on the same faceoff which belonged to squad ‘B’. I made a list of the data that I needed in order to make this function work:

  • Based on the user’s squadID, what squadID should they be voting on
  • Does the user still have votes available
  • Which faceoff has the smallest number of votes
  • Which faceoffs has the user already voted on

Everything seemed straightforward except I didn’t have any data that kept track of the total number of votes that each faceoff had received. In a database table called Faceoffs, I created a new column “TotalVotes”, that would increment whenever a vote was cast. One mistake I made after creating this db migration; I went back to alter the column with “defaultTo(0)” and forgot to run <knex migrate:rollback>. This minor mistake made it so the column defaulted to “null” instead of 0 which made it impossible to increment. After getting the new column to increment correctly it was just a matter of putting the user’s previous votes into an object for quick lookup, running a for loop to find the smallest vote count, and implementing an if statement to save the index that corresponds to the faceoff that was to be voted on.

Here is the code in the front end, utilizing the above data points:

After successfully fixing the voting issue the rest of the app can be tested to see what other functions need attention. As it stands now, here are the shipped features:

  • Parent can create an account
  • Parent can add children to account
  • User can sign in with a password and PIN
  • User is prompted to read the story
  • User can submit an illustration and side quest
  • User is placed on a team and in a squad
  • User can place votes on another squad’s faceoffs

The next big challenge to tackle on this project is to remove the seed data and get the data science API to interact with the back end database. The DS API will handle organizing the users into teams and squads based on a computer generated skill level…which the seed data allows us to completely bypass at the moment. I foresee this being quite challenging due to the project being cross-functional. The other focus will be on flushing out the app making sure points get calculated and win/loss records are accurately displayed on the leaderboard. And then of course making sure the app can be reset for the next week’s game.

My big takeaway from working on this project is teamwork. Looking at the code for the first time was extremely daunting, however knowing that there’s a team of devs with you supersedes that feeling. Even though there were many hours of time alone looking at the code and brainstorming ideas, the few hours spent talking through solutions were absolutely invaluable and monumental in the progress I made as a developer. I am looking forward to the next adventure.

--

--