I know this post already has an accepted answer and I really like Nicolas Filotto's answer but I thought I would just share that there is yet another way to accomplish this task (as with anything in Java) and that is by using BufferReader()/InputStreamReader(System.in) which allows you to gather input from the Console without blocking code operation if properly done. Of course an additional thread is used for the question time limit mechanism.
A bit of a twist with this code however, the questions which the User is asked are retrieved from a Text file so that any test can be used with the code. This eliminates the need to hard code the test. Each line within the test text file is a question consisting of three parts (delimited with the Pipe (|) character. The 1st part is the question itself, the 2nd part is the actual answer, and the optional 3rd part is the number of seconds allowed to answer the question. If the 3rd part is not supplied with the question then a default of 10 seconds is used. If 0 is supplied for the 3rd part then the question contains no time limit. The Test file contents should look something like this:
1: What is: 1 + 1 = ?\n a: 1 b: 2 c: 3 d: 4\n|b|10
2: What is: 5 + 5 = ?\n a: 1 b: 12 c: 10 d: 15\n|c|20
3: What is: 6 + 4 = ?\n a: 10 b: 11 c: 12 d: 13\n|a
4: What is: 2 + 3 = ?\n a: 3 b: 4 c: 5 d: 6\n|c|10
5: What is: 4 + 3 = ?\n a: 7 b: 6 c: 5 d: 8\n|a|10
As you can see in the file contents question 3 uses the default time limit. The \n and \t tags are permitted within the file text and are handled accordingly within the processing code.
I won't delve into the code operation since I have commented it quite well as to what does what. Here is a full runnable:
package timedquestions;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
public class TimedQuestions {
private static String redColor = "\u001B[31m"; // for setting output text color red
private static String blueColor = "\u001B[34m"; // for setting output text color blue
private static String purpleColor = "\u001B[35m"; // for setting output text color purple
private static String resetColor = "\u001B[39;49m"; // for resetting output text color to default
private static boolean questionAnswered = false; // Keeps track of whether a question was answered
private static int questionCount = 0; // Keeps count of number of questions asked to User
private static int rightAnswers = 0; // Keeps count of questions answered Correctly
private static int wrongAnswers = 0; // Keeps count of questions answered Incorrectly
private static int defaultSecondsForTimout = 10; // The default question time limit
private static int secondsForTimout = 10; //Current set time limit for question (can change by question)
private static boolean timeOut = false; // Keeps track of whether or not the question has timed out.
public static void main(String[] args) {
// Start the test...
startTest("Test001.txt"); // Your test file path & name goes here.
// Calculate and display score for test...
double scr = (double) ((rightAnswers/questionCount)*100);
int score = (int) Math.ceil((double)rightAnswers/(double)questionCount*100.0);
System.out.println("\n======================================================");
System.out.println("Out of a total of " + questionCount + " questions\n"
+ "you got " + rightAnswers + " questions Correct and\n"
+ "you got " + wrongAnswers + " questions Wrong.\n"
+ "Your score is " + + score + "% "
+ "which gives you a grade of '" + getLetterGrade(score) + "'.");
System.out.println("======================================================");
// Done.
}
private static void startTest(String testFilePath) {
// Get the number of lines (questions) within the supplied Test file...
int fileLinesCount = 0;
LineNumberReader lnr;
try {
lnr = new LineNumberReader(new FileReader(new File(testFilePath)));
lnr.skip(Long.MAX_VALUE);
fileLinesCount = (lnr.getLineNumber() + 1);
//System.out.println("Within this Test there are " + fileLinesCount + " questions.");
lnr.close();
}
catch (FileNotFoundException ex) { System.out.println(ex.getMessage()); return; }
catch (IOException ex) { System.out.println(ex.getMessage()); return; }
// Display test information to the User via a Message Box
// and allow User to start the test.
String msg = "Within this Test there are " + fileLinesCount + " questions.\n" +
"You will have " + secondsForTimout + " seconds to answer each test question.\n" +
"If a question runs out of time then you will get that question WRONG.\n\n" +
"Press the OK button to start the Test:";
JDialog dialog = new JDialog();
dialog.setAlwaysOnTop(true);
JOptionPane.showMessageDialog (dialog, msg, "Test Inforamtion...", JOptionPane.PLAIN_MESSAGE);
dialog.dispose();
// Open a Bufferreader to read in questions from the Test file.
try (BufferedReader fileReader = new BufferedReader(new FileReader(testFilePath))) {
// Iterate through the Test file and process one question at a time....
String testFileLine = "";
while ((testFileLine = fileReader.readLine()) != null) {
// Split the file line question into specific parts.
// 1st part will be the question itself, 2nd part will be
// the answer and the 3rd part will be the number of seconds
// allowed to answer question. If the 3rd part is not supplied
// within the file line question then a default of 10 seconds
// is used as set by the defaultSecondsForTimout class global
// variable.
String[] fileQuestionParts = testFileLine.split("\\|");
String question = fileQuestionParts[0];
// Allow for newline and tab tags within text string.
question = "\n" + question.replaceAll("\\\\n", "\n").replaceAll("\\\\t", "\t");
//Get the question answer from file line
String questionAnswer = fileQuestionParts[1];
// If the seconds time out is provided within the question file
// line then grab it for use. If 0 is supplied then there is no
// time limit on that particular question.
if (fileQuestionParts.length > 2) { secondsForTimout = Integer.parseInt(fileQuestionParts[2]); }
// otherwise use the default of 10 seconds.
else { secondsForTimout = defaultSecondsForTimout; }
String answerResult = "";
questionCount++; // Increment the questionCount variable
questionAnswered = false; // Set the qustionAnswered variable
// Ask the retrived question to User....
answerResult = askTimedQuestion(question, secondsForTimout);
questionAnswered = true;
// If the User entered quit then quit the test.
if (answerResult.equalsIgnoreCase("quit")) { break; }
// If the User supplied input is the right answer then...
if (answerResult.equalsIgnoreCase(questionAnswer)) {
System.out.print(blueColor + " CORRECT\n " + resetColor);
rightAnswers++; // Increment the rightAnswers variable.
}
// If the User supplied input is the wrong answer then...
else {
wrongAnswers++; // Increment the wrongAnswers variable.
System.out.print(redColor + " WRONG\n " + resetColor); }
}
// Close the file reader.
fileReader.close();
}
catch (IOException e) {
// Display file errors
System.out.println("\nERROR! - " + redColor + e.getMessage() + resetColor);
}
}
// Method for asking the questions retrieved from the supplied Test file
private static String askTimedQuestion(String question, int secondsPerQuestion) {
// The secondsPerQuestion parameter allows you to change
// the number of seconds for each question if seconds are
// applied to a particular question within the Test file.
secondsForTimout = secondsPerQuestion;
// Establish a new Thread for perform our question timing...
Thread timerThread = new Thread(new Runnable() {
@Override
public void run() {
try {
// See if this thread has been interrupted. If it has then
// we stop our timer While/Loop (a gracefull Thread Stop).
while (!Thread.currentThread().isInterrupted()) {
for (int seconds = secondsForTimout; seconds >= 1; seconds--){
// Break out of this timer FOR loop if the question
// was answered by using Thread.interrupt().
if (questionAnswered) { Thread.currentThread().interrupt(); break;}
// Show that timer is ticking away...
System.out.print(purpleColor + "*" + resetColor);
// ==========================================================
// Or you can use this...
//if (seconds < secondsForTimout) { System.out.print("-"); }
//System.out.print(seconds);
// ==========================================================
Thread.sleep(1000);
}
// If the question wasn't answered and our timer loop has
// expired then inform User that time is up and set the
// timeOut variable to true.
if (!questionAnswered) {
System.out.print("\b\b\b\u001B[31mYour time is up for this question!\u001B[39;49m");
timeOut = true;
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
// Catch Exceptions for BufferReader()/InputStreamReader()...
try {
// Declare a BufferReader object so as to get input from User.
// We use BufferReader along with InputStreamReader(System.in)
// for this.
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// Display the supplied Question from file to User...
if (questionCount > 1) {
System.out.println("\n------------------------------------------------------");
}
System.out.print(blueColor + "[" + secondsForTimout + " seconds]" + resetColor);
System.out.println(question);
// Declare User input variable & initialize to a Null String
String input = "";
// Reset the timeOut variable
timeOut = false;
// Make sure our timer thread is dead before restarting it.
//while (timerThread.isAlive()){}
// Start the Timer Thread
if (secondsPerQuestion > 0) { timerThread.start(); }
// Loop through input from the User
do {
// Wait until we have User input data to complete a readLine()
// or until our timer thread has set the timeOut variable to
// true.
while (!br.ready()) {
// If our timer thread has set the timeOut variable to
// true then let's get outta this question altogether.
// First we get out of our 'wait for input' loop...
if (secondsPerQuestion > 0 && timeOut) { break; }
}
// Then we get out of our Do/While Loop.
if (secondsPerQuestion > 0 && timeOut) { break; }
// No time-out so let's move on...
// Let's see what the User supplied for an answer.
// If just ENTER was supplied then input will contain
// a Null String and the User can still enter an answer
// until the question is timed out.
input = br.readLine();
// remove any unwanted text from System.out.print()
// that had made its way through the timer thread.
System.out.print("\b\b\b\b\b");
} while ("".equals(input));
// Stop the timer thread.
timerThread.interrupt();
return input;
}
catch (IOException ex) { return ""; }
}
// Determine & return the Letter Grade for the
// supplied integer test percentage score. This
// is a typical North American Standard (I think?).
private static String getLetterGrade(int score) {
String grade = "";
if (score >= 90) { grade = "A"; }
if (score >= 80 && score <= 89) { grade = "B"; }
if (score >= 70 && score <= 79) { grade = "C"; }
if (score >= 60 && score <= 69) { grade = "D"; }
if (score >= 0 && score <= 59) { grade = "F"; }
return grade;
}
}
As I said...just another way.