Search

Creating the Concentration Game PAIRS with Bash

Exploring the nuances of writing a pair-matching memory game and
one-dimensional arrays in Bash.

I've always been a fan of Rudyard Kipling. He wrote some great novels and
stories, mostly about British colonial-era India. Politically correct in our
modern times? Not so much, but still, his books are good fun for readers and still
are considered great literature of its time. His works include The Jungle
Book,
Captains Courageous, The Just So Stories and The Man
Who Would Be King, among
many others.

He also wrote a great spy novel about a young English boy who is raised as an
Indian native and thence recruited by the British government as a spy. The
boy's name is the title of the book: Kim. In the story, Kim is trained to
have an eidetic memory with a memory game that involves being shown a
tray of stones of various shapes, sizes and colors. Then it's hidden, and
he has to recite as many patterns as he can recall.

For some reason, that scene has always stuck with me, and I've even tried to
teach my children to be situationally aware through similar games like
"Close your eyes. Now, what color was the car that just passed us?"
Since most of us are terrible observers (see, for example, how conflicting
eyewitness accident reports can be), it's undoubtedly good practice for
general observations about life.

Although it's tempting to try to duplicate this memory game as a program,
the reality is that with just a shell script, it would be difficult. Perhaps
you display a random pattern of letters and digits in a grid, then clear the
screen, then ask the user to enter patterns, but that's really much more
of a game for a screen-oriented, graphical application—not shell scripts.

But, there's a simplified version of this that you can play with a deck
of cards: Concentration. You've probably played it yourself at some point
in your life. You place the cards face down in a grid and then flip up two at
a time to try to find pairs. At the beginning, it's just random guessing,
but as the game proceeds, it becomes more about your spatial memory, and by the
end, good players know what just about every unflipped card is at the beginning of
their turn.

Designing PAIRS

That, of course, you can duplicate as a shell script, and since it is going to
be a shell script, you also can make the number of pairs variable. Let's
call this game PAIRS.

As a minimum, let's go with four pairs, which should make debugging easy.
Since there's no real benefit to duplicating playing card values,
it's just as easy to use letters, which means a max of 26 pairs, or 52
slots. Not every value is going to produce a proper spread or grid, but if
you
aim for 13 per line, players then can play with anywhere from 1–4 lines of
possibilities.

Playability would be enhanced by having clickable spots, but this is
a shell script, dude, so you'll have to suffer through a
Chess-style grid notation: line,slot.

The letter A is at 2,4, and the letter E is at 3,9. Even
better though, because the first value should only ever be 1–4, you could omit
the comma to make things more succinct, making the A at 24 and the
E at 39. There'll be three-digit values too, like the
Z at 413, but that's still easy to pull apart and process.

Actually, now that there's a 13x4 grid pattern, let's rethink the
flexibility of the game. Instead of allowing an arbitrary number of pairs,
let's make the game less flexible and constrain users to being able
to specify only how many rows of 13 they want. Even more so, only even numbers of
rows will be acceptable. (Obviously with a single row of 13, it'd be hard
to have only pairs show up!)

Data Representation

Bash has decent support for arrays, so my first inclination is to make this a
linear array and simply divide by 13, as needed to convert "display"
coordinates to actual array coordinates. A multi-dimensional array would be
better, but Bash doesn't offer any support for that particular data
structure. There's a workaround by using an associative array, but that
requires Bash 4.x, and MacOS X still ships with Bash 3.x, which means a lot of
our readers (shhh, I know not all of you are full-time Linux folk) would be
left out in the cold. Ah, fun, fun.

Enough chat though, let's get to the coding side of things,
although I'll have to continue the development in my next column.

Coding the Grid

A reasonable place to start this whole project is by looking at how to work
with a one-dimensional array in Bash. An array slot is assigned a value
like this:

myArray[2]=value

And it's referenced in the slightly clumsy format:

echo ${myArray[2]}

Bash wants you to use the declare statement to identify arrays prior to
use—declare -a myArray—and you can assign initial values like this:

declare -a myArray=(cat dog pig frog snake)

There are some handy shortcuts with @ and * to learn
about, but let's just start coding.

First things first, here's the Bash function I've written to
initialize the array with A–Z values (I'll shuffle the values in a
different function):

While it may appear that there's lots to consider here, the heart of the
function is the for loop within a while loop. The
while steps through rows,
and the for loop steps through the 13 slots for each row.

The most interesting line might be this:

board[ $index ]=${letters[$letter]}

The value of a given slot in the board array is going to be set to the
$letter index value of the $letters array. That array is set in the opening
declarations:

The result is interesting, because Bash really wants to have 0-based indexing,
and here I'm using it all as 1-based indexing instead. But, that I shall show
you in my next article, the continuation of the PAIRS game-programming
adventure!

Dave Taylor has been hacking shell scripts on UNIX and Linux systems for a
really long time. He's the author of Learning Unix for Mac OS
X and Wicked Cool Shell Scripts. You can find him on Twitter
as @DaveTaylor, and you can reach him through his tech Q&A site: Ask Dave Taylor.