in which four cards make a gang

I really enjoy programming with my kids. For me helping them learn how computers work is more about training them in logical thinking, creativity, and problem solving than it is about teaching them to accomplish specific tasks with software. My goal isn't to help them land lucrative programming jobs when they get older, but to expand their horizons with skills they can use in any kind of profession.

Over the past couple years, we've gotten the chance to create a few different games in several different styles. My kids were recently gifted a deck of Gang of Four cards and were playing it nearly every day. This made it easy for them to get on board with the idea of turning it into a computer game when I suggested it. Adapting a card game turns out to be a great way for beginners to learn about programming since you start with a very clear goal, and it is easily broken up into natural steps.

I've tried to document here the topics that came up as we progressed through building one of these games, but I also think they learned a lot from just seeing how the program comes together bit by bit and by debugging when we ran into problems. While my kids can type and do often work independently, I've found that the best approach usually has me at the keyboard guiding through the steps in a kind of socratic style of questioning. There are certainly times when I'll cheat and simply cut off an avenue of thought that I feel will be unproductive or frustrating, but ideally I try to stick with asking questions and typing.

Right now our weapon of choice is the Lua programming language due to its relentless simplicity and the availability of the wonderful LÖVE game framework. While we've done graphical games with LÖVE, this one makes more sense to start out as a plain Lua game that uses console input and output. The complete source code is available on GitLab.

make_deck = function()
  local deck = {}
  for i=1,10 do
    for _,c in pairs({0.1, 0.2, 0.3}) do
      table.insert(deck, i + c) -- two of each
      table.insert(deck, i + c)
  table.insert(deck, 1.4) -- special 1+ card
  table.insert(deck, 11.1) -- two phoenixes
  table.insert(deck, 11.2)
  table.insert(deck, 12.3) -- dragon
  return lume.shuffle(deck)

Constructing the deck leads to some good opening questions of how to represent cards and what hands should look like. After seeing it deal out the exact same hands a few times, it also offered an opportunity to talk about what it means for a process to be deterministic and why you need to seed your random number generator to make the game fun.

Once you add a loop which prints your hand and asks you which cards you want to play, the game is playable (in a "hot-seat" multiplayer style) as long as you already know the rules. Of course you will want to add checks for legal plays, but the minimum required for a playable prototype is here. Working in small discrete steps like this really helps with kids because reaching each milestone feels like a big win.

After we added in functions to enforce the rules, we began to add computer players. Writing AI may seem like a really advanced topic, but for a card game like this it's pretty straightforward. Granted our computer players don't always make the most strategic decisions, but they get by pretty well with a basic strategy of always trying to get rid of their lowest-ranked cards. Here again we found a way to break it into smaller steps—first the computer players are added to the rounds but only know how to pass, then they learn to play during single-card rounds, then they learn doubles and triples, etc. Writing computer players also led to a discussion about re-usable functions; many of the things we needed we had just implemented to determine whether a given hand was legal.

writing pong on the porch

In Gang of Four, the most powerful hand is a "gang", a set of four or more which can always beat any non-gang hand. It's not unusual for a gang of four to appear, but a gang of five is pretty rare. We've only seen a gang of six once, and while it is theoretically possible to get a gang of seven (there is only one way to make a gang of seven since most numbers only have six cards) the odds are astronomically against it. Still, the possibility that a gang of seven could exist kept my kids' fascination.

But now that we have a functioning simulation of the game, we can run through a simulation of dealing out hands over and over again and check for gangs. We found that running a repeated dealing simulation could sometimes find a gang of seven after as little as 3,000 games, but sometimes it would take up to 120,000. Not only does this give a good opportunity to talk about permutations, histograms, and simple optimizations[1], but it also serves as a great demonstration of using code to satisfy your own curiosity.

gang of four cards

The grand finale of this enterprise involved the realization that the ComputerCraft mod for MineCraft provides a way to run Lua code in-game which should be compatible with what we just wrote. While this wasn't quite seamless,[2] seeing our code that we'd so far only run in a regular terminal running on an in-game machine was quite a thrill.

We still have a few steps further open to us from here. Implementing multiplayer over a simple socket interface is not too difficult[3], but it forces you to revisit the assumptions you have so far about input and output. Another fun direction would be to add a GUI for the game using LÖVE's graphical capabilities. Or implementing a user interface in a MineTest Lua mod where the cards are individual items. The point is being able to just explore whatever direction the kids interest takes them.

[1] The only possible gang of seven is a hand of seven ones, so you can quickly check for a gang of seven on a sorted hand by seeing if the seventh card is a 1. There are lots of other optimizations you can make, but they were excited to see significant speed boosts from replacing the naive check with this one. But there was also a complexity cost if we wanted to keep the old code around for compatibility with detecting gangs of 4, 5, or 6.

[2] If I were to do this over, I would use OpenComputers instead since it's a lot better-documented and open source.

[3] We actually already implemented multiplayer, but this post is getting long enough as it is.

« older | 2016-06-05T15:13:54Z | newer »