[Java] [Getting started with rock-paper-scissors games] Java

16 minute read

1.First of all

I will check the syntax and specifications of Java using the game of rock-paper-scissors game that I made.

1-1. Game specifications

The rules follow the general rock-paper-scissors rules. However, the player and COM have 5 points of physical strength, and if you win the rock-paper-scissors, you can deal 1 point of damage to your opponent. The player who wins the opponent’s physical strength to 0 points or less first wins. No draws will occur and will continue until either the player or COM wins.

The processing contents are displayed on the console, and the contents displayed on the console are also output to the log file.

1-2. What is Java?

Java is a language developed by the once-existing US IT company Sun Microsystems (now Oracle America). Since JDK 1.0 was announced in January 1996, the version has been continuously updated, and Java SE 14 announced in March 2020 will be the latest version.

Following the origin of the language, it seems that development was originally started as an embedded language for home appliances based on C++. However, in the middle of development, the embedded target has shifted from home appliances to PDAs, and it has also become a language for Web development. Perhaps because of such various fields, it has grown into an extremely versatile language used in a wide range of industries today. In familiar places, it is also used for developing Android applications. Kotlin “It’s not (in Java Pisen era anymore)”

Java has built up a high degree of reliability based on its track record of being active on the front line for over 20 years, but due to the rise of the JVM language and ~~ the announcement method of Oracle was bad ~~ It is undeniable that the popularity has been cold and cold because of the widespread misunderstanding that “will be done?”

However, the demand is still top class in all languages, and even if we are out of the options of new development projects, maintenance and repair projects for systems written in Java will remain, so we will continue to do so in the future. There is no doubt that it will continue to be used in the span of several decades.

1-3. Development environment construction procedure

To build a Java development environment, use a package called JDK (Java Development Kit), which is a collection of programs required for Java development, or use an IDE () that includes a JDK. It is common to use Eclipse and IntelliJ IDEA).

This article describes the procedure for building a development environment using OpenJDK (strictly speaking, OpenJDK 14x series), which is one of the JDKs announced by various organizations.

** * Description for Windows 10 64bit version. We have not confirmed the operation in other environments. **

1-3a. Download OpenJDK

First, let’s download OpenJDK. Go to the site that handles OpenJDK.

OpenJDK

In the block under the heading Download at the center of the site, there is a link to the download page for OpenJDK 14x. Click the link to move to the target page.

JDK 14.0.1 GA Release

A download link for the JDK for Linux, macOS, and Windows is attached near the center of the download page. Click the JDK for Windows link to start the download. I could download Wi-Fi in my home in about 1 minute, so if it’s an average level of Wi-Fi or wired, it should be completed immediately.

The downloaded file should be compressed in .zip format, so decompress it. The unzipped directory should have a directory with a name like jdk-14.0.1. This is the main body of OpenJDK, and the Java compiler etc. are included in this.

In the development environment construction work in other programming languages, it may be necessary to launch the installer etc. from here and proceed with the development environment construction, but with Java this is temporarily completed.

1-3b. Operation check

Check if OpenJDK was downloaded and unzipped correctly.

First, put the main body of OpenJDK (directory with a name such as jdk-14.0.1) directly under C drive.

Then, please confirm that the executable files java.exe and javac.exe are included in the directory bin stored in the directory. The former is an application for executing programs written in Java, and the latter is a Java compiler.

In order to check the operation, launch a shell such as Command Prompt or PowerShell and hit the command as shown below. It is OK if the following is displayed.

OK when displayed like this


C:\jdk-14.0.1\bin> .\java.exe -version
openjdk version "14.0.1" 2020-04-14
OpenJDK Runtime Environment (build 14.0.1+7)
OpenJDK 64-Bit Server VM (build 14.0.1+7, mixed mode, sharing)

C:\jdk-14.0.1\bin>.\javac.exe -version
javac 14.0.1

C:\jdk-14.0.1\bin>

1-3c. Let’s go through the Path

Java can be developed even in the above state, but let’s put Path in the above bin directory so that it can be easily developed.

It would be perfect if we could do this. Thank you for your hard work.

2. Special notes

Regarding the basic grammar etc., we will read the source code at the end of the article, but we will note things that can not be read from the source code and things to note.

2-1. Extension

The extension is .java.

2-2. Character code

(Although it depends on the development environment) If you build the development environment by the procedure described in this article, the standard character code will be MS932. MS932 is an extension of Shift JIS with a character code also called windows-31j. After all, if the source code does not include Japanese, it can be written in Shift JIS or UTF-8, and if the source code contains Japanese, it can be written in Shift JIS.

If you want to know the standard character code of your environment, execute the following program.

Test.java


class Test {
    public static void main(String[] args) {
        System.out.println(System.getProperty("file.encoding"));
    }
}

By the way (I think this is the case), I’m sure there are some people who want to include Japanese in the source file but want to write the character code in UTF-8. The Java compiler can specify the character code when compiling, so refer to the following command.

Compile with

UTF-8


C:\Users\AGadget> javac .\Test.java -encoding UTF-8

2-3. Coding rules

It seems that Java coding standards have been announced by various organizations.

Isn’t it better to refer to one of the coding standards?

2-4. Flow from compilation to execution

This section describes the procedure from compilation to execution.

First, prepare the source file. In Java, storing 1 class in 1 file and matching the file name with the class name seems to be a good etiquette in coding. The sample file below also follows this.

Test.java


class Test {
    public static void main(String[] args) {
        System.out.println("Hello world.");
    }
}

Once you have the source files, compile and generate an intermediate file (.class file).

compile


C:\Users\AGadget> javac .\Test.java

C:\Users\AGadget>

You should now have an intermediate file called Test.class. Run this with java.exe.

execute


C:\Users\AGadget> java Test
Hello world.

C:\Users\AGadget>

The procedure for compiling and executing Java is a little complicated, so please refer to another explanation article etc. if necessary.

2-5. Specify the character code and compile

In the development environment built by the procedure described in this article, the standard character code of the compiler is Shift JIS. If you want to compile with any character code, type the command as follows.

Compile with

UTF-8


C:\Users\AGadget> javac .\Test.javaa -encoding UTF-8

C:\Users\AGadget>

2-6. Entry point

The Java entry points are the following methods.

public static void main(String[] args) {
    // here is the entry point
}

As a general rule, if you change the above format-for example, if you change public to private or change the method name from main to Main, you will get an entry point. Please note that it will not be recognized.

2-7. All processing is contained in the class

Unlike languages that use classes, or languages that don’t have classes in the first place, Java needs to store all the work in one of the classes. This also applies to the main method, which is the Java entry point, and it is necessary to define a class for storing the main method.

Main.java


/**
 * Main class that stores the main method
 */
class Main {

    /**
     * Main method
     */
    public static void main(String[] args) {
        System.out.println("Hello world.");
    }
}
```As an aside-when I first touched Java it was one of the confusing specifications. Until then, I had only touched C and JavaScript, so this specification felt quite strange. ~~ To be honest ~~





## 2-8. == operator and equals method

In Java, the **== operator ** is used to compare whether the referents are equal, and the **equals method ** is used to compare the same values.


#### **`Test.java`**
```java

class Test {
    public static void main(String[] args) {
        StringBuilder a = new StringBuilder("Hello world.");
        StringBuilder b = new StringBuilder("Hello world.");
        System.out.println(a.toString() == b.toString());
        System.out.println(a.toString().equals(b.toString()));
    }
}

try to run


C:\Users\AGadget> javac .\Test.java

C:\Users\AGadget> java Test
false
true

C:\Users\AGadget>

2-9. A return statement is required for all conditional branches in a method that has a return value

See the example below.

Test.java


class Test {

    public static void main(String[] args) {
        System.out.println(testMethod("Windows Mobile"));
    }

    private static String testMethod(String OS) {
        if (OS.equals("AndroidOS")) {
            return "Android OS is oriented";
        }
        if (OS.equals("iOS")) {
            return "iOS is oriented";
        }
        if (OS.equals("BlackBerry")) {
            return "BlackBerry is my intention";
        }
        System.out.println("[Error] Winmo is NG .. .");
        System.exit(0);
    }

}

This program seems to compile without problems at first glance, but it is actually thrown as a syntax error.

C:\Users\AGadget> javac .\Test.java
.\Test.java:19: ERROR: no return statement specified
    }
    ^
1 error

C:\Users\AGadget>

It seems that in Java, when a method returns a return value, the return statement must be written in all possible branches.

You can compile it with the following modification.

Test.java


class Test {

    public static void main(String[] args) {
        System.out.println(testMethod("Windows Mobile"));
    }

    private static String testMethod(String OS) {
        if (OS.equals("AndroidOS")) {
            return "Android OS is oriented";
        }
        if (OS.equals("iOS")) {
            return "iOS is oriented";
        }
        if (OS.equals("BlackBerry OS")) {
            return "BlackBerry OS is the focus";
        }
        System.out.println("[Error] Winmo is NG .. .");
        System.exit(0);
        return ""; // Implemented because it is syntactically required
    }

}

3. Source code

Main.java


import java.util.Scanner;

/**
 * Main class for rock-paper-scissors games
 * @author AGadget
 */
class Main {

    /**
     * Main method
     */
    public static void main(String[] args) {
        StandardIO standardIO = StandardIO.getInstance();
        Player player = new Player(5, "Qii Taro");
        COM com = new COM(5, "COM");
        TurnCount turnCount = new TurnCount();
        while (player.canGame() && com.canGame()) {
            turnCount.countUp();
            turnCount.printTurnCountMessage();
            player.printStatusMessage(com.getNameWidth());
            com.printStatusMessage(player.getNameWidth());
            standardIO.printlnEmptyLine(1);
            player.choiceAction();
            com.choiceAction();
            standardIO.printlnEmptyLine(1);
            standardIO.println(generateChoicedActionMessage(player.getAction(), com.getAction()));
            player.damageProcess(com.getAction());
            com.damageProcess(player.getAction());
            standardIO.printlnEmptyLine(5);
        }
        standardIO.println(generateGameResultMessage(player.canGame(), com.canGame(), player.getName(), com.getName()));
        standardIO.printlnEmptyLine(1);
        pause();
    }

    /**
     * A method that creates and returns a message indicating the action selected by both game characters
     * @param action1 The action selected by game character 1.
     * @param action2 The action selected by game character 2
     * @return message
     */
    private static String generateChoicedActionMessage(String action1, String action2) {
        return ">" + action1 + "vs" + action2;
    }

    /**
     * A method that creates and returns a message that represents the outcome of the game
     * @param canGame1 It is a flag whether game character 1 can continue the game.
     * @param canGame2 A flag indicating whether game character 2 can continue the game
     * @param name1 The name of game character 1
     * @param name2 Game character 2 name
     * @return message
     */
    private static String generateGameResultMessage(boolean canGame1, boolean canGame2, String name1, String name2) {
        if (canGame1 && !canGame2) {
            return ">" + name1 + "is a victory!";
        }
        if (!canGame1 && canGame2) {
            return ">" + name2 + "is a victory!";
        }
        System.out.println("[Error] A tie has occurred");
        System.exit(0);
        return ""; // A return statement that is unnecessary in processing is written because it causes a syntax error
    }

    /**
     * A method that stops processing until the Enter key is pressed
     */
    private static void pause() {
        System.out.print("Press Enter .. .");
        Scanner scanner = new Scanner(System.in);
        scanner.nextLine();
    }
}

StandardIO.java


import java.util.Calendar;
import java.text.SimpleDateFormat;
import java.io.IOException;
import java.io.FileWriter;
import java.io.BufferedWriter;
import java.io.PrintWriter;
import java.util.Scanner;

/**
 * A class that handles all input and output to the console log file
 * Adopts Singleton design pattern
 * @author AGadget
 */
class StandardIO {
    private static StandardIO instanceStandardIO = new StandardIO(); // Instance of this objectprivate String logFilePath; // path to the log file

    /**
     * Constructor
     * Using the Calendar object, the log file name is created from the current date and time
     */
    private StandardIO() {
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyMMddHHmmss");
        this.logFilePath = "log_" + simpleDateFormat.format(calendar.getTime()) + ".txt";
    }

    /**
     * A method that returns an instance of this object
     * This method is required because it uses the Singleton design pattern.
     * @return is an instance of this object
     */
    public static StandardIO getInstance() {
        return instanceStandardIO;
    }

    /**
     * This method outputs the character string specified in the argument to the console and log file.
     * A line feed character is added to the end of the output value
     * @param message Character string to output
     */
    void println(String message) {
        System.out.println(message);
        try {
            FileWriter fileWriter = new FileWriter(this.logFilePath, true);
            PrintWriter printWriter = new PrintWriter(new BufferedWriter(fileWriter));
            printWriter.println(message);
            printWriter.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * A method that outputs an arbitrary number of blank lines to the console and log file
     * The number of lines can be specified with an argument
     * @param numberOfEmptyLine
     */
    void printlnEmptyLine(int numberOfEmptyLine) {
        for (int i = 0; i <numberOfEmptyLine; i += 1) {
            this.println("");
        }
    }

    /**
     * This method outputs the character string specified in the argument to the console and log file.
     * There is no line feed character at the end of the output value
     * @param message Character string to output
     */
    void print(String message) {
        System.out.print(message);
        try {
            FileWriter fileWriter = new FileWriter(this.logFilePath, true);
            PrintWriter printWriter = new PrintWriter(new BufferedWriter(fileWriter));
            printWriter.print(message);
            printWriter.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * A method that accepts standard input, outputs the input value to a log file, and returns it to the caller
     * @return is the standard input value
     */
    String readLine() {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        this.printlnOnlyToLogFile(input);
        return input;
    }

    /**
     * It is a method that outputs the string specified in the argument "only" to the log file.
     * → It is supposed to be used as a set with the method in charge of standard input processing.
     * A line feed character is added to the end of the output value
     * @param message Character string to output
     */
    private void printlnOnlyToLogFile(String message) {
        try {
            FileWriter fileWriter = new FileWriter(this.logFilePath, true);
            PrintWriter printWriter = new PrintWriter(new BufferedWriter(fileWriter));
            printWriter.println(message);
            printWriter.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

GameCharacter.java


/**
 * An abstract class that represents a game character
 * @author AGadget
 */
abstract class GameCharacter {
    protected StandardIO standardIO = StandardIO.getInstance(); // Object that is in charge of output processing
    private final int MAX_LIFE = 5; // Upper limit of physical strength common to all game characters
    private int life; // physical strength
    private String name; // name
    protected final String[] ACTIONS = {"goo", "choki", "par"}; // A list of actions that can be selected
    protected String action; // Selected action

    /**
     * An abstract method to select an action
     * Please implement the process of selecting the action in some way and assigning the selected action to the field that holds the selected action
     */
    abstract void choiceAction();

    /**
     * Constructor
     * @param initialLife The physical strength at the start of the game
     * @param name The name of the game character
     */
    GameCharacter(int initialLife, String name) {
        if (initialLife <0 && initialLife >this.MAX_LIFE? true :false) {
            System.out.println("[Error] The initial power value at the start of the game is invalid (" + initialLife + ")");
            System.exit(0);
        }
        this.life = initialLife;
        this.name = name;
    }

    /**
     * A method that responds whether the game can be continued
     * @return returns true if it can continue
     */
    boolean canGame() {
        return this.life >0? true :false;
    }

    /**
     * This method returns the width of the name
     * Unlike the value that can be obtained with String.length() etc., returns a value that counts half-width characters as 1 and full-width characters as 2
     */
    int getNameWidth() {
        char[] nameToCharArray = this.name.toCharArray();
        int nameWidth = 0;
        for (int i = 0; i <nameToCharArray.length; i += 1) {
            nameWidth += String.valueOf(nameToCharArray[i]).getBytes().length == 1 ?1 :2;
        }
        return nameWidth;
    }

    /**
     * A method that returns its own status as a message
     * The message consists of a name part and a physical strength part
     * → The name part should have the same length and width as the other party's name.
     * → The physical strength part is expressed like a physical strength gauge using symbols.
     * @param opponentNameWidth The width of the opponent's name (a special value that counts half-width characters as 1 and full-width characters as 2)
     */
    void printStatusMessage(int opponentNameWidth) {
        StringBuilder namePart = new StringBuilder(this.name);
        for (int i = 0; i <opponentNameWidth-this.getNameWidth(); i += 1) {
            namePart.append(" ");
        }
        StringBuilder lifePart = new StringBuilder();
        for (int i = 0; i <this.life; i += 1) {
            lifePart.append("■");
        }for (int i = 0; i <this.MAX_LIFE-this.life; i += 1) {
            lifePart.append("□");
        }
        standardIO.println(namePart.toString() + ": "+ lifePart.toString());
    }

    /**
     * A method that returns the selected action
     * @return The selected action
     */
    String getAction() {
        return this.action;
    }

    /**
     * This is the method that handles damage
     * From the combination of the actions of yourself and the opponent, check if damage will occur, and if damage occurs, reduce your physical strength
     * @param opponentAction The action of the opponent
     */
    void damageProcess(String opponentAction) {
        if (this.action.equals(this.ACTIONS[0]) && opponentAction.equals(this.ACTIONS[2])
            || this.action.equals(this.ACTIONS[1]) && opponentAction.equals(this.ACTIONS[0])
            || this.action.equals(this.ACTIONS[2]) && opponentAction.equals(this.ACTIONS[1])
        ) {
            this.life -= 1;
            return;
        }
    }

    /**
     * A method that returns a name
     * @return name
     */
    String getName() {
        return this.name;
    }
}

Player.java


/**
 * Class that represents a player
 * @author AGadget
 */
class Player extends GameCharacter {

    /**
     * Constructor
     * @param initialLife The physical strength at the start of the game
     * @param name The name of the game character
     */
    Player(int initialLife, String name) {
        super(initialLife, name);
    }

    /**
     * A method to select an action
     * Implemented processing using standard input because it is processing for players
     */
    @Override
    void choiceAction() {
        String prompt = this.generatePrompt();
        while (true) {
            standardIO.print(prompt);
            String input = standardIO.readLine();
            try {
                this.action = this.ACTIONS[Integer.parseInt(input)-1];
                return;
            } catch (Exception error) {
                standardIO.printlnEmptyLine(1);
                standardIO.println(">Illegal input");
                standardIO.println(">Please re-enter");
                standardIO.printlnEmptyLine(1);
            }
        }
    }

    /**
     * A method that creates and returns the prompt required for action selection processing
     * @return is a string that will be the prompt
     */
    private String generatePrompt() {
        StringBuilder prompt = new StringBuilder();
        for (int i = 0; i <this.ACTIONS.length; i += 1) {
            prompt.append("[" + (i + 1) + "]" + this.ACTIONS[i] + "");
        }
        prompt.append(": ");
        return prompt.toString();
    }
}

COM.java


import java.util.Random;

/**
 * Class that represents COM
 * @author AGadget
 */
class COM extends GameCharacter {

    /**
     * Constructor
     * @param initialLife The physical strength at the start of the game
     * @param name The name of the game character
     */
    COM(int initialLife, String name) {
        super(initialLife, name);
    }

    /**
     * A method to select an action
     * Implemented processing using random numbers because it is processing for COM
     */
    @Override
    void choiceAction() {
        Random random = new Random();
        this.action = ACTIONS[random.nextInt(ACTIONS.length)];
    }
}

TurnCount.java


/**
 * Class that represents the number of turns
 * @author AGadget
 */
class TurnCount {
    private StandardIO standardIO = StandardIO.getInstance(); // Object that is in charge of output processing
    private int turnCount; // number of turns

    /**
     * Constructor
     */
    TurnCount() {
        turnCount = 0;
    }

    /**
     * A method that advances the number of turns
     */
    void countUp() {
        this.turnCount += 1;
    }

    /**
     * This method outputs the current number of turns as a message
     */
    void printTurnCountMessage() {
        standardIO.println("[No.] + this.turnCount + "Turn"");
    }
}