Graph Critique

This graph critique will be focused more on the context of data visualization and the ability of the visualization to provide insight to its readers. The following two graphs depict stock prices and resource prices over time and are relatively simple to interpret.

Screenshot of a stock prices over time stats page

Screenshot of a stock prices over time stats page

Screenshot of a resource prices over time stats page

Screenshot of a resource prices over time stats page

These two graphs originate from a strategy game called Offworld Trading Company and are presented to players at the end of a game. Offworld Trading Company is a game about extracting resources and trading commodities on a shared market among players with the goal of buying out every other company. So in terms of the general concept of this game, this has an analog to real world stock markets and commodities trading and shouldn’t be difficult to relate to.

There is also an interesting link between data visualization and games because at its core, all games are about taking in information that is presented to you and using that information to perform the best actions while operating under the rules of the game. In information rich games such as strategy games, balancing the amount of information to show players is important and an important consideration during game design. Displaying too much information would be overwhelming to new players while abstracting too much information would cause players to be unable to tell how their actions affect the consequences thus making gameplay confusing. So most game developers are already quite adept at visualizing data and balancing these two needs. However statistics or summary screens that appear after games have ended are still often an afterthought and often doesn’t convey much meaningful information to players.

Sometimes players just want to customize their interface to have as much data as possible Source

So for these two graphs, the problem that they have is related to context. The considerations from real world analog where these types of charts are common does not apply because these markets have already ceased to exist when these graphs are presented to the player. They cannot be used to predict future stock prices and the resource price trends that are shown would not represent the same trends in another game due to the randomization of available resources. So to the players involved in the game, the graphs only tells the players what they already roughly know and it is meaningless for people who didn’t participate.

Gameplay screenshot of a game in Offworld Trading Company

Gameplay screenshot of a game in Offworld Trading Company

Graph Improvement

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.

Some of the different types of resources and buildings in the game

Some of the different types of resources and buildings in the game

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.

Animated slider… potentially dangerous

Animated slider… potentially dangerous

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.

Time Building Action Buildings Player Message
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
Time Player Geothermal.Plant Solar.Panel Wind.Turbine Electrolysis.Reactor Hydroponic.Farm Water.Pump
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.

Hacking with matrices and outputting raw html to generate images

Hacking with matrices and outputting raw html to generate images

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.

ggvis layer_lines doesn’t play well with tooltips

ggvis layer_lines doesn’t play well 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;
}

Style Formatting

With all that done, most of the remaining work is styling so it looks appropriate. For that purpose, I decided to employ most of the designs and colors of the game so it actually looks like it belongs to the game. Most of the formatting is done with CSS except for ggvis which isn’t affected by CSS rules. To change the colors of the lines so they are the same as the colors used in game, another hack is used, which is creating a new column containing the exact color representing each player. Aside from that, most of the remaining styling options in ggvis is can be gleaned around information scattered around the internet. What was the most useful was perhaps reading the vega documentation which ggvis is based on and understanding how the options relate to ggvis options. At the end, more than fifty options were specified to customize the ggvis graph in order to look similar to the graphs within the game.

Final Visualization

With all that done, I can now present the final visualization to you… in a link. Unfortunately it seems impossible to render Shiny within a HTML page and this places a significant limitation of what Shiny applications can accomplish on the web. Still, the functionality it provides is quite impressive and is worth exploring to see whether it fits your specific needs.

I am including a static screenshot of the application, the link to the application hosted on the shinyapps website, and the source files.

Screenshot of the final visualization
Visualization hosted on shinyapps
Source files

Further Improvements

There are a lot of remaining ideas I have for this visualization that could not be implemented due to time and complexity. Here’s a number of items that can be still explored.

This is in essence still a relatively simple chart but each component requires a lot of customization in order to make them function and look appropriate. It was interesting working with Shiny and I hope you enjoyed reading through this post as well. Any feedback is appreciated and it would be great to hear your thoughts.