How can I keep track of the unguessed letters yet in my JTextArea? In the non gui version I used an arraylist, worked fine but should I leave the arraylist and use it to track unguessed caracters ? The method is still console based, just started implementing the gui made the layout now connecting it. What I did so far was append the number of letters that the chosen word contains

public void guessWord(){
        for (int i = 0; i<wordToGuess.length();i++){ //create all letters represented by "_"
            al.add(i," _ ");
        }
        //System.out.println(al);
        for(String s: al){
            wordToGuessGUI.append(s);
        }

        Scanner sc = new Scanner(System.in);
        while(wrongGuesses<5 && al.contains(" _ ")){ // If 5 mistakes or the word is completed
        letter = sc.next(); //get input
        if(wordToGuess.contains(letter)){ //does the word contain the input letter
            guessedLetter = wordToGuess.indexOf(letter); //location of the guessed letter
            al.set(guessedLetter,letter); //replace the "_" with the letter guessed
            System.out.println(al); //print the current state of the word
            if(!al.contains(" _ ")) System.out.println("Congratulations, you won!");
        }
        else {
            System.out.println("Wrong, try again! " +wrongGuesses +" out of 5 wrong attempts");
            wrongGuesses++;
            if(wrongGuesses>=5) System.out.println("Game Lost"); // game lost if 5 incorrect answers
        }

    }}

Recommended Answers

All 16 Replies

Hmm, in general is it a bad idea to use JTextArea for this? I need a way to set a letter on a position, like ArrayList's set.. Can I use the ArrayList for the GUI? JTextArea has insert but I think it inserts new item doesnt replaces a current one?

MVC!
Separate the GUI from the game logic as much as possible. Store everything in the game logic, and provide accessors for any user interface (console, GUI, HTML etc) to enter moves and keep them selves up to date.

Hm, makes it clearer to read?

Also, I have NPE in here ..

letterGUI.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e){
                setLetter(letterGUI.getText());
                letterGUI.setText("");
            }
        });

I am setting what is entered in the GUI to the string that takes the input character by the user but if(wordToGuess.contains(letter)) has null at start( I think) and when I run it it gives NPE

How can I solve that?

Without the whole code it's very hard to see where an NPE starts from.

Um, I changed the logic in a file and then the gui in another it looks like this currently

class HangmanLogic{
    private String[] words = {"first","second","third","fourth"}; 
    private static String wordToGuess;
    Random rnd = new Random();
    private static String letter;
    private int wrongGuesses = 0;
    private int guessedLetter;
    private ArrayList<String> al = new ArrayList<String>();


    HangmanLogic(){


        wordToGuess = words [rnd.nextInt(words.length)]; // Choose a random word from the list

    }

    public static void main (String[] args){
        HangmanLogic hm = new HangmanLogic();
        System.out.println(wordToGuess);
        hm.guessWord();
    }

    public static void setLetter(String s){
        letter = s;
    }

    public void guessWord(){
        for (int i = 0; i<wordToGuess.length();i++){ //create all letters represented by "_"
            al.add(i," _ ");
        }
        System.out.println(al);

        Scanner sc = new Scanner(System.in);

        while(wrongGuesses<5 && al.contains(" _ ")){ // If 5 mistakes or the word is completed
        letter = sc.next(); //get input

        if(wordToGuess.contains(letter)){ //does the word contain the input letter
            guessedLetter = wordToGuess.indexOf(letter); //location of the guessed letter
            al.set(guessedLetter,letter); //replace the "_" with the letter guessed
            System.out.println(al); //print the current state of the word
            if(!al.contains(" _ ")) System.out.println("Congratulations, you won!");
        }
        else {
            System.out.println("Wrong, try again! " +(wrongGuesses+1) +" out of 5 wrong attempts");
            wrongGuesses++;
            if(wrongGuesses>=5) System.out.println("Game Lost"); // game lost if 5 incorrect answers
        }

    }}
}

and the GUI class

class Hangman{
    private ArrayList<String> al = new ArrayList<String>();
    private JFrame frame = new JFrame("Hangman");
    private JTextArea wordToGuessGUI = new JTextArea(10,20);
    private JTextField usedLettersGUI = new JTextField(20);
    private JTextField letterGUI = new JTextField(1);
    private StringBuilder sbWordToGuess = new StringBuilder();
    private JTextArea finalMessage = new JTextArea(2,20);
    private HangmanLogic hml= new HangmanLogic();

    Hangman(){

        JPanel messagesNorth = new JPanel(new GridLayout(2,2,10,5));
        //letterGUI.setHorizontalAlignment(SwingConstants.RIGHT);

        messagesNorth.add(new JLabel("Used letters"));
        messagesNorth.add(new JLabel("Enter a letter"));
        messagesNorth.add(usedLettersGUI);
        messagesNorth.add(letterGUI);

        JPanel messagesCenter = new JPanel(new GridLayout(2,1));
        wordToGuessGUI.setEditable(false);
        messagesCenter.add(wordToGuessGUI);

        JPanel messageSouth = new JPanel(new GridLayout(2,1));
        messageSouth.add(new JLabel("Game information"));
        messageSouth.add(finalMessage);
        finalMessage.setEditable(false);

        frame.getContentPane().add(messagesNorth,"North");
        frame.getContentPane().add(new JScrollPane(messagesCenter),"Center");
        frame.getContentPane().add(messageSouth,"South");

        //frame.setLayout(new FlowLayout());
        frame.setSize(500,250);     
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);

        letterGUI.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e){
                hml.setLetter(letterGUI.getText());
                letterGUI.setText("");
            }
        });

    }

    public static void main (String[] args){
        Hangman hm = new Hangman();
    }
}

I assume now I need to go back to the logic class and set the parameters to read/write to the gui textfields/areas right?
Then I am confused about the part where in the console mode I use sc.next() to get input by the user but in GUI mode i need an action to be performed, so how do I read the letter in the while loop in this case? because when i am in the while loop even if I set it in the actionListener it won't change it, Do I need multithreading for this?

With Swing you automatically are multi-threaded. There's always the thread your main runs on, and the Swing thread where all the mouse clicks, keyboard entry,a nd all your listeners are processed. You don't use while loops because, in effect, Swing already has the while loop waiting for user input in the GUI.

There are lots of ways to do this, but, for example, you could set up your model with no UI code of any sort, but with public methods like:

public int tryLetter(String aLetterToTry) 
   // returns 0 if word is completed, 
   // 1 if letter is correct, 
   // 2 if not correct, 
   // 3 if out of tries
   // (an enum would be better, if you know about them yet)
public string String getWordSoFar();  // returns word with underscores and correct letters

Now in your GUI you instantiate a model, and in your actionPerformed you can do stuff like:

int result = model.tryLetter(inputTextField.getText());
outputTextField.setText(model.getWordSoFar());
switch (result)
   case 0:  display success message
   case 1:  display OK so far, keep trying
   case 2:  display sorry, keep trying
   case 3:  display out of tries, game over

This separates your app into a model that has complete logic, but doesn't care about the UI, and a GUI (or console UI, or web interface etc) that doesn't care about how the game works.
You've broken one big problem into two smaller ones, which is exactly how huge systems get written in real life.

Oh, that makes sense! So just call tryLetter in action performed if I understood correct?

Yes, all that sample pseudo-code is in the actionPerfomed that is called every time the user enters a letter.

Hey James, I got the GUI running now and it works pretty well. Now I want to add actual pictures where for each wrong letter new image would be added such as arms/legs etc. Any ideas where to start at?

Counting the number of wrong guesses is part of the game definition, so it belongs in the model. You'll need a getWrongGuesses() method to return that value so the GUI can use it.
Now the GUI. Start with a JPanel in which to build up and display the hanging man. You can use JLabels as the easiest way to display actual images (load the image, create an ImageIcon from it, set that in the JLabel). This is one rare case when you may want to use a null layout manager for the JPanel so you can position each image exactly to fit together properly.
Any time the user enters a letter incorrectly call getWrongGuesses() and use that to make the right number of JLabels visible or invisible. (Put all that into its own method, of course)

Great advice, thank you

Hmm, I have the following now Picture

this is how I get the icon
private Icon hanged1 = new ImageIcon( getClass().getResource( "1.PNG" ));
and then I pass it as parameter for JLabel, which displays the image in JPanel gridLayout(2,1)

Problem - The picture seems a bit small and I really don't like the grey background. How can I extend the size of it? I tried google-ing around but all I found was overriding method paint of JLabel. Is that the only way(looking for something easier/more comfortable)?

If you load it as an Image you can use getScaledInstace to re-size it before converting to ImageIcon, but the reuslts may not be great. Better to do it in some proper image editing program - in which case you can also set a transparent background and save as a GIF that supports transparent backgrounds.

So, for example use GIMP to scale the image to match my Panel and save it as a GIF but why transparent?
Also, how can I set my frame size to be final so it won't be possible for users to resize it?
Would final frame.size(int,int) work?

setResizable(false) should do the trick with the frame, i ll just scale the images to match the panel. Also, I want to add hints for the words. My idea is 2 hashmaps 1 for words and 1 for hints ( hints on same position as the word on the other hashmap), would that work?

Actually never mind, i got the game running at the moment seems pretty decent, marking the thread as solved, if I have further questions i ll ask :)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.