Once I had the idea of creating a dashboard to play Tic Tac Toe against Tableau, I had to come up with a way. I knew I wanted to use actions in a dashboard – and that meant I’d need dimensions to pass as filters. That meant I’d need to have data at the level of a space on the board. There are nine spaces. But I’d also need to have every possible move so that clicking on a square could send an action filter specific to a given board. So I’d need every possible board. This is more than just the possible outcomes of the game – in fact, even more important, I’d need every possible board of every possible game at every possible state: Win, Draw or In Progress.
I’ve never seen such a data set. So, I set out to create one. I wrote an application in C# to generate the data as flat files. I subsequently imported the data into SQL Server so I could manipulate it as needed. I won’t go into the code (you can download it here), but here’s an illustration of the recursive process it used to play through every possible game:
Notice that every board has 9 records of data, one for each space – even empty spaces. In fact, especially empty spaces as those are the ones that will eventually trigger the action filters they must be in the data.
Starting with an empty board, the application placed an X in the first available spot (board 2), then an O in the next (board 3), then an X in the next (board 4), and so on. At every point, the application checked to see if the game was a final outcome (X Win, O Win, or Draw) and if so would stop, go back to the previous board and try the next possible play. Notice that board 8 is an X win. Board 9 then is a continuation of board 7 with the next possible X move.
When I first wrote the application, I had no idea how many iterations it would take (I’m sure I could have calculated it), how long it would run, whether it would consume all my memory and blow up, or how many records it would create. It actually worked very well, going through every possibility and writing the data out in a matter of seconds. I was excited…
…but then I opened the file and did a record count. My heart sank…
Although I only had 549,946 possible boards – each of those boards had 9 spaces or squares. That meant I had nearly 5 million records of data. That’s not too much for Tableau at all. I work with many times that amount of data every day. In fact, you can download the full data here as an extract in a packaged workbook.
But it is over the 1,000,000 record limit for Tableau Public! And I wanted to share Tic Tac Toe with the world.
So…
…I got creative. I’ll show you how in Part 2.
I must admit to losing you a bit here, even with fairly rudimentary counting then the number of possible board positions is only 3^9 = 19683 surely? i.e. an O, X or blank in each of the 9 positions. That’s before we start to reduce it by taking into account the fact players take turns and so there can only be an equal number of X’s and O’s, or one more of either, depending who went first.
You ever have one of those moments where something that was very clear one moment suddenly is redefined and a new sense of clarity redefines the paradigm? Your comment just did that for me. You are exactly right! There can only be 19,683 combinations of boards. But… there are 5 million ways of achieving those combinations. I need the paths to allow for navigating from board to board (a future post will show this). However, I made the assumption that I needed to keep the paths separate. This is not true and much of the effort I went through to whittle down a 5 million record data set to a < 1 million record data set could have been simplified by realizing that a path that led to from board A to B to C and a path that led from X to Y to Z could be simplified to having paths A to B to C and X to Y to C, if C and Z were the same boards. I'll expand on this in a future post, but in the meantime, thank you for the paradigm shift!
Oh, wait! Now I remember why I couldn’t do that. It would work if I didn’t require Tableau to pick the “grand-child” of a board when I take a move. That is, it doesn’t pick the next board, it picks the “best” of the generation after that. I really do need every combination. I’ll show why soon…
Makes sense now, because you’re doing moves against the player. Thanks for the explanation.