This site requires JavaScript, please enable it in your browser!
Greenfoot back
Tommy99
Tommy99 wrote ...

2015/10/15

Hangman Game Problem

Tommy99 Tommy99

2015/10/15

#
Hello, I am almost finished with my Hangman game, but I have two major problems. The first is that I get an infinite loop whenever I click on any letter (I click to see if the letter is found in the hangman phrase) that is part of the unknown phrase. My second problem is replacing the underscores with the letters if they are correct. Also, I don't know how to detect if I have won the game or not. Any help is greatly appreciated. Here's my code for the Mask class (The unknown phrase):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import greenfoot.*;
import java.awt.Color;
 
/**
 * The mask is a label that displays underscores to hide letters of the phrase.
 */
public class Mask extends Label
{
    private String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
    public String phrase;
    private String mask = "";
    String newMask;
 
    public Mask(String msg)
    {
        super(msg,25);
        setFillColor(Color.BLACK);
        setPhrase(msg);
    }
 
    /**
     * Resets the phrase and creates a new mask.
     */
    public void setPhrase(String msg)
    {
        phrase = msg;
        mask = createMask();
        setValue(mask);
    }
 
    /**
     * Replaces each letter of the phrase with an underscore.
     */
    public String createMask()
    {
        for(int pos = 0;pos<phrase.length();pos++){
            char symbol = phrase.charAt(pos);
            //If it's a space, it isn't in alphabet. It's not a letter.
            if(ALPHABET.indexOf(Character.toString(symbol).toUpperCase())==-1){
                mask+=symbol+" ";
            } else {
                mask+="_ ";
            }
        }
 
        //add code here
 
        return mask;
    }
 
    /**
     * accessor method
     */
    public String getMask()
    {
        return mask;
    }
 
    /**
     * Replaces all underscores in the mask that corresponds to the
     * letter parameter and returns true is that letter was found in the phrase
     * or false if the letter was not found in the phrase.
     */
    public boolean showLetter(String letter)
    {
        phrase = phrase.toUpperCase();
 
        int pos = phrase.indexOf(letter);
        if(pos != -1) {
            do {
                 
                //Cut the "a"
 
                pos = phrase.indexOf(letter);
                //Make a new string and take the digit out.
                //add code here
 
                newMask = phrase.substring(0,pos)+phrase.charAt(pos)+phrase.substring(pos);
 
 
            } while (pos != -1);
            setValue(mask);
        } else {
            return false;
        }
        return true;
    }
 
    /**
     * The player has won if there are no more underscores in the phrase.
     */
    public boolean checkForWin()
    {
        return true;   //add code here to check if all blanks have been filled
    }
}
(It is a subclass of the imported Label class. I have not changed the Label class.) Here's the code for the World class, in case it helps:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
import java.util.*;
import java.io.*;
 
public class OldWest extends World
{
    private String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
     
    private Mask theMask;
    private String theMessage;
    private Scaffold scaffold;
    private Label result;
 
 
    public OldWest()
    {   
        // Create a new world with 600x400 cells with a cell size of 1x1 pixels.
        super(800, 400, 1);
        scaffold = new Scaffold();
        addObject(scaffold, getWidth()/2, getHeight()/2);
        addLetters();
 
 
        theMessage = "all's well that ends well.";
        theMask = new Mask(theMessage);
        addObject(theMask,getWidth()/2,getHeight()-70);
    }
 
     
     
 
    /**
     * Adds the letter tiles to the top of the world
     */
    private void addLetters()
    {
       for(int pos = 0;pos<ALPHABET.length();pos++){
             
            addObject(new Letter(ALPHABET.substring(pos,pos+1)), pos*31+13,30);
             
        }
    }
 
    /**
     * After a letter tile has been clicked, check to see if the letter
     * is in the phrase. If it is, reveal it in the mask. Otherwise, display the
     * next image on the scaffold.
     */
    public void checkForLetter(String letter)
    {
        if(theMask.showLetter(letter)) {
            if(theMask.checkForWin()) {
                result = new Label("You win", 50);
                addObject(result,getWidth()/2,getHeight()/2);
            }
        } else {
            if(!scaffold.endOfGame()) {
                scaffold.nextImage();
            } else {
                result = new Label("Game Over. You Lose",50);
                addObject(result,getWidth()/2,getHeight()/2);
            }
        }
    }
 
    
}
danpost danpost

2015/10/15

#
Line 75 of the Mask class is the only line within the 'do' loop that assigns a value to 'pos', whose value determines whether the loop continues or not; but, because neither 'letter' nor 'phrase' is changed during the execution of the loop, 'pos' will always be assigned the same value it was set to before the loop at line 69. Therefore, once inside the loop, it will repeat indefinitely.
Tommy99 Tommy99

2015/10/16

#
Oh okay, thank you. How do you suggest I fix this though? I'm not sure what to do. I tried a couple of things but I just get new errors. Do you have any suggestions?
danpost danpost

2015/10/17

#
Tommy99 wrote...
Oh okay, thank you. How do you suggest I fix this though? I'm not sure what to do. I tried a couple of things but I just get new errors. Do you have any suggestions?
Even before that, I would like to suggest that you change line 27 in the Mask class to the following:
1
phrase = msg.toUpperCase();
Then, you do not have to continually change the case in the other methods Also, I do not see where 'ALPHABET' is being used within the class. Now, as far as the 'do' loop. I would suggest that instead of a 'do' loop that you use a 'for' loop and just check each character of the 'phrase' string for the given letter. Also, for simplification, you can start the method with the following:
1
2
letter = letter.toUpperCase();
if (phrase.indexOf(letter) < 0) return false;
followed by adjusting the mask (using a 'for' loop) and ending with a 'return true;' statement. Make sure you account for the spaces in the mask and set the altered mask as the value of the label. In your code above, you build a 'newMask' string, but set 'mask', which is never changed, as the new value of the label. Also, 'newMask' does not get the spacing that 'mask' was given when created.
Tommy99 Tommy99

2015/10/17

#
I have no idea how to do that final part, and I am still extremely confused right now. I tried using a "for" loop but now I get a terminal window error.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import greenfoot.*;
import java.awt.Color;
 
/**
 * The mask is a label that displays underscores to hide letters of the phrase.
 */
public class Mask extends Label
{
    private String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
The error:
 
[code]java.lang.StringIndexOutOfBoundsException: String index out of range: 1
    at java.lang.String.charAt(String.java:646)
    at Mask.showLetter(Mask.java:104)
    at OldWest.checkForLetter(OldWest.java:51)
    at Letter.act(Letter.java:30)
    at greenfoot.core.Simulation.actActor(Simulation.java:594)
    at greenfoot.core.Simulation.runOneLoop(Simulation.java:552)
    at greenfoot.core.Simulation.runContent(Simulation.java:215)
    at greenfoot.core.Simulation.run(Simulation.java:205)
public String phrase; private String mask = ""; public String newMask; private StringBuilder quessedWord; public int counter = 0; public Mask(String msg) { super(msg,25); setFillColor(Color.BLACK); setPhrase(msg); } /** * Resets the phrase and creates a new mask. */ public void setPhrase(String msg) { phrase = msg.toUpperCase(); mask = createMask(); setValue(mask); } /** * Replaces each letter of the phrase with an underscore. */ public String createMask() { for(int pos = 0;pos<phrase.length();pos++){ char symbol = phrase.charAt(pos); //If it's a space, it isn't in alphabet. It's not a letter. if(ALPHABET.indexOf(Character.toString(symbol).toUpperCase())==-1){ mask+=symbol+" "; } else { mask+="_ "; } } //add code here return mask; } /** * accessor method */ public String getMask() { return mask; } /** * Replaces all underscores in the mask that corresponds to the * letter parameter and returns true is that letter was found in the phrase * or false if the letter was not found in the phrase. */ /*public boolean showLetter(String letter) { phrase = phrase.toUpperCase(); int pos = phrase.indexOf(letter); if(pos != -1) { //If the letter is part of the phrase. do { //Letter and phrase don't change. //Cut the "a" int next_pos = phrase.indexOf(letter,pos+1); //Make a new string and take the digit out. //I must break out of the loop. //add code here newMask = phrase.substring(0,pos)+phrase.charAt(pos)+phrase.substring(pos); //pos = phrase.indexOf(letter); } while (pos != -1); setValue(mask); } else { return false; } return true; }*/ public boolean showLetter(String letter) //2nd try. { letter = letter.toUpperCase(); if (phrase.indexOf(letter) < 0) { return false; } for (int i=0; i<mask.length(); i++) { if ( mask.charAt( i ) == letter.charAt(i) ){ quessedWord.setCharAt( i, letter.charAt(i) ); counter++; }//Replace the ?'s with the character } if(counter!=0){ return true; } return false; } /** * The player has won if there are no more underscores in the phrase. */ public boolean checkForWin() { return true; //add code here to check if all blanks have been filled //Invoke checkForLetter } }
danpost danpost

2015/10/17

#
You are using 'letter.charAt(i)' in the for loop. The 'letter' string is only one character long. Change it to 'letter.charAt(0)' ('0', instead of 'i'). Also shouldn't you be comparing the letter to the characters within the phrase, not the mask? Another issue may be that you are building the 'mask' string at the end of the original mask ('mask' needs to be cleared of characters before creating a new mask -- this may require that 'mask' be copied to another string or a new mask be copied to mask at the appropriate location). Please read the page linked to 'Posting code? read this!' below the 'Post a reply' box so your code is line numbered and easier to work with.
Tommy99 Tommy99

2015/10/17

#
I took your advice, but I have no idea what to do from here. I don't know how to update the mask. Please help, I just want to finish this. It's causing too much stress haha.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public boolean showLetter(String letter) //2nd try.
    {
         
 
        letter = letter.toUpperCase();
        if (phrase.indexOf(letter) < 0) {
            return false;
        }
 
        for (int i=0; i<mask.length(); i++) {
            if ( phrase.charAt( i ) == letter.charAt(0) ){
                quessedWord.setCharAt( i, letter.charAt(0) );
                 
                 
                 
                counter++;
 
            }//Replace the ?'s with the character
 
        }
        if(counter!=0){
            return true;
        }
        return false;
 
    }
Tommy99 Tommy99

2015/10/17

#
Also, how do I check if I have won (gotten all of the letters)?
danpost danpost

2015/10/17

#
You either changed how your 'mask' was created or you are getting an error message which you are not informing us of. Your 'for' loop iterates beyond the length of the phrase by a factor of two because you are using the length of the mask as the limit in the loop. Your counter is not being reset to zero at the beginning of the 'showLetter' method. So, once a letter is found, the method will never again return a 'false' value. You could create a "final" mask (a string that represent what the mask should look like when the phrase is solve) to compare to the current mask when a letter is found. You would create this final mask string at the time you set the phrase.
Tommy99 Tommy99

2015/10/18

#
Alright, I took your advice again and got some help from StackOverflow, and I think I understand it much more now. Thank you very much for your help.
You need to login to post a reply.