You now have all of the basics needed for you to write very sophisticated programs with graphical user interfaces.
A long time ago, Lesson 3 (Defining Your Own Commands) to be exact, you learned about procedural abstraction. It is the ability to assign a meaningful identifier to a bunch of instructions. Procedures with names are for your benefit – they allow you to control complexity, to break a problem into meaningful pieces. This capability, along with hierarchical structuring, became more and more important as the size of your programs grew.
Now it’s time to learn a about data abstraction. If you think of procedures as verbs, the nouns in a computer program are data.
You’ve worked a lot with numbers as data. You’ve been working with boolean values as data for the last few lessons. In the last lesson you learned how you can manipulate the characters in a word and the words in a sentence. In all of your work with these forms of data, the values of your data had the meaning expected. You used numbers where you needed to; you used the boolean values true and false where a predicate is needed.; And in your hangman game, you manipulated words.
You have also written programs with graphical user interfaces. You’re getting very familiar with recursion. You’ve used global variables to hold information accessed by multiple procedures. It’s time you got to work.
In this lesson you are going to do just that. You are going to write a game that I’m sure you will enjoy playing when you’re done – Mastermind. The challenge to you is to stop reading the lesson as soon as you can. Try to write the program on your own. When you need help, return to the lesson. When you complete your version of Mastermind, come back to finish reading the lesson. Compare and think about how your program evolved, versus the one in this lesson.
In this lesson, you will learn
- How to use a word, and the characters used to construct it, to represent concepts/data (data abstraction),
- how counting starting with zero instead of one can be useful in a computer program, and
- the need to anticipate edge conditions and handle them properly and make them part of your program testing.
You’re Assignment – Write Mastermind
Congratulations, you’ve just been hired as a programmer for a company that manufactures cell phones. Your first assignment is to write a game: Mastermind. Your company is getting ready to release a new phone. The new phone has a jLogo interpreter in it instead of the support for Java that the previous phone had. You’ve been hired because you have done so well on your jLogo lessons.
To bring you up to speed, the programmer that wrote Mastermind in Java for the previous phone, Jayne, has given you some of her notes, and access to a copy of the game. Here is her Java applet that implements her version of Mastermind.
|alt=”Your browser understands the <APPLET> tag but isn’t running the applet, for some reason.” Your browser is completely ignoring the <APPLET> tag!|
The object of Mastermind is to guess a secret code that the program has chosen. The computer randomly picks four colors from a pallet of six. In Jayne’s version of Mastermind, you get to choose whether all four colors are unique or if duplicate colors are allowed in the secret code. It has been decided that you do not have to provide this functionality; only the unique-color secret codes will be provided.
To play the game, you select colors from the column of six choices, filling in the row of empty frames which makes up your current guess. Your guess may contain duplicate color choices if this helps you solve the puzzle.
Clicking the left mouse button on a color choice box, fills the current empty guess box. If you want to change the choices in your guess, clicking on the [Clear Color] button, clears your previous choice (the guess box to the left of the current one). When your guess is complete, clicking on the [Guess] button provides feedback, guess hints.
If the guess is correct, “You Win” is displayed. Otherwise, two hints are given as feedback:
- the number of correct choices in the guess (in black), and
- the number of choices that are a correct color, but are not in the proper position in the sequence (in grey).
If you didn’t win, and there is still room on the display, another set of empty boxes are drawn for your next guess.
When you run out of room for another guess, “Sorry! Answer was:” is displayed along with the the secret code.
Try out the applet. See what it does when you interact with it. The program you write should perform as similarly as possible with this one.
Since Jayne wrote her version of Mastermind in Java, it would not be easy to translate it into jLogo. Translating from one computer language into another isn’t like translating English to Spanish. But, looking at her overall design might give you some ideas about how you can approach the task.
Jayne has a drawing of the methods in her program, including which methods invoke which others. Methods in a Java program are analogous to the procedures you have been writing.
Here is her drawing.
After Jayne completed the program she wrote the following description of the highest-level of the program’s execution. It goes well with Figure 15.1, giving a bit more insight to what the program does.
One of the most time-consuming things she did was to layout all the pieces of the program on the cellphone’s display. To save you time, she has provided a drawing that contains display coordinates for everything.
How to Proceed
This lesson is a bit different from those that have come before it. You do not have to continue reading; you know all of the jLogo primitive procedures needed to complete the Mastermind program. You know all of the programming techniques necessary to write it. How you go about it is up to you.
What follows are sections that you should treat as suggestions from your mentor. You are now an apprentice programmer. What you still need are tips of the trade. As a programmer, you have joined the ranks of the learners for life.
My suggestion to you at this point is for you to try to write the program on your own.
If you complete it – congratulations! Now read (or at least scan) the remaining sections and compare how you approached the problem versus what is suggested. Send me an e-mail (email@example.com) with your code in it and I’ll reply with my code so that you can compare them and learn even more.
If you get stuck, read one or more of the following sections. The Mastermind program has a couple of tricky parts to it, but you can write it if you’ve come this far in the lessons.
Getting Started – A Functioning GUI
Your experience with all of the programs you have written has taught you that the best way to get started is to get something working, and then extend it. Jayne said that the first thing that she did was to get all of the user-interface working. She said that, at first,she didn’t pick a secret code, she didn’t worry about checking matches or checking for a win. Her first pass at the program simply
- drew the choice boxes and buttons.
Once this was working, she
- drew an initial set of guess box frames.
And, once this was working, she
- got mouse clicks on the color choice boxes working.
This bit of programming consisted of filling in the guess boxes with the selected color as choices are clicked on. This turned out to be non-trivial since which guess box to fill needs to change. To do this, a global variable is needed to keep track of which box is the next one to fill.
Once this was working, she
- added support for the [Clear Color] button
since it was so similar to making the choices. As it turned out, this was non-trivial too.
Before reading any more of this lesson, take a crack at writing all of this initial code.
The next two sections present programming tricks of the trade, tips for the apprentice in the Art of Computer Programming. These tips are useful for writing all of the code described above.
Counting Starting With Zero Instead of One
Since we were very young, when we were first introduced to mathematics, we learned to count things starting with one. Count on your fingers – one, two, three, four…
Well, in programming, there is an advantage to start your counting with zero in many iterative processes. Drawing the guess boxes is just such a case. Read on to see why.
First, I’ll define a few symbolic constants so that I can use them in the explanation.
to guessBoxSize output 20 end to guessBoxGap output 5 end to guessBoxesLeftX output -15 end to guessesTopY output 130 end
Figure 15.4 shows what the values depict.
Given these symbolic constants and two variables (
guessNumber), the lower-left corner of every guess box can easily be computed.
boxNumToFill will start out at zero when the current box is the left-most one. It will be incremented as the boxes get filled in. When it contains three, the current box is the last (right-most) box.
guessNumber will start out at zero for the top row of guess boxes. As guesses are made, and a new row of guess boxes is needed, it is incremented.
This gets us to the equations:
Let: a = the size of a guess box (guessBoxSize) b = the size of a gap between any two guess boxes (guessBoxGap) c = the left edge of the left-most box (guessBoxesLeftX) d = the bottom edge of the top row of boxes (guessesTopY) n = current box number in range 0...3 (boxNumToFill) m = current guess number 0...9 (guessNumber) x = the current guess box's left x value y = the current guess box's bottom y value then, x = c + n(a+b) and y = d - m(a+b)
Make sure you understand why our variables (
guessNumber) must start with values of zero.
To see how this simplifies things, rewrite the equations assuming the variables start with values of one, e.g.,
boxNumToFill iterates through values: 1, 2, 3, and 4.
Finally, operators can be written for each of the equations. Here is the code for the first.
to curGuessBoxX output sum guessBoxesLeftX product :boxNumToFill sum guessBoxSize guessBoxGap end
and Figure 15.5 shows it graphically.
I’ve got some things for you to try.
- What happens in your program when all four guess boxes have been filled in and you keep clicking on choices?
- What happens in your program when all four guess boxes are empty (the current box is the first) and you click on [Clear Color] ?
By testing these situations, you are exercising edge conditions.
Wrapping Up the GUI Code
What’s left to do?
A few things need to happen when the [Guess] button is clicked on.
- The program needs to display guess hints. For now, just draw two zeros, or random digits…
- Move on to a new set of guess boxes. This involves incrementing the
guessNumberglobal variable, reseting the
boxNumToFillglobal variable, and drawing four new boxes (but you should be able to use the same procedure that drew the first boxes).
- When incrementing
guessNumberexceeds the maximum allowable guess count, the program should display something like “Sorry, you lose.”
You will also need to write a procedure that displays “You Win!” but this can wait if you tired of working on the GUI and want to move on.
Representing the Answer and Guess in the Program
Now that you have all of the graphical user interface complete, it’s time to add the logic for the game itself. A *LOT* of code had to be written for the interface… The good news is that the logic for the actual game can be a lot less code, that is… if you use words to represent the secret code and guesses.
The obvious way to represent the secret code and the guess is with a bunch of global variables, one for each color in the secret code and one for each box of the guess. You could do this. Try this if you want, but I’ll warn you – you will be writing a lot of if commands.
The alternative is to use a sentence or word for the secret code and guess. I suggest using words; you have a lot of experience manipulating them now that you’ve written the Hangman game.
The words will be composed of the first characters of each of the colors that are in the game.
r for red o for orange y for yellow g for green b for blue v for violet
The easiest way for me to demonstrate how using a word will work is to show you how my version of the program generates the secret code. There are quite a few different ways in which to select four unique letters from our set of six. I’m going to show you the one I like the best.
The basic steps in the approach I’m going to take are listed in Table 15.1.
The algorithm I’m using comes from Brian Harvey’s book “Computer Science Logo Style.” It’s quite clever and definitely a technique worth learning. Writing code is an art and so it’s best to learn from the masters when you have the chance.
- To make the most out of the following hints you should stop at the end of paragraphs. Do not peek ahead at the code that I give next until you’ve at least pondered how you would write the code on your own. You should really try to write your own version of the code before checking mine out.
In the last lesson you learned the primitives that are available to assemble/disassemble words:
butlast. And, the first exercise I asked you to do was to write an operator (rotate) which takes the leading character off of a word and appends it onto the end. You should now enhance rotate so that it takes two inputs: a number of times to rotate, and a word.
to rotate :num :wd if equal? :num 0 [output :wd] output rotate (difference :num 1) (word butfirst :wd first :wd) end
This new version can be used to get a random letter. Test it…
? println rotate random 6 "roygbv bvroyg ? println rotate random 6 "roygbv oygbvr ? println rotate random 6 "roygbv gbvroy
Got this working? Ok, then add the declaration of a global variable which will hold the secret code to the top of your Mastermind code. Then add your the procedure(s) to fill it with four random characters from the six choices. Here is most of my code. I’ve put comments in place of code for all of the steps from Table 15.1.
;global variable containing the program assembled secret code make "secretCode " to makeSecretHelper :choices ; stop-rule: secretCode has all four characters? ; rotate choices a random amount ; append first character of choices on to secretCode ; recursive invocation - removes chosen character from choices end to makeSecretCode make "secretCode " makeSecretHelper "roygbv end
So – finish the code…
Make sure to take advantage of trace to watch how the global variable (secretCode) changes as your code executes.
Once you have some code, working or not (at least try), compare it with my code for makeSecretCode.