For my wife's last baby shower, I used Ruby to make cards for a bingo game. This is a walkthrough of that process, which might be interesting if you're new to Ruby. In this first part, I'll frame the problem, and work out a reasonable data structure.
h2. The background
In 2012 my wife was pregnant with our third child, and my 11 year old daughter wanted to plan the baby shower. One of the games she planned was "Baby Bingo", by which she meant bingo where the squares are baby-related pictures. She asked me to help prepare the bingo cards, a task which seems algorithmic and slightly maths-y. Quick! To a text editor!
h3. What's bingo?
I thought everyone knew what bingo was, but I had to explain bingo to a couple of people this week, so here's a brief description of the game. Skip this bit if you're already familiar with the game.
Bingo is a game of chance. Players each get a bingo card, which is a square grid of randomly selected numbers. Like this:
| 30 | 62 | 2 | 37 | 11 |
| 76 | 21 | 90 | 94 | 35 |
| 80 | 83 | 98 | 84 | 43 |
| 1 | 96 | 33 | 57 | 27 |
| 92 | 67 | 49 | 95 | 16 |
All the players listen to the bingo caller who chooses numbers at random, calling each number out. The players mark off a number from their card if it gets called.
Play continues and players mark off more and more numbers from their cards. Eventually some player will have a line across their bing card made out of marked off numbers. The line can be a row, column, or a corner-to-corner diagonal. That player shouts out "bingo!" and wins a prize.
h3. Why write this article?
It's possible to learn a programming language or tool by working through tutorials and trying stuff out on your own. But I reckon it also helps to watch someone else do the thing you're trying to learn. It helps you to pick up little idioms that often don't get covered by tutorials. So, I've documented what I did to muddle through this project, and hopefully that can help someone just learning Ruby.
h2. Framing the problem
So let's use Ruby to make some bingo cards. Here are the features I want in my bingo card maker:
**No repeats.** I'm not sure how real bingo does it, but I don't want the same thing showing up in multiple squares on a single bingo card.
**No draws.** It would be nice to avoid draws, where two players get bingo at the same time. I think real bingo has draws which are broken by who's first to yell out "bingo", but that's for serious bingo players and we want a more casual party atmosphere.
**Images, not numbers.** The bingo squares should be pictures of things like babies, bottles, rattles etc. rather than numbers.
**Printable.** Obviously the output needs to be something we can print out.
Let's get started. Initially I'll ignore all of those things I just mentioned, but I'll fix each one up eventually.
h2. First step: a data structure for bingo cards
Data structures are important, and I usually start a project like this by working out a data structure and trying it out.
We need a square grid. Ruby has arrays, and arrays-of-arrays. So we can make a bingo grid as an array of rows, where each row is an array of numbers.
Hmm, a long single column of numbers. Maybe that's right… it's quite hard to tell.
h2. Nicer output
Let's make card output a bit better.
That looks nicer, and confirms our card structure is okay. It would look even cooler with random numbers though, right?
Whoah, that looks crazy. We're getting some two-digit numbers, so our output looks jumbled up.
h2. Maybe nicer output THIS time
It's easy to add a space after each number:
…except that doesn't work -- you'll see a @String can't be coerced into Fixnum@ error. That's because Ruby sees you're trying to add a string to a number, which doesn't make sense. Ruby will let you do all sorts of useful maths-y things with strings, like @puts ("Na" * 2 + " ") * 8 + "Batman!"@, but here Ruby wants you to be clear about what you mean by "adding" a string and a number.
In this case you want to join them together as strings. So use the @to_s@ method to turn the number into a string:
..and that works, kind of. It's all irregular though. What we really want is for each number to take up a certain width, irrespective of how many digits it takes. Ruby strings have a @center@ method that does the trick:
Bam! Awesome. Your numbers are random, run it a few times to see them changing. We're miles away from meeting the features I want, but I'm confident the data structure is working, so it's time to go make dinner. In Part 2 I'll use a Ruby class to wrap up all this functionality.
by Daniel Baird first appeared here on 19 July 2013.