Learn Programming in Java


 

<<Previous | ToC | Next >>

Don't Get Too Creative

This is the third of four programs you already did in English, which you are translating to Java as a way to become familiar with the peculiarities of Java. Don't Make Unnecessary Changes. If your browser has tabs or separate windows, open a new tab (or window) with this link to The Six Things in Java, so you can refer to it as often as needed.

The purpose of this assignment is to learn to use arrays in Java by translating into working Java the existing and (already) working English Seaman program.

Seaman

Again we start with an existing English program. If you saved yours, (or get it from the Done archive, then) use it. Otherwise here is mine:
 
Variable nerrs = 0 "Hide word from 2nd player"
Variable gottem = 0   Repeat 25
      Print " "
"Play Seaman"     Next
  Part 1   Done
  repeat  
    Part 2 "Print dashes and letters"
    next   Print gWord " (" nerrs " misses)"
  done   If gottem=posn then Stop
    If nerrs=6 then Stop
"Part 1"   Done
  Print instructions  
  Input whole word "Input letter"
  Hide word from 2nd player   Input guess,1
  done   Print "Got " guess
    Done
"Part 2"
  Print dashes and letters "Decide if input is correct" 
  Input letter   Let try = 0 
  Decide if input is correct   Let got = 0 
  {Do Print Seaman}   Repeat posn 
  done     Add 1 to try
      if myWord[try]=guess then Do ItsEqual
"Print instructions"     Next
  Print "This is Seaman, yada, yada"   if got = 0 then Do UnEqual
  Done   {Do Print Seaman}
    Done
"Input whole word"
  Array myWord "UnEqual"
  Array gWord   Add 1 to nerrs
  Variable posn   Done
  Print "Enter your word"  
  Let posn = 0 "ItsEqual"
  Repeat   Let item try of gWord = guess
    Input guess,1   Add 1 to gottem
    If guess < 'A' then Exit   Add 1 to got
    Add 1 to posn   Done
    Let item posn of myWord = guess  
    Let item posn of gWord = "_"
    Next
  Print myWord " is " posn " letters. " gWord 
  Done  
You could merge your main English program (here in mine, that would be "Play Seaman") with the Java main(), or else add a subroutine call from main() to whatever you called your PlaySeaman() method. Recall that the best way to do this to insert empty subroutine shells for all the subroutines -- in Java they are called "methods" for no particular reason -- like this (these are my names here on the left, use your own)...
class Main {
  static void main(...) {
    PlaySeaman();}
  static void PlaySeaman() {
    }
  static void Part1() {
    }
  static void Part2() {
    }
  static void PrintInstructions() { 
    }
  static void InputWholeWord() {
    }
  static void HideWord() {
    }
  static void PrintDashes() {
    }
  static void InputLetter() {
    }
  static void Decide() {
    }
  static void UnEqual() {
    }
  static void ItsEqual() {
    }
}

Replit has a little triangle in the left margin of every line that begins a block of code (above right), so you can click on that triangle and it collapes the whole block into a single line. This is very helpful because it helps you to focus on THE MAIN THING, which is the particular subroutine you are working on (see discussion here) with everything else not even visible. It also helps you see if you got the braces unpaired, which is arguably the most annoying syntax bug Java programmers need to deal with. After all your method headers are in place, you can begin to convert the English code for each, one at a time.

You may recall, we picked letters out of an array variable in English. Java also has arrays, and we will use them. The canonical way to refer to the third item in a list in English is "item 3 of theList" whereas in Java we use brackets "theList[3]". You can use the same form in English also (and it works). English arrays start at item number 1, whereas Java arrays always start with index [0], but it doesn't matter, you can ignore the 0th item and it still works correctly.

Remember, variables that are used in more than one subroutine -- ah, method -- must be declared at the class level, inside the first brace, but before any subr-- I mean method that uses them. So we will declare them as (the 'static' is required because we are not yet -- and there is no need to be -- doing this as OOPS)...

static char[] myWord = new char[64];
static char[] gWord = new char[64];
Do you think 64 is enough? Google tells me there are a few English words longer than 40, but only one of each size, so if your player picks one of those, the other person would know immediately (after consulting Google) which word it is.

We can simply set the designated array element (letter) to the appropriate value, same as English:

myWord[posn] = letter;
gWord[posn] = '_';
Notice that character constants in Java use apostrophes (single quotes) while String constants use double quotes. These are character (char) arrays, not strings.

Testing those same letters is as simple as using the array reference:

if (myWord[try]==guess) ItsEqual();
and you can put characters back into the array:
gWord[try] = guess;
Oh wait, "try" is a reserved word in Java, you need to spell your variable differently, perhaps with an "x" or added "v" (maybe in your code you already did), but be sure to change it everywhere in your program.

The last line of my English "Input whole word" subroutine (your code is probably different, but you may need to do the same thing somewhere) prints the whole (initially) dashes gWord. There is a hint at how to do this in the general discussion on arrays, but it seems unclear, so let's look closer...

It's an array, so the only way to print the whole word is one letter at a time, using a for-loop.

System.out.print("The guessed word so far is ");
for (int n=1; n<=posn; n++)
  System.out.print("" + gWord[n]);
System.out.println("");
Note that Java arrays start at zero. If you preserve the English semantics (where the word starts at array position [1]) when you convert the program to Java, then you'd also want the for-loop to start at n=1 and test n <= posn to stop after (rather than before) you do the nth position in the array. Notice also the println after the for-loop terminates. When you are printing a bunch of stuff (like this array of characters) on the same line, the output will be left on the same line, and the next time you go to print another line it will start where you left off instead of on a new line. Unless you really want it on the same line, this is poor form. The final println cures that.

Don't forget, Java is strongly typed, so to read the letters as characters, you need to use Zystem.ReadLetter(), and you may also need to filter out the extra Enter key inputs, like you did back in RPS.

OK, do you think you can do the rest? Make it work in Java, then we'll look at the different ways to do ASCII Graphics.

Bonus, If your English program looks more or less like mine (above), then your translation into Java has maybe eleven 'static void' subroutines with no parameters. Mostly that's all you can do with them -- at least it was in RPS -- but here we can get a little creative (now that it works) and make it formally look better, that is, have better form. We are looking for subroutines that compute a value used somewhere else, preferably immediately in the subroutine that called it, where we can return that value as a function result instead of a global variable, and also for subroutines that use a value computed in the caller which could be passed as a parameter instead of a global variable.

Looking through the whole program, I see in my version that variable guess has that property, it is defined (by Input) in subroutine InputLetter, but used in subroutine  Decide, and from there also in ItsEqual. For that to work properly, you have to put its declaration up at the class level. And that's OK, but it looks bad. It's better to declare it locally in subroutine Part2, then set it as a return value from (now char function) InputLetter, and pass it as a parameter to Decide. The same name is also used in InputWholeWord, but only locally, so we can keep it local there, and eliminate the class variable entirely.

Here's the improved code:

static void Part2() {
  char guess;
  PrintDashes();
  guess = InputLetter();
  Decide(guess);
}

static char InputLetter() {
  char guess;
  while (true) {
    guess = Zystem.ReadLetter();
    if (guess >= 'A') break;}
  System.out.println("Got " + guess);
  return guess;
}

static void Decide(char guess) {
  // No further changes, except...
 

Variable guess is still used in one more subroutine, called by Decide. I think you can figure out how to get it there without using a class variable, right? Can you find another place where you can get rid of a class variable by using a parameter and function result?

This is an important skill. You can write working Java programs using global variables to pass data around, but you will find it difficult to keep track of those global variables in big programs. If and when you decide to take the APCS exam as a measure of your Java skill, they will expect you to do this properly, and they will grade you on it. Now is a good time to begin.

Don't forget, you are working with an early version of this tutorial, so if it's not clear to you what to do, or why the Replit (or BlueJ) compiler doesn't like what you gave it, that's my fault, not yours. Summon a Mentor, and two things will happen:

1. Somebody will come and work with you to get past the problem.

2. I will make the documentation better -- maybe next week, more likely next term. We need your help. Really.

-- or use the "Ask" feature in Zoom.

Next: ASCII Graphics

<<Previous | ToC | Next >>

Revised: 2023 February 3