diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8117b8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +out/ +*.iml diff --git a/lab-01/README.md b/lab-01/README.md index ac1aae2..6d1ca84 100644 --- a/lab-01/README.md +++ b/lab-01/README.md @@ -46,6 +46,8 @@ interface Task { ### TaskGenerator ```java +import by.toharrius.quizer.tasks.Task; + /** * Interface, который описывает один генератор заданий */ @@ -61,7 +63,10 @@ interface TaskGenerator { ``` ### Quiz + ```java +import by.toharrius.quizer.tasks.Task; + /** * Class, который описывает один тест */ @@ -70,10 +75,10 @@ class Quiz { * @param generator генератор заданий * @param taskCount количество заданий в тесте */ - Quiz(TaskGenerator generator, int taskCount) { + Quiz(TaskGenerator generator, int taskCount) { // ... } - + /** * @return задание, повторный вызов вернет слелующее * @see Task @@ -81,7 +86,7 @@ class Quiz { Task nextTask() { // ... } - + /** * Предоставить ответ ученика. Если результат {@link Result#INCORRECT_INPUT}, то счетчик неправильных * ответов не увеличивается, а {@link #nextTask()} в следующий раз вернет тот же самый объект {@link Task}. @@ -89,35 +94,35 @@ class Quiz { Result provideAnswer(String answer) { // ... } - + /** * @return завершен ли тест */ boolean isFinished() { // ... } - + /** * @return количество правильных ответов */ int getCorrectAnswerNumber() { // ... } - + /** * @return количество неправильных ответов */ int getWrongAnswerNumber() { // ... } - + /** * @return количество раз, когда был предоставлен неправильный ввод */ int getIncorrectInputNumber() { // ... } - + /** * @return оценка, которая является отношением количества правильных ответов к количеству всех вопросов. * Оценка выставляется только в конце! @@ -137,11 +142,11 @@ class Quiz { /** * @return тесты в {@link Map}, где * ключ - название теста {@link String} - * значение - сам тест {@link Quiz} + * значение - сам тест {@link by.toharrius.quizer.Quiz} */ -static Map getQuizMap() { - // ... -} +static Map getQuizMap(){ + // ... + } ``` ### public static void main() @@ -237,6 +242,8 @@ class EquationTaskGenerator implements TaskGenerator { `TaskGenerator`, который позволяет объединить несколько других `TaskGenerator`. ```java +import by.toharrius.quizer.tasks.Task; + class GroupTaskGenerator implements TaskGenerator { /** * Конструктор с переменным числом аргументов @@ -271,6 +278,8 @@ class GroupTaskGenerator implements TaskGenerator { `TaskGenerator`, который отдает задания из заранее заготовленного набора. ```java +import by.toharrius.quizer.tasks.Task; + class PoolTaskGenerator implements TaskGenerator { /** * Конструктор с переменным числом аргументов @@ -279,8 +288,8 @@ class PoolTaskGenerator implements TaskGenerator { * @param tasks задания, которые в конструктор передаются через запятую */ PoolTaskGenerator( - boolean allowDuplicate, - Task... tasks + boolean allowDuplicate, + Task... tasks ) { // ... } @@ -292,8 +301,8 @@ class PoolTaskGenerator implements TaskGenerator { * @param tasks задания, которые передаются в конструктор в Collection (например, {@link LinkedList}) */ PoolTaskGenerator( - boolean allowDuplicate, - Collection tasks + boolean allowDuplicate, + Collection tasks ) { // ... } @@ -310,6 +319,8 @@ class PoolTaskGenerator implements TaskGenerator { ### TextTask ```java +import by.toharrius.quizer.tasks.Task; + /** * Задание с заранее заготовленным текстом. * Можно использовать {@link PoolTaskGenerator}, чтобы задавать задания такого типа. @@ -320,12 +331,12 @@ class TextTask implements Task { * @param answer ответ на задание */ TextTask( - String text, - String answer + String text, + String answer ) { // ... } - + // ... } ``` @@ -363,37 +374,42 @@ default int getDiffNumber(); ## Generator как nested class в Task★ -Сейчас вся иерархия `Task` дублируется и для `Generator` в отдельном пакете, это не очень хорошая практика, т. к. за таким кодом сложно следить. Давайте сделаем все `Generator`'ы внутреннеми классами в соответствующих `Task`. Например, вместо `ExpressionTaskGenerator` будет `ExpressionTask.Generator`. При этом `GroupTaskGenerator` и `PoolTaskGenerator` остануться в отдельном пакете, т.к. они не привязаны к конкретному типу задачи. +Сейчас вся иерархия `Task` дублируется и для `Generator` в отдельном пакете, это не очень хорошая практика, т. к. за таким кодом сложно следить. Давайте сделаем все `Generator`'ы внутреннеми классами в соответствующих `Task`. Например, вместо `ExpressionTaskGenerator` будет `ExpressionTask.Generator`. При этом `GroupTaskGenerator` и `PoolTaskGenerator` останутся в отдельном пакете, т.к. они не привязаны к конкретному типу задачи. ```java interface Task { - interface Generator { /* ... */ } + interface Generator { /* ... */ + } // ... } -interface MathTask extends Task { - interface Generator extends Task.Generator { /* ... */ } +interface MathTask extends by.toharrius.quizer.tasks.Task { + interface Generator extends by.toharrius.quizer.tasks.Task.Generator { /* ... */ + } // ... } -abstract class AbstractMathTask implements Task { - static abstract class Generator implements MathTask.Generator { /* ... */ } +abstract class AbstractMathTask implements by.toharrius.quizer.tasks.Task { + static abstract class Generator implements MathTask.Generator { /* ... */ + } // ... } class ExpressionTask extends AbstractMathTask { - static class Generator extends AbstractMathTask.Generator { /* ... */ } + static class Generator extends AbstractMathTask.Generator { /* ... */ + } // ... } class EquationTask extends AbstractMathTask { - static class Generator extends AbstractMathTask.Generator { /* ... */ } + static class Generator extends AbstractMathTask.Generator { /* ... */ + } // ... } ``` ## ★ Real ExpressionTask и EquationTask -Сделайте, чтобы `ExpressionTaskGenerator` и `EquationTaskGenerator` работал с double вместо int. Так же нужно изменить сигнатуру в методах `getMinNumber()`, `getMaxNumnber()`, `getDiffNumber` интерфейса `MathTask`, чтобы они возвращали double. +Сделайте, чтобы `ExpressionTaskGenerator` и `EquationTaskGenerator` работал с double вместо int. Также нужно изменить сигнатуру в методах `getMinNumber()`, `getMaxNumnber()`, `getDiffNumber` интерфейса `MathTask`, чтобы они возвращали double. Добавьте в `ExpressionTaskGenerator` и `EquationTaskGenerator` конструктор, который после `maxNumber` принимает еще `int precision` - количество знаков после запятой в генерируемых числах. В других конструкторах считается, что `precision = 0`, т.e. генерируются только целые числа. diff --git a/lab-01/by/toharrius/quizer/CopyParameter.java b/lab-01/by/toharrius/quizer/CopyParameter.java new file mode 100644 index 0000000..2f7f0eb --- /dev/null +++ b/lab-01/by/toharrius/quizer/CopyParameter.java @@ -0,0 +1,4 @@ +package by.toharrius.quizer; +public enum CopyParameter { + FLAG +} diff --git a/lab-01/by/toharrius/quizer/Main.java b/lab-01/by/toharrius/quizer/Main.java new file mode 100644 index 0000000..616da2a --- /dev/null +++ b/lab-01/by/toharrius/quizer/Main.java @@ -0,0 +1,131 @@ +package by.toharrius.quizer; + +import by.toharrius.quizer.task_generators.GroupTaskGenerator; +import by.toharrius.quizer.task_generators.PoolTaskGenerator; +import by.toharrius.quizer.tasks.imminence_tasks.GenerousTask; +import by.toharrius.quizer.tasks.TextTask; +import by.toharrius.quizer.tasks.imminence_tasks.TrickyTask; +import by.toharrius.quizer.tasks.math_tasks.EquationTask; +import by.toharrius.quizer.tasks.math_tasks.ExpressionTask; +import by.toharrius.quizer.tasks.math_tasks.MathOperation; +import org.jetbrains.annotations.NotNull; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +public class Main { + private static final String helpMessage = "list : view available quizes\n" + + " : run particular quiz\n" + + "help : view this message\n" + + "exit | quit | stop : terminate session"; + private static Map quizes; + /** + * @return тесты в {@link Map}, где + * ключ - название теста {@link String} + * значение - сам тест {@link by.toharrius.quizer.Quiz} + */ + private static @NotNull Map getQuizMap() { + var quizes = new HashMap(); + { + var gen = new PoolTaskGenerator(false, + new TextTask("Какая (по слухам) подработка у С*****ча?", "сборщик мусора"), + new TextTask("Как называется человек, убегающий от каннибала?", "фастфуд"), + new TextTask("Какой вид порно не могут снять бомжи?", "домашнее"), + new TextTask("Как называется оглушающий удар татара?", "татарстан")); + quizes.put("stupid-questions", new Quiz(gen, 4)); + } + { + var gen = new PoolTaskGenerator(true, + new GenerousTask("Каков правильный ответ?"), + new TrickyTask("Это утверждение ложно.")); + quizes.put("know-your-luck", new Quiz(gen, 7)); + } + { + var gen_eq_add = new EquationTask.Generator(2, 55, + EnumSet.of(MathOperation.ADD)); + var gen_eq_42_all = new EquationTask.Generator(42, 42, + EnumSet.allOf(MathOperation.class)); + var gen_ex_div = new ExpressionTask.Generator(17, 42, + EnumSet.of(MathOperation.DIVIDE)); + var gen_ex_all = new ExpressionTask.Generator(41, 42, + EnumSet.allOf(MathOperation.class)); + var gen_eq = new GroupTaskGenerator(gen_eq_42_all, gen_eq_add); + var gen_ex = new GroupTaskGenerator(new ExpressionTask.Generator[]{gen_ex_div, gen_ex_all}); + var gen_group = new GroupTaskGenerator(gen_ex, gen_eq, + new EquationTask.Generator(gen_eq_42_all, CopyParameter.FLAG)); + quizes.put("tricky-math", new Quiz(gen_group, 5)); + } + return quizes; + } + + private static String inputLineNormalized() throws IOException { + return consoleReader.readLine().toLowerCase().trim(); + } + + private static void tryLaunchQuiz(String query) throws IOException { + if (quizes.containsKey(query)) { + System.out.println("Starting quiz \"" + query + "\"!"); + Quiz clone; + try { + clone = new Quiz(quizes.get(query)); + } catch (Exception e) { + throw new RuntimeException("Unable to clone", e); + } + runProblemSet(clone); + } else { + System.out.println("Sorry, not found. Type \"list\" to see available"); + } + } + + private static void runProblemSet(Quiz quiz) throws IOException { + while (!quiz.isFinished()) { + var task = quiz.nextTask(); + System.out.println("Task " + (quiz.getCurrentTaskIndex() + 1) + + " out of " + quiz.getTaskCount() + + ": " + task.getText()); + var answer = inputLineNormalized(); + System.out.println(switch (quiz.provideAnswer(answer)) { + case OK -> "Nice!"; + case WRONG -> "Nope."; + case INCORRECT_INPUT -> "Bad input format. Please, try again."; + }); + } + System.out.println("Quiz finished! Your statistics:"); + System.out.println("Correct answers: " + quiz.getCorrectAnswerNumber()); + System.out.println("Wrong answers: " + quiz.getWrongAnswerNumber()); + System.out.println("Total tries: " + quiz.getTotalAnswerNumber()); + System.out.println("Final mark: " + quiz.getMark()); + } + + public static void main(String[] args) throws IOException { + System.out.println(helpMessage); + quizes = getQuizMap(); + consoleReader = new BufferedReader(new InputStreamReader(System.in)); + boolean interactionFinished = false; + while (!interactionFinished) { + var query = inputLineNormalized(); + switch (query) { + case "list" -> { + if (quizes.isEmpty()) { + System.out.println("There are no quizes now, coming soon ):"); + } else { + System.out.println("The total of " + quizes.size() + " quizes are present"); + quizes.forEach((name, q) -> System.out.println(name)); + } + } + case "help" -> { + System.out.println(helpMessage); + } + case "exit", "quit", "stop", ":q" -> { + interactionFinished = true; + } + default -> tryLaunchQuiz(query); + } + } + } + private static BufferedReader consoleReader; +} diff --git a/lab-01/by/toharrius/quizer/Quiz.java b/lab-01/by/toharrius/quizer/Quiz.java new file mode 100644 index 0000000..05be97e --- /dev/null +++ b/lab-01/by/toharrius/quizer/Quiz.java @@ -0,0 +1,109 @@ +package by.toharrius.quizer; + +import by.toharrius.quizer.task_generators.TaskGenerator; +import by.toharrius.quizer.tasks.Task; + +/** + * Class, который описывает один тест + */ +public class Quiz implements Cloneable { + private int correctAnswerNumber = 0; + private int wrongAnswerNumber = 0; + private int incorrectInputNumber = 0; + private final TaskGenerator taskGenerator; + private final int taskCount; + private Task currentTask = null; + private Result lastResult = null; + /** + * @param generator генератор заданий + * @param taskCount количество заданий в тесте + */ + public Quiz(TaskGenerator generator, int taskCount) { + this.taskGenerator = generator; + this.taskCount = taskCount; + } + + /** + * Copy constructor refreshing state + * @param other object to copy + */ + public Quiz(Quiz other) throws Exception { + this(other.taskGenerator.getClass().getConstructor(TaskGenerator.class, CopyParameter.class) + .newInstance(other.taskGenerator, CopyParameter.FLAG), other.taskCount); + } + public int getTaskCount() { + return taskCount; + } + + /** + * @return задание, повторный вызов вернет следующее + * @see Task + */ + public Task nextTask() { + if (lastResult != Result.INCORRECT_INPUT) { + currentTask = taskGenerator.generate(); + } + return currentTask; + } + + /** + * Предоставить ответ ученика. Если результат {@link Result#INCORRECT_INPUT}, то счетчик неправильных + * ответов не увеличивается, а {@link #nextTask()} в следующий раз вернет тот же самый объект {@link Task}. + */ + public Result provideAnswer(String answer) { + lastResult = currentTask.validate(answer); + switch (lastResult) { + case OK -> ++correctAnswerNumber; + case WRONG -> ++wrongAnswerNumber; + case INCORRECT_INPUT -> ++incorrectInputNumber; + } + return lastResult; + } + + /** + * @return завершен ли тест + */ + public boolean isFinished() { + return getCurrentTaskIndex() == taskCount; + } + + public int getCurrentTaskIndex() { + return getCorrectAnswerNumber() + + getWrongAnswerNumber(); + } + + /** + * @return количество правильных ответов + */ + public int getCorrectAnswerNumber() { + return correctAnswerNumber; + } + + /** + * @return количество неправильных ответов + */ + public int getWrongAnswerNumber() { + return wrongAnswerNumber; + } + + /** + * @return количество раз, когда был предоставлен неправильный ввод + */ + public int getIncorrectInputNumber() { + return incorrectInputNumber; + } + + public int getTotalAnswerNumber() { + return getCorrectAnswerNumber() + + getIncorrectInputNumber() + + getWrongAnswerNumber(); + } + + /** + * @return Оценка, которая является отношением количества правильных ответов к количеству всех вопросов. + * Оценка выставляется только в конце! + */ + public double getMark() { + return (double)getCorrectAnswerNumber() / getTaskCount(); + } +} diff --git a/lab-01/by/toharrius/quizer/Result.java b/lab-01/by/toharrius/quizer/Result.java new file mode 100644 index 0000000..570cd28 --- /dev/null +++ b/lab-01/by/toharrius/quizer/Result.java @@ -0,0 +1,10 @@ +package by.toharrius.quizer; + +/** + * Enum, который описывает результат ответа на задание + */ +public enum Result { + OK, // Получен правильный ответ + WRONG, // Получен неправильный ответ + INCORRECT_INPUT // Некорректный ввод. Например, текст, когда ожидалось число +} diff --git a/lab-01/by/toharrius/quizer/task_generators/GroupTaskGenerator.java b/lab-01/by/toharrius/quizer/task_generators/GroupTaskGenerator.java new file mode 100644 index 0000000..2f7aacd --- /dev/null +++ b/lab-01/by/toharrius/quizer/task_generators/GroupTaskGenerator.java @@ -0,0 +1,46 @@ +package by.toharrius.quizer.task_generators; + +import by.toharrius.quizer.CopyParameter; +import by.toharrius.quizer.tasks.Task; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.concurrent.ThreadLocalRandom; + +public class GroupTaskGenerator implements TaskGenerator { + ArrayList generators; + /** + * Конструктор с переменным числом аргументов + * + * @param generators генераторы, которые в конструктор передаются через запятую + */ + public GroupTaskGenerator(TaskGenerator... generators) { + this.generators = new ArrayList<>(); + Collections.addAll(this.generators, generators); + } + + public GroupTaskGenerator(TaskGenerator other, CopyParameter f) { + this(((GroupTaskGenerator)other).generators); + } + + /** + * Конструктор, который принимает коллекцию генераторов + * + * @param generators генераторы, которые передаются в конструктор в Collection (например, {@link ArrayList}) + */ + GroupTaskGenerator(Iterable generators) { + this.generators = new ArrayList<>(); + generators.forEach(this.generators::add); + } + + /** + * @return результат метода generate() случайного генератора из списка. + * Если этот генератор выбросил исключение в методе generate(), выбирается другой. + * Если все генераторы выбрасывают исключение, то и тут выбрасывается исключение. + */ + public Task generate() { + var r = ThreadLocalRandom.current(); + int index = r.nextInt(generators.size()); + return generators.get(index).generate(); + } +} diff --git a/lab-01/by/toharrius/quizer/task_generators/PoolTaskGenerator.java b/lab-01/by/toharrius/quizer/task_generators/PoolTaskGenerator.java new file mode 100644 index 0000000..6f36dd1 --- /dev/null +++ b/lab-01/by/toharrius/quizer/task_generators/PoolTaskGenerator.java @@ -0,0 +1,65 @@ +package by.toharrius.quizer.task_generators; + +import by.toharrius.quizer.CopyParameter; +import by.toharrius.quizer.tasks.Task; + +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; + +public class PoolTaskGenerator implements TaskGenerator { + private final ArrayList pool; + private final boolean allowDuplicate; + /** + * Конструктор с переменным числом аргументов + * + * @param allowDuplicate разрешить повторения + * @param tasks задания, которые в конструктор передаются через запятую + */ + public PoolTaskGenerator( + boolean allowDuplicate, + Task... tasks + ) { + this(allowDuplicate, Arrays.stream(tasks).collect(Collectors.toList())); + } + + public PoolTaskGenerator(TaskGenerator other, CopyParameter f) { + this(((PoolTaskGenerator)other).allowDuplicate, + ((PoolTaskGenerator)other).pool); + } + + /** + * Конструктор, который принимает коллекцию заданий + * + * @param allowDuplicate разрешить повторения + * @param tasks задания, которые передаются в конструктор в Collection (например, {@link LinkedList}) + */ + PoolTaskGenerator( + boolean allowDuplicate, + Collection tasks + ) { + this.pool = new ArrayList<>(tasks); + this.allowDuplicate = allowDuplicate; + if (!allowDuplicate) { + if (pool.stream().distinct().count() != pool.size()) { + throw new IllegalArgumentException("Duplicates found"); + } + Collections.shuffle(pool); + } + } + + /** + * @return случайная задача из списка + */ + public Task generate() { + if (allowDuplicate) { + var r = ThreadLocalRandom.current(); + int index = r.nextInt(pool.size()); + return pool.get(index); + } else { + var task = pool.get(pool.size() - 1); + pool.remove(pool.size() - 1); + return task; + } + } +} diff --git a/lab-01/by/toharrius/quizer/task_generators/TaskGenerator.java b/lab-01/by/toharrius/quizer/task_generators/TaskGenerator.java new file mode 100644 index 0000000..db7eb85 --- /dev/null +++ b/lab-01/by/toharrius/quizer/task_generators/TaskGenerator.java @@ -0,0 +1,16 @@ +package by.toharrius.quizer.task_generators; + +import by.toharrius.quizer.tasks.Task; + +/** + * Interface, который описывает один генератор заданий + */ +public interface TaskGenerator { + /** + * Возвращает задание. При этом новый объект может не создаваться, если класс задания иммутабельный + * + * @return задание + * @see Task + */ + Task generate(); +} diff --git a/lab-01/by/toharrius/quizer/tasks/Task.java b/lab-01/by/toharrius/quizer/tasks/Task.java new file mode 100644 index 0000000..25120ae --- /dev/null +++ b/lab-01/by/toharrius/quizer/tasks/Task.java @@ -0,0 +1,22 @@ +package by.toharrius.quizer.tasks; + +import by.toharrius.quizer.Result; + +/** + * Interface, который описывает одно задание + */ +public interface Task { + /** + @return текст задания + */ + String getText(); + + /** + * Проверяет ответ на задание и возвращает результат + * + * @param answer ответ на задание + * @return результат ответа + * @see Result + */ + Result validate(String answer); +} diff --git a/lab-01/by/toharrius/quizer/tasks/TextTask.java b/lab-01/by/toharrius/quizer/tasks/TextTask.java new file mode 100644 index 0000000..b1d6c3e --- /dev/null +++ b/lab-01/by/toharrius/quizer/tasks/TextTask.java @@ -0,0 +1,38 @@ +package by.toharrius.quizer.tasks; + +import by.toharrius.quizer.Result; +import by.toharrius.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 + ) { + this.text = text; + this.answer = answer; + } + + @Override + public String getText() { + return text; + } + + @Override + public Result validate(String answer) { + return answer.isEmpty() + ? Result.INCORRECT_INPUT + : answer.equals(this.answer) + ? Result.OK + : Result.WRONG; + } +} diff --git a/lab-01/by/toharrius/quizer/tasks/imminence_tasks/AbstractImminenceTask.java b/lab-01/by/toharrius/quizer/tasks/imminence_tasks/AbstractImminenceTask.java new file mode 100644 index 0000000..34dd265 --- /dev/null +++ b/lab-01/by/toharrius/quizer/tasks/imminence_tasks/AbstractImminenceTask.java @@ -0,0 +1,24 @@ +package by.toharrius.quizer.tasks.imminence_tasks; + +import by.toharrius.quizer.Result; +import by.toharrius.quizer.tasks.Task; + +public class AbstractImminenceTask implements Task { + private final String text; + private final Result result; + + protected AbstractImminenceTask(String text, Result result) { + this.text = text; + this.result = result; + } + + @Override + public String getText() { + return text; + } + + @Override + public Result validate(String answer) { + return result; + } +} diff --git a/lab-01/by/toharrius/quizer/tasks/imminence_tasks/GenerousTask.java b/lab-01/by/toharrius/quizer/tasks/imminence_tasks/GenerousTask.java new file mode 100644 index 0000000..96cbdcf --- /dev/null +++ b/lab-01/by/toharrius/quizer/tasks/imminence_tasks/GenerousTask.java @@ -0,0 +1,9 @@ +package by.toharrius.quizer.tasks.imminence_tasks; + +import by.toharrius.quizer.Result; + +public class GenerousTask extends AbstractImminenceTask { + public GenerousTask(String text) { + super(text, Result.OK); + } +} diff --git a/lab-01/by/toharrius/quizer/tasks/imminence_tasks/TrickyTask.java b/lab-01/by/toharrius/quizer/tasks/imminence_tasks/TrickyTask.java new file mode 100644 index 0000000..013bd69 --- /dev/null +++ b/lab-01/by/toharrius/quizer/tasks/imminence_tasks/TrickyTask.java @@ -0,0 +1,9 @@ +package by.toharrius.quizer.tasks.imminence_tasks; + +import by.toharrius.quizer.Result; + +public class TrickyTask extends AbstractImminenceTask { + public TrickyTask(String text) { + super(text, Result.WRONG); + } +} diff --git a/lab-01/by/toharrius/quizer/tasks/math_tasks/AbstractMathTask.java b/lab-01/by/toharrius/quizer/tasks/math_tasks/AbstractMathTask.java new file mode 100644 index 0000000..ed0f1db --- /dev/null +++ b/lab-01/by/toharrius/quizer/tasks/math_tasks/AbstractMathTask.java @@ -0,0 +1,51 @@ +package by.toharrius.quizer.tasks.math_tasks; + +import java.util.EnumSet; + +public abstract class AbstractMathTask implements MathTask { + protected final double a; + protected final MathOperation op; + protected final double b; + protected final double c; + public AbstractMathTask(double a, MathOperation op, double b) { + this.a = a; + this.op = op; + this.b = b; + this.c = op.eval(a, b); + } + + protected abstract static class Generator implements MathTask.Generator { + private final double minNumber; + private final double maxNumber; + private final EnumSet allowed; + private final double roundingCoefficient; + public Generator(double minNumber, double maxNumber, + EnumSet allowed, + int precision) { + this.minNumber = minNumber; + this.maxNumber = maxNumber; + this.allowed = allowed; + this.roundingCoefficient = Math.pow(10, precision); + } + + @Override + public double getMinNumber() { + return minNumber; + } + + @Override + public double getMaxNumber() { + return maxNumber; + } + + @Override + public double getRoundingCoefficient() { + return roundingCoefficient; + } + + @Override + public EnumSet getAllowed() { + return allowed; + } + } +} diff --git a/lab-01/by/toharrius/quizer/tasks/math_tasks/EquationTask.java b/lab-01/by/toharrius/quizer/tasks/math_tasks/EquationTask.java new file mode 100644 index 0000000..b8ce46f --- /dev/null +++ b/lab-01/by/toharrius/quizer/tasks/math_tasks/EquationTask.java @@ -0,0 +1,59 @@ +package by.toharrius.quizer.tasks.math_tasks; + +import by.toharrius.quizer.CopyParameter; +import by.toharrius.quizer.Result; +import by.toharrius.quizer.tasks.Task; + +import java.util.EnumSet; +import java.util.concurrent.ThreadLocalRandom; + +public class EquationTask extends AbstractMathTask { + private final boolean variableLeftSide; + public EquationTask(double a, MathOperation op, double b, boolean variableLeftSide) { + super(a, op, b); + this.variableLeftSide = variableLeftSide; + } + @Override + public String getText() { + return "Решай уравнение: " + + (variableLeftSide + ? "x " + op + " " + b + : a + " " + op + " x" + ) + " = " + c; + } + + @Override + public Result validate(String answer) { + double value; + try { + value = Double.parseDouble(answer); + } catch (NumberFormatException e) { + return Result.INCORRECT_INPUT; + } + var correct = variableLeftSide ? a : b; + return value == correct ? Result.OK : Result.WRONG; + } + + public static class Generator extends AbstractMathTask.Generator { + public Generator(double minNumber, double maxNumber, + EnumSet allowed, + int precision) { + super(minNumber, maxNumber, allowed, precision); + } + public Generator(double minNumber, double maxNumber, EnumSet allowed) { + this(minNumber, maxNumber, allowed, 0); + } + public Generator(Generator other, CopyParameter f) { + this(other.getMinNumber(), other.getMaxNumber(), other.getAllowed()); + } + @Override + public Task generate() { + return new EquationTask( + generateOperand(), + generateMathOperation(), + generateOperand(), + ThreadLocalRandom.current().nextBoolean() + ); + } + } +} diff --git a/lab-01/by/toharrius/quizer/tasks/math_tasks/ExpressionTask.java b/lab-01/by/toharrius/quizer/tasks/math_tasks/ExpressionTask.java new file mode 100644 index 0000000..37bda88 --- /dev/null +++ b/lab-01/by/toharrius/quizer/tasks/math_tasks/ExpressionTask.java @@ -0,0 +1,53 @@ +package by.toharrius.quizer.tasks.math_tasks; + +import by.toharrius.quizer.CopyParameter; +import by.toharrius.quizer.Result; + +import java.util.EnumSet; + +public class ExpressionTask extends AbstractMathTask { + public ExpressionTask(double a, MathOperation op, double b) { + super(a, op, b); + } + + @Override + public String getText() { + return "Решай пример: " + a + ' ' + op + ' ' + b + " = ?"; + } + + @Override + public Result validate(String answer) { + try { + if (Double.parseDouble(answer) == c) { + return Result.OK; + } else { + return Result.WRONG; + } + } catch (NumberFormatException e) { + return Result.INCORRECT_INPUT; + } + } + + public static class Generator extends AbstractMathTask.Generator { + public Generator(double minNumber, double maxNumber, + EnumSet allowed, + int precision) { + super(minNumber, maxNumber, allowed, precision); + } + public Generator(double minNumber, double maxNumber, EnumSet allowed) { + this(minNumber, maxNumber, allowed, 0); + } + public Generator(EquationTask.Generator other, CopyParameter f) { + this(other.getMinNumber(), other.getMaxNumber(), other.getAllowed()); + } + + @Override + public ExpressionTask generate() { + return new ExpressionTask( + generateOperand(), + generateMathOperation(), + generateOperand() + ); + } + } +} diff --git a/lab-01/by/toharrius/quizer/tasks/math_tasks/MathOperation.java b/lab-01/by/toharrius/quizer/tasks/math_tasks/MathOperation.java new file mode 100644 index 0000000..3b083ec --- /dev/null +++ b/lab-01/by/toharrius/quizer/tasks/math_tasks/MathOperation.java @@ -0,0 +1,26 @@ +package by.toharrius.quizer.tasks.math_tasks; + +public enum MathOperation { + ADD, + SUBTRACT, + DIVIDE, + MULTIPLY; + + public String toString() { + return switch (this) { + case ADD -> "+"; + case SUBTRACT -> "-"; + case DIVIDE -> "/"; + case MULTIPLY -> "*"; + }; + } + + public double eval(double a, double b) { + return switch (this) { + case ADD -> a + b; + case SUBTRACT -> a - b; + case DIVIDE -> a / b; + case MULTIPLY -> a * b; + }; + } +} diff --git a/lab-01/by/toharrius/quizer/tasks/math_tasks/MathTask.java b/lab-01/by/toharrius/quizer/tasks/math_tasks/MathTask.java new file mode 100644 index 0000000..7658618 --- /dev/null +++ b/lab-01/by/toharrius/quizer/tasks/math_tasks/MathTask.java @@ -0,0 +1,33 @@ +package by.toharrius.quizer.tasks.math_tasks; + +import by.toharrius.quizer.tasks.Task; +import by.toharrius.quizer.task_generators.TaskGenerator; + +import java.util.EnumSet; +import java.util.concurrent.ThreadLocalRandom; + +public interface MathTask extends Task { + interface Generator extends TaskGenerator { + double getMinNumber(); + double getMaxNumber(); + double getRoundingCoefficient(); + EnumSet getAllowed(); + /** + * @return разница между максимальным и минимальным возможным числом + */ + default double getDiffNumber() { + return getMaxNumber() - getMinNumber(); + } + default double generateOperand() { + var generated = getMinNumber() + Math.random() * getDiffNumber(); + return Math.round(generated + * getRoundingCoefficient()) + / getRoundingCoefficient(); + } + default MathOperation generateMathOperation() { + var r = ThreadLocalRandom.current(); + int index = r.nextInt(getAllowed().size()); + return getAllowed().stream().skip(index).findFirst().get(); + } + } +}