• Bubble
  • Bubble
  • Line
Number Guessing Game - Tcl/Tk
Andrei Clinciu Article AUthor
Andrei Clinciu
  • 2016-11-19T15:15:00Z
  • 5 min to read

This is a Game Coding Tutorial

If you got here by sheer luck please read through the introduction at : https://andreiclinciu.net/?p=80&preview=true

The Game:
We will start with  a simple game where the player guesses a random number between 1 and 100  (or anyother maximum value) generated by the computer.
We'll start small and as we code we will add more functionalities along the way, this will make it easier to understand the process and changes.

You'll see that at every revision and version we will change specific things, and even change the way we did something in a previous version.
It's very important to understand that programming is not something static but a dynamic action of changes and improvements.

As we advance with our games we'll explain the changes less and less and we won't talk about previous concepts unless there is a catch.

In normal projects you need to think out the project on beforehand with all the requirements and then start coding. Requirements can change, and the code will change too so always review the old version with the newer version and see what we've added.

Version 1

Our first version has only 15 lines including drawing the screen and the logic of it all. Let's see how it works.
The first line includes the Graphical toolkit
We set the to guess  variable as a fixed number.
We then draw 3 things or widgets on the screen.
A label
An entry box and a button which tue user will click.
The grid command is called a Geometry manager.
It's the entity that draws the widget on the screen placing it at a predefined column and row location.
This will make it easier for us than calculating and specifying the location. If you want to add or remove widgets it becomes very easy
Since grid will manage everything for us.
We can join the 2 commands in 1 line like lines 6 and 7 or have them separate like lines 4 and 5.

Tk has 2 other geometry managers, pack and place which work differently depending on your needs.
We'll stick to  grid

Notice that the ttk::button function defines .btnGuess and specifies a command to use,
Then later we define the procedure which will read the current data  from the entry .txtGuess.
Then we use if to verify if it's the same as the variable numberToGuess.
The reason we use $::numberToGuess is that :: is the global namespace. Variables in Functions are local so if we used $numberToGuess it wouldn't work.
You can change it and test it out.
We'll see another way to access variables in the next version.

package require Tkset numberToGuess 5ttk::label .lblInfo -text "Guess a number between 1 and 10"grid .lblInfo -row 1 -column 1grid [ttk::entry .txtGuess ] -row 2 -column 1grid [ttk::button .btnGuess -text "Guess!" -command guessTheNumber ] -row 3 -column 1proc guessTheNumber {} {set currentGuess [.txtGuess get]puts "The current number is $currentGuess"if {$currentGuess == $::numberToGuess} {\ttk_messageBox -message "Congratulations, you've guessed it!"} else {\ttk_messageBox -message "That's not the number, try again"}}

Version 2

On the 2d version we would like to do multiple things.
Use a random number each time.
See the total number of guesses the player has used.
Add another button that generates another number when clicked resetting the total guesses.

Cleanup a little bit.

For the random number generation we will use a predefined function called rnd. You can rename it to whatever you want.

We'll place all the other variables, labels, butons and grid functions in a initialize procedure which we will run at the end of the script.
You can play around and see what happens if the data within the initialize procedure where outside of a procedure and understand the logic.
We've added another label and button.

guessTheNumber
we define that we need the global variables   numberToGuess totalGuesses to be accesible within this function. This is the prefered way to access global data.
has now expanded to change the text of the label .lblInfo since it looks better.
You can always use a tk_messageBox if you prefer it that way.
We then show how many guesses he needed before he got the correct answer and we'll also disable the button if he already guessed it.

tryAnotherNumber
This function resets the totalGuesses and choses another random number.

package require Tkproc initialize {} {global numberToGuess totalGuessesset numberToGuess [rnd 1 10]set totalGuesses 0ttk::label .lblInfo -text "Guess a number between 1 and 100"grid .lblInfo -row 1 -column 1grid [ttk::label .lblGuess -text "Guesses: $::totalGuesses"] -row 2 -column 1grid [ttk::entry .txtGuess ] -row 3 -column 1grid [ttk::button .btnGuess -text "Guess!" -command guessTheNumber ] -row 4 -column 1grid [ttk::button .btnAnother -text "Try another number!" -command tryAnotherNumber ] -row 4 -column 2}proc guessTheNumber {} {global numberToGuess totalGuessesset currentGuess [.txtGuess get]if {$currentGuess == $numberToGuess} {\t.lblInfo configure -text "Congratulations, you've guessed it!"\t.btnGuess configure -state disabled} else {\tincr totalGuesses\t.lblInfo configure -text "That's not the number, try again"}.lblGuess configure -text "Guesses: $totalGuesses"}proc tryAnotherNumber {} {global numberToGuess totalGuessesset totalGuesses 0set numberToGuess [rnd 1 10].lblGuess configure -text "Guesses: $totalGuesses"}#Utilities#Function that takes min and max and generates a random integer numberproc rnd {min max} {\texpr {int(($max - $min + 1) * rand()) + $min}}initialize

Version 3

For the 3d version we would like it if the computer would help us know how close we are to the number
We also want to make it more interesting so our numbers will be between 1 and  100.
Also limiting us to a maximum of 10 choices.

Take a moment to think how you could do this.

}

package require Tkproc initialize {} {global numberToGuess totalGuessesset numberToGuess [rnd 1 100]set totalGuesses 0ttk::label .lblInfo -text "Guess a number between 1 and 100"grid .lblInfo -row 1 -column 1grid [ttk::label .lblGuess -text "Guesses: $::totalGuesses"] -row 2 -column 1grid [ttk::entry .txtGuess ] -row 3 -column 1grid [ttk::button .btnGuess -text "Guess!" -command guessTheNumber ] -row 4 -column 1grid [ttk::button .btnAnother -text "Try another number!" -command tryAnotherNumber ] -row 4 -column 2}proc guessTheNumber {} {global numberToGuess totalGuessesset currentGuess [.txtGuess get]if {$currentGuess == $numberToGuess} {\t.lblInfo configure -text "Congratulations, you've guessed it!"\t.btnGuess configure -state disabled} else {\tincr totalGuesses\t.lblInfo configure -text [howClose $currentGuess $numberToGuess]}.lblGuess configure -text "Guesses: $totalGuesses"}proc howClose {currentGuess numberToGuess} {set absDiff [expr {abs($numberToGuess - $currentGuess)}]if {$absDiff > 50} {\tset text "It's freezing outside and you've put the air conditioning on. That's how close you are."} elseif {$absDiff > 30} {\tset text "Nope: Frozen cold"}  elseif {$absDiff > 20} {\tset text "The sun is shining"} elseif {$absDiff > 10} {\tset text "You're very warm"} elseif {$absDiff > 5} { \tset text "HOT"} else  {\tset text "The water's boiling "}return $text}proc tryAnotherNumber {} {global numberToGuess totalGuessesset totalGuesses 0set numberToGuess [rnd 1 100].lblGuess configure -text "Guesses: $totalGuesses"}#Utilities#Function that takes min and max and generates a random integer numberproc rnd {min max} {\texpr {int(($max - $min + 1) * rand()) + $min}}initialize

 

Ideas and comments

Andrei Clinciu
Andrei Clinciu

I'm a Full Stack Software Developer specializing in creating websites and applications which aid businesses to automate. Software which can help you simplify your life. Let's work together!

Building Great Software
The digital Revolution begins when you learn to automate with personalized software.

Find me on social media