Playing Seaman in GameMaker


<<Previous | ToC | Next >>

At this point in your development of Seaman you should have finished laying out your widgets in GameMaker and initialized them in Java. You also have an English description of how the program needs to work. Now we -- I mean you -- will write Java code to do it. Here, for reference is my English code for the keystroke event; yours could be different, so you should make whatever adjustments are necessary:

let guess be the input character
if playing is false then:
if guess is < 'a' then
set playing = true
otherwise
add guess to the guessword
add +1 to nLetts
show next dashes character
otherwise (if playing is true)...
This is going into the (predefined) method KeyFld, which I already uncommented:
public void KeyFld(GameWgt whom, char info) {
  if (didSomethingWithIt) return;       // but call super..
  super.KeyFld(whom,info);} //~KeyFld
We are going to replace everything but the first line. The documentation tells us that "The base class uses KeyFld to insert keystrokes in an entry text field," but we have no entry text field we want the engine inserting text into, so we will eliminate the "super.KeyFld" call to prevent it.

The input character came to us with the (predefined) name "info" but our English calls it "guess". We can copy "info" to a local variable named "guess", or we can change our program description to use "guess", your choice. The English description is not Java, but I'd bet you know how to write Java to do each of those things, right?

Except maybe the "add guess to the guessword" part. We know that guessword is an array of characters, and the input character info (or guess) is a character, so it's just a matter of assigning the next array element to be the value of the input character. We know the number of letters so far is in variable nLetts, which was initialized to zero (no letters yet). We also know that Java array elements begin with index [0]. So the first time into this method, variable nLetts is exactly correct to index the array. Then it gets incremented to one before the method returns to the system. When the second letter arrives, what is in nLetts? And what index do you need to access the second letter in the guessword array? Convince yourself that nLetts is exactly right to index the whole array, for every letter in the word, then write the code to do it. Can you do that?

What about the "show next dashes character" line? The whole dashes array of widgets was initialized to the underscore character and then hidden, so all this line needs to do is unhide it, which is the same method we used to hide it, except with a parameter true. It's an object method (in class GameWgt) so it needs to be applied to an object of that class. All the elements of array dashes are GameWgts, so any one element of the array can be used to unhide that widget. Are you still with me? The first element of that array is index [0] (because this is Java), but what's in the letter count nLetts when you get to this line? Oops. That wasn't a problem when you access the array guessword, why is that? What can you do to fix it this time? Remember the first of our Five basic ideas of computer programming? Sequence. We got the sequence wrong in the English, but we can fix that. Fixing errors is a large part of what we do in programming.

Are you still with me? Check your code so far. Nine lines of English, nine lines of Java (eight if you decided to use info as your input character). Got it? Each indent in the English should have a corresponding indent in Java, and begin with a left ("curly") brace; each outdent in Java needs a right brace to match the left at the corresponding indent. Got it? Each conditional in Java must be surrounded by a pair of parentheses, the left after the keyword 'if' and the right in place of the keyword 'then'. Got it? Finally the English "otherwise" is spelled 'else' in Java. Got it? If you are still rusty in Java, you can review where this is explained, in "Six Ideas in Java".

OK, now we are ready for the game play. Here again is the English (Kitchen) code we worked with back when we were designing for ASCII graphics:

let sofar = nLetts {= unguessed} -- Note 1
repeat {as many times as needed} -- Note 2
  Print dashes and letters  -- Note 3
  If gottem=posn then Stop  -- Note 4
  if nerrs > 6 then Stop    -- Note 4
...
  next {as many times as needed} -- Note 2
...
  Input guess,1             -- Note 5
  if guess < "A" exit       -- Note 6
...
"Decide if input is correct"
  let gotit = false
  Let try = 0               -- Note 7
  repeat nLetts {try each letter}
    if myWord[try]=guess then Do ItsEqual
    Add 1 to try
    next {try next letter}
  if gotit = false then do UnEqual  -- Note 8
  (let the GameEngine draw everything)    -- Note 3
  Done

"UnEqual"
  Add 1 to nerrs
  show the body part corresponding to nerrs -- Note 9
  Done

"ItsEqual"
  Let item try of myWord = guess
  Subtract 1 from sofar
  let gotit = true
  Done

if sofar = 0 print "You got it!"           -- Note 4
if nerrs > 6 print "Better luck next time" -- Note 4

First of all, we need to be aware that this design was written for when the computer is controlling the action. In the GameEngine the user controls the action. An important consequence of that is that the outer "repeat" here (Note 2) is replaced by the event system of the GameEngine (which gets it from Java), and exits from that iteration (Note 4) need separate handling to terminate the game properly; we'll come back to this. The GameEngine also does all the drawing (Note 3) and input (Note 5), all your program needs to do is process the input characters and make sure the widgets are up todate.

We already mentioned that the four blue lines (Note 7) turn into one Java for-loop. I think you can figure that out.

The first line here (Note 1) is initialization for the play portion of the event handler, but the number of letters in the word to be guessed is not known until the word entry part is complete. I would put this assignment in the part of the entry portion of the program where playing is set to true. The declaration still needs to be up where we declared the other class-wide variables (you should probably initialize sofar=0). You can probably preserve some of the subroutine structure of your English code for the guessed letter analysis, and just call the appropriate subroutine(s) within the "otherwise" at the end of the word input (playing == false) segment, that is, after that line's left brace, and before the corresponding right brace, which is followed by right brace which marks the end of the whole KeyFld method, but you should carefully rethink what it does.

In Tom's Kitchen computer, a single character input is always capitalized (Note 6). Java does no such thing, you need to test for lower case and caps both, but at least for lower case. We sort of assumed that the user will press enter when they are done, which is the end of the input word, but also might signify that they are tired of playing the game, and you should politely ask if they want to quit or keep going, then quit if that's what they want to do. Oh wait, this is event-driven. Maybe at the front of the game you can tell them that entering a non-letter will signify they're giving up. Or not, depending on how much you want them to hate you for making your game so hard to play.

In Tom's Kitchen computer, the '=' symbol (Note 8) is used for both assigning values to variables and also for testing equality. Java uses different symbols, the single '=' for assignment and a double'==' for testing equality. Unfortunately -- they got this blunder from C -- they allow assignments inside of expressions, so if you write something like

if (gotit = false) then {
the Java compiler will not complain at all! What happens is that the variable gotit is assigned the value false, and then the result (now in gotit) is tested, but it's now false, so the next line is never executed. What most people want when they write this should have been
if (gotit == false) then {
which means (do not change anything!) just do the next line if gotit is already false. When testing variables of any other type, accidentally typing '=' instead of '==' assigns the value to the variable, but the value is not boolean so the compiler complains of a type fault (although it might not say so, but it still stops and reports an error). The best way to prevent this kind of uncaught error is never to compare a boolean value to true or false, just use the value by itself 'if (gotit)' to test for true, or with an exclamation point (boolean negation) in front to test for false, like this:
if (!gotit) then {
Finally we have (Note 9) when the player guesses wrong, we want to draw the body part. We called a subroutine to do this in ASCII graphics because it was complicated, and there were several different ways to do it that we wanted to explore. If you had an array (say called "bodypart"), it would be as simple as
bodypart[wrong].HideSho(true);
You could still do that if you wish, but building that array is not as easy as it was for the dashes. However, Java has a sort of indexed conditional called "switch" that you can use like this:
switch (wrong) { // the brace is required
case 1:
  LefLeg.HideSho(true);
  break; // don't forget the break, Java will not complain!
case 2:
  RitLeg.HideSho(true); // ..and so on
  break;
...
default: // (anything not covered by the cases)
  // (whatever) (you can omit the default if it does nothing)
  break;} // this brace closes the switch
Do you think you can finish this out?

In the next page I'll try to help you out with the parts that didn't work for you, but first you should give it your best shot.

<<Previous | ToC | Next >>

[2022 November 19]