diff --git a/lab-01/src/by/dudkoandrei/quizer/Main.java b/lab-01/src/by/dudkoandrei/quizer/Main.java new file mode 100644 index 0000000..e51952a --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/Main.java @@ -0,0 +1,73 @@ +package by.dudkoandrei.quizer; + +import by.dudkoandrei.quizer.constants.EquationTaskGeneratorConstants; +import by.dudkoandrei.quizer.constants.ExpressionTaskGeneratorConstants; +import by.dudkoandrei.quizer.constants.TextGeneratorConstats; +import by.dudkoandrei.quizer.task_generators.GroupTaskGenerator; +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; + +public class Main { + + /** + * @return тесты в {@link Map}, где ключ - название теста {@link String} значение - сам тест + * {@link Quiz} + */ + static Map getQuizMap() { + Map quizMap = new HashMap<>(); + + quizMap.put("Text Quiz", new Quiz(TextGeneratorConstats.generator, 3)); + quizMap.put("Integer equations", new Quiz(EquationTaskGeneratorConstants.integerGenerator, 10)); + quizMap.put("Equations", new Quiz(EquationTaskGeneratorConstants.generator, 10)); + quizMap.put("Integer expressions", + new Quiz(ExpressionTaskGeneratorConstants.integerGenerator, 10)); + quizMap.put("Expressions", new Quiz(ExpressionTaskGeneratorConstants.generator, 10)); + quizMap.put("All tasks", new Quiz( + new GroupTaskGenerator(TextGeneratorConstats.generator, + EquationTaskGeneratorConstants.integerGenerator, + EquationTaskGeneratorConstants.generator, + ExpressionTaskGeneratorConstants.integerGenerator, + ExpressionTaskGeneratorConstants.generator), 20)); + + return quizMap; + } + + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + + var quizMap = getQuizMap(); + + System.out.println("Available quizzes:"); + + for (var name : quizMap.keySet()) { + System.out.println(name); + } + + System.out.println("Select quiz:"); + + String name = scanner.nextLine(); + while (!quizMap.containsKey(name)) { + System.out.println("No such quiz. Try again"); + name = scanner.nextLine(); + } + + Quiz quiz = quizMap.get(name); + + System.out.println("Quiz: \"" + name + "\" started"); + while (!quiz.isFinished()) { + System.out.println(quiz.nextTask().getText()); + + String answer = scanner.nextLine(); + Result result = quiz.provideAnswer(answer); + + System.out.println(result + "\n"); + } + + System.out.println("Quiz ended"); + System.out.println("Your score: " + quiz.getMark() * 10); + System.out.println("Correct answers: " + quiz.getCorrectAnswerNumber()); + System.out.println("Wrong answers: " + quiz.getWrongAnswerNumber()); + System.out.println("Incorrect input: " + quiz.getIncorrectInputNumber()); + } +} diff --git a/lab-01/src/by/dudkoandrei/quizer/Quiz.java b/lab-01/src/by/dudkoandrei/quizer/Quiz.java new file mode 100644 index 0000000..f4b5a03 --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/Quiz.java @@ -0,0 +1,119 @@ +package by.dudkoandrei.quizer; + +import by.dudkoandrei.quizer.exceptions.QuizFinishedException; +import by.dudkoandrei.quizer.exceptions.QuizNotFinishedException; +import by.dudkoandrei.quizer.tasks.Task; + +/** + * Class, который описывает один тест. + */ +class Quiz { + + private final Task.Generator taskGenerator; + + private Task currentTask = null; + private final int taskCount; + private boolean answeredOnCurrentTask = false; + private int correctAnswerNumber = 0; + private int wrongAnswerNumber = 0; + private int incorrectInputNumber = 0; + + /** + * @param generator генератор заданий + * @param taskCount количество заданий в тесте + */ + Quiz(Task.Generator generator, int taskCount) { + if (taskCount <= 0) { + throw new IllegalArgumentException("Task count should be positive"); + } + if (generator == null) { + throw new IllegalArgumentException("Generator is null"); + } + this.taskGenerator = generator; + this.taskCount = taskCount; + } + + /** + * @return задание, повторный вызов вернет слелующее + * @see Task + */ + Task nextTask() { + if (isFinished()) { + throw new QuizFinishedException(); + } + + if (answeredOnCurrentTask || currentTask == null) { + currentTask = taskGenerator.generate(); + answeredOnCurrentTask = false; + } + + return currentTask; + } + + /** + * Предоставить ответ ученика. Если результат {@link Result#INCORRECT_INPUT}, то счетчик + * неправильных ответов не увеличивается, а {@link #nextTask()} в следующий раз вернет тот же + * самый объект {@link Task}. + */ + Result provideAnswer(String answer) { + if (isFinished()) { + throw new QuizFinishedException(); + } + + Result result = currentTask.validate(answer); + + switch (result) { + case OK -> { + correctAnswerNumber++; + answeredOnCurrentTask = true; + } + case WRONG -> { + wrongAnswerNumber++; + answeredOnCurrentTask = true; + } + case INCORRECT_INPUT -> incorrectInputNumber++; + } + + return result; + } + + /** + * @return завершен ли тест + */ + boolean isFinished() { + return correctAnswerNumber + wrongAnswerNumber == taskCount; + } + + /** + * @return количество правильных ответов + */ + int getCorrectAnswerNumber() { + return correctAnswerNumber; + } + + /** + * @return количество неправильных ответов + */ + int getWrongAnswerNumber() { + return wrongAnswerNumber; + } + + /** + * @return количество раз, когда был предоставлен неправильный ввод + */ + int getIncorrectInputNumber() { + return incorrectInputNumber; + } + + /** + * @return оценка, которая является отношением количества правильных ответов к количеству всех + * вопросов. Оценка выставляется только в конце! + */ + double getMark() { + if (!isFinished()) { + throw new QuizNotFinishedException(); + } + + return ((double) correctAnswerNumber) / taskCount; + } +} diff --git a/lab-01/src/by/dudkoandrei/quizer/Result.java b/lab-01/src/by/dudkoandrei/quizer/Result.java new file mode 100644 index 0000000..f024e8c --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/Result.java @@ -0,0 +1,10 @@ +package by.dudkoandrei.quizer; + +/** + * Enum, который описывает результат ответа на задание. + */ +public enum Result { + OK, // Получен правильный ответ + WRONG, // Получен неправильный ответ + INCORRECT_INPUT // Некорректный ввод. Например, текст, когда ожидалось число +} diff --git a/lab-01/src/by/dudkoandrei/quizer/constants/EquationTaskGeneratorConstants.java b/lab-01/src/by/dudkoandrei/quizer/constants/EquationTaskGeneratorConstants.java new file mode 100644 index 0000000..aa76ddd --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/constants/EquationTaskGeneratorConstants.java @@ -0,0 +1,19 @@ +package by.dudkoandrei.quizer.constants; + +import by.dudkoandrei.quizer.tasks.Task.Generator; +import by.dudkoandrei.quizer.tasks.math_tasks.EquationTask; +import by.dudkoandrei.quizer.tasks.math_tasks.MathTask.Operation; +import java.util.EnumSet; + +public class EquationTaskGeneratorConstants { + + public static final Generator generator = new EquationTask.Generator( + 0, + 25, + 4, + EnumSet.allOf(Operation.class)); + public static final Generator integerGenerator = new EquationTask.Generator( + 0, + 25, + EnumSet.allOf(Operation.class)); +} diff --git a/lab-01/src/by/dudkoandrei/quizer/constants/ExpressionTaskGeneratorConstants.java b/lab-01/src/by/dudkoandrei/quizer/constants/ExpressionTaskGeneratorConstants.java new file mode 100644 index 0000000..4a0bde9 --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/constants/ExpressionTaskGeneratorConstants.java @@ -0,0 +1,19 @@ +package by.dudkoandrei.quizer.constants; + +import by.dudkoandrei.quizer.tasks.Task.Generator; +import by.dudkoandrei.quizer.tasks.math_tasks.ExpressionTask; +import by.dudkoandrei.quizer.tasks.math_tasks.MathTask.Operation; +import java.util.EnumSet; + +public class ExpressionTaskGeneratorConstants { + + public static final Generator generator = new ExpressionTask.Generator( + 0, + 25, + 4, + EnumSet.allOf(Operation.class)); + public static final Generator integerGenerator = new ExpressionTask.Generator( + 0, + 25, + EnumSet.allOf(Operation.class)); +} diff --git a/lab-01/src/by/dudkoandrei/quizer/constants/TextGeneratorConstats.java b/lab-01/src/by/dudkoandrei/quizer/constants/TextGeneratorConstats.java new file mode 100644 index 0000000..3fc3f9d --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/constants/TextGeneratorConstats.java @@ -0,0 +1,26 @@ +package by.dudkoandrei.quizer.constants; + +import by.dudkoandrei.quizer.task_generators.PoolTaskGenerator; +import by.dudkoandrei.quizer.tasks.Task.Generator; +import by.dudkoandrei.quizer.tasks.TextTask; + +public class TextGeneratorConstats { + + public static final Generator generator = new PoolTaskGenerator( + false, + new TextTask("Fill in the blank: \"_____. I'm your father.\"\n" + + "a) Luke\n" + + "b) Boy\n" + + "c) Skywalker\n" + + "d) No", "d"), + new TextTask("Mace Windu light-saber color?\n" + + "a) Red\n" + + "b) Purple\n" + + "c) Green\n" + + "d) Blue", "b"), + new TextTask("What species of alien is Yoda?\n" + + "a) Ortolan\n" + + "b) Human\n" + + "c) Unknown\n" + + "d) Gungan", "c")); +} diff --git a/lab-01/src/by/dudkoandrei/quizer/exceptions/GeneratorsFailedException.java b/lab-01/src/by/dudkoandrei/quizer/exceptions/GeneratorsFailedException.java new file mode 100644 index 0000000..f8f1c39 --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/exceptions/GeneratorsFailedException.java @@ -0,0 +1,8 @@ +package by.dudkoandrei.quizer.exceptions; + +public class GeneratorsFailedException extends RuntimeException { + + public GeneratorsFailedException() { + super("All generators threw exceptions"); + } +} diff --git a/lab-01/src/by/dudkoandrei/quizer/exceptions/NoMoreTasksException.java b/lab-01/src/by/dudkoandrei/quizer/exceptions/NoMoreTasksException.java new file mode 100644 index 0000000..a7ee9b1 --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/exceptions/NoMoreTasksException.java @@ -0,0 +1,8 @@ +package by.dudkoandrei.quizer.exceptions; + +public class NoMoreTasksException extends RuntimeException { + + public NoMoreTasksException() { + super("No more tasks"); + } +} diff --git a/lab-01/src/by/dudkoandrei/quizer/exceptions/QuizFinishedException.java b/lab-01/src/by/dudkoandrei/quizer/exceptions/QuizFinishedException.java new file mode 100644 index 0000000..31dd763 --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/exceptions/QuizFinishedException.java @@ -0,0 +1,8 @@ +package by.dudkoandrei.quizer.exceptions; + +public class QuizFinishedException extends RuntimeException { + + public QuizFinishedException() { + super("Quiz is finished"); + } +} diff --git a/lab-01/src/by/dudkoandrei/quizer/exceptions/QuizNotFinishedException.java b/lab-01/src/by/dudkoandrei/quizer/exceptions/QuizNotFinishedException.java new file mode 100644 index 0000000..b166049 --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/exceptions/QuizNotFinishedException.java @@ -0,0 +1,8 @@ +package by.dudkoandrei.quizer.exceptions; + +public class QuizNotFinishedException extends RuntimeException { + + public QuizNotFinishedException() { + super("Quiz is not finished"); + } +} diff --git a/lab-01/src/by/dudkoandrei/quizer/task_generators/GroupTaskGenerator.java b/lab-01/src/by/dudkoandrei/quizer/task_generators/GroupTaskGenerator.java new file mode 100644 index 0000000..c987ebd --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/task_generators/GroupTaskGenerator.java @@ -0,0 +1,73 @@ +package by.dudkoandrei.quizer.task_generators; + +import by.dudkoandrei.quizer.exceptions.GeneratorsFailedException; +import by.dudkoandrei.quizer.tasks.Task; +import by.dudkoandrei.quizer.tasks.Task.Generator; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Task.Generator, который позволяет объединить несколько других Task.Generator. + */ +public class GroupTaskGenerator implements Task.Generator { + + private final List generators; + private int lastGeneratorIndex = 0; + + /** + * Конструктор с переменным числом аргументов. + * + * @param generators генераторы, которые в конструктор передаются через запятую + */ + public GroupTaskGenerator(Task.Generator... generators) { + this(Arrays.asList(generators)); + } + + /** + * Конструктор, который принимает коллекцию генераторов. + * + * @param generators генераторы, которые передаются в конструктор в Collection (например, + * {@link ArrayList}) + */ + public GroupTaskGenerator(Collection generators) { + if (generators == null) { + throw new IllegalArgumentException("generators is null"); + } + if (generators.isEmpty()) { + throw new IllegalArgumentException("generators is empty"); + } + if (generators.contains(null)) { + throw new IllegalArgumentException("generators contains null"); + } + + this.generators = new ArrayList<>(generators); + + Collections.shuffle(this.generators); + } + + /** + * @return результат метода generate() случайного генератора из списка. Если этот генератор + * выбросил исключение в методе generate(), выбирается другой. Если все генераторы выбрасывают + * исключение, то и тут выбрасывается исключение. + */ + public Task generate() { + for (int offset = 1; offset <= generators.size(); ++offset) { + try { + int index = getGeneratorIndex(offset); + Task t = generators.get(index).generate(); + lastGeneratorIndex = index; + return t; + } catch (Exception ignored) { + } + } + + throw new GeneratorsFailedException(); + } + + private int getGeneratorIndex(int offset) { + return (lastGeneratorIndex + offset) % generators.size(); + } +} diff --git a/lab-01/src/by/dudkoandrei/quizer/task_generators/PoolTaskGenerator.java b/lab-01/src/by/dudkoandrei/quizer/task_generators/PoolTaskGenerator.java new file mode 100644 index 0000000..9681ad1 --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/task_generators/PoolTaskGenerator.java @@ -0,0 +1,77 @@ +package by.dudkoandrei.quizer.task_generators; + +import by.dudkoandrei.quizer.exceptions.NoMoreTasksException; +import by.dudkoandrei.quizer.tasks.Task; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +/** + * Task.Generator, который отдает задания из заранее заготовленного набора. + */ +public class PoolTaskGenerator implements Task.Generator { + + private final List tasks; + private final boolean allowDuplicate; + private int currentPosition = 0; + private static final Random rnd = new Random(); + + /** + * Конструктор с переменным числом аргументов. + * + * @param allowDuplicate разрешить повторения + * @param tasks задания, которые в конструктор передаются через запятую + */ + public PoolTaskGenerator(boolean allowDuplicate, Task... tasks) { + this(allowDuplicate, Arrays.asList(tasks)); + } + + /** + * Конструктор, который принимает коллекцию заданий. + * + * @param allowDuplicate разрешить повторения + * @param tasks задания, которые передаются в конструктор в Collection (например, + * {@link LinkedList}) + */ + public PoolTaskGenerator(boolean allowDuplicate, Collection tasks) { + if (tasks == null) { + throw new IllegalArgumentException("tasks is null"); + } + if (tasks.isEmpty()) { + throw new IllegalArgumentException("tasks is empty"); + } + if (tasks.contains(null)) { + throw new IllegalArgumentException("tasks contains null"); + } + + this.allowDuplicate = allowDuplicate; + + if (!allowDuplicate) { + this.tasks = tasks.stream().distinct().collect(Collectors.toList()); + } else { + this.tasks = new ArrayList<>(tasks); + } + + Collections.shuffle(this.tasks); + } + + /** + * @return случайная задача из списка + */ + public Task generate() { + if (allowDuplicate) { + return tasks.get(rnd.nextInt(tasks.size())); + } + + if (currentPosition == tasks.size()) { + throw new NoMoreTasksException(); + } + + return tasks.get(currentPosition++); + } +} diff --git a/lab-01/src/by/dudkoandrei/quizer/tasks/Task.java b/lab-01/src/by/dudkoandrei/quizer/tasks/Task.java new file mode 100644 index 0000000..542a9b3 --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/tasks/Task.java @@ -0,0 +1,40 @@ +package by.dudkoandrei.quizer.tasks; + +import by.dudkoandrei.quizer.Result; + +/** + * Interface, который описывает одно задание. + */ +public interface Task { + + /** + * Interface, который описывает один генератор заданий. + */ + interface Generator { + + /** + * Возвращает задание. При этом новый объект может не создаваться, если класс задания + * иммутабельный. + * + * @return задание + * @see Task + */ + Task generate(); + } + + /** + * Возвращает текст задания. + * + * @return текст задания + */ + String getText(); + + /** + * Проверяет ответ на задание и возвращает результат. + * + * @param answer ответ на задание + * @return результат ответа + * @see Result + */ + Result validate(String answer); +} diff --git a/lab-01/src/by/dudkoandrei/quizer/tasks/TextTask.java b/lab-01/src/by/dudkoandrei/quizer/tasks/TextTask.java new file mode 100644 index 0000000..e5bc38a --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/tasks/TextTask.java @@ -0,0 +1,52 @@ +package by.dudkoandrei.quizer.tasks; + +import by.dudkoandrei.quizer.Result; +import by.dudkoandrei.quizer.task_generators.PoolTaskGenerator; + +/** + * Задание с заранее заготовленным текстом. Можно использовать {@link PoolTaskGenerator}, чтобы + * задавать задания такого типа. + */ +public class TextTask implements Task { + + private final String text; + private final String answer; + + /** + * @param text текст задания + * @param answer ответ на задание + */ + public TextTask(String text, String answer) { + if (text == null || answer == null) { + throw new IllegalArgumentException("Arguments shouldn't be null"); + } + if (text.isEmpty() || answer.isEmpty()) { + throw new IllegalArgumentException("Arguments shouldn't be empty"); + } + + this.text = text; + this.answer = answer; + } + + @Override + public String getText() { + return text; + } + + @Override + public Result validate(String answer) { + if (answer == null) { + throw new IllegalArgumentException("answer is null"); + } + + if (answer.isEmpty()) { + return Result.INCORRECT_INPUT; + } + + if (answer.equalsIgnoreCase(this.answer)) { + return Result.OK; + } else { + return Result.WRONG; + } + } +} diff --git a/lab-01/src/by/dudkoandrei/quizer/tasks/math_tasks/AbstractMathTask.java b/lab-01/src/by/dudkoandrei/quizer/tasks/math_tasks/AbstractMathTask.java new file mode 100644 index 0000000..2c400f8 --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/tasks/math_tasks/AbstractMathTask.java @@ -0,0 +1,135 @@ +package by.dudkoandrei.quizer.tasks.math_tasks; + +import by.dudkoandrei.quizer.Result; +import java.text.DecimalFormat; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.Random; + +public abstract class AbstractMathTask implements MathTask { + + protected final double answer; + protected final double eps; + protected String text; + protected static final DecimalFormat df = new DecimalFormat(); + + AbstractMathTask(double answer, int precision) { + if (precision < 0) { + throw new IllegalArgumentException("precision is negative"); + } + + this.answer = answer; + this.eps = Math.pow(0.1, precision); + } + + static boolean isDoubleEqual(double lhs, double rhs, double eps) { + return Math.abs(lhs - rhs) < eps; + } + + static double truncate(double number, int precision) { + if (precision < 0) { + throw new IllegalArgumentException("precision is negative"); + } + + double scale = Math.pow(10, precision); + + return Math.floor(number * scale) / scale; + } + + @Override + public Result validate(String answer) { + double givenAnswer; + + try { + givenAnswer = Double.parseDouble(answer); + } catch (NumberFormatException e) { + return Result.INCORRECT_INPUT; + } + + if (isDoubleEqual(this.answer, givenAnswer, eps)) { + return Result.OK; + } else { + return Result.WRONG; + } + } + + @Override + public String getText() { + return text; + } + + abstract static class Generator implements MathTask.Generator { + + protected final double minNumber; + protected final double maxNumber; + protected final int precision; + protected final EnumSet allowedOperations; + protected static final Random rnd = new Random(); + + Generator( + double minNumber, + double maxNumber, + EnumSet operations) { + this(minNumber, maxNumber, 0, operations); + } + + Generator( + double minNumber, + double maxNumber, + int precision, + EnumSet operations) { + if (operations == null) { + throw new IllegalArgumentException("operations is null"); + } + if (operations.isEmpty()) { + throw new IllegalArgumentException("operations is empty"); + } + if (minNumber > maxNumber) { + throw new IllegalArgumentException("minNumber is greater that maxNumber"); + } + if (precision < 0) { + throw new IllegalArgumentException("precision is negative"); + } + + this.minNumber = minNumber; + this.maxNumber = maxNumber; + this.allowedOperations = operations.clone(); + this.precision = precision; + } + + double getRandomDouble() { + try { + return truncate(rnd.nextDouble(minNumber, maxNumber), precision); + } catch (IllegalArgumentException e) { + return truncate(minNumber, precision); + } + } + + Operation getRandomOperation() { + int index = rnd.nextInt(allowedOperations.size()); + Iterator iter = allowedOperations.iterator(); + + while (index != 0) { + iter.next(); + --index; + } + + return iter.next(); + } + + @Override + public double getMinNumber() { + return minNumber; + } + + @Override + public double getMaxNumber() { + return maxNumber; + } + + @Override + public int getPrecision() { + return precision; + } + } +} diff --git a/lab-01/src/by/dudkoandrei/quizer/tasks/math_tasks/EquationTask.java b/lab-01/src/by/dudkoandrei/quizer/tasks/math_tasks/EquationTask.java new file mode 100644 index 0000000..4e61913 --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/tasks/math_tasks/EquationTask.java @@ -0,0 +1,92 @@ +package by.dudkoandrei.quizer.tasks.math_tasks; + +import java.util.EnumSet; + +public class EquationTask extends AbstractMathTask { + + public EquationTask( + double firstNumber, + double secondNumber, + int precision, + boolean isFirstNumberUnknown, + Operation operation) { + super(operation.solve(firstNumber, secondNumber, isFirstNumberUnknown), precision); + + df.setMaximumFractionDigits(precision); + df.setMinimumFractionDigits(precision); + + if (isFirstNumberUnknown) { + text = "x " + operation + " " + df.format(firstNumber) + " = " + df.format(secondNumber); + } else { + text = df.format(firstNumber) + " " + operation + " x = " + df.format(secondNumber); + } + } + + public static class Generator extends AbstractMathTask.Generator { + + /** + * @param minNumber минимальное число + * @param maxNumber максимальное число + * @param operations разрешённые операции + */ + public Generator( + double minNumber, + double maxNumber, + EnumSet operations) { + super(minNumber, maxNumber, operations); + } + + /** + * @param minNumber минимальное число + * @param maxNumber максимальное число + * @param precision количество знаков после зяпятой в генерируемых числах + * @param operations разрешённые операции + */ + public Generator( + double minNumber, + double maxNumber, + int precision, + EnumSet operations) { + super(minNumber, maxNumber, precision, operations); + } + + /** + * @return задание типа {@link ExpressionTask} + */ + @Override + public EquationTask generate() { + double eps = Math.pow(0.1, precision); + + double firstNumber = getRandomDouble(); + double secondNumber = getRandomDouble(); + boolean isFirstNumberUnknown = rnd.nextBoolean(); + Operation operation = getRandomOperation(); + + if (operation == Operation.DIVIDE && isDoubleEqual(secondNumber, 0.0, eps)) { + operation = Operation.MULTIPLY; + } + if (operation == Operation.MULTIPLY && isDoubleEqual(firstNumber, 0.0, eps)) { + operation = Operation.ADD; + } + if (isFirstNumberUnknown && operation == Operation.DIVIDE + && isDoubleEqual(firstNumber, 0.0, eps)) { + operation = Operation.SUBTRACT; + } + if (!isFirstNumberUnknown && operation == Operation.DIVIDE + && isDoubleEqual(secondNumber, 0.0, eps)) { + operation = Operation.ADD; + } + if (precision == 0 && operation == Operation.MULTIPLY + && (int) secondNumber % (int) firstNumber != 0) { + operation = Operation.SUBTRACT; + } + if (precision == 0 && !isFirstNumberUnknown && operation == Operation.DIVIDE + && (int) firstNumber % (int) secondNumber != 0) { + operation = Operation.ADD; + } + + return new EquationTask(firstNumber, secondNumber, precision, isFirstNumberUnknown, + operation); + } + } +} diff --git a/lab-01/src/by/dudkoandrei/quizer/tasks/math_tasks/ExpressionTask.java b/lab-01/src/by/dudkoandrei/quizer/tasks/math_tasks/ExpressionTask.java new file mode 100644 index 0000000..641b753 --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/tasks/math_tasks/ExpressionTask.java @@ -0,0 +1,69 @@ +package by.dudkoandrei.quizer.tasks.math_tasks; + +import java.util.EnumSet; + +public class ExpressionTask extends AbstractMathTask { + + public ExpressionTask( + double firstNumber, + double secondNumber, + int precision, + Operation operation) { + super(operation.compute(firstNumber, secondNumber), precision); + + df.setMaximumFractionDigits(precision); + df.setMinimumFractionDigits(precision); + + text = df.format(firstNumber) + " " + operation + " " + df.format(secondNumber) + " = ?"; + } + + public static class Generator extends AbstractMathTask.Generator { + + /** + * @param minNumber минимальное число + * @param maxNumber максимальное число + * @param operations разрешённые операции + */ + public Generator( + double minNumber, + double maxNumber, + EnumSet operations) { + super(minNumber, maxNumber, operations); + } + + /** + * @param minNumber минимальное число + * @param maxNumber максимальное число + * @param precision количество знаков после зяпятой в генерируемых числах + * @param operations разрешённые операции + */ + public Generator( + double minNumber, + double maxNumber, + int precision, + EnumSet operations) { + super(minNumber, maxNumber, precision, operations); + } + + /** + * @return задание типа {@link ExpressionTask} + */ + @Override + public ExpressionTask generate() { + double firstNumber = getRandomDouble(); + double secondNumber = getRandomDouble(); + Operation operation = getRandomOperation(); + + if (operation == Operation.DIVIDE + && isDoubleEqual(secondNumber, 0.0, Math.pow(0.1, precision))) { + operation = Operation.MULTIPLY; + } + if (precision == 0 && operation == Operation.DIVIDE + && (int) firstNumber % (int) secondNumber != 0) { + operation = Operation.MULTIPLY; + } + + return new ExpressionTask(firstNumber, secondNumber, precision, operation); + } + } +} diff --git a/lab-01/src/by/dudkoandrei/quizer/tasks/math_tasks/MathTask.java b/lab-01/src/by/dudkoandrei/quizer/tasks/math_tasks/MathTask.java new file mode 100644 index 0000000..72bef9e --- /dev/null +++ b/lab-01/src/by/dudkoandrei/quizer/tasks/math_tasks/MathTask.java @@ -0,0 +1,71 @@ +package by.dudkoandrei.quizer.tasks.math_tasks; + +import by.dudkoandrei.quizer.tasks.Task; + +public interface MathTask extends Task { + + interface Generator extends Task.Generator { + + /** + * @return минимальное число + */ + double getMinNumber(); + + /** + * @return максимальное число + */ + double getMaxNumber(); + + /** + * @return количество знаков после запятой в генерируемых числах + */ + int getPrecision(); + + /** + * @return разница между максимальным и минимальным возможным числом + */ + default double getDiffNumber() { + return getMaxNumber() - getMinNumber(); + } + } + + /** + * Enum, который описывает допустимые операции для {@link MathTask}. + */ + enum Operation { + ADD, + SUBTRACT, + DIVIDE, + MULTIPLY; + + double compute(double lhs, double rhs) { + return switch (this) { + case ADD -> lhs + rhs; + case SUBTRACT -> lhs - rhs; + case MULTIPLY -> lhs * rhs; + case DIVIDE -> lhs / rhs; + }; + } + + @Override + public String toString() { + return switch (this) { + case ADD -> "+"; + case SUBTRACT -> "-"; + case MULTIPLY -> "*"; + case DIVIDE -> "/"; + }; + } + + double solve(double firstNumber, double secondNumber, boolean isFirstNumberUnknown) { + return switch (this) { + case ADD -> secondNumber - firstNumber; + case MULTIPLY -> secondNumber / firstNumber; + case SUBTRACT -> + isFirstNumberUnknown ? secondNumber + firstNumber : firstNumber - secondNumber; + case DIVIDE -> + isFirstNumberUnknown ? firstNumber * secondNumber : firstNumber / secondNumber; + }; + } + } +}