Skip to main content
changed formatting to have code blocks with java code highlighting
Source Link

Ammendment: Changed WordToGuess.getWordsInTopic()WordToGuess.getWordsInTopic() to open stream using try with resources:

public class Main {

  public static void main (String[] args){

    Game game = new Game();
  }
}
public class Main {

  public static void main (String[] args){

    Game game = new Game();
  }
}
import javax.swing.JPanel;
import javax.swing.JOptionPane;

class Game {

  private Progress progress;
  private GUI gui;

  Game() {
    String targetWord = new WordToGuess().getWordToGuess();
    progress = new Progress(targetWord);
    JPanel alphabet = new Alphabet(this);
    gui = new GUI(progress.getProgress(),alphabet);
    gui.setVisible(true);
  }

  void recordGuess(char letter) {
    progress.updateProgress(letter);
    gui.showStatus(progress.getNumberOfWrongGuesses(), progress.getProgress());
    if (progress.playerWins()) {
      displayEndGameMessage("Well done, it was "+progress.getTargetWord()+", another game?");
    }
    if (progress.playerLooses()) {
      displayEndGameMessage("Bad luck, it was "+progress.getTargetWord()+", another game?");
    }
  }

 void displayEndGameMessage(String message){
   int reply = JOptionPane.showConfirmDialog(gui, message, "end", JOptionPane.YES_NO_OPTION);
   if (reply == JOptionPane.YES_OPTION) {
     GUI oldGUI = gui;
     new Game();
     oldGUI.dispose();
   } else {
     System.exit(0);
   }
 }

}
import javax.swing.JPanel;
import javax.swing.JOptionPane;

class Game {

  private Progress progress;
  private GUI gui;

  Game() {
    String targetWord = new WordToGuess().getWordToGuess();
    progress = new Progress(targetWord);
    JPanel alphabet = new Alphabet(this);
    gui = new GUI(progress.getProgress(),alphabet);
    gui.setVisible(true);
  }

  void recordGuess(char letter) {
    progress.updateProgress(letter);
    gui.showStatus(progress.getNumberOfWrongGuesses(), progress.getProgress());
    if (progress.playerWins()) {
      displayEndGameMessage("Well done, it was "+progress.getTargetWord()+", another game?");
    }
    if (progress.playerLooses()) {
      displayEndGameMessage("Bad luck, it was "+progress.getTargetWord()+", another game?");
    }
  }

  void displayEndGameMessage(String message){
    int reply = JOptionPane.showConfirmDialog(gui, message, "end", JOptionPane.YES_NO_OPTION);
    if (reply == JOptionPane.YES_OPTION) {
      GUI oldGUI = gui;
      new Game();
      oldGUI.dispose();
    } else {
      System.exit(0);
    }
  }
}
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Random;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;

public class WordToGuess {

  private static final int MAX_WORD_LENGTH = 20;
  private static final int MIN_WORD_LENGTH = 3;

  private final String word;

  private final Predicate<String> wordLengthPredicate =
      s -> s.length() <= MAX_WORD_LENGTH && s.length() >= MIN_WORD_LENGTH;

  WordToGuess() {
    List<String> candidateWords = getCandidateWords();
    int randomIndex = new Random().nextInt(candidateWords.size());
    word = candidateWords.get(randomIndex);
  }

  String getWordToGuess() {
    return word;
  }

  private List<String> getCandidateWords() {
    List<String> candidateWords = defaultWords();
    try {
      String topic = new TopicProvider().getTopic();
      candidateWords = getWordsInTopic(topic);
    }catch(NoWordsListsException e){
      ErrorReporter.missingFiles();
    }catch (TopicWordsMissingException e){
      ErrorReporter.missingWords();
    } catch (WordListDirectoryMissingException e) {
      ErrorReporter.missingDirectory();
    }
    return candidateWords;
  }

  private List<String> getWordsInTopic(String topic) throws TopicWordsMissingException {
    List<String> candidateWords;
    try {
      Path path = Paths.get(Constants.WORD_LIST_DIRECTORY +topic);
      Stream<String> stream = Files.lines(path);
      candidateWords = stream.map(String::toUpperCase)
                    .filter(wordLengthPredicate)
                    .collect(toList());
      if (candidateWords.size() < 1) {
        throw new TopicWordsMissingException("The file for that topic contained no words");
      }
    } catch (IOException e) {
      throw new TopicWordsMissingException("The file for that topic was missing");
    }
    return candidateWords;
  }

  private static List<String> defaultWords(){
    return List.of("FOXGLOVE", "MICROWAVE","ZOMBIE","PUPPY","RHUBARB","DWARF","BICYCLE",
                   "BUZZARD","OWL","CHAFFINCH","KIRIBATI","LIECHTENSTEIN","MOZAMBIQUE");
  }
}
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Random;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;

public class WordToGuess {

  private static final int MAX_WORD_LENGTH = 20;
  private static final int MIN_WORD_LENGTH = 3;

  private final String word;

  private final Predicate<String> wordLengthPredicate =
      s -> s.length() <= MAX_WORD_LENGTH && s.length() >= MIN_WORD_LENGTH;

  WordToGuess() {
    List<String> candidateWords = getCandidateWords();
    int randomIndex = new Random().nextInt(candidateWords.size());
    word = candidateWords.get(randomIndex);
  }

  String getWordToGuess() {
    return word;
  }

  private List<String> getCandidateWords() {
    List<String> candidateWords = defaultWords();
    try {
      String topic = new TopicProvider().getTopic();
      candidateWords = getWordsInTopic(topic);
    }catch(NoWordsListsException e){
      ErrorReporter.missingFiles();
    }catch (TopicWordsMissingException e){
      ErrorReporter.missingWords();
    } catch (WordListDirectoryMissingException e) {
      ErrorReporter.missingDirectory();
    }
    return candidateWords;
  }

  private List<String> getWordsInTopic(String topic) throws TopicWordsMissingException {
    List<String> candidateWords;
    try {
      Path path = Paths.get(Constants.WORD_LIST_DIRECTORY +topic);
      Stream<String> stream = Files.lines(path);
      candidateWords = stream.map(String::toUpperCase)
                    .filter(wordLengthPredicate)
                    .collect(toList());
      if (candidateWords.size() < 1) {
        throw new TopicWordsMissingException("The file for that topic contained no words");
      }
    } catch (IOException e) {
      throw new TopicWordsMissingException("The file for that topic was missing");
    }
    return candidateWords;
  }

  private static List<String> defaultWords(){
    return List.of("FOXGLOVE", "MICROWAVE","ZOMBIE","PUPPY","RHUBARB","DWARF","BICYCLE",
                    "BUZZARD","OWL","CHAFFINCH","KIRIBATI","LIECHTENSTEIN","MOZAMBIQUE");
  }
}
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;

class TopicProvider {

  String getTopic() throws NoWordsListsException, WordListDirectoryMissingException {
    List<String> categories = getTopics();
    return getPlayerChoice(categories);
  }

  private List<String> getTopics() throws NoWordsListsException, WordListDirectoryMissingException {
    Path path = Paths.get(Constants.WORD_LIST_DIRECTORY);
    List<String> topics;
    try {
      Stream<Path> stream = Files.list(path);
      topics = stream.filter(Files::isRegularFile)
                         .map(toTopic)
                         .collect(toList());
      if (topics.size() < 1) {
        throw new NoWordsListsException("Word lists missing");
      }
    } catch (IOException e) {
      throw new WordListDirectoryMissingException("Word lists directory missing");
    }
    return topics;
  }

  private final Function<Path, String> toTopic = path -> path.getFileName().toString();

  private String getPlayerChoice(List<String> Topics){
    return new TopicDialogue().ShowTopicDialogue(Topics);
  }
}
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;

class TopicProvider {

  String getTopic() throws NoWordsListsException, WordListDirectoryMissingException {
    List<String> categories = getTopics();
    return getPlayerChoice(categories);
  }

  private List<String> getTopics() throws NoWordsListsException, WordListDirectoryMissingException {
    Path path = Paths.get(Constants.WORD_LIST_DIRECTORY);
    List<String> topics;
    try {
      Stream<Path> stream = Files.list(path);
      topics = stream.filter(Files::isRegularFile)
                          .map(toTopic)
                          .collect(toList());
      if (topics.size() < 1) {
        throw new NoWordsListsException("Word lists missing");
      }
    } catch (IOException e) {
      throw new WordListDirectoryMissingException("Word lists directory missing");
    }
    return topics;
  }

  private final Function<Path, String> toTopic = path -> path.getFileName().toString();

  private String getPlayerChoice(List<String> Topics){
    return new TopicDialogue().ShowTopicDialogue(Topics);
  }
}
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import java.util.List;

class TopicDialogue {

  private String topic;

  String ShowTopicDialogue(List<String> categoryNames){
    String[] categories = categoryNames.toArray(new String[0]);
    topic = categories[0];
    JComboBox<String> jComboBox = new JComboBox<>(categories);
    jComboBox.addActionListener(e -> topic = (String) jComboBox.getSelectedItem());
    JOptionPane.showMessageDialog(null, jComboBox, "Choose a topic", JOptionPane.PLAIN_MESSAGE);
    return topic;
  }

}
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import java.util.List;

class TopicDialogue {

  private String topic;

  String ShowTopicDialogue(List<String> categoryNames){
    String[] categories = categoryNames.toArray(new String[0]);
    topic = categories[0];
    JComboBox<String> jComboBox = new JComboBox<>(categories);
    jComboBox.addActionListener(e -> topic = (String) jComboBox.getSelectedItem());
    JOptionPane.showMessageDialog(null, jComboBox, "Choose a topic", JOptionPane.PLAIN_MESSAGE);
    return topic;
  }

}
public class Progress {

  private static final String placeHolder = "*";
  private final char[] progress;
  private int wrongGuessCount =0;
  private final String targetWord;
  private final int MAX_BAD_GUESSES = 11;

  Progress(String targetWord){
    this.targetWord=targetWord;
    String progressString = targetWord.replaceAll("[A-Z]", placeHolder);
    progress = progressString.toCharArray();
  }

  String getProgress(){
        return new String(progress);
    }

  String getTargetWord(){
        return targetWord;
    }

  boolean playerWins(){
        return !new String(progress).contains(placeHolder);
    }

  boolean playerLooses(){
        return (wrongGuessCount > MAX_BAD_GUESSES);
    }

  int getNumberOfWrongGuesses(){
        return wrongGuessCount;
    }

  void updateProgress(char guess) {
    if (isGoodGuess(guess)) {
      for (int n = 0; n < targetWord.length(); n++) {
        if (targetWord.charAt(n) == guess) {
          progress[n] = guess;
        }
      }
    } else {
      wrongGuessCount++;
    }
}
  boolean isGoodGuess(char guess){
        return targetWord.contains(Character.toString(guess));
    }
}
public class Progress {

  private static final String placeHolder = "*";
  private final char[] progress;
  private int wrongGuessCount =0;
  private final String targetWord;
  private final int MAX_BAD_GUESSES = 11;

  Progress(String targetWord){
    this.targetWord=targetWord;
    String progressString = targetWord.replaceAll("[A-Z]", placeHolder);
    progress = progressString.toCharArray();
  }

  String getProgress(){
        return new String(progress);
    }

  String getTargetWord(){
        return targetWord;
    }

  boolean playerWins(){
        return !new String(progress).contains(placeHolder);
    }

  boolean playerLooses(){
        return (wrongGuessCount > MAX_BAD_GUESSES);
    }

  int getNumberOfWrongGuesses(){
        return wrongGuessCount;
    }

  void updateProgress(char guess) {
    if (isGoodGuess(guess)) {
      for (int n = 0; n < targetWord.length(); n++) {
        if (targetWord.charAt(n) == guess) {
          progress[n] = guess;
        }
      }
    } else {
      wrongGuessCount++;
    }
}
  boolean isGoodGuess(char guess){
        return targetWord.contains(Character.toString(guess));
    }
}
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.BoxLayout;
import java.awt.Dimension;

public class GUI extends JFrame {

  private JLabel progressLabel;
  private GibbetLabel gibbetLabel;

  GUI(String prog, JPanel alphabet) {
    JPanel container = new JPanel();
    container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
    add(container);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    gibbetLabel = new GibbetLabel();
    container.add(gibbetLabel);

    progressLabel = new ProgressLabel(prog);
    container.add(progressLabel);

    container.add(alphabet);
    setTitle("Hangman");
    setSize(new Dimension(350, 450));
    setLocationRelativeTo(null);
    setVisible(true);
  }

  void showStatus(int count, String prog){
    progressLabel.setText(prog);
    gibbetLabel.updateGibbetImage(count);
  }

}
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.BoxLayout;
import java.awt.Dimension;

public class GUI extends JFrame {

  private JLabel progressLabel;
  private GibbetLabel gibbetLabel;

  GUI(String prog, JPanel alphabet) {
    JPanel container = new JPanel();
    container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
    add(container);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    gibbetLabel = new GibbetLabel();
    container.add(gibbetLabel);

    progressLabel = new ProgressLabel(prog);
    container.add(progressLabel);

    container.add(alphabet);
    setTitle("Hangman");
    setSize(new Dimension(350, 450));
    setLocationRelativeTo(null);
    setVisible(true);
  }

  void showStatus(int count, String prog){
    progressLabel.setText(prog);
    gibbetLabel.updateGibbetImage(count);
  }

}
import javax.swing.JPanel;
import javax.swing.JButton;

class Alphabet extends JPanel{

  Alphabet(Game game){

    for(char c = 'A'; c <= 'Z'; ++c){
      JButton button = new JButton(Character.toString(c));
      button.addActionListener(e -> {
        game.recordGuess(e.getActionCommand().charAt(0));
        button.setEnabled(false);
      });
      add(button);
    }
  }
}
import javax.swing.JPanel;
import javax.swing.JButton;

class Alphabet extends JPanel{

  Alphabet(Game game){

    for(char c = 'A'; c <= 'Z'; ++c){
      JButton button = new JButton(Character.toString(c));
      button.addActionListener(e -> {
        game.recordGuess(e.getActionCommand().charAt(0));
        button.setEnabled(false);
      });
      add(button);
    }
  }
}
import javax.swing.JLabel;
import java.awt.Font;
import java.awt.Component;

public class ProgressLabel extends JLabel {

    ProgressLabel(String prog){
        setText(prog);
        setFont(new Font("Monospaced", Font.PLAIN, 20));
        setAlignmentX(Component.CENTER_ALIGNMENT);
    }
}
import javax.swing.JLabel;
import java.awt.Font;
import java.awt.Component;

public class ProgressLabel extends JLabel {

    ProgressLabel(String prog){
        setText(prog);
        setFont(new Font("Monospaced", Font.PLAIN, 20));
        setAlignmentX(Component.CENTER_ALIGNMENT);
    }
}
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.ArrayList;

public class GibbetLabel extends JLabel {
    private static final int IMAGE_COUNT = 13;
    private ArrayList<File> gibbetImages = new ArrayList<>();

    GibbetLabel() {
        for (int i = 0; i < IMAGE_COUNT; i++) {
            try {
                gibbetImages.add(new File("images/" + i + ".png"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        setIcon(new ImageIcon(String.valueOf(gibbetImages.get(0))));
        setAlignmentX(Component.CENTER_ALIGNMENT);
    }
    void updateGibbetImage(int count){
        setIcon(new ImageIcon(String.valueOf(gibbetImages.get(count))));
    }
}
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.ArrayList;

public class GibbetLabel extends JLabel {
    private static final int IMAGE_COUNT = 13;
    private ArrayList<File> gibbetImages = new ArrayList<>();

    GibbetLabel() {
        for (int i = 0; i < IMAGE_COUNT; i++) {
            try {
                gibbetImages.add(new File("images/" + i + ".png"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        setIcon(new ImageIcon(String.valueOf(gibbetImages.get(0))));
        setAlignmentX(Component.CENTER_ALIGNMENT);
    }
    void updateGibbetImage(int count){
        setIcon(new ImageIcon(String.valueOf(gibbetImages.get(count))));
    }
}

Ammendment: Changed WordToGuess.getWordsInTopic() to open stream using try with resources:

public class Main {

  public static void main (String[] args){

    Game game = new Game();
  }
}
import javax.swing.JPanel;
import javax.swing.JOptionPane;

class Game {

  private Progress progress;
  private GUI gui;

  Game() {
    String targetWord = new WordToGuess().getWordToGuess();
    progress = new Progress(targetWord);
    JPanel alphabet = new Alphabet(this);
    gui = new GUI(progress.getProgress(),alphabet);
    gui.setVisible(true);
  }

  void recordGuess(char letter) {
    progress.updateProgress(letter);
    gui.showStatus(progress.getNumberOfWrongGuesses(), progress.getProgress());
    if (progress.playerWins()) {
      displayEndGameMessage("Well done, it was "+progress.getTargetWord()+", another game?");
    }
    if (progress.playerLooses()) {
      displayEndGameMessage("Bad luck, it was "+progress.getTargetWord()+", another game?");
    }
  }

 void displayEndGameMessage(String message){
   int reply = JOptionPane.showConfirmDialog(gui, message, "end", JOptionPane.YES_NO_OPTION);
   if (reply == JOptionPane.YES_OPTION) {
     GUI oldGUI = gui;
     new Game();
     oldGUI.dispose();
   } else {
     System.exit(0);
   }
 }

}
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Random;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;

public class WordToGuess {

  private static final int MAX_WORD_LENGTH = 20;
  private static final int MIN_WORD_LENGTH = 3;

  private final String word;

  private final Predicate<String> wordLengthPredicate =
      s -> s.length() <= MAX_WORD_LENGTH && s.length() >= MIN_WORD_LENGTH;

  WordToGuess() {
    List<String> candidateWords = getCandidateWords();
    int randomIndex = new Random().nextInt(candidateWords.size());
    word = candidateWords.get(randomIndex);
  }

  String getWordToGuess() {
    return word;
  }

  private List<String> getCandidateWords() {
    List<String> candidateWords = defaultWords();
    try {
      String topic = new TopicProvider().getTopic();
      candidateWords = getWordsInTopic(topic);
    }catch(NoWordsListsException e){
      ErrorReporter.missingFiles();
    }catch (TopicWordsMissingException e){
      ErrorReporter.missingWords();
    } catch (WordListDirectoryMissingException e) {
      ErrorReporter.missingDirectory();
    }
    return candidateWords;
  }

  private List<String> getWordsInTopic(String topic) throws TopicWordsMissingException {
    List<String> candidateWords;
    try {
      Path path = Paths.get(Constants.WORD_LIST_DIRECTORY +topic);
      Stream<String> stream = Files.lines(path);
      candidateWords = stream.map(String::toUpperCase)
                    .filter(wordLengthPredicate)
                    .collect(toList());
      if (candidateWords.size() < 1) {
        throw new TopicWordsMissingException("The file for that topic contained no words");
      }
    } catch (IOException e) {
      throw new TopicWordsMissingException("The file for that topic was missing");
    }
    return candidateWords;
  }

  private static List<String> defaultWords(){
    return List.of("FOXGLOVE", "MICROWAVE","ZOMBIE","PUPPY","RHUBARB","DWARF","BICYCLE",
                   "BUZZARD","OWL","CHAFFINCH","KIRIBATI","LIECHTENSTEIN","MOZAMBIQUE");
  }
}
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;

class TopicProvider {

  String getTopic() throws NoWordsListsException, WordListDirectoryMissingException {
    List<String> categories = getTopics();
    return getPlayerChoice(categories);
  }

  private List<String> getTopics() throws NoWordsListsException, WordListDirectoryMissingException {
    Path path = Paths.get(Constants.WORD_LIST_DIRECTORY);
    List<String> topics;
    try {
      Stream<Path> stream = Files.list(path);
      topics = stream.filter(Files::isRegularFile)
                         .map(toTopic)
                         .collect(toList());
      if (topics.size() < 1) {
        throw new NoWordsListsException("Word lists missing");
      }
    } catch (IOException e) {
      throw new WordListDirectoryMissingException("Word lists directory missing");
    }
    return topics;
  }

  private final Function<Path, String> toTopic = path -> path.getFileName().toString();

  private String getPlayerChoice(List<String> Topics){
    return new TopicDialogue().ShowTopicDialogue(Topics);
  }
}
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import java.util.List;

class TopicDialogue {

  private String topic;

  String ShowTopicDialogue(List<String> categoryNames){
    String[] categories = categoryNames.toArray(new String[0]);
    topic = categories[0];
    JComboBox<String> jComboBox = new JComboBox<>(categories);
    jComboBox.addActionListener(e -> topic = (String) jComboBox.getSelectedItem());
    JOptionPane.showMessageDialog(null, jComboBox, "Choose a topic", JOptionPane.PLAIN_MESSAGE);
    return topic;
  }

}
public class Progress {

  private static final String placeHolder = "*";
  private final char[] progress;
  private int wrongGuessCount =0;
  private final String targetWord;
  private final int MAX_BAD_GUESSES = 11;

  Progress(String targetWord){
    this.targetWord=targetWord;
    String progressString = targetWord.replaceAll("[A-Z]", placeHolder);
    progress = progressString.toCharArray();
  }

  String getProgress(){
        return new String(progress);
    }

  String getTargetWord(){
        return targetWord;
    }

  boolean playerWins(){
        return !new String(progress).contains(placeHolder);
    }

  boolean playerLooses(){
        return (wrongGuessCount > MAX_BAD_GUESSES);
    }

  int getNumberOfWrongGuesses(){
        return wrongGuessCount;
    }

  void updateProgress(char guess) {
    if (isGoodGuess(guess)) {
      for (int n = 0; n < targetWord.length(); n++) {
        if (targetWord.charAt(n) == guess) {
          progress[n] = guess;
        }
      }
    } else {
      wrongGuessCount++;
    }
}
  boolean isGoodGuess(char guess){
        return targetWord.contains(Character.toString(guess));
    }
}
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.BoxLayout;
import java.awt.Dimension;

public class GUI extends JFrame {

  private JLabel progressLabel;
  private GibbetLabel gibbetLabel;

  GUI(String prog, JPanel alphabet) {
    JPanel container = new JPanel();
    container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
    add(container);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    gibbetLabel = new GibbetLabel();
    container.add(gibbetLabel);

    progressLabel = new ProgressLabel(prog);
    container.add(progressLabel);

    container.add(alphabet);
    setTitle("Hangman");
    setSize(new Dimension(350, 450));
    setLocationRelativeTo(null);
    setVisible(true);
  }

  void showStatus(int count, String prog){
    progressLabel.setText(prog);
    gibbetLabel.updateGibbetImage(count);
  }

}
import javax.swing.JPanel;
import javax.swing.JButton;

class Alphabet extends JPanel{

  Alphabet(Game game){

    for(char c = 'A'; c <= 'Z'; ++c){
      JButton button = new JButton(Character.toString(c));
      button.addActionListener(e -> {
        game.recordGuess(e.getActionCommand().charAt(0));
        button.setEnabled(false);
      });
      add(button);
    }
  }
}
import javax.swing.JLabel;
import java.awt.Font;
import java.awt.Component;

public class ProgressLabel extends JLabel {

    ProgressLabel(String prog){
        setText(prog);
        setFont(new Font("Monospaced", Font.PLAIN, 20));
        setAlignmentX(Component.CENTER_ALIGNMENT);
    }
}
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.ArrayList;

public class GibbetLabel extends JLabel {
    private static final int IMAGE_COUNT = 13;
    private ArrayList<File> gibbetImages = new ArrayList<>();

    GibbetLabel() {
        for (int i = 0; i < IMAGE_COUNT; i++) {
            try {
                gibbetImages.add(new File("images/" + i + ".png"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        setIcon(new ImageIcon(String.valueOf(gibbetImages.get(0))));
        setAlignmentX(Component.CENTER_ALIGNMENT);
    }
    void updateGibbetImage(int count){
        setIcon(new ImageIcon(String.valueOf(gibbetImages.get(count))));
    }
}

Ammendment: Changed WordToGuess.getWordsInTopic() to open stream using try with resources:

public class Main {

  public static void main (String[] args){

    Game game = new Game();
  }
}
import javax.swing.JPanel;
import javax.swing.JOptionPane;

class Game {

  private Progress progress;
  private GUI gui;

  Game() {
    String targetWord = new WordToGuess().getWordToGuess();
    progress = new Progress(targetWord);
    JPanel alphabet = new Alphabet(this);
    gui = new GUI(progress.getProgress(),alphabet);
    gui.setVisible(true);
  }

  void recordGuess(char letter) {
    progress.updateProgress(letter);
    gui.showStatus(progress.getNumberOfWrongGuesses(), progress.getProgress());
    if (progress.playerWins()) {
      displayEndGameMessage("Well done, it was "+progress.getTargetWord()+", another game?");
    }
    if (progress.playerLooses()) {
      displayEndGameMessage("Bad luck, it was "+progress.getTargetWord()+", another game?");
    }
  }

  void displayEndGameMessage(String message){
    int reply = JOptionPane.showConfirmDialog(gui, message, "end", JOptionPane.YES_NO_OPTION);
    if (reply == JOptionPane.YES_OPTION) {
      GUI oldGUI = gui;
      new Game();
      oldGUI.dispose();
    } else {
      System.exit(0);
    }
  }
}
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Random;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;

public class WordToGuess {

  private static final int MAX_WORD_LENGTH = 20;
  private static final int MIN_WORD_LENGTH = 3;

  private final String word;

  private final Predicate<String> wordLengthPredicate =
      s -> s.length() <= MAX_WORD_LENGTH && s.length() >= MIN_WORD_LENGTH;

  WordToGuess() {
    List<String> candidateWords = getCandidateWords();
    int randomIndex = new Random().nextInt(candidateWords.size());
    word = candidateWords.get(randomIndex);
  }

  String getWordToGuess() {
    return word;
  }

  private List<String> getCandidateWords() {
    List<String> candidateWords = defaultWords();
    try {
      String topic = new TopicProvider().getTopic();
      candidateWords = getWordsInTopic(topic);
    }catch(NoWordsListsException e){
      ErrorReporter.missingFiles();
    }catch (TopicWordsMissingException e){
      ErrorReporter.missingWords();
    } catch (WordListDirectoryMissingException e) {
      ErrorReporter.missingDirectory();
    }
    return candidateWords;
  }

  private List<String> getWordsInTopic(String topic) throws TopicWordsMissingException {
    List<String> candidateWords;
    try {
      Path path = Paths.get(Constants.WORD_LIST_DIRECTORY +topic);
      Stream<String> stream = Files.lines(path);
      candidateWords = stream.map(String::toUpperCase)
                    .filter(wordLengthPredicate)
                    .collect(toList());
      if (candidateWords.size() < 1) {
        throw new TopicWordsMissingException("The file for that topic contained no words");
      }
    } catch (IOException e) {
      throw new TopicWordsMissingException("The file for that topic was missing");
    }
    return candidateWords;
  }

  private static List<String> defaultWords(){
    return List.of("FOXGLOVE", "MICROWAVE","ZOMBIE","PUPPY","RHUBARB","DWARF","BICYCLE",
                    "BUZZARD","OWL","CHAFFINCH","KIRIBATI","LIECHTENSTEIN","MOZAMBIQUE");
  }
}
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;

class TopicProvider {

  String getTopic() throws NoWordsListsException, WordListDirectoryMissingException {
    List<String> categories = getTopics();
    return getPlayerChoice(categories);
  }

  private List<String> getTopics() throws NoWordsListsException, WordListDirectoryMissingException {
    Path path = Paths.get(Constants.WORD_LIST_DIRECTORY);
    List<String> topics;
    try {
      Stream<Path> stream = Files.list(path);
      topics = stream.filter(Files::isRegularFile)
                          .map(toTopic)
                          .collect(toList());
      if (topics.size() < 1) {
        throw new NoWordsListsException("Word lists missing");
      }
    } catch (IOException e) {
      throw new WordListDirectoryMissingException("Word lists directory missing");
    }
    return topics;
  }

  private final Function<Path, String> toTopic = path -> path.getFileName().toString();

  private String getPlayerChoice(List<String> Topics){
    return new TopicDialogue().ShowTopicDialogue(Topics);
  }
}
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import java.util.List;

class TopicDialogue {

  private String topic;

  String ShowTopicDialogue(List<String> categoryNames){
    String[] categories = categoryNames.toArray(new String[0]);
    topic = categories[0];
    JComboBox<String> jComboBox = new JComboBox<>(categories);
    jComboBox.addActionListener(e -> topic = (String) jComboBox.getSelectedItem());
    JOptionPane.showMessageDialog(null, jComboBox, "Choose a topic", JOptionPane.PLAIN_MESSAGE);
    return topic;
  }

}
public class Progress {

  private static final String placeHolder = "*";
  private final char[] progress;
  private int wrongGuessCount =0;
  private final String targetWord;
  private final int MAX_BAD_GUESSES = 11;

  Progress(String targetWord){
    this.targetWord=targetWord;
    String progressString = targetWord.replaceAll("[A-Z]", placeHolder);
    progress = progressString.toCharArray();
  }

  String getProgress(){
        return new String(progress);
    }

  String getTargetWord(){
        return targetWord;
    }

  boolean playerWins(){
        return !new String(progress).contains(placeHolder);
    }

  boolean playerLooses(){
        return (wrongGuessCount > MAX_BAD_GUESSES);
    }

  int getNumberOfWrongGuesses(){
        return wrongGuessCount;
    }

  void updateProgress(char guess) {
    if (isGoodGuess(guess)) {
      for (int n = 0; n < targetWord.length(); n++) {
        if (targetWord.charAt(n) == guess) {
          progress[n] = guess;
        }
      }
    } else {
      wrongGuessCount++;
    }
}
  boolean isGoodGuess(char guess){
        return targetWord.contains(Character.toString(guess));
    }
}
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.BoxLayout;
import java.awt.Dimension;

public class GUI extends JFrame {

  private JLabel progressLabel;
  private GibbetLabel gibbetLabel;

  GUI(String prog, JPanel alphabet) {
    JPanel container = new JPanel();
    container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
    add(container);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    gibbetLabel = new GibbetLabel();
    container.add(gibbetLabel);

    progressLabel = new ProgressLabel(prog);
    container.add(progressLabel);

    container.add(alphabet);
    setTitle("Hangman");
    setSize(new Dimension(350, 450));
    setLocationRelativeTo(null);
    setVisible(true);
  }

  void showStatus(int count, String prog){
    progressLabel.setText(prog);
    gibbetLabel.updateGibbetImage(count);
  }

}
import javax.swing.JPanel;
import javax.swing.JButton;

class Alphabet extends JPanel{

  Alphabet(Game game){

    for(char c = 'A'; c <= 'Z'; ++c){
      JButton button = new JButton(Character.toString(c));
      button.addActionListener(e -> {
        game.recordGuess(e.getActionCommand().charAt(0));
        button.setEnabled(false);
      });
      add(button);
    }
  }
}
import javax.swing.JLabel;
import java.awt.Font;
import java.awt.Component;

public class ProgressLabel extends JLabel {

    ProgressLabel(String prog){
        setText(prog);
        setFont(new Font("Monospaced", Font.PLAIN, 20));
        setAlignmentX(Component.CENTER_ALIGNMENT);
    }
}
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.ArrayList;

public class GibbetLabel extends JLabel {
    private static final int IMAGE_COUNT = 13;
    private ArrayList<File> gibbetImages = new ArrayList<>();

    GibbetLabel() {
        for (int i = 0; i < IMAGE_COUNT; i++) {
            try {
                gibbetImages.add(new File("images/" + i + ".png"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        setIcon(new ImageIcon(String.valueOf(gibbetImages.get(0))));
        setAlignmentX(Component.CENTER_ALIGNMENT);
    }
    void updateGibbetImage(int count){
        setIcon(new ImageIcon(String.valueOf(gibbetImages.get(count))));
    }
}
added 231 characters in body
Source Link
scripta
  • 519
  • 2
  • 11

Ammendment: Changed WordToGuess.getWordsInTopic() to open stream using try with resources:

Path path = Paths.get(Constants.WORD_LIST_DIRECTORY +topic);
try (Stream<String> stream = Files.lines(path)){
  ...

uml Major classes follow:

uml Major classes follow:

Ammendment: Changed WordToGuess.getWordsInTopic() to open stream using try with resources:

Path path = Paths.get(Constants.WORD_LIST_DIRECTORY +topic);
try (Stream<String> stream = Files.lines(path)){
  ...

uml Major classes follow:

Tweeted twitter.com/StackCodeReview/status/1489207067000905732
deleted 2 characters in body
Source Link
scripta
  • 519
  • 2
  • 11

enter image description here I've repartitioned the code into objects several times, without changing the player's experience of the game at all. Some partitionings were clearly better than others. In other cases I wasn't sure. My question is: how do you judge the quality of a partitioning? - preferably, before you commit to coding. I'm assuming that some of you use diagrams (UML) in the same way as architects explore different floor plans in their drawings. I've had an attempt at a UML diagram for my code. Can you judge the quality from a diagram like that? I'm struggling a bit to understand UML - so the diagram may be pretty flawed! (hints on how to draw UML welcome).

enter image description here I've repartitioned the code into objects several times, without changing the player's experience of the game at all. Some partitionings were clearly better than others. In other cases I wasn't sure. My question is: how do you judge the quality of a partitioning? - before you commit to coding. I'm assuming that some of you use diagrams (UML) in the same way as architects explore different floor plans in their drawings. I've had an attempt at a UML diagram for my code. Can you judge the quality from a diagram like that? I'm struggling a bit to understand UML - so the diagram may be pretty flawed! (hints on how to draw UML welcome).

enter image description here I've repartitioned the code into objects several times, without changing the player's experience of the game at all. Some partitionings were clearly better than others. In other cases I wasn't sure. My question is: how do you judge the quality of a partitioning? - preferably, before coding. I'm assuming that some of you use diagrams (UML) in the same way as architects explore different floor plans in their drawings. I've had an attempt at a UML diagram for my code. Can you judge the quality from a diagram like that? I'm struggling a bit to understand UML - so the diagram may be pretty flawed! (hints on how to draw UML welcome).

added 97 characters in body
Source Link
scripta
  • 519
  • 2
  • 11
Loading
added 11 characters in body
Source Link
scripta
  • 519
  • 2
  • 11
Loading
Source Link
scripta
  • 519
  • 2
  • 11
Loading