Snack Time II: Building sentences.


Just like the good sale items at a discount store, everything in a computer is in limited supply.

There is a limited amount of memory, a limited amount of disk space, and probably only one display and sound card. The programs in a computer (and even different parts of a program) need to learn to share.

Most computer programs don't know anything about sharing. They only know what they want. They're a lot like a two-year-old.

You can't run a computer if every program grabs whatever it wants and won't share. Think what would happen if your game stored the secret number in a memory location and then another program started using that memory location for a loop counter. Your secret number could change a thousand times a second!

One solution to this is to only let a computer run a single program at a time.

This is how early computers were built. Even today, some computers only run a single program at a time. Computers like a PlayStation or X-Box only run one game at a time, not multiple games all at once.

Computers equipped with Windows, Linux or Mac OS/X can run several programs at once.

They do this by having a master program called an operating system that really owns all the resources. Resources are things like memory, disk space and access to the screen or sound card.

The three most popular operating systems are Windows, Linux and Mac OS/X. There are many more operating system programs, but these three are the most popular today.

The operating system decides which program gets to use memory, which one gets access to a sound card, which one can access the disk drive and things like that. In techno-speak, the job of the Operating System is to allocate resources.

Every program that runs on a computer has to ask the operating system if it can have something. A program even needs to ask for permission to use the CPU to run itself.

Even a single computer program, running all by itself needs to decide how to allocate resources. The games we're writing are small, and they can get as much memory and disk space as they need. However, there is only one pair of speakers, and our game has to decide how it will use them.

In lesson 9, we had the computer say "too high", "too low" or "you win" instead of using the tk_messageBox to tell the user where to guess next.

If you tried adding the background music to the game in lesson 9, you might have noticed that the background music kept playing while the computer said "too high" and "too low".

This is because the play command starts a sound playing, and then immediately runs the next command in your script. It doesn't wait for the sound finish.

This lets you have two sounds: the background music and the "Too Low" message playing at once.

The snack extension has built-in rules for how to share the speakers when two parts of a program both want to use them like this. It knows how to add the sounds together so that they can both come out the speaker at once.

In computer-speak, this is called executing the commands in a non-blocking mode. That means it starts one command and then immediately starts the next command, whether the first command is finished or not.

In blocking mode, the program works on one command until it's done and then goes on to the next. We say that the first command is blocking the execution of the second command.

Most parts of the computer program run in blocking mode. For instance, look at the code below - we need to finish assigning the value to the variable number_of_cats before we use that value to calculate how much food they'll eat in the expr command.

set number_of_cats 2
set pounds_of_food_per_day [expr $number_of_cats * 0.25]

Playing sounds is one of the few exceptions in which a command starts something and doesn't wait for it to finish. We don't want to start playing the background sound, and then need to wait for the song to finish before we can play the game!

But even with sounds, sometimes we want a sound to play in blocking mode. We don't want anything new to happen until a sound is finished playing.

Lets look back at our game from the previous lesson. We built up the messages for the tk_messageBox by appending the "Left", "Right", "Up" and "Down" instructions to the message variable.

You can download some wave files to say those words here:

What do you think will happen if we write some code like this to make the computer say "right down":

package require snack
snack::sound down
down read down.wav

snack::sound right
right read right.wav

set cmd "right play; "
append cmd "down play"

button .b -text "Playit" -command $cmd
grid .b

After all the discussion about how snack runs in a non-blocking mode, you probably guessed that it would play the two words on top of each other. It comes out sounding like "dightn"

When snack::sound creates a new sound command, that new command has many options. One that we need right now is -blocking. The -blocking argument tells the computer to not allow anyone else to use the speakers until this sound is finished. It blocks the other sounds.

The -blocking argument needs to be followed by a boolean value to tell it whether blocking is ON or OFF.

Remember, boolean variables can have one of two values. We usually think of them as TRUE or FALSE. Tcl/Tk will let you use several different words for TRUE and FALSE.

We can use ON and OFF, or TRUE and FALSE, or even 1 and 0. In this case, ON and OFF makes it most obvious what the program is doing, so we'll use those boolean values here.

Try that code again, but change the play commands to look like this:

package require snack
snack::sound down
down read down.wav

snack::sound right
right read right.wav

set cmd "right play -blocking ON; "
append cmd "down play -blocking ON"

button .b -text "Playit" -command $cmd
grid .b

That sounds a bit better, doesn't it?

If you had sound files with individual words in them, you could make the computer talk in full sentences that it builds as it needs them, like "Wouldn't you rather play a nice game of chess?"

The words "Up", "Down", "Left" and "Right" are OK for instructions, but they aren't really enough for making a sentence. But if we add a couple more wav files, we can make real sentences.

The go and andthen files allow our program to build real sentences like "Go left and then go down".

Download these files, and then look at the next game. This is the same as the previous game, except that instead of using tk_messageBox to tell the player where to go next, it uses the snack command so that the computer can talk to you and tell you where to guess next.

Look at what this code does in the body of the if commands. In the previous exercise, we set and appended the message for the tk_messageBox command. This time we're building new sets of commands in the cmd variable.

We use the semi-colon (;) to tell Tcl/Tk where the end of a command is. When we write a program, we usually end a command with the end of the line, but we can also use a semi-colon to tell Tcl/Tk where the command ends.

# Tell Tcl/Tk to find and load the snack package
package require snack

# Create sound commands and read the sound data
snack::sound up
up read up.wav

snack::sound down
down read down.wav

snack::sound left
left read left.wav

snack::sound right
right read right.wav

snack::sound go
go read go.wav

snack::sound andthen
andthen read andthen.wav

snack::sound youwin
youwin read youwin.wav

# Calculate 2 secret numbers between 1 and 10
set secretX [expr 1 + int(rand() * 10)]
set secretY [expr 1 + int(rand() * 10)]

# buttonNum is used to make unique names for the buttons
set buttonNum 0

for {set x 1} {$x <= 10} {incr x} {
  for {set y 1} {$y <= 10} {incr y} {
    # Initialize the cmd variable to an empty string.
    #  It will get filled in the if statements.

    set cmd {go play -blocking ON; }
    set leftright 0
    # If $x is the same as $secretX and 
    #    $y is the same as $secretY, the player has won.
    # In that case, put the command to play "you win" in the
    #   cmd variable.

    if {($x == $secretX) && ($y == $secretY)} {
      set cmd "youwin play -blocking ON;"
    # If $x is less than the $secretX, the player is too
    #  low and needs to move to the right.
    # set the cmd to play the sound for right.
    #  Note blocking - this makes snack finish this sound before it
    #  starts the next one.

    if {$x < $secretX} {
      append cmd "right play -blocking ON;"
      set leftright 1
    # If $x is greater than the $secretX, the player is too
    #  high and needs to move to the left.
    # set the cmd to play the sound for left.

    if {$x > $secretX} {
      append cmd "left play -blocking ON;"
      set leftright 1

    # If $y is less than the $secretY, the player is too
    #  low and needs to move down.
    # set the cmd to play the sound for down.
    #  This play command does not need to block, since there will
    # be no sound command after it.

    if {$y < $secretY} {
      if {$leftright == 1} {
        append cmd "andthen play -blocking ON;"
        append cmd "go play -blocking ON;"
      append cmd "down play"
    # If $y is greater than the $secretY, the player is too
    #  high and needs to move up.
    # set the cmd to play the sound for up.

    if {$y > $secretY} {
      if {$leftright == 1} {
        append cmd "andthen play -blocking ON;"
        append cmd "go play -blocking ON;"
      append cmd "up play"
    button .b_$buttonNum -text " " -command $cmd
    grid .b_$buttonNum -column $x -row $y
    incr buttonNum

Type (or copy/paste) this code into Komodo Edit, and see how it works. Try changing the messages to leave out the "Go". Instead of saying "Go left and then go down", it will just say "left and then down".

Here's the important parts of this lesson:

Every time we play these games, we need to start the game to play it again. Most games pop up a "Wanna Play Again" button after you're done.

We'll learn how to make games that can be replayed in the next lesson.



Copyright 2007 Clif Flynt