Main Graph
For the graph improvement, I propose a focus on graphing out the actions of the players rather than the consequences. This is not to replace the existing graphs above, but to complement them so that players can better understand how their actions affect the outcomes in the game.The two main decisions available to players in this game are constructing buildings to extract resources and trading commodities on the market for profit. It is also interesting to note here that while watching full replays of games are typically the method used for competitive players to improve, it is much more difficult to observe these two types of actions for this game because players can build buildings anywhere on the map, and purchasing or selling on the market by other players do not generate any visual cues. Hence, data visualization can also play a role here in providing understanding of what happened across all players rather than just through the point of view of a single player.
As currently it does not seem possible to extract data out of the binary replay files, data has to be manually collected through viewing the replay from the point of view for each player. For this blog post I will only be focusing on constructing a number of buildings over time graph for the number of buildings players have built over time. Since the goal is to complement the existing graphs, creating a line grpah similar to the ones already exist was the most obvious action.
With the graph generated, it becomes readily apparent that this graph doesn’t convey enough information as well to be useful. The graph doesn’t show the types of building that has been constructed at each point so this information needs to be included. Different types of buildings produce different resources and knowing this is key to understanding whether a resource will have more supply than demand so a fourth dimension needs to be added to the graph. The tricky thing about this is that there are a total of twenty different buildings in the game so even adding points and styling them is not a viable solution.
Buildings Table
Adding a table to visualize the category of tables is now necessary, but conveying data across time in a table generates too many rows, occupying too much vertical space, and makes it tedious to read through all the information. One solution to this problem is animating the table over time which allows the time component to be removed from the graph and allows the table to be much more easier to read. One of the options available to make interactive charts using R is Shiny and they do have animated sliders which appears to meet what is required here.
The smoother the animation is the better but animation tends to be performance intensive so it is prudent to explore the capabilities of the animated slider. The animated slider at each step returns an updated variable and we can use this variable to update their corresponding visualizations. So the main variables to tweak for the animated slider is the step size between each frame and the time interval of each animation. Just by animating the graphs, we can already determine that when the animation interval drops to 200 and below, the animation breaks very early in the chart and freezes everything else in the application. So for this visualization, I will proceed with an interval of 500 and a step size of 0.5.
Performance testing of animated slider
Following that, minimizing computation during animations is also crucial to allow for smoother animation. The building table requires transformation from the original data showing player actions to a table with each column representing the different buildings. So most of the heavier data transformations have been done offline and generated as separate csv files to minimize impact on performance. The first table below represents the original data and the second table represents the transformation. What might be also evident that having a 22 column wide table with the column names being building names would occupy too much horizontal space.
142 |
15:45 |
Ms. Launch |
Takeover |
0 |
Ms. Launch |
|
143 |
15:45 |
Ms. Launch |
Takeover |
63 |
Totakeke |
Totakeke has taken over Ms. Launch |
144 |
15:55 |
Electrolysis Reactor |
Build |
64 |
Totakeke |
Totakeke has built a Electrolysis Reactor |
145 |
15:57 |
Electrolysis Reactor |
Build |
65 |
Totakeke |
Totakeke has built a Electrolysis Reactor |
146 |
17:22 |
Ms. Production |
Takeover |
0 |
Ms. Production |
|
147 |
17:22 |
Ms. Production |
Takeover |
93 |
Totakeke |
Totakeke has taken over Ms. Production |
145 |
15:45 |
Ms. Launch |
0 |
0 |
0 |
0 |
0 |
0 |
146 |
15:45 |
Totakeke |
0 |
4 |
5 |
6 |
5 |
6 |
147 |
15:55 |
Totakeke |
0 |
4 |
5 |
7 |
5 |
6 |
148 |
15:57 |
Totakeke |
0 |
4 |
5 |
8 |
5 |
6 |
149 |
17:22 |
Ms. Production |
0 |
0 |
0 |
0 |
0 |
0 |
150 |
17:22 |
Totakeke |
0 |
7 |
7 |
12 |
7 |
10 |
In order to prevent the table from going too wide, there are two hacks being employed here. First, the buildings are replaced by icons of the buildings taken from the wikipedia site. This is actually more tricky than it seems because data frames don’t store image data, so the image paths need to be transformed into html output (i.e.
“) for Shiny to render. The rendered table needs to be specified so that it outputs raw html and this can be accomplsihed by using the function (sanitize.text.function = function(x) x). The second hack here is to split the columns of the table into two and stack them vertically. This is difficult to accomplish using data frames that have strict data types so the data is transformed into matrices using the rbind function in R.
To top this off, conditional formatting of the values was also used to highlight which values increased or decreased from the previous interval. Again, there are no built-in functions within Shiny for this so another hack similar to the one used for the images was employed. First, the data was transformed again to indicate which values have increased or decreased at each time interval so the data transformation is tied to the time slider interval value. A combination of HTML div tags and CSS formatting is then used to format the text color and text property at each interval.
Message Log
So the graph is almost done but it is missing the granular information of when and what exactly was built. The table doesn’t accomplish this as it only updates every interval so every action within an interval cannot be separately identified. One method to address this is to layer points on the graph and use tooltips to display this information to the user. Unfortunately among the available interactive chart packages with tooltips enabled, rCharts proved to be too new and lacks documentation for simple customizations while ggvis bugs out when layer_line is used with tooltips.
Since the graph issue is difficult to fix and decreasing the step size of the animation was not an option either, I decided to print out the actual actions as text messages while using the time slider value to filter. The first idea was to simply extract the last few messages given the current time indicated by the slider. This solution had issues as not only did it present limited amount of messages, if the amount of messages within the step or time interval were more than the amount of messages displayed, then some of the earlier messages would be lost. So the ideal would be to display all the messages while not taking up too much space… which is what a chat box does using a scroll bar.
ShinyChat, hipper than IRC
Thankfully someone did implement a chat room interface using Shiny so I basically used the same implementation for the message log. The first thing required to make it scrollable is to simply use CSS to specify the size of the region and use the overflow-y property. The second thing that was required was javascript because the chat box by default doesn’t scroll to bottom every time it is updated. A javascript function was necessary to force the chat box to scroll to bottom at a specific time interval.
CSS Code
#chat {
padding: .5em;
border: 1px solid #777;
height: 150px;
width: 340px;
overflow-y: scroll;
font-size: 12px;
}
Javscript Code
var oldContent = null;
window.setInterval(function () {
var elem = document.getElementById('chat');
if (oldContent != elem.innerHTML) {
scrollToBottom();
}
oldContent = elem.innerHTML;
}, 50);
// Scroll to the bottom of the chat window.
function scrollToBottom() {
var elem = document.getElementById('chat');
elem.scrollTop = elem.scrollHeight;
}