Previous |
| Next |
It would be neater if we had pictures of the bins with beans in them, instead of numbers in the buttons.
We can change just a few lines of code to convert this:
To this:
Believe it or not, those are just buttons. Tcl/Tk can put text, or images or even both in a button. We just need to use some different options when we create the button.
But, in order to make a button with an image in it, we need an image. Or maybe even 9 images.
We can load images into our Tcl/Tk program in a lot of ways. The easiest is to have the image in a file on our disk and tell Tcl/Tk to load it.
Image files come in lots of formats. In the early days of computers, every time someone wrote a program to create images on a computer, they came up with a new way to describe it. There were dozens of different ways to define and image, and nobody could understand anyone else's format.
In the early 1980's a company named Compuserve developed a way to
describe an image that they called the Graphic Interchange Format. It
was better than a lot of the existing formats, and people started using
the GIF
format instead of other formats, and it became an
industry standard.
Nowadays, there are 3 main image formats.
The GIF
style images can be made smallest, so we mostly
use these in games.
To load an image into your Tcl/Tk program, you use the
image
command. The image
command has a few
optional commands - the most important one is create
.
The create
command can create a couple types of images, but
the only one we'll use is the photo
command. We can tell
Tcl/Tk what to name the image, or we can take a name that Tcl/Tk makes
up on its own.
If we've got an image in a file named 0bean.gif
and we
want to create an image named nobeans
, we'd use a command
like this:
|
We can put that image onto a button just like we put text onto a button,
but instead of a -text
option, we use a -image
option.
|
You can also use a -image
option with the label
command.
|
You can make your own images for Tchuka Ruma bins with beans in them or click here to download a zip file with the images of bins with beans in them.
Download the zip file and unpack it onto your system. Try making
some images and then put the images onto buttons and labels. Don't
forget to grid
the new widgets you make.
So, how do we go about changing our Tchuka Ruma game to use images instead of numbers.
We really don't need to change very much.
The first thing we'll need to do is create the images and save them someplace where we can find them later. We can make a new procedure to load the images, or we can merge the image loading code into one of the procedures we've already written.
Most programs have a bunch of stuff that they need to do once (when the program starts), other stuff that gets done at the start of a game, stuff that gets done when the player takes a turn, and finally, stuff that's done when the when the game ends.
You don't want to do things like loading all the images every time a player takes a turn, and probably not even every time they start a new game.
We could load the images as part of the buildBoard
procedure, but it's not a really good way to design the program. A
program is easier to modify if a procedure just does one sort of thing
(like load images, or draw the board). This keeps procedures small and
simple enough that we can understand them next year when we decide to
change the program to do something new.
So, we'll add a new procedure to load the images.
The next trick, is where do we store the names of the images?
There are a couple of good solutions to that problem, and both of them
involve using an array
.
We can make a new array variable (maybe called images
and index it with the number of beans shown in the image. Code to
create these images and save the image names would look like this.
|
Each image create
command returns the name of the image
it created. The names are something like image1
,
image2
, etc. We don't care what the names are, as long as
we've saved that name in a variable that makes sense to us.
Having a new array is a good solution to the problem, but, then we'll
need to add another global
command to all the procedures
that need access to the images, but we won't need to add that line to
procedures that only need to access the number of beans. This is a little
confusing.
For big programs, where you might have thousands of different types of data, using more arrays is a good idea. It helps to organize the data you're working with. For a program as small as Tchuka Ruma it's better to stick with just one global array and use a different index to reference the image names.
What will happen if we do something like the next code? Remember that we use board(0) to hold the number of beans in the leftmost bin, and board(1) to hold the number of beans in the next bin, etc.
|
Ooops. When we put 2 beans into the leftmost bin (0), we'll overwrite the name of the image of a bin with no beans with the number 2.
That won't be good.
So, we'll have to use to use something other than the number of beans in
the image as the array index. We could use words like joe
,
sam
, dog
, cat
and so forth, but
that would make it really hard to remember that 0 beans is joe
,
2 beans is dog
, etc. We need to come up with some index
that makes sense.
We can use any word as the index for an array. In fact, we can use any string of letters, numbers, or punctuation marks (except spaces or parentheses) as an array index.
An array that's indexed with words instead of numbers is called an
associative array
. Mathematicians use arrays that are
indexed with numbers (not words). Sometimes mathematicians need
arrays that are indexed with sets of numbers.
For instance, a mathematician might describe a button in the 100 button number game by its row and column position. So the button in row 2, column 3 would be button(2,3).
We can do the same things with the Tcl/Tk arrays. We can use two (or more) words, and separate them with commas, periods, dashes, or whatever.
Using commas to separate parts of an array index is usually the best way to make a complex index out of simple parts. That's not a hard-and-fast rule, but if you don't have a good reason to use something else: use a comma.
We can distinguish the array index for our images from the index that
references the number of beans in a bin by adding image,
to
the index like this:
|
The whole procedure looks like this:
|
The next thing to do is to change the buildBoard
procedure.
This is pretty easy. First, we change the button commands to not have
a -text
option. Then
we change the label
command to not have any options at all.
Our program will use the showBeans
procedure to configure
the image for the buttons and label after
we build the board.
The new buildBoard
procedure looks like this:
|
We also need to change the showBeans
procedure to
configure
the buttons and labels with an image instead of
putting the number in the button. Take a hard look at how the image
is selected in this code, then read the discussion just after the
code.
|
The line to configure button .b_$i
looks a lot like the
code that would configure the button to show a number. The code that
made the button display a number looks like this:
|
The number of beans in the bins is saved in the board
array, indexed by the position of the bin. The variable i
holds the bin position we're looking at right now. So, if
i
has a 0 in it, $board($i)
says to get me
the number of beans in the leftmost bin (the bin referenced by
$i
).
Now lets look at the line that configures the button to show an image:
|
Up to the -image
part of the line, it looks pretty
simple. Then we hit the $board(image,$board($i))
.
This is actually pretty simple, too. It's just 3 simple things
combined into something a bit less simple.
Tcl/Tk will look at the array index from the innermost set of parentheses out. So it will go through these steps:
$i
is the number of the bin
$board($i)
is the number of beans in the bin referenced by $i
image,$board($i)
is the index for an image with the number of beans in the bin referenced by $i
$board(image,$board($i))
is the name of the image referenced
by the index for an image with the number of beans in the bin referenced by $i
... That lived in the house that Jack built...
OK, we'll skip the nursery rhymes, but it's the same idea of all the parts referencing another part and getting closer to the part you're interested in. It's not at all uncommon in computer programs to have one variable hold the name or a a value that references another variable.
This is like an index or table of contents in a book - you've got something here, where you can find it easily that tells you where to go next to find something you're interested in.
We don't need to touch the moveBeans
procedure. This is
the reason that many games have a set of data and procedures to modify
the data, and another set of procedures to look at the data and display
it for the player.
The biggest and most complex part of the program is usually the code that knows how to play the game. If we have separate procedures for changing the data and displayig the data, we can change the way a game looks without touching the largest part of the code.
Here's this complete program.
|
The real Tchuka Ruma boards have a
final bin that's twice as wide as the other bins.
You can create your own images using WinPaint
, xpaint
,
Corel Draw
or some other program.
Try making your some new images for the goal that have larger bins.
Be sure to save the image in GIF
format.
You'll need to make 9 images. Change the
loadImages
procedure to load your goal images
You'll
probably want to use a different word to distinguish the goal
images from bin images in the board
index. (Maybe
set board(goal,0)...
instead of set board(image,0)...
.
Finally change showBeans
procedure to use the new images.
image create
command.
This is getting to be a decent looking game. But, we had to sit down and make up all the images for the buttons. This isn't too bad for 8 beans, but what would you do if there were 48 beans (that's what a 2 person Mancala game needs).
Next lesson, we'll look at letting the computer draw the bins and beans for us.
Previous | Next |