ICT159 Foundations of Programming ICT159 – Assignment 2 This is an individual assignment, and is worth 20% of the total assessment for the unit. This assignment has 4 exercises. Each exercise builds on the exercises that precede it. Thus, it is important that you do them in the order from Exercise 1 to Exercise 4. Student ID Name Final Mark Objectives Construct algorithms to solve problems using a combination of sequence, selection and iteration constructs. Implement such algorithms in a common programming language, C. Read/write data to/from files. Use arrays of records (structs). Search arrays of records. Apply the methodology of top-down design to the construction of solutions and implement these solutions in a modular way. This is an individual assignment and must be completed by you alone. Any unauthorised collaboration/collusion may be subject to investigation and result in penalties if found to be in breach of University policy. Submission Guidelines Your assignment must be submitted via LMS following the guidelines listed below. Failure to adhere to these guidelines will result in marks being lost or a fail grade being awarded. Create a folder named ICT159_StudentID_Surname_Assignment2. Make sure you replace StudentID with your student ID and Surname with your surname For each exercise, create a folder named Exercise 1, Exercise 2, etc. Put the source code (i.e., C program) files of each exercise into its folder. This includes both the .c and .h files Answer the questions directly on this Word document (the C programs need to be included separately; see the previous points). Once done, convert it into PDF and save it inside the folder ICT159_StudentID_Surname_Assignment2. Compress the folder into ICT159_StudentID_Surname_Assignment2.zip and upload it to LMS. You should always keep a copy of all your work. Keep the backup of your work in cloud storage like OneDrive. Losing your work/files (for any reason) a few days before the deadline is not a valid ground for extension. Note that: You may be required to demonstrate and explain your solution in person after the submission. If such demonstration is needed, you will be contacted. If you do not defend/demonstrate in-person, no marks will be given, resulting in a mark of 0. You must use modular design and modular programming. Failure to use modular programming will result in a straight 0 for the entire assignment. Problem We would like to write a computer program that will help the school of IT manage and monitor the performance of first year students in the four units they take in Semester 1, namely: ICT159, ICT171, ICT100 and MAS162. A student can enrol in at least one unit and at most 4 units. In other words, there will be students who are enrolled in 4 units, 3 units, 2 units, or just 1 unit. To keep track of enrolments, the school manager has created has created four (4) files ICT159.txt, ICT171.txt, ICT100.txt and MAS162.txt. The content of each file is as follows: ICT159 Foundations of Programming 4 Exercises 20 Assignment1 10 Assignment2 20 Examination 50 AB123456 Laga 5 9 18 33 CDB77777 Rai 8 10 20 47 … This file is about ICT159. The first row of the file has the unit code, which is always a string of 6 characters. The second row is the unit name, which is a string of arbitrary length (it can be of any length). The third row is the number of assessable components of the unit. For example, in ICT159, we have 4 assessable components listed in the following 4 rows. Each of the four following rows contain the name of the assessable components and its weight. For example, Exercises are worth 20%, Assignment1 is worth 10%, etc. After that, starting from row 8 of the example above, we have the list of students. Each row (referred to as Student Record) is one student and has the following information: - Student ID, which is a string of fixed length (always 8 characters) The student’s last name The student’s mark in each of the assessable components. For example, student Laga has scored 5 out of 100 in the weekly Exercises, 9 out of 100 in Assignment1, 18 out of 100 in Assignment2 and 33 out of 100 in the Examination. Note that if a student did not submit an assignment, it will show as a 0 for that assignment. 2 Exercise 1 [25 marks] A file, e.g., ICT159.txt is valid if: - - Its first row has the unit code of length that is exactly 6 characters (Error Code: -1). The second row is a string that starts with an alphabetical character in upper case. For example: “Foundations of Programming” is valid but “foundations of Programming” is not because the first character (“f” in this case) is in lower case (Error Code: -2). The third row is a number that is larger or equal to 1 and less or equal to 4 (Error Code: -3). The weight of each of the assessed components is an integer number between 1 and 100 (Error Code: -4). The sum of the weights of all the assessed components should be equal to 100 (Error Code: 5). The mark of a student in each assessed component should be a number between 0 and 100 (Error Code: -6). We would like to write a module that checks whether the content of a file is valid according to the criteria listed above (note that there are other error cases, but in this assignment, we limit ourselves to those listed above). The module should take as input the filename and should return: - Value 1 if the file is valid. If the file is not valid, it should return the Error Code and the row number in the file where the error has been detected. Note that row numbers start from 1. The main program, should ask the user to enter the filename. It then checks whether the file exists. If it does not exist, then it will keep asking the user to reenter the file name or Q or q to abort and exit the program. Once the use has entered the name of a file that exists, it then checks whether the file is valid or not. If it is valid, it should print “The file is valid.”. If not, it should print the error code, the line number where it occurred, and a description of the error. a. Provide the structure chart for solving the problem above. The structure chart should include the high level algorithm and the different modules. It should also show the parameters, their types and how they are passed across different modules. Provide your answer in the box below. 3 4 b. Provide in the table below a description of what each module does. In the description, indicate what is the input (and their type) and what is the output (and their type). Add as many rows as needed. Module Name Main File_exists Validate_file validate_unit_code validate_unit_name validate_assessable_components validate_student_records Description This module handles the main program logic, including user input and invoking the file validation process. This module checks if the specified file exists. Input: filename (char array). Output: exists (int). This module validates the entire file content. Input: filename (char array). Output: validation_result (int). This module validates the unit code. Input: unit_code (char array). Output: is_valid (int). This module validates the unit name. Input: unit_name (char array). Output: is_valid (int). This module validates the assessable components. Input: components (array of char arrays), num_components (int). Output: is_valid (int). This module validates the student records. Input: student_records (array of char arrays), num_students (int). Output: is_valid (int). c. Provide the algorithms of each of the modules that compose your structure chart and the table above. You must follow the format we use in the lecture notes and in the weekly labs. Algorithms: Main: 1. Start 2. Prompt user to enter the filename 3. Check if file exists using file_exists(filename) 4. If file does not exist, prompt again or allow exit (Q/q) 5. If file exists, call validate_file(filename) 6. If validate_file returns 1, print "The file is valid." 7. Otherwise, print the error code, line number, and description of the error 8. End File_Exists: 1. Start 2. Try to open the file in read mode 3. If file opens successfully, return 1 4. If file does not open, return 0 5. End Validate_file: 1. Start 2. Open the file and read line by line 3. Validate each line according to the criteria 4. Return 1 if all lines are valid 5. If any line is invalid, return the error code and line number 6. End Validate_unit_code: 1. Start 2. Check if length of unit_code is exactly 6 3. If valid, return 1 4. If invalid, return -1 5. End 5 validate_assessable_components: 1. Start 2. For each component, check if weight is between 1 and 100 3. Calculate the sum of weights 4. If sum is not 100, return -5 5. If all weights are valid and sum is 100, return 1 6. If any weight is invalid, return -4 7. End validate_student_records: 1. Start 2. For each student record, check if marks are between 0 and 100 3. If all marks are valid, return 1 4. If any mark is invalid, return -6 5. End d. Implement your solution using C programming language. Put all your code inside a folder named Exercise 1 as well as the .txt files you used to test it. Exercise 2 [25 marks] In this exercise, if the file is valid, we would like to read it into a data structure inside our program so that we can do some processing and analysis on the data. a. Which data structure would you use to store the record of one student? Provide the C code of the data structure in the box below (you are only required to provide the code for the data structure, you do not need to provide the code for the main function and other modules). Data Structure to Store the Record of One Student typedef struct { char student_id[9]; char last_name[50]; int marks[4]; // Marks for the 4 assessable components } Student; b. Which data structure would you use to store the records of ALL students? Provide the C code of the data structure in the box below (you are only required to provide the code for the data structure, you do not need to provide the code for the main function and other modules). Data Structure to Store the Records of ALL Students: typedef struct { Student students[100]; // Assuming a maximum of 100 students int student_count; } StudentRecords; c. Which data structure would you use to store the information about a unit (as provided in the .txt file)? Provide the C code of the data structure in the box below (you are only required to provide the 6 code for the data structure, you do not need to provide the code for the main function and other modules). Data Structure to Store the Information about a Unit: typedef struct { char unit_code[7]; char unit_name[100]; int num_components; struct { char name[50]; int weight; } components[4]; // Assuming a maximum of 4 assessable components d. Provide the structure chart of a program that asks the user to enter the filename, it then checks } Unit; the file is valid or not. If it is not valid, it should print the error code, the line number where it whether occurred, and a description of the error. If the file is valid, it should: - read the information about the unit into the data structure you defined in Question c above read the information about all students into the data structure you defined in Questions a and b above. This structure chart should be an extension of the one you developed in Question c of Exercise 1. It should show all modules, including the high level module, as well as the parameters and parameter passing between modules. 7 Main Program | |-- get_filename() | |-- Parameters: none | |-- Returns: char* (filename) | |-- file_exists(filename) | |-- Parameters: char* (filename) | |-- Returns: int (1 if exists, 0 if not) | |-- validate_file(filename) | |-- Parameters: char* (filename) | |-- Returns: int (1 if valid, error code if not) | |-- print_error(error_code, line_number) | |-- Parameters: int (error_code), int (line_number) | |-- Returns: void | |-- read_unit_info(filename, Unit* unit) | |-- Parameters: char* (filename), Unit* (pointer to Unit struct) | |-- Returns: void | |-- read_student_records(filename, StudentRecords* records) | |-- Parameters: char* (filename), StudentRecords* (pointer to StudentRecords struct) | |-- Returns: void 8 e. Provide in the table below a description of what each module does. In the description, indicate what is the input (and their type) and what is the output (and their type). Add as many rows as needed. Module Name get_filename file_exists validate_file print_error read_unit_info read_student_records Description Prompts the user to enter a filename or Q/q to quit. Returns the entered filename as a char*. Checks if a file with the given filename exists. Takes char* as input and returns int (1 if exists). Validates the content of the file according to specified criteria. Returns 1 if valid, error code if not. Prints the error code and line number where the error occurred. Takes int error code and line number as input. Reads the unit information from the file and stores it in a Unit struct. Takes char* filename and Unit* as input. Reads the student records from the file and stores them in a StudentRecords struct. Takes char* filename and StudentRecords* as input. f. Implement your solution using C programming language. Your code should reuse the functions you created for Exercise 1. Put all your code inside a folder named Exercise 2. Exercise 3 [30 Marks] We would like now to determine the total mark and final grade of each student and save the final marks and grades of all students into a single file named UnitCode_results.txt. If the unit code is ICT159, then the file name will be ICT159_results.txt. If it is ICT171, then the file name is ICT171_results.txt. The result file should be structured as follows: ICT159 Foundations of Programming 4 Exercises 20 Assignment1 10 Assignment2 20 Examination 50 AB123456 Laga 5 9 18 33 23 N CDB77777 Rai 8 10 20 47 44 N … It follows the same format as the original input file, except that for each student we also add the total mark (the one highlighted in red) and the final grade (the one highlighted in green). The colors are here just for illustration. If: - a unit has four assessed components of weights a, b, c and d, respectively, and a students received mark1, mark2, mark3 and mark4 in the four assessed components Then the final mark is M = (a * mark1 + b * mark2 + c * mark3 + d * mark4) / (a + b + c + d). The final grade is either HD, D, C, P, SX, N. It is determined according to the table below Mark Grade 80 to 100 HD 9 70 to 79 D 60 to 69 C 50 to 59 P 45 to 49 SX 0 to 44 N a. What changes would you make to the data structures you defined in Exercise 2, Questions a and b so that it can also hold/store the final mark of each student and their final grade? Provide the code of the new data structures in the box below. Updated Data Structure: typedef struct { char student_id[9]; char last_name[50]; int marks[4]; // Marks for the 4 assessable components float total_mark; // Total mark char grade[3]; // Final grade } Student; typedef struct { Student students[100]; // Assuming a maximum of 100 students int student_count; } StudentRecords; b. Provide the algorithm of a module that takes as input a student record and outputs the student’s final mark and her/his grade. You must use the data structures you defined in Question a above. (You are not required to provide the algorithm of the main function). Algorithm to Compute Final Mark and Grade: void compute_final_mark_and_grade(Student *student, Unit unit) { int total_weight = 0; float total_mark = 0.0; for (int i = 0; i < unit.num_components; i++) { total_mark += (unit.components[i].weight * student->marks[i]); total_weight += unit.components[i].weight; } 10 11 c. Provide the algorithm of the module(s) that goes through all students and for each student, it computes their final mark and final grade. You must reuse the modules you defined in Question a and b above. Algorithm to Compute Final Mark and Grade for All Students: void compute_all_students_final_marks_and_grades(StudentRecords *records, Unit unit) { for (int i = 0; i < records->student_count; i++) { compute_final_mark_and_grade(&records->students[i], unit); } } 12 d. Provide the algorithm of the module that saves the results of Question c into a file named UnitCode_results.txt where UnitCode should be replaced by the actual unit code (e.g., for ICT159, it should be ICT159_results.txt). Algorithm to Save Results to a File: void save_results_to_file(const char *filename, Unit unit, StudentRecords records) { FILE *file = fopen(filename, "w"); if (!file) { printf("Failed to open the file for writing.\n"); return; } fprintf(file, "%s\n", unit.unit_code); fprintf(file, "%s\n", unit.unit_name); fprintf(file, "%d\n", unit.num_components); for (int i = 0; i < unit.num_components; i++) { fprintf(file, "%s %d\n", unit.components[i].name, unit.components[i].weight); } for (int i = 0; i < records.student_count; i++) { fprintf(file, "%s %s %d %d %d %d %.2f %s\n", records.students[i].student_id, records.students[i].last_name, records.students[i].marks[0], records.students[i].marks[1], records.students[i].marks[2], records.students[i].marks[3], records.students[i].total_mark, records.students[i].grade); } fclose(file); } 13 e. We would like now to put together all the modules of this exercise into a complete program. In other words, the program asks the user to enter the filename, it then checks whether the file is valid or not. If it is not valid, it should print the error code, the line number where it occurred, and a description of the error. If the file is valid, it should: - read the information about the unit into the data structure you previously defined read the information about all students into the data structure you previously defined determine the total mark and final grade of each student and save the final marks and grades of all students into a single file named UnitCode_results.txt. Provide the entire structure chart (the structure chart should build on/extend the one you defined in Exercise 2). Structure Chart: Main Program | |-- get_filename() | |-- Parameters: none | |-- Returns: char* (filename) | |-- file_exists(filename) | |-- Parameters: char* (filename) | |-- Returns: int (1 if exists, 0 if not) | |-- validate_file(filename) | |-- Parameters: char* (filename) | |-- Returns: int (1 if valid, error code if not) | |-- print_error(error_code, line_number) | |-- Parameters: int (error_code), int (line_number) | |-- Returns: void | |-- read_unit_info(filename, Unit* unit) | |-- Parameters: char* (filename), Unit* (pointer to Unit struct) | |-- Returns: void | |-- read_student_records(filename, StudentRecords* records) | |-- Parameters: char* (filename), StudentRecords* (pointer to StudentRecords struct) | |-- Returns: void 14 | |-- compute_final_mark_and_grade(Student* student, Unit unit) | |-- Parameters: Student* (pointer to Student struct), Unit (unit struct) | |-- Returns: void | |-- compute_all_students_final_marks_and_grades(StudentRecords* records, Unit unit) | |-- Parameters: StudentRecords* (pointer to StudentRecords struct), Unit (unit struct) | |-- Returns: void | |-- save_results_to_file(filename, Unit unit, StudentRecords records) | |-- Parameters: char* (filename), Unit (unit struct), StudentRecords (student records struct) | |-- Returns: void f. Provide the algorithm of the MAIN (high level) program, which should call the modules you defined so far in this exercise and also in previous exercises. Algorithm of the Main program: int main() { char filename[100]; while (1) { 15 printf("Enter the filename (or Q/q to quit): "); g. Provide the entire C program. Put all your codes inside a folder called Exercise 3. 16 Exercise 4 [20 Marks] We would like to extend the program you developed in Exercise 3 so that we can output on the screen the number of students who received HD, the number of students who received D, etc….. In other words, the program should output something like this: The number of students who received HD is 4 The number of students who received D is 7 The number of students who received C is 11 The number of students who received P is 12 The number of students who received SX is 21 The number of students who received N is 3 a. Provide the algorithm of the module that counts how many students achieved HD, how many students achieved D, etc. If this module has to call other modules, then you need to provide the algorithms of these modules as well. Important note: think carefully about the principles of modular programming, which are: abstraction, single responsibility, reusability, low coupling. 17 Algorithm: Algorithm count_students_by_grade(student_records): Initialize counters for each grade: HD_count = 0, D_count = 0, C_count = 0, P_count = 0, SX_count = 0, N_count = 0 For each student in student_records: If student.grade is "HD": Increment HD_count Else if student.grade is "D": Increment D_count Else if student.grade is "C": Increment C_count Else if student.grade is "P": Increment P_count Else if student.grade is "SX": Increment SX_count Else if student.grade is "N": Increment N_count Output "The number of students who received HD is " + HD_count Output "The number of students who received D is " + D_count Output "The number of students who received C is " + C_count Output "The number of students who received P is " + P_count Output "The number of students who received SX is " + SX_count Output "The number of students who received N is " + N_count 18 b. Provide the entire structure chart of the program that asks the user to enter the filename, it then checks whether the file is valid or not. If it is not valid, should print the error code, the line number where it occurred, and a description of the error. If the file is valid, it should: - read the information about the unit into the data structure you defined above - read the information about all students into the data structure you defined above - determine the total mark and final grade of each student and - save the final marks and grades of all students it into a single file named UnitCode_results.txt. - Outputs on screen the number of students who received each grade (Question a above) The structure chart should extend the one you developed in Exercise 3. 19 +------------------+ | Main Module | +------------------+ | +---------------------------------------------+ | | | +-------------------------+ +------------------+ | | Validate File Module | | Read Unit Info | +-------------------------+ +------------------+ | | | | | +-------------------------+ +------------------+ | Validate Unit Code | | | Read Student | +-------------------------+ | Records Module | | +------------------+ +-------------------------+ | Validate Unit Name | | | | | | +------------------+ | +-------------------------+ | Compute Final | | | | Mark and Grade | | +-------------------------+ +------------------+ | | Validate Assessable | | Components | | | +------------------+ | +-------------------------+ | Save Results to | | | | File Module | | +-------------------------+ +------------------+ | | | +-------------------------+ +------------------+ | | Validate Student Records| | Count Students | | +-------------------------+ | by Grade Module | | +------------------+ +-------------------------+ | | | | +------------------+ | +-------------------------+ | Print Results | | | Module | | | +------------------+ | | | +-------------------------------------------+ 20 | | c. Provide the entire program that implements the structure chart of Question b. You must build on and reuse the modules you defined in the previous exercises. // Function to count the number of students who achieved each grade void count_students_by_grade(StudentRecords records); 21 int main() { // Main program logic as in Exercise 3, with additional function call to count_students_by_grade } void count_students_by_grade(StudentRecords records) { int HD_count = 0, D_count = 0, C_count = 0, P_count = 0, SX_count = 0, N_count = 0; for (int i = 0; i < records.student_count; i++) { if (strcmp(records.students[i].grade, "HD") == 0) { HD_count++; } else if (strcmp(records.students[i].grade, "D") == 0) { D_count++; } else if (strcmp(records.students[i].grade, "C") == 0) { C_count++; } else if (strcmp(records.students[i].grade, "P") == 0) { P_count++; } else if (strcmp(records.students[i].grade, "SX") == 0) { SX_count++; } else if (strcmp(records.students[i].grade, "N") == 0) { N_count++; } } printf("The number of students who received HD is %d\n", HD_count); printf("The number of students who received D is %d\n", D_count); printf("The number of students who received C is %d\n", C_count); printf("The number of students who received P is %d\n", P_count); printf("The number of students who received SX is %d\n", SX_count); printf("The number of students who received N is %d\n", N_count); } 22