首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Java 8的Hangman游戏

使用Java 8的Hangman游戏
EN

Code Review用户
提问于 2015-03-17 16:00:27
回答 1查看 3.2K关注 0票数 10

我已经习惯了Java,我用Java 8中的事件驱动编程编写了一个简单的挂人游戏,请告诉我在程序的可读性和设计方面的任何改进,以及关于如何整理代码的任何提示。

此外,欢迎关于使用JavaDoc风格评论的任何反馈意见。

Main -用于启动游戏

的类

代码语言:javascript
复制
package Hangman;

public class Main {
    public static void main(String[] args){
        new Logic().go();
    }
}

逻辑-包含所有游戏逻辑

代码语言:javascript
复制
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 -包含与GUI

相关的所有内容

代码语言:javascript
复制
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 -用于绘制刽子手图形

代码语言:javascript
复制
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);
        }
    }
}

信息类,它处理从用户

获得的所有信息。

代码语言:javascript
复制
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 -类,用于加载和选择一个单词,供用户猜测

代码语言:javascript
复制
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();
        }
    }
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2015-03-22 13:42:47

注释

欢迎有关使用JavaDoc样式评论的任何反馈意见。

方法注释的结构是:

代码语言:javascript
复制
/** methodName - what method does */

但是JavaDocs实际上是这样构造的:

代码语言:javascript
复制
/**
 * short description of what method does.
 * <p>
 * Longer description, special cases, etc.
 * 
 * @param paramName what param does
 * @param anotherParam what it does
 * @return what is returned
 */

重要的部分是,方法名称不应该是注释的一部分(在生成JavaDoc时它将自动添加),还有方法参数和返回值的注释。您还可以添加附加注释

您的许多代码注释有些多余,例如lettersGuessed: letters the user has guessed

结构

  • Information是一个非常通用的名称。什么信息?它似乎只是包含所有你不确定放在哪里的东西:它包含字母表,单词,用户输入等等。我会把它分成不同的类。我明白了为什么要这样做:这样GUI就可以得到一个对象,然后它就可以获得要呈现的状态。与其创建一个包含所有信息的god对象,不如向GUI方法添加参数,并以这种方式将信息传递给它们(例如drawLettersGuessed() -> drawLettersGuessed(Set<Character> guessedLetters))。
  • 您的Logic类也做得太多了:它处理游戏状态(updateSecretWordisGuessValid等),但也处理游戏循环(例如playAgain)。
  • 另一方面,您的GUI结构很好,将DrawingPanel和通用GUI分离开来。

Misc

  • 使用显式的私有/公共关键字而不是默认的关键字。
  • WordReader总是必须读取一个文件,对吗?否则就没用了。因此,我将添加一个构造函数,并在构造它时读取该文件。
  • size() != 0可以写成!isEmpty()
  • 与其在word文件不存在或为空时崩溃,不如提供一个信息丰富的错误消息(Could not find file /path/to/file/filenameFile /path/to/file/filename is empty or malformed)。
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/84276

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档