1
\$\begingroup\$

In this program you have to solve 20 exercises. The time you need will be measured. If you break a record, you'll get an entry in the highscore list.

The program can be downloaded here. You can only start it via command line, not by double-click.

If I can improve my code to make it more readable, please tell me how.

Main.java

import java.time.Duration;
import java.time.Instant;
import java.util.Scanner;
import java.io.File;
import java.io.ObjectOutputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.FileInputStream;
import java.io.IOException;

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

    private static String saveName = ".mathematicianSave";
    private static Scanner scanner = new Scanner(System.in);
    private RecordList recordList;

    public Main() {
        if (new File(saveName).exists()) {
            try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(saveName))) {
                recordList = (RecordList)ois.readObject();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        } else {
            recordList = new RecordList(5);
        }
    }

    private void mainLoop() {
        System.out.println("[0] Leave\n");
        while(true) {
            System.out.println("[1] Play");
            System.out.println("[2] Highscore");
            System.out.print("Input: ");
            String decision = scanner.nextLine();
            System.out.println();
            if (decision.equals("1")) {
                calculate();
            } else if (decision.equals("2")) {
                showHighscore();
            } else if (decision.equals("0")) {
                save();
                break;
            }
        }
    }

    private void calculate() {
        System.out.println("Division exercises have to be solved without rest.");
        System.out.println("You have to solve twenty exercises.");
        System.out.println("Be as fast as you can!\n");

        Instant startTime = Instant.now();

        for (int i = 0; i < 20; i++) {
            Exercise exercise = Exercise.getRandomExercise();
            System.out.print(exercise);
            int guess = Integer.parseInt(scanner.nextLine());
            exercise.solve(guess);
            if (!exercise.solve(guess)) {
                System.out.println("\nYou have made a mistake.");
                System.out.println("More luck next time.\n");
                return;
            }
        }
        System.out.println();

        Instant endTime = Instant.now();
        Duration timeElapsed = Duration.between(startTime, endTime);
        double seconds = timeElapsed.toMillis() / 1000.0;

        Record record = new Record(seconds);
        System.out.println("You have needed " + seconds + " seconds.");
        if (recordList.add(record)) {
            System.out.println("You have set a new record!");
            System.out.print("Your name: ");
            record.setName(scanner.nextLine());
        }
        System.out.println();
    }

    private void showHighscore() {
        System.out.println(recordList);
    }

    private void save() {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(saveName))) {
            oos.writeObject(recordList);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Exercise.java

import java.util.Random;

public class Exercise {
    private int number1;
    private int number2;
    private int solution;
    private String operationSymbol;

    private Exercise(int number1, int number2, int solution, String operationSymbol) {
        this.number1 = number1;
        this.number2 = number2;
        this.solution = solution;
        this.operationSymbol = operationSymbol;
    }

    public static Exercise getRandomExercise() {
        Random random = new Random();
        int choice = random.nextInt(4);

        int number1;
        int number2;
        int solution;
        String operationSymbol;
        if (choice == 0) {
            number1 = random.nextInt(26);
            number2 = random.nextInt(26);
            solution = number1 + number2;
            operationSymbol = "+";
        } else if (choice == 1) {
            number1 = random.nextInt(16) + 10;
            number2 = random.nextInt(26);
            solution = number1 - number2;
            operationSymbol = "-";          
        } else if (choice == 2) {
            number1 = random.nextInt(15) + 1;
            number2 = random.nextInt(15) + 1;
            solution = number1 * number2;
            operationSymbol = "*";          
        } else {
            number1 = random.nextInt(151);
            number2 = random.nextInt(15) + 1;
            solution = number1 / number2;
            operationSymbol = "/";          
        }
        return new Exercise(number1, number2, solution, operationSymbol);
    }

    public boolean solve(int guess) {
        return solution == guess;
    }

    @Override 
    public String toString() {
        return number1 + " " + operationSymbol + " " + number2 + " = ";
    }
}

Record.java

import java.io.Serializable;

public class Record implements Serializable {
    private String name;
    private double seconds;

    public Record(double seconds) {
        name = null;
        this.seconds = seconds;
    }

    public Record(String name, double seconds) {
        this.name = name;
        this.seconds = seconds;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public double getSeconds() {
        return seconds;
    }
}

RecordList.java

import java.util.Comparator;
import java.util.List;
import java.util.ArrayList;
import java.io.Serializable;

public class RecordList implements Serializable {
    private List<Record> records;
    private int limit;

    public RecordList(int limit) {
        records = new ArrayList<>();
        this.limit = limit;
    }

    private void sort() {
        records.sort(new Comparator<Record>() {
            @Override
            public int compare(Record record1, Record record2) {
                Double double1 = record1.getSeconds();
                Double double2 = record2.getSeconds();
                return double1.compareTo(double2);
            }
        });
    }

    public boolean add(Record record) {
        if (records.size() < limit) {
            records.add(record);
            sort();
            return true;
        }

        Record lowestRecord = records.get(records.size() - 1);
        if (record.getSeconds() < lowestRecord.getSeconds()) {
            records.remove(lowestRecord);
            records.add(record);
            sort();
            return true;
        }

        return false;
    }

    @Override
    public String toString() {
        if (records.isEmpty()) {
            return "No records.\n";
        }
        StringBuilder returnValue = new StringBuilder("--- Records ---\n\n");
        for (int i = 0; i < records.size(); i++) {
            returnValue.append(i+1);
            returnValue.append(". ");
            returnValue.append(records.get(i).getName());
            returnValue.append(" - ");
            returnValue.append(records.get(i).getSeconds());
            returnValue.append("\n");
        }
        return returnValue.toString();
    }
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I find your code very readable.

  • "Division exercises have to be solved without rest" should instead read "division exercises should be solved without remainders" or something. "without rest" doesn't mean anything to me.
  • It is crashing when symbols like / and . are used in an answer, instead remind user of above rule. In general, handle invalid input instead of having program crash.
  • Consider a validation using a modulus test for the division exercises to make sure the random numbers generated and divided have a whole number for the answer. If not, get another random number.
  • Goal is to persist high score so that it is available next time the program is launched. Main.java looks like it should persist this high score to a file called .mathematicianSave but it does not unless I chose to exit at the menu with 0. Most of my exits have been because of crashes.
  • Menu item [0] Leave only appears when program first launches
  • in the getRandomExercise() method, I would not hard code the upper limits for number1 and number2 but instead use a variable declared at start of method.
  • in the calculate() method of Main class, I would also not hard code the 5 but instead make it a variable called numberOfQuestions and declare it at start of method.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.