Hangman in Java

Do not miss this exclusive book on Binary Tree Problems. Get it now for free.

In the realm of word games, few have stood the test of time quite like Hangman. This beloved game, with its simple yet captivating premise, has entertained and challenged players for centuries. But have you ever wondered about the origins of Hangman? How did this game of guessing letters and avoiding a grim fate come to be? In this article at OpenGenus, we delve into the intriguing history of Hangman, tracing its roots back through the ages and exploring its enduring appeal in the modern era and try to implement it in Java.

Table of Content

  • What is Hangman?
  • Initial UI
  • CustomUtil.java
  • Words.java
  • Dangman.java
  • Final Game
  • Few Things to mention

What is Hangman?

Hangman is a classic word-guessing game that can be played by two or more players. The objective of the game is to guess a hidden word by suggesting letters, while avoiding the drawing of a stick figure being hanged.

  1. Choose a word: The computer or the other player selects a word or phrase to be guessed. This word is kept secret from you.

  2. Display the word: The computer program will display a series of dashes or underscores, representing each letter of the chosen word. For example, if the word is "hangman," you might see " " on the screen.

  3. Start guessing: You begin by suggesting a letter that might be in the word. Type in your guess and submit it to the computer program.

  4. Correct guesses: If your guess is correct and the letter is in the word, the computer program will reveal the letter in its correct position(s) on the screen. For example, if you guess "a" and it appears twice in the word, the program will update the display to " a a ."

  5. Incorrect guesses: If your guess is incorrect and the letter is not in the word, the computer program will keep track of the incorrect guesses and may display a visual representation of the gallows or a hangman figure.

  6. Continue guessing: You keep guessing letters, one at a time, until either the word is completely revealed, or the hangman figure is fully displayed.

  7. Win or lose: If you successfully guess the word before the hangman figure is completed, you win the game. However, if the hangman figure is fully displayed before you guess the word, you lose.

Initial UI

We will start the project by creating a new project with Java Swing as a dependency and initializing to UI in main class & function.

public class Main {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(()-> new Dangman().setVisible(true));

    }
}

Dangman is just a punny name I chose for the hangman game, you can choose any name according to your choice. So now UI will be initiated from the main function.InvokeLater() method ensures that the task is being executed on event dispatch thread(EDT) which is the thread responsible for handling the user interface in Swing applications.

This will be the layout which we will try to implement that I have rougly made in draw.io. Lower boxes will contain buttons with alphabets to guess for the word.

CustomUtil.java

So we will use this class to define some basic functions that will be used throughout the game.

Starting the function that will load the images.
The images I have already made and I have put them in resources directory.

 public static JLabel loadImage(String resource){
        BufferedImage image;
        try{
            InputStream inputStream = CustomUtil.class.getResourceAsStream(resource);
            image = ImageIO.read(inputStream);
            return new JLabel(new ImageIcon(image));
        }catch(Exception e){
            System.out.println("Exception : "+e);
        }
        return null;
    }

It fetches the images one by one according to the number of guesses.
Now we need to update this image also, so we will create another method to update it.

 public static void updateImage(JLabel imageContainer,String resource){
        BufferedImage image;
        try{
            InputStream inputStream = CustomUtil.class.getResourceAsStream(resource);
            image = ImageIO.read(inputStream);
            imageContainer.setIcon(new ImageIcon(image));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

Now we will write a method to hide the word that will be chosen for the game.

public static String hideWords(String word){
        String hiddenWord = "";
        for(int i = 0;i < word.length() ;i++){
            if(!((word.charAt(i)) ==' ')){
                hiddenWord+="*";
            }
            else
                    hiddenWord+=" ";
        }
        return hiddenWord;
    }

and that's pretty much it for this class.

Words.java

This is where we will read the text file and load the random word chosen from the text file.
So let's see how the data should be in the text file. Its also located in resources directory as well.
The first word of each line will denote the category of the words, and words after that will belong to that category.

category,word1,word2,word3....

We will put them all in a HashMap to use it conveniently.

public class Words {
    private HashMap<String, String[]> wordList;
    private ArrayList<String> categories;

    public Words() {
        try {
            wordList = new HashMap<>();
            categories = new ArrayList<>();

            String filePath = this.getClass().getResource("/data.txt").getPath();
            if(filePath.contains("%20")) filePath = filePath.replaceAll("%20", " ");
            System.out.println(filePath);
            BufferedReader reader = new BufferedReader(new FileReader(filePath));
            String line;
            while ((line = reader.readLine()) != null) {
                String[] parts = line.split(",");
                String category = parts[0];
                categories.add(category);
                String[] values = Arrays.copyOfRange(parts, 1, parts.length);
                wordList.put(category, values);
            }
        } catch (IOException e) {
            System.out.println("Error: " + e);
        }
    }
}

We will create another method to load the word to the game.

 public String[] loadChallenge() {
        Random rand = new Random();
        String category = categories.get(rand.nextInt(categories.size()));
        String[] categoryWords = wordList.get(category);
        String word = categoryWords[rand.nextInt(categoryWords.length)];
        return new String[]{category.toUpperCase(), word.toUpperCase()};
    }

That leaves us with one class left now.

Dangman.java

As this will be our main window, this class will extend JFrame.In this class we will add a constructor which will set the properties of the frame. I will update the game properties separately in a function and that function will be called from the constructor.

public class Dangman extends JFrame {
    public Dangman(){
        super("Hangman Game");
        setSize(Constants.FRAME_SIZE);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setLayout(null);
        setResizable(false);
        words = new Words();
        letterButtons = new JButton[26];
        wordChallenge = words.loadChallenge(); 
        updateGUI();  //function to update the game
        createResult();
    }
 }

We will declare some components for the UI.

    private final Words words;
    private JLabel hangmanImage,categoryLabel,
                   hiddenWordLabel,resultLabel,wordLabel;
    private int incorrectGuesses;
    private String[] wordChallenge;
    private final JButton[] letterButtons;
    private JDialog result;

The Words class is responsible for fetching words from a text file, which will be discussed later on.
The hangmanImage label will display different images of the hangman based on the player's input. The CategoryLabel will provide a hint by showing the category of the chosen words. The hiddenWordLabel will conceal the word and update as the player correctly guesses letters. The resultLabel will display the game's outcome. The wordLabel will store the word and be used later. IncorrectGuesses will keep track of the player's incorrect guesses. The wordChallenge array will store the chosen word for the game. The letterButtons array will contain 26 buttons, each representing an alphabet. Finally, the result variable will be used to display the final result of the game.
We will load the hangman images, initially the first image which will be updated eventually as the game will progress.
Using grid layout, we will will initialize the button array with each of them containing an alphabet.

 for (char c = 'A' ;  c <= 'Z' ; c++) {
             JButton button = new JButton(Character.toString(c));
             button.addActionListener(this);
             int currentIndex = c - 'A';
             letterButtons[currentIndex] = button;
             buttonPanel.add(letterButtons[currentIndex]);
        }

We will set the styles and default texts for the buttons like restart, and
using getContentPane(), we will get the main frame of the program and will add the components to them.

getContentPane().add(categoryLabel);
getContentPane().add(hangmanImage);
getContentPane().add(hiddenWordLabel);
getContentPane().add(buttonPanel);

Now we need to add some action to the buttons we added, otherwise game will be unplayable.

So we will implement the ActionListener in the Dangman class, and we will override the actionPerformed() method.

 public void actionPerformed(ActionEvent e) {
        String command = e.getActionCommand();
        if(command.equals("Reset") || command.equals("Restart")){
            resetGame();
            if(command.equals("Restart")){
                result.setVisible(false);
            }
        }else if(command.equals("Exit")){
            dispose();
        }else{
            JButton button = (JButton) e.getSource();
            button.setEnabled(false);

            if(wordChallenge[1].contains(command)){
                button.setBackground(Color.green);

                char[] hiddenWord = hiddenWordLabel.getText().toCharArray();
                for(int i = 0 ; i < wordChallenge[1].length(); i++){
                    if(wordChallenge[1].charAt(i)==command.charAt(0)){
                        hiddenWord[i] = command.charAt(0);
                    }
                }

                hiddenWordLabel.setText(String.valueOf(hiddenWord));

                if(hiddenWordLabel.getText().contains("*")){
                    resultLabel.setText("That's correct");
                    resultLabel.setVisible(true);
                }


            }else{
                button.setBackground(Color.red);
                incorrectGuesses++;
               CustomUtil.updateImage(hangmanImage,"/"+(incorrectGuesses+1)+".png");
                if(incorrectGuesses>=6){
                    resultLabel.setText("Game over");
                    result.setVisible(true);
                }
            }
            wordLabel.setText("It was "+wordChallenge[1]);
            System.out.println(incorrectGuesses);
        }
    }

The first line of the method retrieves the action command associated with the event. The action command is typically set for buttons or menu items to identify their purpose.

The if-else statement checks the value of the action command. If it equals "Reset" or "Restart", it calls the "resetGame()" method and hides a component called "result" if the command is "Restart".

If the action command equals "Exit", it calls the "dispose()" method to close the current window.

If none of the above conditions are met, it means that a button was clicked. The code retrieves the button that triggered the event using e.getSource() and casts it to a JButton object.

If the clicked button's text matches a character in wordChallenge[1], it changes its background color to green, updates a hidden word label by replacing asterisks with correct guesses, and checks if there are still asterisks left. If there are, it sets a result label to display "That's correct".

If the clicked button's text does not match any character in wordChallenge[1], it changes its background color to red, increments an incorrectGuesses counter, updates an image using CustomUtil.updateImage(), and checks if incorrectGuesses is greater than or equal to 6. If so, it sets a result label to display "Game over".

Now we will implement the reset button action.

 private void resetGame(){
        wordChallenge = words.loadChallenge();
        incorrectGuesses = 0;
        CustomUtil.updateImage(hangmanImage,"/1.png");
        categoryLabel.setText(wordChallenge[0]);
        String hiddenWord = CustomUtil.hideWords(wordChallenge[1]);
        hiddenWordLabel.setText(hiddenWord);
        for(int i  = 0 ; i < letterButtons.length ; i++){
            letterButtons[i].setEnabled(true);
        }
    }

Now as the game finishes, we also need to show the result whether player loses or wins.

 private void createResult(){
        result = new JDialog();
        result.setTitle("Score");
        result.setResizable(false);
        result.setLocationRelativeTo(this);
        result.setLayout(new GridLayout(3,1));
        result.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                resetGame();
            }
        });
        resultLabel = new JLabel();
        wordLabel = new JLabel();
        JButton restart = new JButton("Restart");
        restart.addActionListener(this);
        result.add(resultLabel);
        result.add(wordLabel);
        result.add(restart);
    }

Now if everything is fine, our game should run successfully. I chose my own set of words and images to make it even more interesting.

Final Game

Few Things to Mention

To keep the code minimal and understandable, I have avoided the customization and appearence attributes. I have added them as a constant in a separate class and later can be imported in other classes. It can be customized even more, with better images, better words etc.

Complete Code

Sign up for FREE 3 months of Amazon Music. YOU MUST NOT MISS.