I’ve been getting used to Java and I’ve written a simple hangman game using event-driven programming in Java 8. Please let me know of any improvements regarding the readability and design of the program. **Main - used to start the game** package Hangman; public class Main { public static void main(String[] args){ new Logic().go(); } } **Logic - contains all the game logic** package Hangman; import java.awt.Toolkit; import java.util.ArrayList; import java.util.List; /** Logic - Deals with the logic aspect of Hangman */ public class Logic { private GUI gui; private Information information; private WordReader wordReader; /** Logic constructor */ public Logic(){ wordReader = new WordReader(); information = new Information(wordReader); gui = new GUI(this, information); } /** checkUserGuess - runs the code for when a user guesses a letter that's in the secret word */ void checkUserGuess(String guess){ if(isGuessValid(guess)) { updateSecretWord(guess); gui.drawSecretWord(); if(information.getLettersGuessed().size() != 0) { // No letters have been guessed by the user at the beginning gui.drawLettersGuessed(); } // Checks if the user has won or lost if (isGameWon()) { winSequence(); }else if (information.getGuessesRemaining() == 0) { loseSequence(); } } } void go(){ information.createAlphabetSet(); wordReader.readFile("words.txt"); gui.build(); setUpGame(); } /** isGameWon - sees if the user has won the game */ private boolean isGameWon(){ for(boolean isLetterShown : information.getLettersRevealed()){ if(!isLetterShown) { return false; } } return true; } /** isGuessValid - get input from the user & checks to see if it's a valid guess */ private boolean isGuessValid(String guessedLetter){ if(guessedLetter.length() == 1 && !information.getLettersGuessed().contains(guessedLetter.charAt(0)) && information.getAlphabet().contains(guessedLetter.charAt(0))) { gui.setText(null); information.addGuessedLetter(guessedLetter.charAt(0)); return true; }else{ Toolkit.getDefaultToolkit().beep(); gui.getTextField().requestFocusInWindow(); } return false; } /** loseSequence - when the the user runs out of guesses */ private void loseSequence(){ for(int i = 0; i < information.getLettersRevealed().length; i++){ information.revealLetter(i); } gui.drawSecretWord(); playAgain("Tough luck. The secret word was " + information.getSecretWord() + ".\nWould you like to play another game of hangman?"); } /** playAgain - Allows the user to play another game of hangman */ private void playAgain(String message){ if(gui.playAgainDialog(message)){ setUpGame(); gui.updateDrawing(); }else{ System.exit(0); } } /** setUpGame - sets up the variables appropriately */ void setUpGame(){ information.resetGameData(); // Redraws the JLabels when playing another game of hangman gui.drawSecretWord(); gui.drawLettersGuessed(); gui.drawGuessesRemaining(); } /** updateSecretWord - updates which letters of the secret word have been revealed */ private void updateSecretWord(String guessedLetter){ List<Integer> changeBool = new ArrayList<>(); if(information.getSecretWord().contains(guessedLetter)){ // Searches through secretWord & notes down all letters that equal the user's guess for(int i=0; i<information.getSecretWord().length(); i++){ if(information.getSecretWord().charAt(i) == guessedLetter.charAt(0)) { changeBool.add(i); } } // Changes the boolean value for those letters @ their corresponding indexes for(Integer i : changeBool) { information.revealLetter(i); } }else{ information.setGuessesRemaining(information.getGuessesRemaining() - 1); gui.drawGuessesRemaining(); gui.updateDrawing(); } } /** winSequence - when the user has correctly guessed the secret word */ private void winSequence(){ playAgain("Well done! You guessed " + information.getSecretWord() + " with " + information.getGuessesRemaining() + " guesses left!\nWould you like to play another game of hangman?" ); } } **GUI - contains everything related to the GUI** package Hangman; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import static javax.swing.SwingUtilities.invokeLater; import javax.swing.WindowConstants; /** GUI - Deals with the GUI aspect of Hangman */ public class GUI { private JFrame frame; private JPanel contentPane; private JPanel containerPanel; private JTextField textField; private JLabel guessesRemainingLabel; private JLabel lettersGuessedLabel; private JLabel secretWordLabel; private static final Dimension FRAME_SIZE = new Dimension(250, 350); private Information information; private Logic logic; /** GUI constructor */ public GUI(Logic logic, Information information){ this.logic = logic; this.information = information; } void build(){ invokeLater( () -> { buildFrame(); buildContentPane(); buildContainerPanel(); buildLabel(); buildInputPanel(); displayFrame(); } ); } private void buildContentPane(){ contentPane = new DrawingPanel(information); contentPane.setLayout(new BorderLayout()); contentPane.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0)); frame.setContentPane(contentPane); } private void buildContainerPanel(){ containerPanel = new JPanel(new BorderLayout()); contentPane.add(BorderLayout.SOUTH, containerPanel); } private void buildFrame(){ frame = new JFrame("Hangman"); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); frame.setMinimumSize(FRAME_SIZE); } private void buildInputPanel(){ JButton checkButton = new JButton("Guess"); GuessListener guessListener = new GuessListener(); checkButton.addActionListener(guessListener); textField = new JTextField(); textField.addActionListener(guessListener); JPanel inputPanel = new JPanel(new BorderLayout()); inputPanel.add(BorderLayout.CENTER, textField); inputPanel.add(BorderLayout.EAST, checkButton); inputPanel.setAlignmentX(Component.LEFT_ALIGNMENT); containerPanel.add(BorderLayout.SOUTH, inputPanel); } private void buildLabel(){ JPanel labelPanel = new JPanel(); labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.PAGE_AXIS)); guessesRemainingLabel = new JLabel("Guesses remaining: " + String.valueOf(Information.NUM_OF_GUESSES)); lettersGuessedLabel = new JLabel("Already guessed: "); secretWordLabel = new JLabel(); guessesRemainingLabel.setAlignmentX(Component.LEFT_ALIGNMENT); lettersGuessedLabel.setAlignmentX(Component.LEFT_ALIGNMENT); secretWordLabel.setAlignmentX(Component.LEFT_ALIGNMENT); labelPanel.add(guessesRemainingLabel); labelPanel.add(lettersGuessedLabel); labelPanel.add(secretWordLabel); containerPanel.add(BorderLayout.NORTH, labelPanel); } private void displayFrame(){ frame.setSize(FRAME_SIZE); frame.setLocationRelativeTo(null); frame.setVisible(true); } /** drawGuessesRemaining - Shows the guesses remaining that the user has left */ void drawGuessesRemaining(){ invokeLater(() -> guessesRemainingLabel.setText("Guesses remaining: " + String.valueOf(information.getGuessesRemaining()))); } /** drawLettersGuessed - Shows the letters guessed by the user */ void drawLettersGuessed(){ StringBuilder lettersBuilder = new StringBuilder(); for (Character guessedLetter : information.getLettersGuessed()) { String s = guessedLetter + " "; lettersBuilder.append(s); } setLettersGuessedLabel(lettersBuilder.toString()); } /** drawSecretWord - draws secretWord with dashes & letters that the user has correctly guessed so far */ void drawSecretWord(){ StringBuilder word = new StringBuilder(); for(int i = 0; i < information.getLettersRevealed().length; i++){ if (information.getLettersRevealed()[i]) { String letter = information.getSecretWord().charAt(i) + " "; word.append(letter); } else { word.append("_ "); } } setSecretWordLabel(word.toString()); } /** playAgainDialog - asks the user if they want to play another game of hangman */ boolean playAgainDialog(String message){ int optionChosen = JOptionPane.showConfirmDialog(frame, message, "Play again?", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE); return optionChosen == JOptionPane.YES_OPTION; } void updateDrawing(){ contentPane.repaint(); } // GETTERS JTextField getTextField(){ return textField; } // SETTERS void setLettersGuessedLabel(String text){ invokeLater(() -> lettersGuessedLabel.setText("Already guessed: " + text)); } void setText(String text){ invokeLater(() -> textField.setText(text)); } void setSecretWordLabel(String text){ invokeLater(() -> secretWordLabel.setText(text)); } // LISTENERS /** GuessListener - deals with the user's guess */ private class GuessListener implements ActionListener { @Override public void actionPerformed(ActionEvent ev){ logic.checkUserGuess(getTextField().getText()); } } } **DrawingPanel - used to draw the hangman figure** package Hangman; import java.awt.BasicStroke; import java.awt.Graphics; import java.awt.Graphics2D; import javax.swing.JPanel; /** DrawingPanel - Panel used to draw the hangman figure onto the frame */ public class DrawingPanel extends JPanel { // Constants to do with the hanging frame private static final int STARTING_X_POSITION = 55; private static final int STARTING_Y_POSITION = 30; private static final int BASE_Y_POSITION = STARTING_Y_POSITION + 200; private static final int WIRE_LENGTH = 40; // Constants to do with the hangman himself private static final int HEAD_RADIUS = 17; private static final int BODY_LENGTH = 50; private static final int BODY_X_POSITION = STARTING_X_POSITION + 120; private static final int ARM_LENGTH = 25; private static final int ARM_Y_POSITION = STARTING_Y_POSITION + WIRE_LENGTH + (2 * HEAD_RADIUS) + 20; private static final int LEG_LENGTH = 30; private static final int LEG_Y_POSITION = STARTING_Y_POSITION + WIRE_LENGTH + (2 * HEAD_RADIUS) + 50; private Information information; // Solely used to get the number of guesses remaining /** DrawingPanel constructor */ public DrawingPanel(Information information){ this.information = information; } /** drawHead - draws the head onto the panel */ private void drawHead(int xPosition, int yPosition, Graphics g){ g.drawOval(xPosition, yPosition, 2 * HEAD_RADIUS, 2 * HEAD_RADIUS); } /** drawBody - draws the body onto the panel */ private void drawBody(int xPosition, int yPosition, Graphics g){ g.drawLine(xPosition, yPosition, xPosition, yPosition + BODY_LENGTH); } /** drawLeftArm - draws the left arm onto the panel */ private void drawLeftArm(int xPosition, int yPosition, Graphics g){ g.drawLine(xPosition, yPosition, xPosition - ARM_LENGTH, yPosition); } /** drawLeftLeg - draws the left leg onto the panel */ private void drawLeftLeg(int xPosition, int yPosition, Graphics g){ g.drawLine(xPosition, yPosition, xPosition - LEG_LENGTH, yPosition + LEG_LENGTH); } /** drawRightArm - draws the right arm onto the panel */ private void drawRightArm(int xPosition, int yPosition, Graphics g){ g.drawLine(xPosition, yPosition, xPosition + ARM_LENGTH, yPosition); } /** drawRightLeg - draws the right leg onto the panel */ private void drawRightLeg(int xPosition, int yPosition, Graphics g){ g.drawLine(xPosition, yPosition, xPosition + LEG_LENGTH, yPosition + LEG_LENGTH); } /** setUpDrawing - draws the wire frame of the hangman drawing when the user hasn't lost a guess yet */ private void setUpDrawing(int xPosition, int yPosition, Graphics g){ // Base of the frame Graphics2D g2D = (Graphics2D) g; g2D.setStroke(new BasicStroke(2)); g2D.drawLine(xPosition - 15, BASE_Y_POSITION, xPosition + 50, BASE_Y_POSITION); // Frame support g2D.setStroke(new BasicStroke(1)); g2D.drawLine(xPosition, yPosition, xPosition, BASE_Y_POSITION); g2D.drawLine(xPosition, yPosition + 20, xPosition + 20, yPosition); g2D.drawLine(xPosition, yPosition, BODY_X_POSITION, yPosition); // Wire from which the person hangs from g2D.drawLine(BODY_X_POSITION, yPosition, BODY_X_POSITION, yPosition + WIRE_LENGTH); } /** paintComponent - used to draw the hangman drawing onto the panel */ @Override protected void paintComponent(Graphics g){ super.paintComponent(g); switch (information.getGuessesRemaining()) { case 0: drawLeftArm(BODY_X_POSITION, ARM_Y_POSITION, g); case 1: drawRightArm(BODY_X_POSITION, ARM_Y_POSITION, g); case 2: drawLeftLeg(BODY_X_POSITION, LEG_Y_POSITION, g); case 3: drawRightLeg(BODY_X_POSITION, LEG_Y_POSITION, g); case 4: drawBody(BODY_X_POSITION, STARTING_Y_POSITION + WIRE_LENGTH + (2 * HEAD_RADIUS), g); case 5: drawHead(BODY_X_POSITION - HEAD_RADIUS, STARTING_Y_POSITION + WIRE_LENGTH, g); case 6: setUpDrawing(STARTING_X_POSITION, STARTING_Y_POSITION, g); } } } **Information - class that deals with all the information obtained from the user** package Hangman; import java.util.Arrays; import java.util.HashSet; import java.util.Set; /** Information - Deals with all the information that the user has inputted into one game of Hangman */ public class Information { private static final int ALPHABET_SIZE = 26; static final int NUM_OF_GUESSES = 6; private String secretWord; private Set<Character> alphabet; private Set<Character> lettersGuessed; // letters the user has guessed private boolean[] lettersRevealed; // determines if the letter should be revealed or not private int guessesRemaining; private WordReader wordReader; /** Information constructor */ public Information(WordReader wordReader){ this.wordReader = wordReader; } /** addGuessedLetter - adds the guessed letter to lettersGuessed */ void addGuessedLetter(char guessedLetter){ lettersGuessed.add(guessedLetter); } /** createAlphabetSet - Creates the alphabet set that's used to ensure that the user's * guess is not a number nor a special character */ void createAlphabetSet(){ alphabet = new HashSet<>(ALPHABET_SIZE); for(Character c = 'a'; c<='z'; c++){ alphabet.add(c); } } /** resetGameData - clears the information from the previous game of Hangman */ void resetGameData(){ guessesRemaining = NUM_OF_GUESSES; setSecretWord(wordReader.chooseSecretWord()); lettersRevealed = new boolean[getSecretWord().length()]; Arrays.fill(lettersRevealed, false); lettersGuessed = new HashSet<>(ALPHABET_SIZE); } /** revealLetter - causes a letter to be shown on the GUI (occurs only once the user has correctly guessed a letter * in secretWord) */ void revealLetter(int index){ lettersRevealed[index] = true; } // GETTERS public Set<Character> getAlphabet() { return alphabet; } public int getGuessesRemaining() { return guessesRemaining; } public Set<Character> getLettersGuessed() { return lettersGuessed; } public boolean[] getLettersRevealed() { return lettersRevealed; } String getSecretWord(){ return secretWord; } // SETTERS public void setGuessesRemaining(int guessesRemaining) { this.guessesRemaining = guessesRemaining; } public void setSecretWord(String secretWord) { this.secretWord = secretWord; } } **WordReader - class for loading and selecting a word for the user to guess** package Hangman; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** WordReader - Deals with loading and selecting a word from a file containing a collection of words */ public class WordReader { String[] wordList; /** chooseSecretWord - randomly selects a word from wordLst for the user to guess */ String chooseSecretWord(){ return wordList[(int)(Math.random() * wordList.length)]; } /** readFile - read in the list of words from a specific location and saves the words in wordList */ void readFile(String location){ try(BufferedReader input = new BufferedReader( new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream(location)))){ wordList = input.readLine().split(" "); }catch(IOException ioException) { ioException.printStackTrace(); } } }