• Post Reply Bookmark Topic Watch Topic
  • New Topic

Text-based Hangman game: can't get char[] of letters to display correctly  RSS feed

 
Greenhorn
Posts: 25
1
Android
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I need to read in a text file that contains a list of words. Once I do, I need to then read in the user's guess for whatever letter they input then compare that to whatever random word was chosen. But I can't seem to get my arrays to save the correct guesses. I'm not sure if it's the way I wrote my loops but whenever I test it, the previous guessed letters will disappear and only the current guess is displayed which is obviously what should not happen. My int value for the incorrect guesses is also incrementing... incorrectly. I just need a different perspective from someone to point out what I am doing wrong. Whenever I try and fix it, I just end up making myself more confused.

 
Andrea Menjivar
Greenhorn
Posts: 25
1
Android
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
In case you need to see my results
Screenshot-(140).png
[Thumbnail for Screenshot-(140).png]
In case you'd like to see my results
 
Saloon Keeper
Posts: 3331
46
Eclipse IDE Firefox Browser Java MySQL Database VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I haven't tracked down your specific problem but I think your code could use some cleaning up.

  • You use a variable "counter" all over the place and it doesn't seem to be used to count anything. Some places it's a char from an array, in other places it seems to be an index into an array.
  • You need to have a method do only a single clearly defined task. Your checkUserGuess() for instance is also responsible for displaying the guessed letter.
  • Your getRandomWord() uses getRandomNumber(). getRandomNumber() has an arbitrary limit of 34493. I presume this is how many lines are in the file. Why not load all the words into your  "words" list and then use: randomWord = words[ rand.nextInt( words.size() ) ]; // where "rand" is an instance of Random.
  • You have "char[] word". It is not an array of word. It is better named something that describes what it is, like "wordLetters".
  • Ultimately you should move things out of main() and have a constructor for Hangman(). Then do away with the "static" modifiers for your methods and fields.

  •  
    Rancher
    Posts: 2240
    28
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    I suggest some changes to your debugging print statements to show the values of the variables being used.  When I execute the code I get this:
    Guess a letter:
    TEST
    false
    true
    false
    false
    true
    false
    TEST2
    TEST2

    which tells me nothing about what is happening.

    Another suggestion for testing: Hardcode the word and the user's input:
    Add some more print statements in the checkUserGuess method that print the values of all the variables used.

    Line 85 should not be inside another method:
     
    Carey Brown
    Saloon Keeper
    Posts: 3331
    46
    Eclipse IDE Firefox Browser Java MySQL Database VI Editor Windows
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Norm Radder wrote:Another suggestion for testing: Hardcode the word and the user's input:

    Agreed. I might add a couple of bad letters to your string on line 1.
     
    Sheriff
    Posts: 11494
    180
    Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Carey Brown wrote:I might add a couple of bad letters to your string on line 1.

    If OP was not having a problem implementing just basic functionality, I'd agree that it might be a good idea to test for corner cases. As it is, the happy path isn't even in sight yet so I don't think now is the time to think about corner cases. Do that when you have a better understanding of the problem and the appropriate solution.
     
    Junilu Lacar
    Sheriff
    Posts: 11494
    180
    Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    @Carey, I think I understand what you meant by "bad letters" now; you meant "incorrect guesses," right? So, that's still kind of in the exception path, although I wouldn't necessarily consider incorrect guesses as corner cases. I think Norm was hinting at just getting the execution correct if the user guessed correctly every time.  Once OP has that working, then tackle the next part of the full solution and change the test to include incorrect guesses.
     
    Sheriff
    Posts: 4293
    127
    Chrome Eclipse IDE Java Postgres Database VI Editor
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    @OP: I think that if you implement the suggestions for how to organize your code, the bugs will disappear or become more obvious.

    Another programming tip: Don't write: ... write
     
    Andrea Menjivar
    Greenhorn
    Posts: 25
    1
    Android
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Thanks everyone for your suggestions. I rushed through this code but am doing the whole thing over again. Hopefully, it'll come out a lot better this time around.
     
    Andrea Menjivar
    Greenhorn
    Posts: 25
    1
    Android
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Hey again. I realized that my problem was when I was taking in the character input from the user and attempted to compare that to the chars in my char array. It turns out if you input the letter a, it will interpret that as NOT being the same as the letter a in the array. If you enter 'a' with the quotation marks, THEN it interprets that as being correct. Now my problem is figuring out how to solve that problem without having the user enter quotes along with a character. Any suggestions?
     
    Junilu Lacar
    Sheriff
    Posts: 11494
    180
    Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    That doesn't sound right. Can you show us your code that you think exhibits this behavior?
     
    Andrea Menjivar
    Greenhorn
    Posts: 25
    1
    Android
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Junilu Lacar wrote:That doesn't sound right. Can you show us your code that you think exhibits this behavior?


    I redid the whole thing. This is my main class:



    And the object class holding my methods:
     
    Junilu Lacar
    Sheriff
    Posts: 11494
    180
    Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    There are a number of problems with your HangmanClass class.  First of all, when you feel there's a need to add "Class" to your class name, then there's something wrong with the way you're naming your classes.  It's like me calling you "AndreaPerson".  Look at all the other classes in the standard library.  Is ArrayList is just ArrayList. It's not called ArrayListClass.  Can you imagine how annoying that would be if all classes in the standard library had "Class" unnecessarily tacked on to the end of their names?  StringClass, IntegerClass, TreeMapClass, HashMapClass, DateClass, CalendarClass, ...  It would be silly, right?

    The class that has your static void main() method is usually referred to as the "Driver".  In your case, it's better to name the class that you call "Hangman" as the "HangmanDriver".  Then you can just call the class that actually represents the game as "Hangman"
     
    Andrea Menjivar
    Greenhorn
    Posts: 25
    1
    Android
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Junilu Lacar wrote:There are a number of problems with your HangmanClass class.  First of all, when you feel there's a need to add "Class" to your class name, then there's something wrong with the way you're naming your classes.  It's like me calling you "AndreaPerson".  Look at all the other classes in the standard library.  Is ArrayList is just ArrayList. It's not called ArrayListClass.  Can you imagine how annoying that would be if all classes in the standard library had "Class" unnecessarily tacked on to the end of their names?  StringClass, IntegerClass, TreeMapClass, HashMapClass, DateClass, CalendarClass, ...  It would be silly, right?

    The class that has your static void main() method is usually referred to as the "Driver".  In your case, it's better to name the class that you call "Hangman" as the "HangmanDriver".  Then you can just call the class that actually represents the game as "Hangman"


    I'm sorry. I didn't mean to hurt my progam's feelings.
     
    Junilu Lacar
    Sheriff
    Posts: 11494
    180
    Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    The "issue" that you said you noticed is a mis-diagnosis. The problem is NOT 'a' (with quotes) vs. a (no quotes).  It has to do with your handling (or in this case, mishandling) of the fields you declare on line 6 and 7 of what you call "HangmanClass" -- for the sake of future posts, I'll just call this the "Hangman class" instead and refer to your other class with the main method as the "driver" class.

    In your constructor, you have this code:

    This constructor has no arguments, so lines 11 and 12 are equivalent to this:

    You're assigning the fields to themselves. Since the fields are initially null, you're basically just unnecessarily setting these fields the same value of null again. What was your intent here?

    In the checkUserGuess() method, lines 29 and 30 are this:

    Again, what was your intent here? What this code actually does is it clobbers whatever you had in the correctGuesses and lettersAlreadyGuessed fields and replaces them with totally new array.  The correctGuesses array would then be all false and the lettersAlreadyGuessed array would have all 0s.  I don't think that's what you want.
     
    Junilu Lacar
    Sheriff
    Posts: 11494
    180
    Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Andrea Menjivar wrote:I'm sorry. I didn't mean to hurt my progam's feelings.

    I don't know what your intent is with this comment. Are you trying to make a joke? Are you being sarcastic? It's hard to tell over the internet because text doesn't carry with it tone and inflection.  Leaving your tone and inflection up for interpretation can lead to misunderstanding.  I could interpret that as sarcasm and just stop trying to help you.  Just to be clear, I am actually a little annoyed by your reply.
     
    Knute Snortum
    Sheriff
    Posts: 4293
    127
    Chrome Eclipse IDE Java Postgres Database VI Editor
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Andrea Menjivar wrote:Hey again. I realized that my problem was when I was taking in the character input from the user and attempted to compare that to the chars in my char array. It turns out if you input the letter a, it will interpret that as NOT being the same as the letter a in the array. If you enter 'a' with the quotation marks, THEN it interprets that as being correct. Now my problem is figuring out how to solve that problem without having the user enter quotes along with a character. Any suggestions?

    I think the confusion lies in the difference between "a", which is a literal String, and 'a', which is a literal char.  Scanner#next() will return a String.
     
    Junilu Lacar
    Sheriff
    Posts: 11494
    180
    Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    I think the confusion lies in the difference between "a", which is a literal String, and 'a', which is a literal char.  Scanner#next() will return a String.

    OP's code already extracts charAt(0) from the returned value. See line 45 of her driver class.
     
    Andrea Menjivar
    Greenhorn
    Posts: 25
    1
    Android
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Junilu Lacar wrote:
    Andrea Menjivar wrote:I'm sorry. I didn't mean to hurt my progam's feelings.

    I don't know what your intent is with this comment. Are you trying to make a joke? Are you being sarcastic? It's hard to tell over the internet because text doesn't carry with it tone and inflection.  Leaving your tone and inflection up for interpretation can lead to misunderstanding.  I could interpret that as sarcasm and just stop trying to help you.  Just to be clear, I am actually a little annoyed by your reply.


    Well, I was a little annoyed by your reply as well. I can always change the name of my program later on. I was feeling frustrated and you berating me over a class name didn't help. I am open to suggestions but not people who just criticizes the part of the code that has nothing to do with the actual problem. It's fine if you don't want to help. I'm not open to taking attitude that doesn't offer any real help.
     
    Junilu Lacar
    Sheriff
    Posts: 11494
    180
    Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    I did not "berate" you. That was how you received the message I was trying to send. As I said, tone and inflection is not carried over in text. If my response came off as having a "berating" tone to it, that was not my intention. 

    The importance of good names cannot be overemphasized. This is something that even many professional developers overlook and it leads to large amounts of unnecessarily bad and misleading code. There is even an entire chapter of the book "Clean Code" that discusses just this one topic, that of choosing good names.  There is even a joke which is also seriously true, that some of the best programmers in the world say that the TWO hardest problems in programming/computer science are:

    1. Cache invalidation
    2. Choosing good names
    3. Off-by-one errors

    By far, #2 is the most difficult of these problems for many programmers.

    So, when I "berated" you on the name you chose, it wasn't because I was nitpicking. Good names that reflect the purpose of the things to which they are attached lead you to a better understanding of your program. Names that do not match what they are intended to represent lead you astray and they are often the source of confusion and misunderstanding. So, by fixing the names first, you can address the issue of cognitive dissonance that the bad names create. Cognitive dissonance gets in the way of you understanding the other problems in your program and figuring out how to fix it. If you'd rather keep going with your poorly chosen names, that's your choice. Just don't keep complaining that you're confused because you are, in part, the problem.
     
    Junilu Lacar
    Sheriff
    Posts: 11494
    180
    Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    If you were annoyed by the seemingly trivial nature of my response regarding names, you could have simply asked why that was important, since you can always come back and fix that later. I would have replied the same way I had now, except probably in a nicer tone. I know how inconsequential and trivial choosing good names seem to most programmers, especially beginners like you. However, it has been my experience from mentoring many professional developers that once they start getting into the habit of choosing good names, the quality of the programs start improving in leaps and bounds. That's because they can do away with the cognitive dissonance that was keeping them from seeing their program clearly.

    In the followup to my response regarding names, I told you what, in fact, was the problem in your code.
     
    Junilu Lacar
    Sheriff
    Posts: 11494
    180
    Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    And in case you're wondering what kind of cognitive dissonance was created by the class names Hangman and HangmanClass, it's this: There's no clear distinction between "Hangman" and "HangmanClass".  Why are these two separate classes? What's the difference between the two? Isn't Hangman also a class? And Isn't HangmanClass also a class? As I was alluding to, it's the same kind of confusion that can be created by me calling you "AndreaPerson" instead of just "Andrea".  If there were another person named Andrea and I referred to them as just Andrea (naturally, right?) and I referred to you as AndreaPerson, anyone would naturally wonder "Why AndreaPerson? What's the significance of 'Person' in that name?"  If it turns out that it was just an arbitrary choice on my part, people would think I'm just being silly.

    Program parts should have a clear purpose. That purpose should be reflected in their names. Similarities in names makes you naturally think there's an association between two things.  Differences in the names makes you think there must be some kind of important distinction. Take for example a List and an ArrayList. The names imply a relationship and also a distinction.

    So, what is the distinction that is implied by the names Hangman vs. HangmanClass? Nothing, right? So right there, through those poorly chosen names, you have built in some confusion about the organization of your program. I suggested that you name the class with the static main method as HangmanDriver.  The main method is the "driver" of this program, that is, it drives the execution by coordinating user input with the Hangman class' behavior of tracking the current state of the game.  Since one class is the "driver" and there's only one other class in the program, the names also imply that the Hangman class is "driven by" the HangmanDriver class. Now you have established a clear relationship between these classes. Having a clear understanding of the relationship between two classes gives you a better understanding of what responsibilities each class has in relation to the overall goal of the program.

    Good, understandable programs make it easy to see which parts are responsible for doing which tasks in the program. If you don't have a clear understanding of that, that's where problems and confusion can creep in. I was feeling sorry for you because it was almost 3 a.m. and you were still up, struggling to understanding your program.  I was simply trying to help you clear up some of that fog in your head that you had created.
     
    Andrea Menjivar
    Greenhorn
    Posts: 25
    1
    Android
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Hello again everyone. I more or less finally finished this program and got it working the way it should. I don't know... I guess the code could be better but at least it works now. Thanks again everyone for your help. I attached my code again below (if you care at all about the progress of this post). I just need to fix the exception handling and then I'll be done (although that's way out of the scope of why this question got posted).
    This forum won't let me attach text documents so I also included some words all the way on the bottom if you wanted to run this (just copy and paste into your own txt document).


    MAIN TEST CLASS:



    OBJECT CLASS:



    SOME WORDS:

    capone
    capons
    capped
    capper
    capric
    caprin
    capsid
    captor
    captus
    carafe
    carate
    carats
    carbon
    carboy
    carded
    carder
    cardia
    careen
    career
    carers
    caress
    carets
    cargos
    carhop
    carica
    caries
    carina
    caring
    carlot
    carnal
    carols
    caroms
    carpal
    carped
    carpel
    carper
    carpet
    carpus
    carrel
    carron
    carrot
    carson

     
    Junilu Lacar
    Sheriff
    Posts: 11494
    180
    Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Congratulations on getting it working. Your solution is not very different in essence to what I came up with.  In my solution, I also have two classes but the second class is responsible for functionality related to reading in and managing the words.

    None of the methods in my code are very long. Actually, for a beginner, you did pretty good in terms of keeping the size of your methods down, except for the huge main() method you still have. See how good names of given to every part of the program helps make it more coherent and cohesive? The only part I felt a need to comment was the part where I had a loop in WordList.secret() to make sure that the next secret word given out was not the same as the last one. The rest of the code is pretty much self-documenting and doesn't require any comment to clarify.
     
    Junilu Lacar
    Sheriff
    Posts: 11494
    180
    Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Junilu Lacar wrote:
    The only part I felt a need to comment was the part where I had a loop in WordList.secret() to make sure that the next secret word given out was not the same as the last one. The rest of the code is pretty much self-documenting and doesn't require any comment to clarify.

    In fact, with a quick refactoring, I can also get rid of the comment and make that method more self-documenting. Again, a good name is key to making this improvement.
     
    Junilu Lacar
    Sheriff
    Posts: 11494
    180
    Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Compare what you wrote in your main() method:

    with this:

    There are a couple of differences to note:

    1. The semantics - the expression "incorrectGuesses == maxGuesses" is a formula. It still has to be deciphered/translated into the idea that "the player still didn't guess what the word was (because he'd reached the maximum number of incorrect guesses allowed)" The second version is a little better but there's still a little more brain power required to deduce that since there are more letters to guess and the game is done, then the player must have not have guessed what the word was. The code could be refactored even further to express the intent even more clearly:



    These may seem very subtle and unnecessary but the principle of expressiveness and clarity cannot be dismissed. The more you try to stick with these principles, the clearer your program becomes and the easier it is to maintain/extend later on.

    2. The use of comments - the first version relies on a comment to explain what the code is doing, the second doesn't have comments but still manages to explain what's going on by making the code more expressive.
     
    • Post Reply Bookmark Topic Watch Topic
    • New Topic
    Boost this thread!