Assignment 2 - CS ToDo
In assignment 2 you will be implementing a todo list, to help Ben keep track of all the jobs he has to do for DPST1091! Todo lists are a classic first project for any aspiring developer, so we decided to let you implement your own todo list in C, to get some more practice working with linked lists.
Overview
Assignment Structure
This assignment will test your ability to create, use, manipulate and solve problems using linked lists. To do this, the todo list is implemented as a linked list of tasks, and another separate linked list is used to keep track of your “completed tasks”.
We have defined some structs in the provided code to get you started. You may modify any of the structs if you wish, but you should not need to.
struct todo_list
- Purpose:
- To store all of the information about our todo list.
- Contains:
struct task *tasks
- A pointer to the first task in the todo list.
struct completed_task *completed_tasks
- A pointer to the first completed task in the list
struct task
- Purpose:
- To store information about a task on your list.
- Contains:
char task_name[MAX_TASK_LENGTH]
- The name of the task (e.g “Finish-Assignment-2”)
char category[MAX_CATEGORY_LENGTH]
- The task’s category (e.g “Assignments”)
enum priority priority
- The task’s priority - how urgent is it that the task is completed?) (e.g HIGH)
struct task *next
- A pointer to the next task in the list.
struct completed_task
- Purpose:
- To hold information about a task that has already been completed.
- Contains:
struct task *task
- A pointer to a task that has been completed, and removed from the
tasks
list. Thenext
pointer of this task should always point toNULL
.
- A pointer to a task that has been completed, and removed from the
int start_time
- The time at which we started this task. It will be given in 24-hour time (e.g
1234
corresponds to a time of 12:34pm). You have been provided with functions to process these times.
- The time at which we started this task. It will be given in 24-hour time (e.g
int finish_time
- The time at which we finished this task.
struct completed_task *next
- A pointer to the next completed task in the list.
Commands
To help Ben manage his jobs, you will need to setup the todo list program for him, which will allow creation of a todo list and handy commands to manipulate it.
- Setup of todo list (Stage 1.1) : Create and initialise the todo list.
- Commands (Stage 1.2 onward) : The program now reads command continuously until
CTRL-D
.- Each command will begin with a character and then potentially have strings or enums in string format after it.
- The number of arguments in a command depends on the first character. For example, to add a task, the command is
a
followed by 3 arguments, whereas counting tasks with then
command requires no extra arguments. - Some commands have helper functions provided to help you read in and format the arguments that follow. These functions are mentioned in their respective parts of this spec.
How To Get Started
There are a few steps to getting started with CS ToDo.
- Create a new folder for your assignment work and move into it.
mkdir ass2 cd ass2
- Download the starter code (cs_todo.c) here or use this command on your CSE account to copy the file into your current directory:
cp -n /web/dp1091/23T2/activities/cs_todo/cs_todo.c .
- Run
1091 autotest cs_todo
to make sure you have correctly downloaded the file.
1091 autotest cs_todo
Read through Stage 1.
Spend a few minutes playing with the reference solution -- get a feel for how the assignment works.
1091 cs_todo
- Think about your solution, draw some diagrams to help you get started.
- Start coding!
About the Starter Code
The provided starter code has done some setup for you. This is explained below.
Before the main function, the starter code has:
- Imported libraries, defined some initial
#define
's and enums, and defined the structs described above. - Declared a
command_loop
function which you will have to use in stages 1.1 and beyond. - Declared some functions, which are used to help manage the todo list. Some of these you will not need to use, most of them you will. Please read the comments and the spec as we will suggest certain provided functions for you to use.
In the main function, the starter code has:
- Created a a pointer to a
struct todo_list
calledtodo
. - Prompts for you to write your own code!
Your Tasks
This assignment consists of four stages. Each stage builds on the work of the previous stage, and each stage has a higher complexity than its predecessor. You should complete the stages in order.
Stage 1
For stage 1 of this assignment, you will be creating a basic structure for CS ToDo. This will include:
- Setting up an empty todo list
- Adding tasks to the todo list
- Printing tasks in the todo list
- Updating priority of tasks in the todo list
- Counting the number of tasks in the todo list
For stage 1 of this assignment, you will be creating a basic structure for CS ToDo. This will include:
- Setting up an empty todo list
- Adding tasks to the todo list
- Printing tasks in the todo list
- Updating priority of tasks in the todo list
- Counting the number of tasks in the todo list
Stage 1 Diagram
At the end of stage 1, your linked lists should look similar to this!
At the end of stage 1, your linked lists should look similar to this!
Stage 1.1 - Setup
In stage 1.1, we will initialise the provided todo
variable found in the main()
function.
You should use malloc
to allocate some space in memory for the todo_list
structure, and set the tasks
and completed_tasks
fields to NULL
— as we don’t have any tasks in our lists yet!
Once you are done, you can compile your program with
dcc cs_todo.c -o cs_todo
In stage 1.1, we will initialise the provided todo
variable found in the main()
function.
You should use malloc
to allocate some space in memory for the todo_list
structure, and set the tasks
and completed_tasks
fields to NULL
— as we don’t have any tasks in our lists yet!
Once you are done, you can compile your program with
dcc cs_todo.c -o cs_todo
Examples
Stage 1.2 - Adding Tasks
Command
a [task] [category] [priority]
There’s not much point in having a todo list if we can’t put any tasks on it. So, in this stage, we will allow the user to add tasks to the end of their todo list! To add a task, the user will type the command a
, followed by:
- the task’s name,
- the task’s category,
- the task’s priority.
For example, to add the task "finish_assignment_2" in the category "assignments" with a high priority, you would type the following:
Enter Command: a finish_assignment_2 assignments high
To process these inputs, we have provided you with the function parse_add_task_line()
. It will extract the name, category, and priority from the line for you. This function is ready to be used by you, each time you add a task to your todo list (you just have to call it!). An example of how you should use this function is below.
// Create a string to scan the entire command input into.
char buffer[MAX_STRING_LENGTH];
fgets(buffer, MAX_STRING_LENGTH, stdin);
// Create variables for each part of the command being scanned in
// (name of task, category of task and priority of task)
char task_name[MAX_TASK_LENGTH];
char task_category[MAX_CATEGORY_LENGTH];
enum priority task_priority;
parse_add_task_line(buffer, task_name, task_category, &task_priority);
In this assignment, you will need to continuously loop the program until CTRL+D
is pressed, while also being able to enter commands (much like assignment 1!). We tend to call this a "command loop", and we have already set one up for you in the command_loop
function!
Currently, this command loop only handles a single command - 'a'. You should write code for 1.2 inside this loop to handle adding of a task - remember to use the related parse_add_task_line()
code!
a [task] [category] [priority]
There’s not much point in having a todo list if we can’t put any tasks on it. So, in this stage, we will allow the user to add tasks to the end of their todo list! To add a task, the user will type the command a
, followed by:
- the task’s name,
- the task’s category,
- the task’s priority.
For example, to add the task "finish_assignment_2" in the category "assignments" with a high priority, you would type the following:
Enter Command: a finish_assignment_2 assignments high
To process these inputs, we have provided you with the function parse_add_task_line()
. It will extract the name, category, and priority from the line for you. This function is ready to be used by you, each time you add a task to your todo list (you just have to call it!). An example of how you should use this function is below.
// Create a string to scan the entire command input into.
char buffer[MAX_STRING_LENGTH];
fgets(buffer, MAX_STRING_LENGTH, stdin);
// Create variables for each part of the command being scanned in
// (name of task, category of task and priority of task)
char task_name[MAX_TASK_LENGTH];
char task_category[MAX_CATEGORY_LENGTH];
enum priority task_priority;
parse_add_task_line(buffer, task_name, task_category, &task_priority);
In this assignment, you will need to continuously loop the program until CTRL+D
is pressed, while also being able to enter commands (much like assignment 1!). We tend to call this a "command loop", and we have already set one up for you in the command_loop
function!
Currently, this command loop only handles a single command - 'a'. You should write code for 1.2 inside this loop to handle adding of a task - remember to use the related parse_add_task_line()
code!
Assumptions/Restrictions/Clarifications
- New tasks should be added to the end of the todo list.
- The same task will never be added to the todo list. This means that two tasks will never share the same name and category. However, it is perfectly fine to share just one of these.
- You can assume that the task name and category will only include alphanumeric characters (upper/lowercase letters and numbers) and underscores (_). This allows for a wide range of inputs, for example, both "complete_stage_3_with_testing" and "2" are valid task names. You do not need to do any error checking for this.
- You can assume that the priority given will be one of "low", "medium", or "high". You do not need to do any error checking for this.
- You can assume that both
task
and category
will be at most MAX_TASK_LENGTH
and MAX_CATEGORY_LENGTH
characters long, respectively.
- New tasks should be added to the end of the todo list.
- The same task will never be added to the todo list. This means that two tasks will never share the same name and category. However, it is perfectly fine to share just one of these.
- You can assume that the task name and category will only include alphanumeric characters (upper/lowercase letters and numbers) and underscores (_). This allows for a wide range of inputs, for example, both "complete_stage_3_with_testing" and "2" are valid task names. You do not need to do any error checking for this.
- You can assume that the priority given will be one of "low", "medium", or "high". You do not need to do any error checking for this.
- You can assume that both
task
andcategory
will be at mostMAX_TASK_LENGTH
andMAX_CATEGORY_LENGTH
characters long, respectively.
Examples
Stage 1.3 - Printing Tasks
Command
p
Now that we are able to add tasks to our todo list, it would be useful if we could also print the list out, to view the tasks we have to complete. When the user types the command p
, the list of tasks should be printed.
In order to match the autotests, you should loop through the list of tasks and, for each of them, call the provided print_one_task
function (rather than calling printf
yourself). This will ensure that the output from your program matches the autotest output exactly.
p
Now that we are able to add tasks to our todo list, it would be useful if we could also print the list out, to view the tasks we have to complete. When the user types the command p
, the list of tasks should be printed.
In order to match the autotests, you should loop through the list of tasks and, for each of them, call the provided print_one_task
function (rather than calling printf
yourself). This will ensure that the output from your program matches the autotest output exactly.
Assumptions/Restrictions/Clarifications
- This command should not print any completed tasks (added in stage 2). It should just print the tasks in our
tasks
list.
- This command should not print any completed tasks (added in stage 2). It should just print the tasks in our
tasks
list.
Examples
Stage 1.4 - Update Priority
Command
i [task] [category]
Priority of tasks can change over time. This can be due to deadlines, oversights or a variety of other issues. As a result, it would be nice to be able to adjust the priority of tasks in your todo list.
The i
command is followed by the name of the task, as well as, the category of the task. It increases the priority of the task matching this name and category upon being called. In the case where the task is at the highest priority, it should be reset back to the lowest priority.
Just like with 1.2, you will need to extract task
and category
from the input line involving this command. The code below will do this for you by using another one of our helper functions, parse_task_category_line()
, make sure to use it!
// Fetch `[task] [category]` from stdin
char buffer[MAX_STRING_LENGTH];
fgets(buffer, MAX_STRING_LENGTH, stdin);
// Create strings for `task`/`category` and populate them using the contents
// of `buffer`
char task[MAX_TASK_LENGTH];
char category[MAX_CATEGORY_LENGTH];
parse_task_category_line(buffer, task, category);
In the case where the task
does not exist in the given category
, the following error should be printed:
Could not find task '[task]' in category '[category]'.
i [task] [category]
Priority of tasks can change over time. This can be due to deadlines, oversights or a variety of other issues. As a result, it would be nice to be able to adjust the priority of tasks in your todo list.
The i
command is followed by the name of the task, as well as, the category of the task. It increases the priority of the task matching this name and category upon being called. In the case where the task is at the highest priority, it should be reset back to the lowest priority.
Just like with 1.2, you will need to extract task
and category
from the input line involving this command. The code below will do this for you by using another one of our helper functions, parse_task_category_line()
, make sure to use it!
// Fetch `[task] [category]` from stdin
char buffer[MAX_STRING_LENGTH];
fgets(buffer, MAX_STRING_LENGTH, stdin);
// Create strings for `task`/`category` and populate them using the contents
// of `buffer`
char task[MAX_TASK_LENGTH];
char category[MAX_CATEGORY_LENGTH];
parse_task_category_line(buffer, task, category);
In the case where the task
does not exist in the given category
, the following error should be printed:
Could not find task '[task]' in category '[category]'.
Assumptions/Restrictions/Clarifications
- You can assume that both
task
and category
will be at most MAX_STRING_LENGTH
characters. - If you increase the priority of a task that already has a
HIGH
priority, then that task's priority should become LOW
.
- You can assume that both
task
andcategory
will be at mostMAX_STRING_LENGTH
characters. - If you increase the priority of a task that already has a
HIGH
priority, then that task's priority should becomeLOW
.
Examples
Stage 1.5 - Counting Tasks
Command
n
When we’re planning our time, it might be nice to know how many tasks we have left to complete. As such, we need to implement the command n
, to count the number of tasks in our todo list, and print this information out.
This command should print the message "There are [num] tasks in your list!" (replacing [num] with the number of tasks in the list).
n
When we’re planning our time, it might be nice to know how many tasks we have left to complete. As such, we need to implement the command n
, to count the number of tasks in our todo list, and print this information out.
This command should print the message "There are [num] tasks in your list!" (replacing [num] with the number of tasks in the list).
Assumptions/Restrictions/Clarifications
- This command should not count any completed tasks (added in stage 2). It should just count the tasks in our
tasks
list.
- This command should not count any completed tasks (added in stage 2). It should just count the tasks in our
tasks
list.
Examples
Testing and Submission
Remember to do your own testing
Are you finished with this stage? If so, you should make sure to do the following:
- Run
1091 style
, and clean up any issues a human may have reading your code. Don't forget -- 20% of your mark in the assignment is based on style and readability! - Autotest for this stage of the assignment by running the
autotest-stage
command as shown below. - Remember -- give early, and give often. Only your last submission counts, but why not be safe and submit right now?
1091 style cs_todo.c
1091 autotest-stage 01 cs_todo
give dp1091 ass2_cs_todo cs_todo.c
Remember to do your own testing
Are you finished with this stage? If so, you should make sure to do the following:
- Run
1091 style
, and clean up any issues a human may have reading your code. Don't forget -- 20% of your mark in the assignment is based on style and readability! - Autotest for this stage of the assignment by running the
autotest-stage
command as shown below. - Remember -- give early, and give often. Only your last submission counts, but why not be safe and submit right now?
1091 style cs_todo.c 1091 autotest-stage 01 cs_todo give dp1091 ass2_cs_todo cs_todo.c