3
\$\begingroup\$

I started learning Java 2 weeks ago, so I don't know that much. I recently made a working console quiz and I'm wondering if there is a simpler way to do it.

import java.util.*;
import java.io.*;

class StudyBot
{
    static int[][][] GradeValues = {{{93, 100}, {90, 92}}, {{87, 89}, {83, 86}, {80, 82}}, {{77, 79}, {73, 76}, {70, 72}}, {{67, 69}, {64, 66}}, {{0, 63}}};
    static Scanner istr = new Scanner(System.in), iint = new Scanner(System.in);
    static Map<String, Object> data = new HashMap<String, Object>();
    static boolean word_Prob, multi_Choice;
    static double correct = 0, incorrect = 0;
    static int question_Number = 0, amount_Of_Questions = 10;
    static String result = "";

    public static void main(String[] args)
    {
        new StudyBot();
    }

    public StudyBot()
    {
        setupData(); mainMenu();
    }

    void mainMenu()
    {
        printline(0);
        print("[StudyBot] Welcome to StudyBot!");
        printline(0);
        print("[StudyBot] Pick a Mode: Study OR Add Data");
        String input = istr.nextLine();
        if(input.equalsIgnoreCase("Study")) start();
        else if(input.equalsIgnoreCase("Add Data")) addData();
        else print("Invaild mode!"); mainMenu();
    }

    void start()
    {
        for(int index = 0; index < (amount_Of_Questions + 1); index++)
        {           
            if(!(index < amount_Of_Questions))
            {
                double percentage = (correct / amount_Of_Questions) * 100;
                printline(0); 
                print("[# of Questions] " + amount_Of_Questions); 
                print("[Correct] " + correct); 
                print("[Incorrect] " + incorrect); 
                print("[Percentage] " + percentage + "%"); 
                print(""); 
                print("[Grade] " + getLetterGrade(percentage)); 
                print("[GPA] " + getGPA(percentage));
                break;
            }
            else setupData(); pickQuestion();
        }
    }

    void setupData()
    {
        /* INPUT DATA HERE
         * 
         * key = question
         * value = answer
         * 
         * EX: data.put(key1, value1);
         *     data.put(key2, value2);
         *     data.put(key3, value3);
         *     etc....
         */
    }

    void addData()
    {
        print("Enter key:");
        String key = istr.nextLine();
        if(!(key.equalsIgnoreCase("main menu")))
        {
            print("Enter value:");
            String value = istr.nextLine();
            data.put(key, value);
            addData();
        }
        else mainMenu();
    }

    void displayData(String s)
    {
        Set set = data.entrySet(); 
        Iterator i = set.iterator();
        while(i.hasNext())
        {
            Map.Entry e = (Map.Entry)i.next();
            String key = e.getKey().toString();
            String k = (String) e.getKey(), v = (String) e.getValue();
            String data = ((s != null ? (key.equals(s) ? (k + ": " + v) : null) : (k + ": " + v)));
            if(data != null) print(data);
        }
    }

    void pickQuestion()
    {
        List<String> keys = new ArrayList<String>(data.keySet());
        String key = keys.get(rand(keys.size()));
        String value = (String) data.get(key);
        askQuestion(key, value);
    }

    void askQuestion(String question, String answer)
    {
        questionType();
        if(word_Prob) wordProblem(question, answer);
        else multiChoice(question, answer);
    }

    void wordProblem(String question, String answer)
    {
        question_Number++;
        printline(0); 
        print("[Question " + question_Number + "] What is " + question + "?"); 
        printline(1);
        String user_Answer = istr.nextLine();
        String[] user_Words = user_Answer.split(" "), answer_Words = answer.split(" ");
        List<String> matched_Words = new ArrayList();
        for(String i : user_Words)
            for(String j : answer_Words)
                if(i.equalsIgnoreCase(j)) matched_Words.add(i);
        int num_Of_Matched_Words = matched_Words.size();
        int x = num_Of_Matched_Words, y = answer_Words.length;
        String result = ((x >= (y / 2)) ? "Correct!" : (((x > (y / 3)) && ((y / 2) > x)) ? "Almost!" : "Incorrect! Answer was " + answer));
        if(result.equals("Correct!")) correct++;
        else if(result.equals("Incorrect! Answer was " + answer)) incorrect++;
        else if(result.equals("Almost!")) correct += 0.5;
        print(result);
    }

    void multiChoice(String Q, String A)
    {
        boolean repeat = true;
        String question = Q, answer = A;
        String[] ABCD_key = {randKey(), randKey(), randKey()};
        while(repeat)
        {
            ABCD_key[0] = (ABCD_key[0].equals(question)) || (ABCD_key[0].equals(ABCD_key[1])) || (ABCD_key[0].equals(ABCD_key[2])) ? randKey() : ABCD_key[0];
            ABCD_key[1] = (ABCD_key[1].equals(question)) || (ABCD_key[1].equals(ABCD_key[0])) || (ABCD_key[1].equals(ABCD_key[2])) ? randKey() : ABCD_key[1];
            ABCD_key[2] = (ABCD_key[2].equals(question)) || (ABCD_key[2].equals(ABCD_key[0])) || (ABCD_key[2].equals(ABCD_key[1])) ? randKey() : ABCD_key[2];
            repeat = (!(ABCD_key[0].equals(ABCD_key[1])) && !(ABCD_key[0].equals(ABCD_key[2])) && !(ABCD_key[1].equals(ABCD_key[2]))) ? false : true;
        }
        repeat = true;
        String[] ABCD_option = {(String) data.get(question), (String) data.get(ABCD_key[0]), (String) data.get(ABCD_key[1]), (String) data.get(ABCD_key[2])};
        int x = rand(0, 3), y = rand(0, 3), z = rand(0, 3), t = rand(0, 3);
        while(repeat)
        {
            y = (y == x) ? rand(0, 3) : y;
            z = (z == x || z == x) ? rand(0, 3) : z;
            t = (t == z || t == y || t == x) ? rand(0, 3) : t;
            repeat = ( !(y == x) && !(y == z) && !(y == t) && !(z == x) && !(z == t) && !(x == t) ) ? false : true;
        }
        String a = ABCD_option[x], b = ABCD_option[y], c = ABCD_option[z], d = ABCD_option[t];
        String letter = a.equals(ABCD_option[0]) ? "A" : (b.equals(ABCD_option[0]) ? "B" : (c.equals(ABCD_option[0]) ? "C" : (d.equals(ABCD_option[0]) ? "D" : "")));
        boolean has_Repeated_Questions = ((!(letter.equals("A"))) && (!(letter.equals("B"))) && (!(letter.equals("C"))) && (!(letter.equals("D"))));
        if(!has_Repeated_Questions)
        {
            question_Number++;
            displayQuestion(question, a, b, c, d);
            String user_Answer = istr.nextLine();
            boolean matches = user_Answer.equalsIgnoreCase(letter);
            boolean menu = user_Answer.equalsIgnoreCase("main menu");
            result = (menu ? "" : (matches ? "Correct!" : "Incorrect! Answer was " + letter));
            if(menu) mainMenu();
            if(matches) correct++;
            else incorrect++;
            print(result);
        }
        else multiChoice(question, answer);
    }

    void displayQuestion(String question, String a, String b, String c, String d)
    {
        printline(0); 
        print("[Question " + question_Number + "] What is " + question + "?"); 
        printline(1); 
        print("[A] " + a); 
        print("[B] " + b); 
        print("[C] " + c); 
        print("[D] " + d); 
        printline(1);
    }

    String randKey()
    {
        List<String> keys = new ArrayList<String>(data.keySet());
        return keys.get(rand(keys.size()));
    }

    void questionType()
    {
        Random random = new Random();
        boolean type = (random.nextInt(2) == 1) ? true : false;
        this.word_Prob = type;
        this.multi_Choice = !type;
    }

    String getLetterGrade(double percentage)
    {
        return isBetween(percentage, GradeValues[0][0][0], GradeValues[0][0][1]) ? "A" : (isBetween(percentage, GradeValues[0][1][0], GradeValues[0][1][1]) ? "A-" : (isBetween(percentage, GradeValues[1][0][0], GradeValues[1][0][1]) ? "B+" : 
              (isBetween(percentage, GradeValues[1][1][0], GradeValues[1][1][1]) ? "B" : (isBetween(percentage, GradeValues[1][2][0], GradeValues[1][2][1]) ? "B-" : (isBetween(percentage, GradeValues[2][0][0], GradeValues[2][0][1]) ? "C+" : 
                  (isBetween(percentage, GradeValues[2][1][0], GradeValues[2][1][1]) ? "C" : (isBetween(percentage, GradeValues[2][2][0], GradeValues[2][2][1]) ? "C-" : (isBetween(percentage, GradeValues[3][0][0], GradeValues[3][0][1]) ? "D+" : 
                      (isBetween(percentage, GradeValues[3][1][0], GradeValues[3][1][1]) ? "D" : (isBetween(percentage, GradeValues[4][0][0], GradeValues[4][0][1]) ? "F" : ("None")))))))))));
    }

    double getGPA(double percentage)
    {
        return isBetween(percentage, GradeValues[0][0][0], GradeValues[0][0][1]) ? 4.0 : (isBetween(percentage, GradeValues[0][1][0], GradeValues[0][1][1]) ? 3.7 : (isBetween(percentage, GradeValues[1][0][0], GradeValues[1][0][1]) ? 3.3 : 
              (isBetween(percentage, GradeValues[1][1][0], GradeValues[1][1][1]) ? 3.0 : (isBetween(percentage, GradeValues[1][2][0], GradeValues[1][2][1]) ? 2.7 : (isBetween(percentage, GradeValues[2][0][0], GradeValues[2][0][1]) ? 2.3 : 
                  (isBetween(percentage, GradeValues[2][1][0], GradeValues[2][1][1]) ? 2.0 : (isBetween(percentage, GradeValues[2][2][0], GradeValues[2][2][1]) ? 1.7 : (isBetween(percentage, GradeValues[3][0][0], GradeValues[3][0][1]) ? 1.3 : 
                      (isBetween(percentage, GradeValues[3][1][0], GradeValues[3][1][1]) ? 1.0 : (isBetween(percentage, GradeValues[4][0][0], GradeValues[4][0][1]) ? 0.0 : (0.0)))))))))));
    }

    boolean isBetween(double x, double lower, double upper) 
    {
        return lower <= x && x <= upper;
    }

    int rand(int min, int max)
    {
        Random random = new Random();
        return random.nextInt((max - min) + 1) + min;
    }

    int rand(int max)
    {
        Random random = new Random();
        return random.nextInt(max);
    }

    void print(Object o)
    {
        System.out.println(o);
    }

    void printline(int type)
    {
        switch(type) 
        {
            case 0: print("============================================================="); break;
            case 1: print("-------------------------------------------------------------"); break;
        }
    }
}
\$\endgroup\$
0

2 Answers 2

2
\$\begingroup\$

Your a beginner and we can see it.
So let's educate you a few important things.

Access modifiers.

This is the first thing that I noticed, and almost every beginner will make this fault.

class StudyBot {

At this moment is your access modifier package private, and this is a choice what's rarely need's to be made.
The normal setup for a class is public, protected or private.

The same counts for your variable :

static int[][][] GradeValues = {{{93, 100}, {90, 92}}, {{87, 89}, {83, 86}, {80, 82}}, {{77, 79}, {73, 76}, {70, 72}}, {{67, 69}, {64, 66}}, {{0, 63}}};
static Scanner istr = new Scanner(System.in), iint = new Scanner(System.in);
static Map<String, Object> data = new HashMap<String, Object>();

Make them private, if you don't need to acces them outside this class.

Static, Final when to choose?

We just spoke of your variable to have an acces modifier.
Some of your variable you never want to instantiate a second time.
If this is the case, make them also final.
But don't forget a static variable is the same for all instances.
If you change your result and a second instance is running, they will mix it up.
So always think good when you make a variable static. Will it interfere with other instances or not.

Naming variable

Your naming is overall good.
Some small errors like :

static int[][][] GradeValues = {{{93, 100}, {90, 92}}, {{87, 89}, {83, 86}, {80, 82}}, {{77, 79}, {73, 76}, {70, 72}}, {{67, 69}, {64, 66}}, {{0, 63}}};
static Scanner istr = new Scanner(System.in), iint = new Scanner(System.in);

The first one(GradeValue) should start with a lowercase.
istr is just a bad name because if you don't see the instance, you never know what it means.
A better name could be scanner.
As for iint, well it's never used in your class, so remove it.

1 line 1 thing.

public StudyBot() {
    setupData(); mainMenu();
}

While it's not wrong, we do prefer to have this in 2 lines to improve the readability for other people.

public StudyBot() {
    setupData();
    mainMenu();
}

The same counts for here :

else setupData(); pickQuestion();

You even make an (unwanted) fault here.
For the readers this seems like the 2 methods should be executed in the else.
The actually effect is that `pickQuestion(); is always executed, think of the "goto fail" bug of apple.

This should be better :

else {
    setupData();
}
pickQuestion();

As you can see, you do more things like this but please put curly braces with each if and else.

Boolean operators

for(int index = 0; index < (amount_Of_Questions + 1); index++)

Here you count 1 to amount_Of_Questions but actually you can say smaller or equal.
The +1 is done by every iteration, witch means every iteration you take time to add 1 to amount_Of_Questions.

Better could be :

for(int index = 0; index <= (amount_Of_Questions + 1); index++)

Almost the same for here :

if(!(index < amount_Of_Questions))

Here you say if it's not smaller then.
Not smaller then is the same as : bigger or the same.

if(index >= amount_Of_Questions)

But if we look further, index can never be bigger then amount_Of_Questions because the last iteration is when it's equal.

So an even better improvement could be :

if(index = amount_Of_Questions)

Usage of for each, and beyond

void displayData(String s) {
    Set set = data.entrySet(); 
    Iterator i = set.iterator();
    while(i.hasNext())
    {
        Map.Entry e = (Map.Entry)i.next();
        String key = e.getKey().toString();
        String k = (String) e.getKey(), v = (String) e.getValue();
        String data = ((s != null ? (key.equals(s) ? (k + ": " + v) : null) : (k + ": " + v)));
        if(data != null) print(data);
    }
}

You iterate over the whole entryset of a Map.
You even bring casting to it because you work with the iterator.
Let us use a foreach :

private void displayData(String s) {
    for (Entry e : data.entrySet()) {
        String key = e.getKey().toString();
        String k = (String) e.getKey(), v = (String) e.getValue();
        String data = ((s != null ? (key.equals(s) ? (k + ": " + v) : null) : (k + ": " + v)));
        if(data != null) {
            print(data);
        }
    }
}

As you can see, it's already a lot smaller and cleaner.
But I'm not finding this clean or good.
Let us use some methods of Map.

private void displayData(String key) {
    if (data.containsKey(key)) {
        Object value = data.get(key);
        print(key + ": " + String.valueOf(value));
    }
}

So you wanted to print when you had an key in the Map.
Map has a method called containsKey(Object key) so we use it to know if we need to fetch the value and print it.
When the key is present, we will fetch the value, and print it out.
One of the best things to do is call String.valueOf if you want to convert a Object to a String.

I'm going to leave it here for the moment.
I hope you are going to refactor this code and create a follow-up question with the refactored code.

\$\endgroup\$
0
\$\begingroup\$

In my personal opinion, doesn't exist one correct code, if you test your program and he works, you're good to go!

I see your source and i think your code have to much methods that are simple functions like

print(Obeject o ){ System.out.println(o)}.

I think is more clean you call direct the print! So one developer could read your code and quickly understand what are your doing!

Try comment your code, that is a good practice and really make difference.

I recommend you to test your program in different ways, eventually, you could improve your program!

Good Luck!

\$\endgroup\$

Your Answer

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