In India and Africa they play several games with some beans and a board with small bins in it. The games all involve moving beans one at a time from one bin to another. The games are called Awari, Mancala, and several other names.
Most of these are games that two people play, like we'd play checkers, chess, Chinese checkers or go.
There's a solitaire puzzle named Tchuka Ruma that we can make a program to play.
When we start the game, a Tchuka Ruma board looks like this:
The way the game works is that on each turn, you take all the beans out of one bin and drop them one at a time into the bins to the right. When you reach the end of the board, you loop back and put a bean into the left most bin, and keep going until you run out of beans.
After one play, taking the beans from the rightmost small bin, the game looks like this:
The larger bin on the far right is the goal. Each time we get a bean into that bin, we get a point, and that bean is out of play. We can take beans out of the left 4 circular bins, but not out of the right-most oval bin.
If the last bean you play goes into an empty, round bin, you lose.
If you can get all the beans in the big bin on the right, you win.
This looks like a pretty easy game to program - and it is.
We can use a button for each of the round bins, and a label to show how many beans are in the right-most bin. That will let us click a bin to move the beans to the right, but we won't be able to click the rightmost bin
The game would look like this when we start:
And like this after one turn:
In the games we've made so far, we store all the information that the game is using inside the graphic widgets. For instance, in the previous number guessing game, we configured the buttons like this:
The text to display is the number, and the command associated with the button has that number in it.
This works for simple games, but for more complex (more fun) games, we need to keep the information the game uses and the part the player sees separate.
For Tchuka Ruma, we need to have 5 variables - each variable will contain the count of beans in a bin.
We could do something like naming the variables
bin3, etc. That would work OK with
a game like this where we've only got 5 bins.
But remember that we've got to list every global variable in our
procedures. We can put 5
global commands at the start
of each procedure (we only need one for this game), but in games
like Alien Defense, where there are 50 alien ships at the start of the
game, it gets to be a lot of typing.
The solution to this problem is a thing called the array. An array is a special type of variable that holds more variables. We call the whole variable by a name, and then add a second name to specify one of the variables that is being held inside the main variable. We call the parts of an array elements and we call the second name that identifies each of these elements an index.
In this instance, we'll use an array variable named
The board variable has 5 elements - bin 0, bin 1, bin 2, bin 3 and bin 4.
To be simpler, we can just use 0, 1, 2, 3 and 4, and leave out the
In Tcl/Tk we specify the elements of an array variable by putting the index inside parentheses. When we specify an array variable with the array name and the index, we can treat it just like the variables we've been using so far.
In our program, bins in our board would be
We can use a variable to hold the index. In that case, Tcl/Tk takes the value of the variable holding the index, and uses that to look up the index in the array.
This sounds complex, but it's really not. For instance, to set up a game of Tchuka Ruma, we can use 4 lines like this:
Or we can use a loop like this:
The advantage of using a loop and a variable to hold the index is that it's easy to expand this to an array with 100 indexes, like we might have for the number guessing game in lesson 13
When it comes time to look at a member of an array, we treat the
array variable and index just like a normal variable. We put the
dollar sign in front of
arrayName(index), and Tcl/Tk
knows we want to know what's in that variable.
We could check the contents of the bins with code like this:
One last thing about the arrays (at least, for now). The index is a word - the word can be a number like 0 or a real word like goal.
In this game, we'll use numbers 0, 1, 2 and 3 to index the first 4 bins, and the word goal to index the last bin.
So, after all this, the procedure to initialize the game sounds pretty simple. It would look like this:
The next trick will be building the board.
We're going to add some new wrinkles to making buttons and labels to make the display a little nicer than it would normally appear.
By default, the
grid command puts the widgets right next
to each other. That's usually what we want. But, for this game, it
looks a little more like a real board if we puts space between the
buttons. There's an option we can use with the
command to add some spaces.
-padx tells the
grid command to put
padding between the widgets in the x dimension. As you might guess,
there is also a
To make the label for the final bin show up better, we can use the
-background option to tell Tcl/Tk to make the label
white instead of grey.
The code to build the buttons and the label looks like this:
That leaves just one procedure to write - the one to process a players move.
This is where the rules for the game get implemented. The number guessing games have had very simple rules. The rules for this game are simple, but not very simple.
The rules are:
Here's the code that plays by those rules. Take a look at the
if statement where we check to see if the bin number
is 4. There's a new thing in that command. We'll look at the
else thing just after we look at the code.
In the previous games we've used multiple
if commands to
check if a number was greater than, equal to, or less than the player's
In this game we need to know if the bin is the last one, so we can take that bean out of play and see if the player won, or we need to put the bean in a round bin.
To check which bin the bean is going into, we could have a bunch of lines like this:
But, we can treat all the round bins the same. We really have two conditions - is this the goal bin, or not.
else is a part of the
if command. It
if command to do a different set of code if
the test is false.
A simplified if/else command looks like this:
You might also notice that we've got a lot of code, including more
if statements inside the
Each set of code inside curlies is indented further than the previous set of code. This makes it easier to see how the loops and if commands are grouped.
The last thing that the
moveBeans procedure does is to
showBeans procedure. When you write a program
using this style - the data is contained in variables, not coded
into the GUI - you should write the procedures so that they either
change the GUI, or change the data, but never both. This makes it
easier to change the way the program looks later without needing
to change the code that controls how it plays.
showBeans procedure looks like this. It loops through
the bins and uses the
configure command to make the
buttons show how many beans are in their bin. We don't need to do
anything for the goal bin because we used the
-textvariable option with the label, and Tcl/Tk takes care
of updating it for us.
That's all the procedures in this program. The complete code looks like this:
You'll get tired of restarting the game each time you want to play
again - Try adding some commands to the you lose part of the
procedure ask if you want to play again. If you click yes
it should call the
initializeGame procedure to reset the
game and run it again.
You might also try playing with the
-forground options when the buttons and labels are created
to make the game look more interesting.
ifcommand can have an
elseclause that contains commands to run if the test is not true.
-padyoptions that you can use to make a prettier GUI.
-backgroundcommand that will change the normal background to a different color.
The buttons with a number to show how many beans are in a bin works. But, it looks pretty kludgy.
Next lesson will look at making the game look cooler with pictures of beans in the bins.