Skip to content

Instantly share code, notes, and snippets.

@martinsson
Created December 11, 2017 01:15
Show Gist options
  • Save martinsson/5828bafe127909d72f0756654bf7efd7 to your computer and use it in GitHub Desktop.
Save martinsson/5828bafe127909d72f0756654bf7efd7 to your computer and use it in GitHub Desktop.

If you want a simple solution, find a simple data structure.

A lot of problems have a very simple solution, provided that we use a data structure that reduces the number of necessary operations. Still I find that we often neglect the effort spent in finding the best data structure.

To me this is a serious problem or a great opportunity for improvement! Every project battles with the complexity of the past. Many systems contain enormous amounts of accidental complexity in addition to the essential complexity that serves the user. If you don't believe me try to remember the times where it was so hard to understand the existing code that the only solution, given finite time, was to hack it. Which in turn generates even more complexity and eventually will force someone else to hack around that... Stack complex code on top of complex code and very quickly we have a slow and awkward project to work on. What if we got better at finding simple solutions instead? What if we could easily improve on that?

It's time for a very simple example of overly complex code.

https://gist.github.com/1d75690d57c777e2ac0d4821ed535494

A better solution would be

https://gist.github.com/24933340f1d47c0b7b476598e39576df

That's just to get warmed up and it really doesn't prove my statement that the right data structure simplifies the code. So let's look at TicTacToe. In Tic-tac-toe two players compete on a 3x3 board. A player wins if he holds either a line, a column or a diagonal. What would be a good data structure for calculating this? Perhaps a 3x3 array of arrays? That could work out well but it is likely to generate labourious code like

https://gist.github.com/c3ae0159254684bef1461ca49011a02a

So perhaps a string instead? That'd optimise readability in tests (you could see the board), however updating the string when a player places a mark is difficult.

What about a simple map of coordinates to playerMarkers? That comes close to a good solution. Just pay attention to the coordinates used. Is it a pair of [ row, col ]? Or perhaps just the number of the case, 1-9, or better 0-8. But wait, a map indexed by a value from 0-n ... That's an array. So now we could represent a board with something like an array of characters. But wait that's more or less a string, which is readable. Great!

https://gist.github.com/5c076aea99ffa5340d12b94d1dd3bc58

Now that we have the board we could just go on with that and solve it with a few ifs

https://gist.github.com/b19d3bf5863248666130da1068f0471d

However those hasXXX() methods will take a few lines to implement. Unless of course we bring out our secret weapon of replacing code with data-structures! We could encode row, column and diagonal as data-structures. Like so

https://gist.github.com/a709f7018706a2ae27e26790f5c1531d

From now on every winning combination can be treated equally. You can find a solution using this here

But wait there's one final simplification to be done. We dont need a board! Actually we're only interested in the spots that has the players marker on them. We could change the data structure to have a list of spots for each player and the solution becomes a breeze:

https://gist.github.com/7d48bd1657a7a5f520512522063af067

Disclaimer about primitive obsession

I've been using primitives extensively here. Actually I'll almost never expose primitives outside a class, but I will always try to simplify using primitives, then I'll carefully chose where to encapsulate them. In this case I'd replace the ints with enums which still allows me to use the ordinal to do the above calculations.

Conclusion

We can write much less and much simpler code if we choose the underlying data-structure wisely. The best data structure is almost always the simples possible one. Going primitives first helps me a lot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment