best websites of the world

NextUp: Under the Hood

Saturday, September 20, 2014

For the past two months I've been working on a project that aims to assist people interested in competition but frustrated with people who lack attention. As technology becomes more social, attention becomes more valuable. More specifically, when organizing a competitive setting like a tournament, trying to find the people who are battle each other can be difficult when multiple people are involved. When you begin to allow multiple matches for one tournament and then further multiple this over several tournaments, handling it all can seem daunting. This was the reason I developed NextUp. You can read about what it does at the website but here I'll explain how it does what it does.

participants[misc]

The API at Challonge allows a system to get and post data to the site and update tournaments, participants and matches and the PHP wrapper developed by Tony Drake helped me greatly in working with the data. Though even in working with the data, I actually needed to know more about how the data was handled at Challonge. I was enlightened much later in the project than I should have been.

If you browse through the API you'll notice when creating a participant that there's a field called "misc" which stands for miscellaneous. This allows a system to add data that Challonge doesn't need but the system might to associate participants with another system. When I first began to design the system, I knew I wanted to use this field to store the player phone numbers. That way I wouldn't need to store them at NextUp and could be called later when a match was assigned. This is exactly what the field is currently used for at NextUp, but it has another purpose.

The person who originally gave me the idea for NextUp also told me of another functionality that Challonge didn't have but other bracketing software did which allowed you to create a player and then add that player across multiple tournaments. The reason this is desired is two-fold: first it makes it easier to enter player information once and then apply over multiple tournaments and second (and more important) it enhances the idea that a player should not be able to play in two different matches at the same time. The second idea was something that I focused on finding a solution for using Challonge. Initially as I was building the code with the Challonge API, I was under the assuming that when you create a participant you needed to provide the participant id field. If this was the case, I could simply create a new participant id (using some random string) and then post it to Challonge. Unfortunately, this is not the case. When posting a participant to Challonge and attempting to add that data over multiple tournaments, the id number is not posted or changed. It is defined by Challonge. If you've worked with a database before you can understand what's happening. Every time a new player is added, that player is a new row in the Challonge database and assigned a new unique primary key and linked to a single tournament. You cannot have duplicate unique primary keys (duplicate unique is an oxymoron after all). So, one participant over multiple tournaments in this fashion does not work. Challonge does have a remedy using the player's e-mail address but I haven't tested that functionality. To use e-mail in my view would be another item to ask players at registration and has a higher potential for validity being lost in communication or translation. I was focused on text messaging as it's much faster and more accessible.

To allow the possibility of adding a player across events, I recognized that a phone number is unique in its own right, no one else should have that phone number so it could be used as identification. That easily solved the problem when a phone number is provided but I also needed to be prepared if the phone number isn't provided. The solution was to create a random string of non-numeric characters for the misc field. The reason that is it non-numeric is so that the system can check if the misc field is a number or not. If it is a number, it can send a text message to that number when the player is called for a match. If not, the system doesn't try to send a text message.

This method does have a large drawback in attempting to find a player between Challonge and NextUp using the misc field data. To do this, the system goes through every tournament in a organizer's account and then every player in every tournament and begins to match the misc fields. This is a lot of processing because Challonge doesn't currently have a way to simply get a list of all participants under an organizer's account; it's strictly on a by tournament basis. To speed up the processing, by default the system will only look in tournaments that haven't started yet (pending) but I've also added the option to include tournaments that are in progress (underway).

autoMatch and matchLoop

For those connected to me on social media while I was working on this project you may remember me complaining about the autoMatch function. This function is the core of the system as it is the piece that assigns new matches to stations in the NextUp database (and in turn the monitor). The function gathers all the matches in a particular tournament and looks at the match state. If the match state is open, it means the match is not complete but has players ready to play that match or are currently playing in that match. Challonge has no way of knowing if the players in a match are currently playing or not. My system compares the open matches to matches found in the database. If an open match at Challonge is not in the database, that match can be called but only if both players aren't currently playing in a match and there is an open station for that match. This was originally all the the conditional elements that autoMatch was checking.

At first, everything worked just fine. But during one of the first larger scale tests (using real phone numbers) I realized if autoMatch fired without finishing, it would assign the same match to different open stations. In one test it assigned the same match to three stations and sent 3 text messages to the players of that match! Obviously, this needed a fix but I was stuck for days on it. I even posted on some forums looking for an answer which pointed me in the right direction but ultimately ended up combining many different techniques.

The first thing I did was create a new function called matchLoop. Originally I wanted this function to continuously loop checking for matches but I realized there would be a great deal of processing and memory loss doing this so it has been streamlined (which I'll explain in a bit). Then I moved some of the conditionals in autoMatch into matchLoop; to check if players are available and if a station is open. Both of these checks are done in the NextUp database while the match comparison before this needs a call to the Challonge API so I left that in autoMatch. Now autoMatch creates an array of all the open matches at Challonge that are not in the NextUp database and then calls the matchLoop function passing in that array as an argument. To turn on the loop, two things are checked. First we need to check if the loop is running, if it is it cannot run until it's done. If it's not running, then the function checks for data in the array that was passed into the function. If both things are good, it starts the rest of the conditionals for the matches in the array. The match must also be removed from the array so it does not keep running for the same match. I realized later unsetting the key in the array should be done first otherwise the loop will actually run twice for the same key. I also added the ability to pause matchLoop by checking the database to see if matchLoop is allowed to run. If it's not allowed, then the entire array is emptied which will not allow the loop to run.

You might be wondering how the conditionals work. I've explained the autoMatch conditional that simply checks between open matches and matches at NextUp. The second conditional which checks if the players are currently in matches is more complicated which I'll explain in a moment. The final check just looks for a station in the NextUp database that doesn't have a match assigned to it and is marked as open. Checking for players is related to another thing that is dealt with in the database; a phonebook. When a match is assigned the players of that match are added to a phonebook which stores the Challonge player id, player name and phone number (or participant misc field). The conditional checks if the misc field of the players that are attempting to be assigned to a station are already located within the phonebook. If they are, the match cannot be called (assigned to a station). If the misc data is not in the phonebook, then the match can be assigned.

Additionally to help with the halting the possibility of the function running twice, when a station is opened or closed client-side a trigger is fired defining that the function is running on the server. If the server is still processing the function, client-side will not allow another request to be sent. Even with this client-side check, the autoMatch function can trigger at the end of a match. If multiple matches end at the same time, it still has the possibility of firing multiple times. However, this is where matchLoop should handle additional requests.

Score verification

Score verification is fairly straightforward. Either the player can send a text message result into the function or the organizer can send a result in the monitor. First, the system checks 0 >= result >= 100. If that is not true, then it checks the first character of the result as a string and if that is either "W" or "L" (converting to uppercase first). If that is false, then it is not a valid score.

While all of that seems easy, posting the score to Challonge is not. Challonge requires that the score be formatted as a number and using a dash between those scores (1-2). Additionally, Challonge will also require the participant id number of the winner. In code, this is how we get the right data and formatting:

$scores = array($score1, $score2); //put the scores in an array
$highScore = max($score1, $score2); //compare scores and define the highest one
$makeDash = implode("-", $scores); //create the Challonge score format
$whoWon = array_search($highScore, $scores) == 0 ? $id1 : $id2; //if the highest score is at the first position in scores array then return the first id number, if not use the second id number

At this point I can use $whoWon to determine the winner's id number and the $makeDash for the formatting to post scores to Challonge. If there's anything else you may be wondering about in terms of the code, let me know and I'll explain it here.

No comments: