brandonllocke

Getting Into Go

I’ve recently started playing with Go for sort of an odd reason: to prove that my wife is ridiculously lucky. Not for marrying me, though I’d certainly like to think that’s the case. My wife has a history of winning games by an astounding margin and one of these margins has been bugging me for years. Go helped me show just how ridiculously lucky she is.

Background

First a little background regarding the classic card game “War”. In this game, a deck of 52 cards is split between two players. Then the players each show the top card of their deck to each other and the higher card takes both cards. If the cards match, they have a “war” meaning they add 3 cards to the pot and then show another card and the higher card gets all 10 cards ((1 + 3 + 1) * 2) and play continues. Eventually one player will run out of cards and the player with all the cards is declared the winner.

The Catalyst

My wife and I sat down one night about 10 years ago and played 20 games of war in a row. At the end of the 20 games, she had won 19. Given the rules of the game, our chances of winning each game came out to about 50%. One would expect that playing enough games the win/loss ratio would trend toward 1:1. 20 games isn’t enough to really see this trend, but it still seems fairly improbable to win 19 out of 20 games. I wanted to find out how improbable this was.

Python

About a year ago, I wrote out the game of war in Python to learn more about OOP and just work on my skills. This really helped my grasp OOP more clearly and it was the perfect “beginner” program because I knew the requirements already and I could think entirely about the code and implementation.

The program was simple: run sets of 20 games of war until we received x number ending in a single winner with a 19:1 win/loss ratio. Then average out the number of 20 game sets it took to get those results. We needed the requirement to receive multiple occurrences of this because the number of sets it could take could vary greatly. Technically the first set could be won by one person 19:1 and that would make it seem like 100% of games would end with a 19:1 winner. That’s obviously not true. Running multiple sets should make our average a lot closer to reality.

The issue came up once I started running my calculations. Python was…. slow. I could still get through a game in a few seconds, but each attempt was 20 games and they had to be run one at a time. I ran the Python script for about a week and found only 4 occurrences of the set ending in 19:1. Considering I wanted to find 1000 instances, that would mean the script would have to run 250x longer than it had (maybe?, it’s possible we just got a bunch of “late occurrences in that small set). I really didn’t want to wait 5 years to get the results for such a silly project.

Go For Going

Go offered a set of speed enhancements over Python. First, Go promises easy coroutines and it delivers. Due to this, while I was only able to run one game at a time in Python, I was able to run all 20 games simultaneously in Go. Beyond this, being strictly typed speed things up drastically. Even though the code was simple and I wasn’t doing much type switching, the instantiation of these types in Python took time. Even before introducing coroutines, a single set of games was done much quicker than in Python.

Because of these speed enhancements, I was able to get the results that I wanted in a little over 24 hours, as opposed to the weeks that I would have been waiting before.

So, How Lucky Is She?

She’s incredibly lucky. The average number of sets that were ran during my quest for 1000 matches was… 13,706. This is only slightly more common than being struck by lightning at some point in your life. It’s roughly the odds of getting 4 out of 5 matches on a PowerBall ticket. These odds are slightly worse than your average non-professional golfer hitting a hole in one.

It’s ridiculous and thanks to Go, I know exactly how ridiculous it is.