We moved into our new Folsom, California headquarters last December. While the larger facilities gave our team much-needed breathing room, it also (much more importantly) gave us space for pool, ping pong, foosball, and shuffleboard tables. Naturally, we immediately organized a tournament that we dubbed the “Golden Gear Games.” Over the course of four straight weeks, more than 30 people battled it out in contests of skill and endurance.
Because office tournaments are serious business, we needed a way to officially track our winners as they progressed to each game’s final match.
Enter Ethan Thomason. Ethan joined Inductive Automation in January as a Design Services Engineer and he hit the ground running with his first challenge: to build the ultimate office-tournament tracker in Ignition. As you may know, we build many of our internal systems in Ignition and the tournament tracker would be no exception.
Enjoy Ethan’s detailed write-up of his process below.
After joining the Inductive Automation team in January, I had the opportunity to cut my teeth creating a tournament tracking application for the very first Golden Gear Games.
The goal of the application was to allow administrators to create tournaments in various game types. For the Golden Gear Games, this included pool, ping pong, foosball, and shuffleboard, but the system needed to be versatile enough to handle any imaginable game type with the same look and feel. A requirement also specified that up to 32 contestants could be selected for the tournament and then shuffled randomly into matches. A TV would display all the brackets in the tournament, and a tablet was supplied for the scorekeeping of each match.
Where the Action Begins: The Tournament Creation Screen
Managing Player Rosters
One requirement was that the system needed to accept a roster containing anywhere between 1 and 32 contestants, then automatically shuffle the contestants and add byes as necessary. To accomplish this, I sent the dataset from the “Official Roster” list and passed it into a shared script for processing. The script takes the total number of contestants in the dataset and passes it into the following function to get the next power of 2:
def next_power_of_2(x): return 2 if x <2 else int(2**math.ceil(math.log(x,2)))
This is the total number of contestants needed to make enough brackets for the first round. Subtract the number of contestants in the roster and you have the total number of byes. A for loop then adds these byes to the dataset. Next, I added a column named “random” to the dataset and used randint from the math library to assign a random number for each contestant/bye. Finally, I used system.dataset.sort to sort the dataset by this new random column and our roster of first-round matches was nearly complete!
Who Do I Play Next? Advancing Winners to the Next Round
One of the biggest design challenges I encountered was how to advance winners of each bracket to the next round and correctly keep track of bracket positions. I approached this challenge by creating the final round first in the matchInfo table and retrieving the matchID.
Next, the script creates the next set of rounds above and stores them to matchInfo with nextMatchID set to the matchID of the final round. This process iterated for the number of total rounds in the tournament, each time inserting the nextMatchID previously created. Ultimately the first round is inserted last with the contestant user IDs from the roster with byes we created first. When a contestant wins, the next match is updated with the contestant’s userID.
Here is a simple example using 3 contestants: The next power of 2 is 4, thus we need 1 bye (userID=0 for byes). Below, you see matchID 602 is created first and roundNum is set to 2. Next you see the first-round matches: 603 and 604, both of which have nextMatchID set to 602. Also note that nextMatchPosition is used to determine whether the winner of the match should be set as contestant 1 or 2 for the next round.
Building the Tournament Bracket Interface
After verifying that tournament data and matches were being recorded properly, the next step was to display the brackets. I initially considered the Template Repeater component but settled on the Template Canvas so that I could have complete control of x and y positions and the ability to use different templates in the same canvas component. To simplify things further, I made one project for both the TV and the tablets.
When the first round is 8 brackets or less, we display brackets from the left side to right:
When more than 8 brackets exist in the first round, we bring them in from left and right:
After a match is complete, the winner’s name is selected and a confirmation popup appears to set the score. Since the Golden Gear Games were serious business, we required signatures from both contestants and stored these as blobs in the database. The page then immediately updates the brackets with the new information.
Challenges Crushed by Ignition
The two largest challenges that I faced with this project were database design and displaying bracket information. My strategy of creating the final rounds in the database first and storing the matchID keys in the next roundup seemed to pay off during testing. By using system.db.runPrepUpdate with getKey set to 1 during the matchInfo insert, I was able to get the matchID for the subsequent round without any additional queries. I was initially intimidated by the Template Canvas but ultimately very happy with the results. I was able to optimize the script that retrieves match information from the database and builds the dataset for the Template Canvas to under 50 lines of code, including the logic behind displaying the brackets from both sides or the left side only.
I am confident that Ignition was the best tool for this project because of how rapidly I was able to go from an idea to a finished product and how well it handles database interaction as well as visualization.
Share Your Creations
Have you used Ignition to build something outside of your normal workflow? Let us know what you did with it. We’d love to hear from you.