diff --git a/README.md b/README.md index 8715d4d91..23865ac32 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,18 @@ -# Duke project template +# Duke Program User Guide -This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it. +## Input commands +### Add to task +- todo [description] +- deadline [description]/[by] +- event [description]/[from]/[to] -## Setting up in Intellij +### Manipulate tasks +- mark [task number] +- delete [task number] -Prerequisites: JDK 11, update Intellij to the most recent version. +### Get tasks +- list +- find [search] -1. Open Intellij (if you are not in the welcome screen, click `File` > `Close Project` to close the existing project first) -1. Open the project into Intellij as follows: - 1. Click `Open`. - 1. Select the project directory, and click `OK`. - 1. If there are any further prompts, accept the defaults. -1. Configure the project to use **JDK 11** (not other versions) as explained in [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk).
- In the same dialog, set the **Project language level** field to the `SDK default` option. -3. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: - ``` - Hello from - ____ _ - | _ \ _ _| | _____ - | | | | | | | |/ / _ \ - | |_| | |_| | < __/ - |____/ \__,_|_|\_\___| - ``` +### Exit program +- bye diff --git a/data/tasks.txt b/data/tasks.txt new file mode 100644 index 000000000..a2f061f22 --- /dev/null +++ b/data/tasks.txt @@ -0,0 +1,3 @@ +T|0|hellp +D|1|help /monday +T|0|fasdfsdaf \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 8077118eb..23865ac32 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,18 @@ -# User Guide +# Duke Program User Guide -## Features +## Input commands +### Add to task +- todo [description] +- deadline [description]/[by] +- event [description]/[from]/[to] -### Feature-ABC +### Manipulate tasks +- mark [task number] +- delete [task number] -Description of the feature. +### Get tasks +- list +- find [search] -### Feature-XYZ - -Description of the feature. - -## Usage - -### `Keyword` - Describe action - -Describe the action and its outcome. - -Example of usage: - -`keyword (optional arguments)` - -Expected outcome: - -Description of the outcome. - -``` -expected output -``` +### Exit program +- bye diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java deleted file mode 100644 index 5d313334c..000000000 --- a/src/main/java/Duke.java +++ /dev/null @@ -1,10 +0,0 @@ -public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - } -} diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 000000000..2c6354be6 --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: duke.main.Duke + diff --git a/src/main/java/duke/entity/Parser.java b/src/main/java/duke/entity/Parser.java new file mode 100644 index 000000000..edf47011b --- /dev/null +++ b/src/main/java/duke/entity/Parser.java @@ -0,0 +1,55 @@ +package duke.entity; + +/** + * Parses the user's inputs by making sense of the user command + */ +public class Parser { + private String input; + + public void setInput(String input) { + this.input = input; + } + + /** + * Parse the input to find the index of task to be marked + * + * @return index to be marked + */ + public int parseMarkIndex() { + String stringListNumber = input.substring(5); + return Integer.parseInt(stringListNumber) - 1; + } + + /** + * Parse the input to find the index of task to be deleted + * + * @return index to be deleted + */ + public int parseDeleteIndex() { + String stringListNumber = input.substring(7); + return Integer.parseInt(stringListNumber) - 1; + } + + /** + * Parse input to get deadline date + * + * @return string of deadline date + */ + public String parseDeadlineBy() { + return input.substring(input.lastIndexOf("/") + 1); + } + + public String parseEventFrom() { + String tempInput = input.substring(input.indexOf("/") + 1); + return tempInput.substring(0, tempInput.indexOf("/")); + } + + public String parseEventTo() { + String tempInput = input.substring(input.indexOf("/") + 1); + return tempInput.substring(tempInput.lastIndexOf("/") + 1); + } + + public boolean validateEventInput() { + return input.matches(".*/.*/.*"); + } +} \ No newline at end of file diff --git a/src/main/java/duke/entity/Storage.java b/src/main/java/duke/entity/Storage.java new file mode 100644 index 000000000..9ef7a654c --- /dev/null +++ b/src/main/java/duke/entity/Storage.java @@ -0,0 +1,122 @@ +package duke.entity; + +import duke.exceptions.DukeException; +import duke.task.Deadline; +import duke.task.Event; +import duke.task.Task; +import duke.task.Todo; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Scanner; + +/** + * Class used to deal with loading tasks from the file and saving tasks in the file + */ +public class Storage { + private String filePath; + + public Storage(String filePath) { + this.filePath = filePath; + } + + /** + * Load or creates the data file into an array list of task + * + * @return loaded array list of task + * @throws DukeException if data will not be saved + */ + public ArrayList load() throws DukeException { + String[] filePathArr = filePath.split("/"); + ArrayList tasks = new ArrayList(); + String tempPath = filePathArr[0]; + + for (int i = 0; i < filePathArr.length; i++) { + if (i != 0) { + tempPath = tempPath + "/" + filePathArr[i]; + } + java.nio.file.Path filePath = getPath(tempPath); + boolean pathExists = java.nio.file.Files.exists(filePath); + File pathFile = new File(filePath.toString()); + + try { + if (i == (filePathArr.length - 1) && pathExists) { + readDukeFile(tasks, pathFile); + } else if (!pathExists) { + if (i == (filePathArr.length - 1)) { + pathFile.createNewFile(); + } else { + pathFile.mkdir(); + } + } + } catch (IOException e) { + throw new DukeException("Note: data will not be saved (Duke.txt unable to be created)"); + } + } + return tasks; + } + + public void updateDukeFile(TaskList taskList) { + writeDukeFile(taskList.getTask(0), false); + for (int i = 1; i < taskList.taskSize(); i++) { + writeDukeFile(taskList.getTask(i), true); + } + } + + public void writeDukeFile(Task task, boolean appendFile) { + String line = (task.getIsDone() ? "1" : "0") + "|" + task.getDescription(); + if (task instanceof Todo) { + line = "T|" + line; + } else if (task instanceof Deadline) { + line = "D|" + line; + } else if (task instanceof Event) { + line = "E|" + line; + } + try { + File dukeFile = new File(getPath(this.filePath).toString()); + FileWriter fw = new FileWriter(dukeFile, appendFile); + if (appendFile) { + fw.write("\n" + line); + } else { + fw.write(line); + } + fw.close(); + } catch (IOException ex) {} + } + + private void readDukeFile(ArrayList tasks, File dukeFile) throws FileNotFoundException { + Scanner s = new Scanner(dukeFile); + while (s.hasNextLine()) { + String task = s.nextLine(); + if (task.length() > 4) { + String taskType = task.substring(0, 1); + boolean isMarked = task.substring(2, 3).equals("0") ? false : true; + String taskDesc = task.substring(4); + + if (taskType.equals("T")) { + Task tempTask = new Todo(taskDesc, isMarked); + tasks.add(tempTask); + } else if (taskType.equals("D")) { + Task tempTask = new Deadline(taskDesc, taskDesc.substring(taskDesc.lastIndexOf("/") + 1), isMarked); + tasks.add(tempTask); + } else if (taskType.equals("E")) { + String tempInput = taskDesc.substring(taskDesc.indexOf("/") + 1); + String fromString = tempInput.substring(0, tempInput.indexOf("/")); + String toString = tempInput.substring(tempInput.lastIndexOf("/") + 1); + + Task tempTask = new Event(taskDesc, fromString, toString, isMarked); + tasks.add(tempTask); + } + } + } + } + + private java.nio.file.Path getPath(String path) { + File f = new File(""); + String home = f.getAbsolutePath(); + return java.nio.file.Paths.get(home, path); + } +} diff --git a/src/main/java/duke/entity/TaskList.java b/src/main/java/duke/entity/TaskList.java new file mode 100644 index 000000000..23f6af21e --- /dev/null +++ b/src/main/java/duke/entity/TaskList.java @@ -0,0 +1,53 @@ +package duke.entity; + +import duke.task.Task; +import java.util.ArrayList; + +/** + * Contains the task list with operations to manipulate tasks in the list + */ +public class TaskList { + private ArrayList taskArrayList; + + public TaskList() {} + + public TaskList(ArrayList taskArrayList) { + this.taskArrayList = taskArrayList; + } + + public ArrayList getTaskArrayList() { + return taskArrayList; + } + + public Task getTask(int index) { + return this.taskArrayList.get(index); + } + + public void addTask(Task task) { + taskArrayList.add(task); + } + + public int taskSize() { + return taskArrayList.size(); + } + + public void removeTask(int index) { + this.taskArrayList.remove(index); + } + + /** + * Searches for tasks in task list that matches a given keyword + * + * @param keyword string to search for match + * @return contains the found/matched tasks + */ + public ArrayList findTaskArrayList(String keyword) { + ArrayList tempTaskArrayList = new ArrayList(); + for (Task task : taskArrayList) { + if (task.getDescription().matches("(.*)" + keyword + "(.*)")) { + tempTaskArrayList.add(task); + } + } + return tempTaskArrayList; + } +} diff --git a/src/main/java/duke/entity/Ui.java b/src/main/java/duke/entity/Ui.java new file mode 100644 index 000000000..dbaf0a059 --- /dev/null +++ b/src/main/java/duke/entity/Ui.java @@ -0,0 +1,165 @@ +package duke.entity; + +import duke.exceptions.DukeException; +import duke.task.Deadline; +import duke.task.Event; +import duke.task.Task; +import duke.task.Todo; + +import java.util.ArrayList; +import java.util.Scanner; + +/** + * Interacts with user and outputs information given a response to users inputs + */ +public class Ui { + private Parser parser = new Parser(); + + /** + * Takes input from user's command and processes it depending on their request + * + * @param taskList list of tasks + * @param storage storage object + * @return true if continue program, false otherwise + */ + public boolean manageInput(TaskList taskList, Storage storage) { + Scanner scanner = new Scanner(System.in); + try { + String input = scanner.nextLine(); + this.parser.setInput(input); + + if (input.equals("bye")) { + return false; + } else if (input.equals("list")) { + listTasks(taskList.getTaskArrayList()); + } else if (input.startsWith("mark")) { + selectTask(input, taskList); + listTasks(taskList.getTaskArrayList()); + storage.updateDukeFile(taskList); + } else if (input.startsWith("todo")) { + blankDescription(input); + Task tempTask = new Todo(input.substring(5)); + taskList.addTask(tempTask); + printAddedTask(tempTask, taskList.taskSize()); + storage.writeDukeFile(tempTask, true); + } else if (input.startsWith("deadline") && input.contains("/")) { + Task tempTask = new Deadline(input.substring(9), parser.parseDeadlineBy()); + taskList.addTask(tempTask); + printAddedTask(tempTask, taskList.taskSize()); + storage.writeDukeFile(tempTask, true); + } else if (input.startsWith("event") && parser.validateEventInput()) { + Task tempTask = new Event(input.substring(6), parser.parseEventFrom(), parser.parseEventTo()); + taskList.addTask(tempTask); + printAddedTask(tempTask, taskList.taskSize()); + storage.writeDukeFile(tempTask, true); + } else if (input.startsWith("delete")) { + selectTask(input, taskList); + } else if (input.startsWith("find")) { + findTasks(input, taskList); + } else { + invalidInput(); + } + } catch (DukeException ex) { + System.out.println(ex.toString()); + } + return true; + } + + /** + * Prints goodbye message + */ + public void goodbye() { + System.out.println("____________________________________________________________"); + System.out.println("Bye. Hope to see you again soon!"); + System.out.println("____________________________________________________________\n"); + } + + /** + * Prints greeting message + */ + public void greetings() { + String logo = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + System.out.println("Hello from\n" + logo); + System.out.println("____________________________________________________________"); + System.out.println("Hello! I'm Duke"); + System.out.println("What can I do for you?"); + System.out.println("____________________________________________________________\n"); + } + + private void selectTask(String input, TaskList taskList) { + try { + if (input.startsWith("mark")) { + taskList.getTask(parser.parseMarkIndex()).setIsDone(true); + } + else if (input.startsWith("delete")) { + deleteTask(parser.parseDeleteIndex(), taskList); + } + } catch (NumberFormatException ex) { + printMarkError(); + } catch (ArrayIndexOutOfBoundsException ex) { + printMarkError(); + } catch (IndexOutOfBoundsException ex) { + printMarkError(); + } catch (NullPointerException ex) { + printMarkError(); + } + } + + private void listTasks(ArrayList storedTask) { + System.out.println("____________________________________________________________"); + System.out.println("Here are the tasks in your list:"); + for (int i = 0; i < storedTask.size(); i++) { + System.out.println((i + 1) + ". " + storedTask.get(i).toString()); + } + System.out.println("____________________________________________________________\n"); + } + + private void deleteTask(int removeIndex, TaskList taskList) { + System.out.println("____________________________________________________________"); + System.out.println("Noted. I've removed this task:\n" + + " " + taskList.getTask(removeIndex).toString() + "\n" + + "Now you have " + (taskList.taskSize() - 1) + " tasks in the list."); + System.out.println("____________________________________________________________\n"); + taskList.removeTask(removeIndex); + } + + private void findTasks(String input, TaskList taskList) throws DukeException { + blankDescription(input); + ArrayList foundTaskList = taskList.findTaskArrayList(input.substring(5)); + + System.out.println("____________________________________________________________"); + System.out.println("Here are the matching tasks in your list:"); + for (int i = 0; i < foundTaskList.size(); i++) { + System.out.println((i + 1) + ". " + foundTaskList.get(i).toString()); + } + System.out.println("____________________________________________________________\n"); + } + + private void blankDescription(String input) throws DukeException { + if (input.length() == 4 || input.substring(4).isBlank()) { + throw new DukeException("☹ OOPS!!! Must have a description.\n"); + } + } + + private void invalidInput() throws DukeException { + throw new DukeException("☹ OOPS!!! I'm sorry, but I don't know what that means :-(\n"); + } + + private void printMarkError() { + System.out.println("____________________________________________________________"); + System.out.println("*DID NOT ENTER A VALID NUMBER*"); + System.out.println("____________________________________________________________\n"); + } + + private void printAddedTask(Task task, int counter) { + System.out.println("____________________________________________________________"); + System.out.println("Got it. I've added this task:"); + System.out.println(task.toString()); + System.out.println("Now you have " + counter + " tasks in the list."); + System.out.println("____________________________________________________________\n"); + } +} diff --git a/src/main/java/duke/exceptions/DukeException.java b/src/main/java/duke/exceptions/DukeException.java new file mode 100644 index 000000000..b21a4506c --- /dev/null +++ b/src/main/java/duke/exceptions/DukeException.java @@ -0,0 +1,33 @@ +package duke.exceptions; + +/** + * Exception when there is an inputted error or Duke program fails + */ +public class DukeException extends Exception { + + /** + * Default constructor + */ + public DukeException() {} + + /** + * Instantiates message + * + * @param message exception cause + */ + public DukeException(String message) { + super(message); + } + + /** + * Overrides toString to include exception message + * + * @return exception message + */ + @Override + public String toString() { + return "____________________________________________________________\n" + + super.getMessage() + + "____________________________________________________________\n"; + } +} diff --git a/src/main/java/duke/main/Duke.java b/src/main/java/duke/main/Duke.java new file mode 100644 index 000000000..cb18a73a8 --- /dev/null +++ b/src/main/java/duke/main/Duke.java @@ -0,0 +1,54 @@ +package duke.main; + +import duke.entity.Storage; +import duke.entity.TaskList; +import duke.entity.Ui; +import duke.exceptions.DukeException; + +/** + * Contains the main method where it begins the program and runs the program as the base of the + * Duke program. + */ +public class Duke { + private Storage storage; + private TaskList tasks; + private Ui ui; + + /** + * Constructor initiates the instance variables given the loaded tasks from the data file + * + * @param filePath string of the file path where tasks data will be saved + */ + public Duke(String filePath) { + ui = new Ui(); + storage = new Storage(filePath); + try { + tasks = new TaskList(storage.load()); + } catch (DukeException e) { + System.out.println(e.toString()); + tasks = new TaskList(); + } + } + + /** + * Starts the program after initializing Duke. Loops through manageInput method which takes in the input and + * outputs results to user until they exit the program + */ + public void run() { + ui.greetings(); + boolean exited = true; + while (exited) { + exited = ui.manageInput(tasks, storage); + } + ui.goodbye(); + } + + /** + * Initializes the instance variable Duke duke and runs the program + * + * @param args + */ + public static void main(String[] args) { + new Duke("data/tasks.txt").run(); + } +} \ No newline at end of file diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java new file mode 100644 index 000000000..597ab464c --- /dev/null +++ b/src/main/java/duke/task/Deadline.java @@ -0,0 +1,41 @@ +package duke.task; + +/** + * Represents a deadline task where a deadline for a task is included + */ +public class Deadline extends Task { + protected String by; + + /** + * Instantiates the deadline task variables + * + * @param description description of task + * @param by deadline date of task + */ + public Deadline(String description, String by) { + super(description); + this.by = by; + } + + /** + * Instantiates the deadline task variables + * + * @param description description of task + * @param by deadline date of task + * @param isDone if task is marked as done + */ + public Deadline(String description, String by, boolean isDone) { + super(description, isDone); + this.by = by; + } + + /** + * Overrides toString method to return output as shown in list of tasks + * + * @return string of deadline in listed format + */ + @Override + public String toString() { + return "[D]" + super.toString() + " (by: " + by + ")"; + } +} diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java new file mode 100644 index 000000000..ee984be7b --- /dev/null +++ b/src/main/java/duke/task/Event.java @@ -0,0 +1,46 @@ +package duke.task; + +/** + * Represents an event task where there is a from and to date + */ +public class Event extends Task { + protected String from; + protected String to; + + /** + * Instantiates the event task variables + * + * @param description description of event + * @param from start date of event + * @param to end date of event + */ + public Event(String description, String from, String to) { + super(description); + this.from = from; + this.to = to; + } + + /** + * Instantiates the event task variables + * + * @param description description of event + * @param from start date of event + * @param to end date of event + * @param isDone if task is marked as done + */ + public Event(String description, String from, String to, boolean isDone) { + super(description, isDone); + this.from = from; + this.to = to; + } + + /** + * Overrides toString method to return output as shown in list of tasks + * + * @return string of event in listed format + */ + @Override + public String toString() { + return "[E]" + super.toString() + " (from: " + this.from + " to: " + this.to + ")"; + } +} diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java new file mode 100644 index 000000000..5726962e5 --- /dev/null +++ b/src/main/java/duke/task/Task.java @@ -0,0 +1,80 @@ +package duke.task; + +/** + * Task class used to have a text (description) and isDone attributes + */ +public class Task { + + protected String description; + protected boolean isDone; + + /** + * Instantiates defaults empty string description and isDone as false + */ + public Task() { + this.description = ""; + this.isDone = false; + } + + /** + * Instantiates string description and defaults isDone as false + */ + public Task(String description) { + this.description = description; + this.isDone = false; + } + /** + * Instantiates string description and isDone + */ + public Task(String description, boolean isDone) { + this.description = description; + this.isDone = isDone; + } + + /** + * Overrides toString method to return output as shown in list of tasks + * + * @return string of task in listed format + */ + @Override + public String toString() { + String descriptionNoSlash = (description.indexOf("/") == -1) ? description : (description.substring(0, description.indexOf("/"))); + return "[" + ((this.isDone) ? "X] " : " ] ") + descriptionNoSlash; + } + + /** + * Task description getter + * + * @return description of task + */ + public String getDescription() { + return this.description; + } + + /** + * Task description setter + * + * @param description task description + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Task isDone getter + * + * @return if task if done + */ + public boolean getIsDone() { + return this.isDone; + } + + /** + * Task isDone setter + * + * @param isDone if task if done + */ + public void setIsDone(boolean isDone) { + this.isDone = isDone; + } +} diff --git a/src/main/java/duke/task/Todo.java b/src/main/java/duke/task/Todo.java new file mode 100644 index 000000000..72d476a2b --- /dev/null +++ b/src/main/java/duke/task/Todo.java @@ -0,0 +1,31 @@ +package duke.task; + +/** + * Represents a todo task with a description and isDone + */ +public class Todo extends Task { + + /** + * Instantiates string description and defaults isDone as false + */ + public Todo(String description) { + super(description); + } + + /** + * Instantiates string description and isDone + */ + public Todo(String description, boolean isDone) { + super(description, isDone); + } + + /** + * Overrides toString method to return output as shown in list of tasks + * + * @return string of todo in listed format + */ + @Override + public String toString() { + return "[T]" + super.toString(); + } +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e..f823ba715 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -5,3 +5,40 @@ Hello from | |_| | |_| | < __/ |____/ \__,_|_|\_\___| +____________________________________________________________ +Hello! I'm duke.main.Duke +What can I do for you? +____________________________________________________________ + +____________________________________________________________ +Got it. I've added this task: +[T][ ] todo assignment 1 +Now you have 1 tasks in the list. +____________________________________________________________ + +____________________________________________________________ +Got it. I've added this task: +[D][ ] deadline assignment 2 (by: by monday) +Now you have 2 tasks in the list. +____________________________________________________________ + +____________________________________________________________ +Got it. I've added this task: +[E][ ] event assignment 3 (from: tuesday to: thursday) +Now you have 3 tasks in the list. +____________________________________________________________ + +____________________________________________________________ +Here are the tasks in your list: +1. [T][ ] todo assignment 1 +2. [D][ ] deadline assignment 2 (by: by monday) +3. [E][ ] event assignment 3 (from: tuesday to: thursday) +____________________________________________________________ + +____________________________________________________________ +Here are the tasks in your list: +1. [T][ ] todo assignment 1 +2. [D][ ] deadline assignment 2 (by: by monday) +3. [E][X] event assignment 3 (from: tuesday to: thursday) +____________________________________________________________ + diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb..b40b8d824 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,5 @@ +todo assignment 1 +deadline assignment 2/by monday +event assignment 3/tuesday/thursday +list +mark 3 \ No newline at end of file diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 087374464..aab623331 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -7,7 +7,7 @@ REM delete output from previous run if exist ACTUAL.TXT del ACTUAL.TXT REM compile the code into the bin folder -javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\*.java +javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\duke.main.Duke.java IF ERRORLEVEL 1 ( echo ********** BUILD FAILURE ********** exit /b 1 @@ -15,7 +15,7 @@ IF ERRORLEVEL 1 ( REM no error here, errorlevel == 0 REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ..\bin Duke < input.txt > ACTUAL.TXT +java -classpath ..\bin duke.main.Duke < input.txt > ACTUAL.TXT REM compare the output to the expected output FC ACTUAL.TXT EXPECTED.TXT