package grammar.output;
import grammar.input.stdin.Command;
import grammar.input.stdin.CommandFactory;
import grammar.input.stdin.Command.Action;
import grammar.input.stdin.Command.ExitCommand;
import grammar.input.stdin.Command.HelpCommand;
import grammar.model.Language;
import grammar.util.Utilities;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
public abstract class AbstractTester {
private static final BigDecimal CORRECT_ANSWER_SCORE_INCREMENT = new BigDecimal(1);
private static final BigDecimal PARTIALLY_CORRECT_ANSWER_SCORE_INCREMENT = new BigDecimal("0.5");
private static final int MAX_SCOPE_ITEMS_TO_PRINT = 6;
public abstract String getHelpText();
public abstract Question getNextQuestion();
public interface Question {
public String getCorrectAnswer();
public String getQuestionText();
public boolean isCorrect(String response);
public String isPartiallyCorrect(String response);
public String getHintText();
public String getTextToRead();
}
private Language language;
public AbstractTester(Language language) {
this.language = language;
}
public void play() throws IOException {
BigDecimal score = new BigDecimal(0);
BigDecimal highScore = new BigDecimal(0);
BigDecimal totalQuestionsAnswered = new BigDecimal(0);
BigDecimal totalCorrectAnswers = new BigDecimal(0);
System.out.println("Type help for commands.");
while (true) {
Question q = getNextQuestion();
System.out.println();
System.out.println(q.getQuestionText());
String correctAnswer = q.getCorrectAnswer();
String response = Utilities.readLineFromStdin();
try {
while (true) { // loop broken by exception being thrown
try {
processCommand(response);
if (exit)
return;
}
catch (IllegalStateException e) {
System.out.println("Command ignored - it would leave nothing in scope!");
}
response = Utilities.readLineFromStdin();
}
}
catch (IllegalArgumentException e) {
// not a command
}
if (q.isCorrect(response)) {
totalCorrectAnswers = totalCorrectAnswers.add(new BigDecimal(1));
totalQuestionsAnswered = totalQuestionsAnswered.add(new BigDecimal(1));;
score = score.add(CORRECT_ANSWER_SCORE_INCREMENT);
System.out.println("CORRECT! "+score.toPlainString()+
" point"+(score.compareTo(new BigDecimal(1)) != 0?"s":"")+" ("+
totalCorrectAnswers.multiply(new BigDecimal(100)).divideToIntegralValue(totalQuestionsAnswered).round(MathContext.DECIMAL128)+
"% answered correctly thus far)!");
if (score.compareTo(highScore) > 0)
highScore = score;
}
else if (q.isPartiallyCorrect(response) != null) {
totalCorrectAnswers = totalCorrectAnswers.add(new BigDecimal(1));
totalQuestionsAnswered = totalQuestionsAnswered.add(new BigDecimal(1));;
score = score.add(PARTIALLY_CORRECT_ANSWER_SCORE_INCREMENT);
System.out.println("ALMOST! "+q.isPartiallyCorrect(response)+"Correct answer: "+correctAnswer+". "+
score.toPlainString()+" point"+
(score.compareTo(new BigDecimal(1)) >= 0?"s":"")+" ("+
totalCorrectAnswers.multiply(new BigDecimal(100)).divideToIntegralValue(totalQuestionsAnswered).round(MathContext.DECIMAL128)+
"% answered correctly thus far)!");
if (score.compareTo(highScore) > 0)
highScore = score;
}
else {
totalQuestionsAnswered = totalQuestionsAnswered.add(new BigDecimal(1));;
System.out.println("WRONG! Correct answer: "+correctAnswer+
". Score this time: "+score+"; highest so far: "+highScore+" ("+
totalCorrectAnswers.multiply(new BigDecimal(100)).divideToIntegralValue(totalQuestionsAnswered).round(MathContext.DECIMAL128)+
"% answered correctly thus far).");
score = new BigDecimal(0);
}
System.out.println(q.getHintText());
if (language.isSpeechSynthesisSupported())
language.getSpeechSynthesiser().say(q.getTextToRead());
}
}
private boolean exit = false;
private void processCommand(String in) {
Command command = CommandFactory.interpret(language, in);
Action action = command.getAction();
if (command instanceof ExitCommand) {
exit = true;
}
else if (command instanceof HelpCommand) {
System.out.println(getHelpText());
}
else if (processCommand(command, action)) {
return;
}
else {
throw new Error("Unimplemented command type: "+command);
}
}
public abstract boolean processCommand(Command command, Action action);
protected static <T> void updateScope(Set<T> scope, Set<T> update, Action action) {
Set<T> scopeTmp = new HashSet<T>(scope);
if (action.equals(Action.ADD))
scopeTmp.addAll(update);
else if (action.equals(Action.USE)) {
scopeTmp.clear();
scopeTmp.addAll(update);
}
else if (action.equals(Action.REMOVE))
scopeTmp.removeAll(update);
else
throw new IllegalStateException();
if (scopeTmp.size() == 0)
throw new IllegalStateException("Nothing left in scope!");
scope.clear();
scope.addAll(scopeTmp);
}
protected static <S> void printScope(SortedSet<S> scope, String type) {
StringBuilder sb = new StringBuilder();
sb.append(type+": ");
Set<S> s = new TreeSet<S>();
if (scope.size() > MAX_SCOPE_ITEMS_TO_PRINT) {
Iterator<S> iterator = scope.iterator();
for (int i = 0; i < MAX_SCOPE_ITEMS_TO_PRINT; i++) {
s.add(iterator.next());
}
}
else {
s.addAll(scope);
}
int sz = s.size();
int i = 0;
for (Object o : s) {
sb.append(Utilities.asHumanReadableName(o.toString()));
if (i < sz-1)
sb.append(", ");
i++;
}
if (scope.size() > MAX_SCOPE_ITEMS_TO_PRINT) {
sb.append("... (");
sb.append(scope.size()-MAX_SCOPE_ITEMS_TO_PRINT);
sb.append(" more)");
}
String str = sb.toString();
System.out.println(str);
}
public Language getLanguage() {
return language;
}
}