Programming Fundamentals
Information
- This page contains additional revision exercises for week 08.
- These exercises are not compulsory, nor do they provide any marks in the course.
- You cannot submit any of these exercises, however autotests may be available for some them (Command included at bottom of each exercise if applicable).
Revision Video: Pointers - Motivation for Learning
Revision Video: Pointers - Walkthrough using a swap function
Revision Video: Pointers - Walkthrough (Coding)
Revision Video: Dynamic Memory Allocation - Motivation for Learning
Revision Video: Dynamic Memory Allocation - Walkthrough (Coding)
Revision Video: Linked Lists - Prerequisites
Revision Video: Linked Lists - Creating and traversing a linked list (Coding)
Revision Video: Linked List - Sorted Insert
Revision Video: Linked Lists - Adding elements to a Linked List (Coding)
Revision Video: Linked List - Delete
Revision Video: Linked Lists - Deleting elements from a Linked List (Coding)
Exercise — individual:
Debugging Exercise -- Room Service
Download room_service.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity room_service
Before writing any code, your tutor will walk you through a short activity exploring the difference between functions that use pointers and functions that don't.
Your task is to fix any bugs in the starter code room_service.c so that the program works as intended.
The program should:
- Read in a number of snacks to deliver to Sasha and a number of snacks to deliver to Henry.
- Call
deliver()to add the snacks to each room. The function should remain as avoidfunction. - Print the new total number of snacks in each room.
Expected Output
dcc room_service.c -o room_service ./room_service Enter the number of snacks to deliver to Sasha's room: 3 Enter the number of snacks to deliver to Henry's room: 5 Sasha's room now has 5 snacks. Henry's room now has 9 snacks.
Assumptions/Restrictions/Clarifications
- No error checking is needed.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest room_service
room_service.c
// room_service.c
//
// This program was written by Holly Field (z5477436)
// on 16/07/26
//
// Delivers snacks to Sasha's and Henry's hotel rooms.
//
// This program should scan in a number of snacks to deliver to Sasha and
// a number of snacks to deliver to Henry, add them to the correct rooms,
// then print how many snacks are in each room.
//
// However, this program has a bug! Find and fix it so it works as
// intended.
#include <stdio.h>
// Deliver num_snacks snacks to the room at the given address.
void deliver(int *room, int num_snacks) {
*room += num_snacks;
}
int main(void) {
int sashas_room = 2;
int henrys_room = 4;
int num_snacks_for_sasha;
int num_snacks_for_henry;
printf("Enter the number of snacks to deliver to Sasha's room: ");
scanf("%d", &num_snacks_for_sasha);
printf("Enter the number of snacks to deliver to Henry's room: ");
scanf("%d", &num_snacks_for_henry);
deliver(&sashas_room, num_snacks_for_sasha);
deliver(&henrys_room, num_snacks_for_henry);
printf("Sasha's room now has %d snacks.\n", sashas_room);
printf("Henry's room now has %d snacks.\n", henrys_room);
return 0;
}
Exercise — individual:
Game Lag Reports
Download game_lags.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity game_lags
Your task is to complete game_lags.c using the provided starter code. Each command-line argument represents one recorded lag, given as its duration in milliseconds.
The program should:
- Prompt the user with
"Enter the maximum acceptable lag in milliseconds: "and read a single integer. Any recorded lag with a duration greater than or equal to this integer is considered a lag spike. - Count how many of the recorded lags are lag spikes.
- If there are no lag spikes, print
"No lag spikes recorded!\n"and return frommain. - Otherwise,
malloca new array to hold all the lag spikes. - Pass this array to the provided
average_lagandworst_lagfunctions to build a short report, printing:"Number of lag spikes: %d\n""Average lag spike duration: %.2f ms\n""Worst lag spike duration: %d ms\n"
- Free the array once you're done with it.
Examples
dcc game_lags.c -o game_lags ./game_lags 20 55 12 80 5 Enter the maximum acceptable lag in milliseconds: 30 Number of lag spikes: 2 Average lag spike duration: 67.50 ms Worst lag spike duration: 80 ms ./game_lags 5 10 15 Enter the maximum acceptable lag in milliseconds: 50 No lag spikes recorded!
Assumptions/Restrictions/Clarifications
- You may assume at least one lag duration will be provided as a command-line argument.
- You may assume every command-line argument is a valid non-negative integer.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest game_lags
game_lags.c
// game_lags.c
//
// Written by Holly Field (z5477436)
// on 16/07/26
//
// Builds a report of "lag spikes", which are the recorded frame lag durations
// (in milliseconds) that are at or above a given acceptable threshold.
#include <stdio.h>
#include <stdlib.h>
// Provided function prototypes
double average_lag(int *lags, int count);
int worst_lag(int *lags, int count);
// -----------------------------------------------------------------------------
int main(int argc, char *argv[]) {
int num_lags = argc - 1;
int goal;
printf("Enter the maximum acceptable lag in milliseconds: ");
scanf("%d", &goal);
// Count how many of the recorded lags are lag spikes (>= goal).
int num_spikes = 0;
// Start index from 1 since the 0th argument is the executable
for (int i = 1; i <= num_lags; i++) {
// Use atoi from stdlib to convert the character into an integer!
int duration = atoi(argv[i]);
if (duration >= goal) {
num_spikes++;
}
}
if (num_spikes == 0) {
printf("No lag spikes recorded!\n");
return 0;
}
// malloc an array big enough to hold exactly num_spikes integers, then
// fill it in with just the lag durations that are >= goal.
int *spikes = malloc(num_spikes * sizeof(int));
// copy the values into the array
int index = 0;
for (int i = 1; i <= num_lags; i++) {
int duration = atoi(argv[i]);
if (duration >= goal) {
spikes[index] = duration;
index++;
}
}
printf("Number of lag spikes: %d\n", num_spikes);
printf(
"Average lag spike duration: %.2f ms\n",
average_lag(spikes, num_spikes)
);
printf("Worst lag spike duration: %d ms\n", worst_lag(spikes, num_spikes));
free(spikes);
return 0;
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
// Returns the average of the values in lags, an array of count integers.
double average_lag(int *lags, int count) {
if (count == 0) {
return 0;
}
double sum = 0;
for (int i = 0; i < count; i++) {
sum += lags[i];
}
return sum / count;
}
// Returns the largest value in lags, an array of count integers.
int worst_lag(int *lags, int count) {
int worst = lags[0];
for (int i = 1; i < count; i++) {
if (lags[i] > worst) {
worst = lags[i];
}
}
return worst;
}
Exercise — individual:
Pottery Studio
Download pottery_studio.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity pottery_studio
Pottery has to pass through several stages before it's ready to sell. All pieces will start as soft, freshly-thrown greenware, through leather-hard and a bisque firing, to a final glaze firing that makes it ready for the shelf.
Your task is to complete the program pottery_studio.c using the provided starter code. The program simulates a small pottery studio, tracking pieces as they move through these stages using two linked lists: progress_pieces (pieces still being worked on) and pieces_for_sale (pieces that have finished glaze firing).
You should implement three functions:
create_piece: allocates and returns a newstruct ceramic_piecewith the given name.- All pieces will start at the
GREENWAREstage.
- All pieces will start at the
add_piece: creates a new piece with the given name and appends it to the end ofstudio->progress_pieces.fire_pieces: advances every piece instudio->progress_piecesforward by one stage- The stage progression is:
GREENWARE→LEATHER_HARD→BISQUE_FIRED→GLAZE_FIRED). - Any piece that reaches
GLAZE_FIREDduring this firing is finished, and should be removed fromprogress_piecesand appended to the end ofpieces_for_saleinstead.
- The stage progression is:
free_studio: frees every piece in bothprogress_piecesandpieces_for_sale, as well as thestudiostruct itself.
The provided print_studio, print_pieces, and stage_to_string functions handle all the output for you.
Example
dcc pottery_studio.c -o pottery_studio ./pottery_studio === Initial Studio === Pieces in progress: Tea Bowl GREENWARE Coffee Mug GREENWARE Pieces for sale: (none) === After Firing 1 === Pieces in progress: Tea Bowl LEATHER_HARD Coffee Mug LEATHER_HARD Pieces for sale: (none) === After Firing 2 === Pieces in progress: Tea Bowl BISQUE_FIRED Coffee Mug BISQUE_FIRED Vase LEATHER_HARD Pieces for sale: (none) === After Firing 3 === Pieces in progress: Vase BISQUE_FIRED Bowl LEATHER_HARD Pieces for sale: Tea Bowl GLAZE_FIRED Coffee Mug GLAZE_FIRED === After Firing 4 === Pieces in progress: Bowl BISQUE_FIRED Pieces for sale: Tea Bowl GLAZE_FIRED Coffee Mug GLAZE_FIRED Vase GLAZE_FIRED === After Firing 5 === Pieces in progress: (none) Pieces for sale: Tea Bowl GLAZE_FIRED Coffee Mug GLAZE_FIRED Vase GLAZE_FIRED Bowl GLAZE_FIRED
Assumptions/Restrictions/Clarifications
- No error checking is needed.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest pottery_studio
pottery_studio.c
// pottery_studio.c
//
// This program was written by Holly Field (z5477436)
// on 16/07/26
//
// Simulates pieces of pottery moving through the stages of firing in a
// pottery studio.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NAME_LENGTH 64
#define NUM_FIRINGS 5
enum stage {
GREENWARE,
LEATHER_HARD,
BISQUE_FIRED,
GLAZE_FIRED
};
struct ceramic_piece {
char name[MAX_NAME_LENGTH];
enum stage stage;
struct ceramic_piece *next;
};
struct studio {
struct ceramic_piece *progress_pieces;
struct ceramic_piece *pieces_for_sale;
};
// Student functions
void add_piece(struct studio *studio, char *name);
void fire_pieces(struct studio *studio);
void free_studio(struct studio *studio);
// Helper functions
struct ceramic_piece *create_piece(char *name);
void print_studio(struct studio *studio);
void print_pieces(struct ceramic_piece *head);
const char *stage_to_string(enum stage stage);
void move_to_sale(
struct studio *studio,
struct ceramic_piece *piece,
struct ceramic_piece *prev
);
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE THE MAIN FUNCTION ///////////////////////////////
int main(void) {
struct studio *studio = malloc(sizeof(struct studio));
studio->pieces_for_sale = NULL;
studio->progress_pieces = NULL;
add_piece(studio, "Tea Bowl");
add_piece(studio, "Coffee Mug");
printf("=== Initial Studio ===\n");
print_studio(studio);
for (int i = 1; i <= NUM_FIRINGS; i++) {
printf("\n=== After Firing %d ===\n", i);
fire_pieces(studio);
print_studio(studio);
// Add new pieces partway through, so not everything progresses
// through the stages together
if (i == 1) {
add_piece(studio, "Vase");
}
if (i == 2) {
add_piece(studio, "Bowl");
}
}
free_studio(studio);
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTIONS
// -----------------------------------------------------------------------------
// Creates a new ceramic piece by allocating and initialising memory
struct ceramic_piece *create_piece(char *name) {
struct ceramic_piece *new_piece = malloc(sizeof(struct ceramic_piece));
strcpy(new_piece->name, name);
new_piece->stage = GREENWARE;
new_piece->next = NULL;
return new_piece;
}
// Creates and appends a new piece with the given name to the
// studio's progress pieces.
void add_piece(struct studio *studio, char *name) {
struct ceramic_piece *new_piece = create_piece(name);
if (studio->progress_pieces == NULL) {
studio->progress_pieces = new_piece;
return;
}
struct ceramic_piece *curr = studio->progress_pieces;
while (curr->next != NULL) {
curr = curr->next;
}
curr->next = new_piece;
}
// Fires all progress pieces to the next stage
void fire_pieces(struct studio *studio) {
struct ceramic_piece *curr = studio->progress_pieces;
struct ceramic_piece *prev = NULL;
while (curr != NULL) {
if (curr->stage == GREENWARE) {
curr->stage = LEATHER_HARD;
prev = curr;
curr = curr->next;
} else if (curr->stage == LEATHER_HARD) {
curr->stage = BISQUE_FIRED;
prev = curr;
curr = curr->next;
} else if (curr->stage == BISQUE_FIRED) {
curr->stage = GLAZE_FIRED;
// Piece is ready for sale
move_to_sale(studio, curr, prev);
// update current
if (prev == NULL) {
curr = studio->progress_pieces;
} else {
curr = prev->next;
}
} else {
prev = curr;
curr = curr->next;
}
}
}
// Helper function to remove a piece from the progress list and add it to the
// sale list
void move_to_sale(
struct studio *studio,
struct ceramic_piece *piece,
struct ceramic_piece *prev
) {
// Remove from progress_pieces
if (prev == NULL) {
studio->progress_pieces = piece->next;
} else {
prev->next = piece->next;
}
// Add to pieces_for_sale
piece->next = NULL;
if (studio->pieces_for_sale == NULL) {
studio->pieces_for_sale = piece;
} else {
struct ceramic_piece *sale = studio->pieces_for_sale;
while (sale->next != NULL) {
sale = sale->next;
}
sale->next = piece;
}
}
// Frees all memory associated with the studio
void free_studio(struct studio *studio) {
struct ceramic_piece *curr;
curr = studio->progress_pieces;
while (curr != NULL) {
struct ceramic_piece *next = curr->next;
free(curr);
curr = next;
}
curr = studio->pieces_for_sale;
while (curr != NULL) {
struct ceramic_piece *next = curr->next;
free(curr);
curr = next;
}
free(studio);
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
const char *stage_to_string(enum stage stage) {
if (stage == GREENWARE) {
return "GREENWARE";
}
if (stage == LEATHER_HARD) {
return "LEATHER_HARD";
}
if (stage == BISQUE_FIRED) {
return "BISQUE_FIRED";
}
return "GLAZE_FIRED";
}
void print_pieces(struct ceramic_piece *head) {
if (head == NULL) {
printf(" (none)\n");
return;
}
while (head != NULL) {
printf(" %-15s %s\n",
head->name,
stage_to_string(head->stage));
head = head->next;
}
}
void print_studio(struct studio *studio) {
printf("Pieces in progress:\n");
print_pieces(studio->progress_pieces);
printf("\nPieces for sale:\n");
print_pieces(studio->pieces_for_sale);
}
Exercise — individual:
Mini Linter
Download mini_linter.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity mini_linter
Before splitting into groups, your tutor will live-code a small demo of mini_linter_comments.c, which strips any inline // comment from a line of code.
Each of the programs below will:
- Read lines of "code" from standard input, until
[Ctrl-D]is pressed. - For each line, a result will be printed depending on the role of the program, as outlined below.
1. Removing Inline Comments (Tutor Demo)
Complete remove_inline_comments so that it removes an inline // comment from line, if one is present.
// mini_linter_comments.c
//
// Written by YOUR-NAME (YOUR-ZID)
// on TODAYS-DATE
//
// Tutor demo: Removes inline comments from a line of code.
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 161
void remove_inline_comments(char *line);
void remove_newline(char *line);
// -----------------------------------------------------------------------------
int main(void) {
char line[MAX_LINE_LENGTH];
printf("Enter lines of code:\n");
while (fgets(line, MAX_LINE_LENGTH, stdin) != NULL) {
remove_newline(line);
remove_inline_comments(line);
printf("%s\n", line);
}
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTION
// -----------------------------------------------------------------------------
// Removes any inline comments from the provided line
void remove_inline_comments(char *line) {
// TODO: Follow along as your tutor demonstrates this function
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
// Helper function to remove the newline character from the end of a string
void remove_newline(char *line) {
int i = 0;
while (line[i] != '\n' && line[i] != '\0') {
i++;
}
line[i] = '\0';
}
Example
dcc mini_linter_comments.c -o mini_linter_comments ./mini_linter_comments Enter lines of code: int flag = 1; // this is a flag int flag = 1; int total = 0; int total = 0; [Ctrl-D]
2. Adding Missing Semicolons
Complete add_semicolon so that it adds a semicolon to the end of line, if one isn't already there.
// mini_linter_semicolon.c
//
// Written by YOUR-NAME (YOUR-ZID)
// on TODAYS-DATE
//
// Adds missing semicolons to lines of code.
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 161
void add_semicolon(char *line);
void remove_newline(char *line);
// -----------------------------------------------------------------------------
int main(void) {
char line[MAX_LINE_LENGTH];
printf("Enter lines of code:\n");
while (fgets(line, MAX_LINE_LENGTH, stdin) != NULL) {
remove_newline(line);
add_semicolon(line);
printf("%s\n", line);
}
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTION
// -----------------------------------------------------------------------------
// Adds any missing semicolon to the end of the provided line.
void add_semicolon(char *line) {
// TODO: Complete this function
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
// Helper function to remove the newline character from the end of a string
void remove_newline(char *line) {
int i = 0;
while (line[i] != '\n' && line[i] != '\0') {
i++;
}
line[i] = '\0';
}
Example
dcc mini_linter_semicolon.c -o mini_linter_semicolon ./mini_linter_semicolon Enter lines of code: int x = 5 int x = 5; int y = 10; int y = 10; [Ctrl-D]
3. Removing Trailing Spaces
Complete remove_trailing_spaces so that it removes any trailing spaces from the end of line.
// mini_linter_trailing_spaces.c
//
// Written by YOUR-NAME (YOUR-ZID)
// on TODAYS-DATE
//
// Removes trailing spaces from lines of code.
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 161
void remove_trailing_spaces(char *line);
void remove_newline(char *line);
// -----------------------------------------------------------------------------
int main(void) {
char line[MAX_LINE_LENGTH];
printf("Enter lines of code:\n");
while (fgets(line, MAX_LINE_LENGTH, stdin) != NULL) {
remove_newline(line);
remove_trailing_spaces(line);
printf("%s\n", line);
}
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTION
// -----------------------------------------------------------------------------
// Removes any trailing spaces from the provided line.
void remove_trailing_spaces(char *line) {
// TODO: Complete this function
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
// Helper function to remove the newline character from the end of a string
void remove_newline(char *line) {
int i = 0;
while (line[i] != '\n' && line[i] != '\0') {
i++;
}
line[i] = '\0';
}
Example
dcc mini_linter_trailing_spaces.c -o mini_linter_trailing_spaces ./mini_linter_trailing_spaces Enter lines of code: int y = 10; int y = 10; int z = 20; int z = 20; [Ctrl-D]
4. Checking Operator Whitespace
Complete missing_operand_whitespace so that it returns 1 if any of the operators +, -, *, /, % or = in line is missing whitespace on either side of it, and 0 otherwise.
// mini_linter_operator_whitespace.c
//
// Written by YOUR-NAME (YOUR-ZID)
// on TODAYS-DATE
//
// Detects operators without whitespace around them.
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 161
int missing_operand_whitespace(char *line);
void remove_newline(char *line);
// -----------------------------------------------------------------------------
int main(void) {
char line[MAX_LINE_LENGTH];
printf("Enter lines of code:\n");
while (fgets(line, MAX_LINE_LENGTH, stdin) != NULL) {
remove_newline(line);
if (missing_operand_whitespace(line)) {
printf("Missing whitespace around an operator!\n");
} else {
printf("No issues found.\n");
}
}
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTION
// -----------------------------------------------------------------------------
// Checks if any operands (* - + / % =) have any missing whitespace around them.
// Returns 1 if there is missing whitespace, and 0 otherwise.
int missing_operand_whitespace(char *line) {
// TODO: Complete this function
return 0;
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
// Helper function to remove the newline character from the end of a string
void remove_newline(char *line) {
int i = 0;
while (line[i] != '\n' && line[i] != '\0') {
i++;
}
line[i] = '\0';
}
Example
dcc mini_linter_operator_whitespace.c -o mini_linter_operator_whitespace ./mini_linter_operator_whitespace Enter lines of code: int total = a+b; Missing whitespace around an operator! int total =a + b; Missing whitespace around an operator! int total = a + b; No issues found. [Ctrl-D]
5. Splitting Long Lines
Complete split_long_line so that, if line is longer than 80 characters, it's split into two lines by replacing the closest space at or before position 80 with a newline character.
// mini_linter_split_line.c
//
// Written by YOUR-NAME (YOUR-ZID)
// on TODAYS-DATE
//
// Splits long lines over 80 characters.
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 161
void split_long_line(char *line);
void remove_newline(char *line);
// -----------------------------------------------------------------------------
int main(void) {
char line[MAX_LINE_LENGTH];
printf("Enter lines of code:\n");
while (fgets(line, MAX_LINE_LENGTH, stdin) != NULL) {
remove_newline(line);
split_long_line(line);
printf("%s\n", line);
}
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTION
// -----------------------------------------------------------------------------
// Splits long lines by replacing the closest whitespace before 80 characters
// with a newline character.
void split_long_line(char *line) {
// TODO: Complete this function
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
// Helper function to remove the newline character from the end of a string
void remove_newline(char *line) {
int i = 0;
while (line[i] != '\n' && line[i] != '\0') {
i++;
}
line[i] = '\0';
}
Example
dcc mini_linter_split_line.c -o mini_linter_split_line ./mini_linter_split_line Enter lines of code: int total = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16; int total = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16; short line; short line; [Ctrl-D]
Assumptions/Restrictions/Clarifications
- No error checking is required.
- You may assume no line will ever be longer than 160 characters.
missing_operand_whitespaceonly needs to check the operators+,-,*,/,%and=.- For
split_long_line, you may assume there will always be a space at or before position 80 inline. - All programs that are required to modify the provided
lineshould make these modifications in place. This meanslineitself should be modified, without creating any copies.
Exercise
(●◌◌)
:
Sea Creature Sanctuary
Download sea_creatures.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity sea_creatures
The ocean research centre needs a program to track the sea creatures its divers discover. Since creatures can be added and released at any time, they need a home in memory that isn't fixed in place. malloc lets you create exactly that!
Your task is to complete sea_creatures.c by implementing two functions:
create_creature: Creates a newstruct creatureusingmalloc, initialising itsnameandsizefields with the provided arguments, then returns a pointer to the new creature.release_creature: Frees the memory used by a creature when it is no longer needed.
Example
dcc sea_creatures.c -o sea_creatures --leak-check ./sea_creatures Enter creature name: Blue Whale Enter creature size: 2500 Creating creature... Creature details: Name: Blue Whale Size: 2500 Releasing creature...
Assumptions/Restrictions/Clarifications
- No error checking is required.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest sea_creatures
sea_creatures.c
// sea_creature.c
//
// Written by Holly Field (z5477436)
// on 17/07/26
//
// Introductory activity to learn malloc and free.
// Creates and releases sea creatures.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NAME_LENGTH 64
struct creature {
char name[MAX_NAME_LENGTH];
int size;
};
struct creature *create_creature(char name[], int size);
void release_creature(struct creature *creature);
void print_creature(struct creature *creature);
void remove_newline(char input[MAX_NAME_LENGTH]);
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE THE MAIN FUNCTION ///////////////////////////////
int main(void) {
char name[MAX_NAME_LENGTH];
int size;
printf("Enter creature name: ");
fgets(name, MAX_NAME_LENGTH, stdin);
remove_newline(name);
printf("Enter creature size: ");
scanf("%d", &size);
printf("\nCreating creature...\n");
struct creature *creature = create_creature(name, size);
printf("\nCreature details:\n");
print_creature(creature);
printf("\nReleasing creature...\n");
release_creature(creature);
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTIONS
// -----------------------------------------------------------------------------
// Creates and returns a pointer to a new creature by calling malloc
struct creature *create_creature(char name[], int size) {
struct creature *new_creature = malloc(sizeof(struct creature));
strcpy(new_creature->name, name);
new_creature->size = size;
return new_creature;
}
// Frees the creature by calling free
void release_creature(struct creature *creature) {
free(creature);
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
void print_creature(struct creature *creature) {
printf("Name: %s\n", creature->name);
printf("Size: %d\n", creature->size);
}
void remove_newline(char input[MAX_NAME_LENGTH]) {
int index = 0;
while (input[index] != '\n' && input[index] != '\0') {
index++;
}
input[index] = '\0';
}
Exercise
(●◌◌)
:
Debugging Exercise -- Broken Inventory
Download broken_inventory.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity broken_inventory
Your task is to find and fix the bugs in broken_inventory.c so that the inventory system works as intended, without crashing and without leaking memory.
HINT: There will be a bug in each of the functions:
create_item: creates a new item and returns a pointer to it.append_item: creates and appends a new item to the inventory and returns the updated head of the inventory list.use_item: "uses" an item that matches the given name by decreasing its quantity by 1. If the quantity reaches 0, the item is removed from the inventory.free_items: frees all memory associated with the inventory.
Debugging Tips
Some debugging tips for you:
dccoutput: as you run into issues, dcc will point you to where the errors are. It gives you the line number the issue is on along with some explanation, so make sure you read everything it tells you.- Not every bug is a compiler error: a program can compile with zero warnings and still crash, or leak memory. If your program runs but crashes partway through, trace through the code by hand to work out exactly which line causes it.
--leak-check: always compile with this flag for this activity. A leak won't crash your program or produce any visibly wrong output — it will only show up as a leak-check warning at the end of a run.- COMP1511 debugging guide
Examples
dcc broken_inventory.c -o broken_inventory --leak-check
./broken_inventory
== Starting inventory ==
====== Inventory ======
Potion x3
Sword x1
Using the Potion.
====== Inventory ======
Potion x2
Sword x1
Using the Sword.
====== Inventory ======
Potion x2
Assumptions/Restrictions/Clarifications
- You should not need to modify
mainorprint_inventory.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest broken_inventory
broken_inventory.c
// broken_inventory.c
//
// This program was written by Holly Field (z5477436)
// on 17/07/26
//
// Manages a simple game inventory as a linked list of items.
// The game keeps crashing! Find and fix the bugs below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NAME_LENGTH 20
struct item {
char name[MAX_NAME_LENGTH];
int quantity;
struct item *next;
};
struct item *create_item(char *name, int quantity);
struct item *append_item(struct item *inventory, char *name, int quantity);
struct item *use_item(struct item *inventory, char *name);
void print_inventory(struct item *inventory);
void free_inventory(struct item *inventory);
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE THE MAIN FUNCTION ///////////////////////////
int main(void) {
struct item *inventory = NULL;
inventory = append_item(inventory, "Potion", 3);
inventory = append_item(inventory, "Sword", 1);
printf("== Starting inventory ==\n");
print_inventory(inventory);
printf("\nUsing the Potion.\n");
inventory = use_item(inventory, "Potion");
print_inventory(inventory);
printf("\nUsing the Sword.\n");
inventory = use_item(inventory, "Sword");
print_inventory(inventory);
free_inventory(inventory);
return 0;
}
// ---------------------------------------------------------------------------
// BUGGY FUNCTIONS - FIX THESE
// ---------------------------------------------------------------------------
// Creates a new item and adds it to the front of the inventory list.
struct item *create_item(char *name, int quantity) {
// BUG FIX: new_item was a local (stack) variable, so its address
// became invalid the moment this function returned. Heap-allocate it
// with malloc instead, so it stays valid after we return.
struct item *new_item = malloc(sizeof(struct item));
strcpy(new_item->name, name);
new_item->quantity = quantity;
new_item->next = NULL;
return new_item;
}
// Adds a new item to the inventory and returns the updated inventory.
struct item *append_item(struct item *inventory, char *name, int quantity) {
struct item *new_item = create_item(name, quantity);
// BUG FIX: was not handling the case where inventory is NULL (empty list).
// This lead to a crash when trying to access current->next
if (inventory == NULL) {
return new_item;
}
struct item *current = inventory;
while (current->next != NULL) {
current = current->next;
}
current->next = new_item;
return inventory;
}
// Decreases the quantity of the item with the given name by 1.
// If the quantity reaches 0, the item is removed from the inventory.
struct item *use_item(struct item *inventory, char *name) {
struct item *current = inventory;
struct item *prev = NULL;
while (current != NULL && strcmp(current->name, name) != 0) {
prev = current;
current = current->next;
}
current->quantity--;
if (current->quantity <= 0) {
// BUG FIX: current was being freed before current->next
// was read, so the two lines below were reading from
// memory that had already been freed. Relink the list
// first, then free current afterwards.
if (prev == NULL) {
inventory = current->next;
} else {
prev->next = current->next;
}
free(current);
}
return inventory;
}
// Free all items in the inventory.
void free_inventory(struct item *inventory) {
struct item *current = inventory;
while (current != NULL) {
// BUG FIX: was only freeing the first item in the list
struct item *next = current->next;
free(current);
current = next;
}
}
// ---------------------------------------------------------------------------
// PROVIDED FUNCTIONS - DO NOT CHANGE THESE
// ---------------------------------------------------------------------------
void print_inventory(struct item *inventory) {
printf("====== Inventory ======\n");
struct item *current = inventory;
if (current == NULL) {
printf(" (empty)\n");
return;
}
while (current != NULL) {
printf(" %s x%d\n", current->name, current->quantity);
current = current->next;
}
}
Exercise
(●◌◌)
:
Calm Down!
Download calm_down.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity calm_down
Sometimes a message could really use fewer exclamation marks!!! Your job is to write a "calm down" filter that tones down an overly enthusiastic message.
Complete the program calm_down.c, which:
- Prompts the user to enter a string with
Enter a message:. - Scans in the string from stdin.
- Replaces every
'!'character with a'.', modifying the string in place. - Prints the modified string to stdout.
Example
dcc calm_down.c -o calm_down ./calm_down Enter a message: This is amazing!!! I can't believe it!! This is amazing... I can't believe it..
Assumptions/Restrictions/Clarifications
- No error checking is required.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest calm_down
calm_down.c
// calm_down.c
//
// Written by Holly Field (z5477436)
// on 17/07/26
//
// Calms down an overly enthusiastic message by replacing every '!' with
// a '.'.
#include <stdio.h>
#include <string.h>
#define MAX_LENGTH 200
void remove_newline(char input[MAX_LENGTH]);
// ----------------------------------------------------------------------------
int main(void) {
char message[MAX_LENGTH];
fgets(message, MAX_LENGTH, stdin);
remove_newline(message);
// Replace every '!' with a '.'
for (int i = 0; i < strlen(message); i++) {
if (message[i] == '!') {
message[i] = '.';
}
}
printf("%s\n", message);
return 0;
}
// ----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// ----------------------------------------------------------------------------
// Helper function to remove the newline character from the end of a string
void remove_newline(char input[MAX_LENGTH]) {
int length = strlen(input);
if (length > 0 && input[length - 1] == '\n') {
input[length - 1] = '\0';
}
}
Exercise
(●●◌)
:
Sum of Positive and Negative Numbers
Download sum_of_positive_negative.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity sum_of_positive_negative
Your task is to implement the function sum_of_positive_negative in the provided starter code sum_of_positive_negative.c.
The function should:
- Calculate the sum of all positive numbers in the linked list, and store it in
*positive_sum. - Calculate the sum of all negative numbers in the linked list, and store it in
*negative_sum.
The construction of the linked list from command line arguments has been provided for you.
Examples
dcc sum_of_positive_negative.c -o sum_of_positive_negative ./sum_of_positive_negative 1 -2 3 -4 5 LIST: 1 -> -2 -> 3 -> -4 -> 5 -> NULL Positive sum: 9 Negative sum: -6 ./sum_of_positive_negative 8 10 4 2 1 LIST: 8 -> 10 -> 4 -> 2 -> 1 -> NULL Positive sum: 25 Negative sum: 0 ./sum_of_positive_negative -5 -1 -3 -4 LIST: -5 -> -1 -> -3 -> -4 -> NULL Positive sum: 0 Negative sum: -13
Assumptions/Restrictions/Clarifications
- If the linked list is empty, both
positive_sumandnegative_sumshould be 0.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest sum_of_positive_negative
sum_of_positive_negative.c
// sum_of_positive_negative.c
//
// Written by Holly Field (z5477436)
// on 17/07/26
//
// Calculates the sum of positive and negative numbers in an array.
#include <stdio.h>
#include <stdlib.h>
// DO NOT CHANGE THIS STRUCT
struct node {
struct node *next;
int data;
};
// MODIFY THIS FUNCTION
// sum_of_positive_negative takes in the head of a linked list and calculates
// the sum of positive and negative numbers
void sum_of_positive_negative(
struct node *head,
int *positive_sum,
int *negative_sum
) {
*positive_sum = 0;
*negative_sum = 0;
struct node *current = head;
while (current != NULL) {
if (current->data > 0) {
*positive_sum += current->data;
} else if (current->data < 0) {
*negative_sum += current->data;
}
current = current->next;
}
}
////////////////////////////////////////////////////////////////////////
// DO NOT CHANGE THESE PROTOTYPES
////////////////////////////////////////////////////////////////////////
struct node *args_to_list(int argc, char *argv[]);
void print_list(struct node *head);
void free_list(struct node *head);
// DO NOT CHANGE THIS FUNCTION
int main(int argc, char *argv[]) {
struct node *head = args_to_list(argc, argv);
printf("LIST: ");
print_list(head);
int positive_sum;
int negative_sum;
sum_of_positive_negative(head, &positive_sum, &negative_sum);
printf("Positive sum: %d\n", positive_sum);
printf("Negative sum: %d\n", negative_sum);
free_list(head);
return 0;
}
// DO NOT CHANGE THIS FUNCTION
// Converts command-line args to a linked list of characters
struct node *args_to_list(int argc, char *argv[]) {
struct node *head = NULL;
for (int i = argc - 1; i >= 1; i--) {
struct node *new = malloc(sizeof(struct node));
new->data = atoi(argv[i]);
new->next = head;
head = new;
}
return head;
}
// DO NOT CHANGE THIS FUNCTION
// Prints a linked list of integers
void print_list(struct node *head) {
struct node *curr = head;
if (curr == NULL) {
printf("NULL\n");
return;
}
while (curr != NULL) {
printf("%d", curr->data);
if (curr->next != NULL) {
printf(" -> ");
}
curr = curr->next;
}
printf(" -> NULL\n");
}
void free_list(struct node *head) {
struct node *curr = head;
while (curr != NULL) {
struct node *to_free = curr;
curr = curr->next;
free(to_free);
}
}
Exercise
(●●◌)
:
Route Setter
Download route_setter.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity route_setter
Your task is to complete the program route_setter.c using the provided starter code.
A main function and a series of helper functions have been implemented for you. The program allows a climber to interactively build and analyse a bouldering route using commands. However, the functions that manipulate the linked list are currently incomplete.
You must implement the following functions:
-
append_hold- Add a new hold at the end of the route.
- The new hold should store the provided
new_hold_idanddifficulty.
-
insert_after_hold- Add a new hold into the route immediately after the hold with the given
hold_id. - The new hold should store the provided
new_hold_idanddifficulty.
- Add a new hold into the route immediately after the hold with the given
-
remove_hold- Remove the hold with the given
hold_idfrom the route. - Remember to free any memory that is no longer needed.
- Remove the hold with the given
-
find_crux- Find and return a pointer to the hardest hold in the route.
- The crux is the hold with the highest difficulty.
-
climber_progress- Given a climber's skill level, determine how many holds they can complete in order before encountering a hold that is too difficult.
- A climber can complete a hold if its difficulty is less than or equal to their skill level.
- Return the number of holds completed.
The provided create_hold, print_route, print_commands, and free_route functions handle creating, displaying, and freeing the linked list for you.
Example
dcc route_setter.c -o route_setter ./route_setter Welcome to Route Setter! a 1 2 a 2 4 a 3 7 a 4 3 p ==== Current Route ==== Hold 1 (difficulty 2) Hold 2 (difficulty 4) Hold 3 (difficulty 7) Hold 4 (difficulty 3) i 2 99 5 p ==== Current Route ==== Hold 1 (difficulty 2) Hold 2 (difficulty 4) Hold 99 (difficulty 5) Hold 3 (difficulty 7) Hold 4 (difficulty 3) r 3 p ==== Current Route ==== Hold 1 (difficulty 2) Hold 2 (difficulty 4) Hold 99 (difficulty 5) Hold 4 (difficulty 3) x Crux hold is 99 (difficulty 5) c 4 A climber with skill 4 can climb 2 holds before failing. q
Assumptions/Restrictions/Clarifications
- No error checking is required.
- You may assume that all ids are unique.
- You may assume that all commands are valid.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest route_setter
route_setter.c
// route_setter.c
//
// Written by Holly Field (z5477436)
// on 17/07/2026
//
// A linked list representation of a bouldering route.
#include <stdio.h>
#include <stdlib.h>
// Struct representing a hold in a climbing route
struct hold {
int hold_id;
int difficulty;
struct hold *next;
};
// Your function prototypes
struct hold *append_hold(struct hold *route, int new_hold_id, int difficulty);
struct hold *insert_after_hold(struct hold *route, int hold_id, int new_hold_id,
int difficulty);
struct hold *remove_hold(struct hold *route, int hold_id);
struct hold *find_crux(struct hold *route);
int climber_progress(struct hold *route, int skill);
// Provided function prototypes
struct hold *create_hold(int hold_id, int difficulty);
void print_route(struct hold *route);
void free_route(struct hold *route);
void print_commands();
void command_loop();
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE THE MAIN FUNCTION ///////////////////////////////
int main(void) {
// Create a sample route with 4 holds
printf("Welcome to Route Setter!\n");
command_loop();
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTIONS
// -----------------------------------------------------------------------------
// Append a new hold to the end of the route.
struct hold *append_hold(struct hold *route, int new_hold_id, int difficulty) {
struct hold *new_hold = create_hold(new_hold_id, difficulty);
if (route == NULL) {
return new_hold;
}
struct hold *current = route;
while (current->next != NULL) {
current = current->next;
}
current->next = new_hold;
return route;
}
// Insert a new hold after the hold with hold_id.
struct hold *insert_after_hold(struct hold *route, int hold_id, int new_hold_id,
int difficulty) {
struct hold *current = route;
while (current != NULL) {
if (current->hold_id == hold_id) {
struct hold *new_hold = create_hold(new_hold_id, difficulty);
new_hold->next = current->next;
current->next = new_hold;
return route;
}
current = current->next;
}
return route;
}
// Remove the hold with hold_id from the route.
struct hold *remove_hold(struct hold *route, int hold_id) {
if (route == NULL) {
return NULL;
}
if (route->hold_id == hold_id) {
struct hold *new_head = route->next;
free(route);
return new_head;
}
struct hold *current = route;
while (current->next != NULL) {
if (current->next->hold_id == hold_id) {
struct hold *to_remove = current->next;
current->next = to_remove->next;
free(to_remove);
return route;
}
current = current->next;
}
return route;
}
// Return a pointer to the hardest hold in the route.
struct hold *find_crux(struct hold *route) {
if (route == NULL) {
return NULL;
}
struct hold *crux = route;
struct hold *current = route->next;
while (current != NULL) {
if (current->difficulty > crux->difficulty) {
crux = current;
}
current = current->next;
}
return crux;
}
// Return how many holds a climber can complete.
int climber_progress(struct hold *route, int skill) {
int count = 0;
struct hold *current = route;
while (current != NULL) {
if (current->difficulty <= skill) {
count++;
current = current->next;
} else {
return count;
}
}
return count;
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
// DO NOT CHANGE ANY OF THE CODE BELOW HERE
// Creates a new hold and returns a pointer to it.
struct hold *create_hold(int hold_id, int difficulty) {
struct hold *new_hold = malloc(sizeof(struct hold));
new_hold->hold_id = hold_id;
new_hold->difficulty = difficulty;
new_hold->next = NULL;
return new_hold;
}
// Prints the entire route, one hold per line.
void print_route(struct hold *route) {
printf("==== Current Route ====\n");
if (route == NULL) {
printf("Route is empty.\n");
return;
}
while (route != NULL) {
printf("Hold %d (difficulty %d)\n", route->hold_id, route->difficulty);
route = route->next;
}
}
// Frees every hold in the route.
void free_route(struct hold *route) {
while (route != NULL) {
struct hold *next = route->next;
free(route);
route = next;
}
}
// Print the available commands for the user.
void print_commands() {
printf("Commands:\n");
printf(" a <new_id> <difficulty> - append hold to the route\n");
printf(" i <after_id> <new_id> <difficulty> - insert hold\n");
printf(" r <hold_id> - remove hold\n");
printf(" p - print route\n");
printf(" c <skill> - climber progress\n");
printf(" x - find crux\n");
printf(" q - quit\n\n");
}
// Command loop to process user input.
void command_loop() {
char command;
struct hold *route = NULL;
while (scanf(" %c", &command) == 1 && command != 'q') {
if (command == 'a') {
int new_id;
int difficulty;
scanf("%d %d", &new_id, &difficulty);
route = append_hold(route, new_id, difficulty);
} else if (command == 'i') {
int after_id;
int new_id;
int difficulty;
scanf("%d %d %d", &after_id, &new_id, &difficulty);
route = insert_after_hold(route, after_id, new_id, difficulty);
} else if (command == 'p') {
print_route(route);
} else if (command == 'r') {
int hold_id;
scanf("%d", &hold_id);
route = remove_hold(route, hold_id);
} else if (command == 'c') {
int skill;
scanf("%d", &skill);
printf(
"A climber with skill %d can climb %d holds before failing.\n",
skill, climber_progress(route, skill)
);
} else if (command == 'x') {
struct hold *crux = find_crux(route);
if (crux != NULL) {
printf(
"Crux hold is %d (difficulty %d)\n",
crux->hold_id, crux->difficulty
);
} else {
printf("No crux hold found.\n");
}
} else if (command == '?') {
print_commands();
}
}
free_route(route);
}
Exercise
(●●●)
:
Extension -- Chaotic Route Setter
Download chaotic_route_setter.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity chaotic_route_setter
Your task is to complete the program chaotic_route_setter.c by extending your solution to route_setter.c to implement a new shuffle route command.
The shuffle route command is called when the user types s into the command loop, with no additional arguments. When called, the program should:
- Split the climb into two halves: the first half and the second half.
- Recombine the two halves by zipping them together. This means alternating one hold from the first half, then one hold from the second half, repeating until both halves are exhausted.
For example, given the climb [1, 2, 3, 4, 5, 6], the first half is [1, 2, 3] and the second half is [4, 5, 6]. Zipping them together produces [1, 4, 2, 5, 3, 6].
As an extra challenge, attempt this task without allocating any new memory.
The provided starter code, chaotic_route_setter.c, is mostly the same as route_setter.c, but with some minor changes:
- The
command_loopfunction will need to be modified to recognise the shuffle route command. - There is an added function stub and prototype for
shuffle_routeto implement the shuffling logic. You may want to use this starter code, and copy over your function implementations fromroute_setter.c.
Example
dcc chaotic_route_setter.c -o chaotic_route_setter ./chaotic_route_setter Welcome to Route Setter! a 1 1 a 2 2 a 3 3 a 4 4 a 5 5 a 6 6 p ==== Current Route ==== Hold 1 (difficulty 1) Hold 2 (difficulty 2) Hold 3 (difficulty 3) Hold 4 (difficulty 4) Hold 5 (difficulty 5) Hold 6 (difficulty 6) s p ==== Current Route ==== Hold 1 (difficulty 1) Hold 4 (difficulty 4) Hold 2 (difficulty 2) Hold 5 (difficulty 5) Hold 3 (difficulty 3) Hold 6 (difficulty 6) q
Assumptions/Restrictions/Clarifications
- If a route contains an odd number of holds, the first half should contain one more hold than the second half.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest chaotic_route_setter
chaotic_route_setter.c
// chaotic_route_setter.c
//
// Written by Holly Field (z5477436)
// on 17/07/2026
//
// An extension of the route_setter activity that adds a new command to
// shuffle the route.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// Struct representing a hold in a climbing route
struct hold {
int hold_id;
int difficulty;
struct hold *next;
};
// Your function prototypes
struct hold *append_hold(struct hold *route, int new_hold_id, int difficulty);
struct hold *insert_after_hold(struct hold *route, int hold_id, int new_hold_id,
int difficulty);
struct hold *remove_hold(struct hold *route, int hold_id);
struct hold *find_crux(struct hold *route);
int climber_progress(struct hold *route, int skill);
struct hold *shuffle_route(struct hold *route);
// Provided function prototypes
struct hold *create_hold(int hold_id, int difficulty);
void print_route(struct hold *route);
void free_route(struct hold *route);
void print_commands();
void command_loop();
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE THE MAIN FUNCTION ///////////////////////////////
int main(void) {
// Create a sample route with 4 holds
printf("Welcome to Route Setter!\n");
command_loop();
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTIONS
// -----------------------------------------------------------------------------
// Append a new hold to the end of the route.
struct hold *append_hold(struct hold *route, int new_hold_id, int difficulty) {
struct hold *new_hold = create_hold(new_hold_id, difficulty);
if (route == NULL) {
return new_hold;
}
struct hold *current = route;
while (current->next != NULL) {
current = current->next;
}
current->next = new_hold;
return route;
}
// Insert a new hold after the hold with hold_id.
struct hold *insert_after_hold(struct hold *route, int hold_id, int new_hold_id,
int difficulty) {
struct hold *current = route;
while (current != NULL) {
if (current->hold_id == hold_id) {
struct hold *new_hold = create_hold(new_hold_id, difficulty);
new_hold->next = current->next;
current->next = new_hold;
return route;
}
current = current->next;
}
return route;
}
// Remove the hold with hold_id from the route.
struct hold *remove_hold(struct hold *route, int hold_id) {
if (route == NULL) {
return NULL;
}
if (route->hold_id == hold_id) {
struct hold *new_head = route->next;
free(route);
return new_head;
}
struct hold *current = route;
while (current->next != NULL) {
if (current->next->hold_id == hold_id) {
struct hold *to_remove = current->next;
current->next = to_remove->next;
free(to_remove);
return route;
}
current = current->next;
}
return route;
}
// Return a pointer to the hardest hold in the route.
struct hold *find_crux(struct hold *route) {
if (route == NULL) {
return NULL;
}
struct hold *crux = route;
struct hold *current = route->next;
while (current != NULL) {
if (current->difficulty > crux->difficulty) {
crux = current;
}
current = current->next;
}
return crux;
}
// Return how many holds a climber can complete.
int climber_progress(struct hold *route, int skill) {
int count = 0;
struct hold *current = route;
while (current != NULL) {
if (current->difficulty <= skill) {
count++;
current = current->next;
} else {
return count;
}
}
return count;
}
// Shuffles the route by splitting it into two halves and zipping them together.
// If the route has an odd number of holds, the first half will have one more
// holds than the second half.
struct hold *shuffle_route(struct hold *route) {
if (route == NULL || route->next == NULL) {
return route;
}
// Find the size
int size = 0;
struct hold *current = route;
while (current != NULL) {
size++;
current = current->next;
}
// Find the last node of the first half
int first_half_size = (size + 1) / 2;
struct hold *first_tail = route;
for (int i = 1; i < first_half_size; i++) {
first_tail = first_tail->next;
}
// Split into two lists
struct hold *second = first_tail->next;
first_tail->next = NULL;
struct hold *first = route;
// Zip the two lists together
while (second != NULL) {
struct hold *first_next = first->next;
struct hold *second_next = second->next;
first->next = second;
second->next = first_next;
first = first_next;
second = second_next;
}
return route;
}
// HINT: You will need to edit the command loop to add the "shuffle" command.
// Command loop to process user input.
void command_loop() {
char command;
struct hold *route = NULL;
while (scanf(" %c", &command) == 1 && command != 'q') {
if (command == 'a') {
int new_id;
int difficulty;
scanf("%d %d", &new_id, &difficulty);
route = append_hold(route, new_id, difficulty);
} else if (command == 'i') {
int after_id;
int new_id;
int difficulty;
scanf("%d %d %d", &after_id, &new_id, &difficulty);
route = insert_after_hold(route, after_id, new_id, difficulty);
} else if (command == 'p') {
print_route(route);
} else if (command == 'r') {
int hold_id;
scanf("%d", &hold_id);
route = remove_hold(route, hold_id);
} else if (command == 'c') {
int skill;
scanf("%d", &skill);
printf(
"A climber with skill %d can climb %d holds before failing.\n",
skill, climber_progress(route, skill)
);
} else if (command == 'x') {
struct hold *crux = find_crux(route);
if (crux != NULL) {
printf(
"Crux hold is %d (difficulty %d)\n",
crux->hold_id, crux->difficulty
);
} else {
printf("No crux hold found.\n");
}
} else if (command == '?') {
print_commands();
} else if (command == 's') {
route = shuffle_route(route);
}
}
free_route(route);
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
// DO NOT CHANGE ANY OF THE CODE BELOW HERE
// Creates a new hold and returns a pointer to it.
struct hold *create_hold(int hold_id, int difficulty) {
struct hold *new_hold = malloc(sizeof(struct hold));
new_hold->hold_id = hold_id;
new_hold->difficulty = difficulty;
new_hold->next = NULL;
return new_hold;
}
// Prints the entire route, one hold per line.
void print_route(struct hold *route) {
printf("==== Current Route ====\n");
if (route == NULL) {
printf("Route is empty.\n");
return;
}
while (route != NULL) {
printf("Hold %d (difficulty %d)\n", route->hold_id, route->difficulty);
route = route->next;
}
}
// Frees every hold in the route.
void free_route(struct hold *route) {
while (route != NULL) {
struct hold *next = route->next;
free(route);
route = next;
}
}
// Print the available commands for the user.
void print_commands() {
printf("Commands:\n");
printf(" a <new_id> <difficulty> - append hold to the route\n");
printf(" i <after_id> <new_id> <difficulty> - insert hold\n");
printf(" r <hold_id> - remove hold\n");
printf(" p - print route\n");
printf(" c <skill> - climber progress\n");
printf(" x - find crux\n");
printf(" s - shuffle route\n");
printf(" q - quit\n\n");
}
Exercise
(●●●)
:
Extension -- Advanced Linter
Download advanced_linter.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity advanced_linter
This activity extends the mini linter exercise from the revision session.
Previously, your linter could perform a single modification to a line of code. Your task is now to implement a more capable linter by completing several string manipulation functions.
The program repeatedly reads lines of code from standard input until [CTRL-D] is entered.
For every line, the following functions should be applied in order:
collapse_whitespace- Replace consecutive spaces with a single space.
- It should not collapse any leading spaces (at the start of the line) as this may be valid indentation.
add_operator_spacing- Ensure every
=,+,-,*and/has a single space on either side.
- Ensure every
wrap_long_lines- If a line exceeds 80 characters, replace the last whitespace before the 80th character with a newline followed by a tab character (
'\n'then'\t'). - Continue wrapping until every line is at most 80 characters long.
- If a line exceeds 80 characters, replace the last whitespace before the 80th character with a newline followed by a tab character (
The provided print_result function will display the final linted line.
Example
dcc advanced_linter.c -o advanced_linter
./advanced_linter
Enter lines of code:
int x = 5;
int x = 5;
a =b+c*d/e-f
a = b + c * d / e - f
this is a deliberately very long line that should be wrapped multiple times because it contains far more than eighty characters before reaching the end of the sentence now we go again to show multi wrap this is a deliberately very long line that should be wrapped multiple times because it contains far more than eighty characters before reaching the end of the sentence
this is a deliberately very long line that should be wrapped multiple times
because it contains far more than eighty characters before reaching the end of the
sentence now we go again to show multi wrap this is a deliberately very long
line that should be wrapped multiple times because it contains far more than
eighty characters before reaching the end of the sentence
int total=apples+oranges+bananas+pears+grapes+watermelons+pineapples;
int total = apples + oranges + bananas + pears + grapes + watermelons +
pineapples;
[CTRL-D]
Assumptions/Restrictions/Clarifications
- You may assume every input line is at most 1000 characters long.
- You may assume that for
wrap_long_lines, there will always be a whitespace before the 80th character. - You may assume operators never appear at the beginning or end of a line.
- No error checking is required.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest advanced_linter
advanced_linter.c
// advanced_linter.c
//
// Written by Holly Field (z5477436)
// on 17/07/2026
//
// A mini linter that applies multiple formatting transformations to code lines.
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 1024
// Your function prototypes
void collapse_whitespace(char *line);
void add_operator_spacing(char *line);
void wrap_long_lines(char *line);
// Added helper function prototypes
void shift_right(char *line, int start);
void shift_left(char *line, int start);
int is_operator(char c);
// Provided function prototypes
void lint_line(char *line);
void remove_newline(char *line);
// -----------------------------------------------------------------------------
// DO NOT CHANGE THE MAIN FUNCTION
int main(void) {
char line[MAX_LINE_LENGTH];
printf("Enter lines of code:\n");
while (fgets(line, MAX_LINE_LENGTH, stdin) != NULL) {
remove_newline(line);
lint_line(line);
printf("%s\n", line);
}
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTIONS
// -----------------------------------------------------------------------------
// Replace multiple spaces with a single space.
void collapse_whitespace(char *line) {
int i = 0;
int seen_first_non_space = 0;
while (line[i] != '\0') {
if (line[i] == ' ' && line[i + 1] == ' ' && seen_first_non_space) {
shift_left(line, i);
} else {
if (line[i] != ' ') {
seen_first_non_space = 1;
}
i++;
}
}
}
// Add spaces around operators (= + - * /).
void add_operator_spacing(char *line) {
int i = 0;
while (line[i] != '\0') {
// Check if the current character is an operator
if (is_operator(line[i])) {
if (i > 0 && line[i - 1] != ' ') {
// Character before operator is not a space, insert a space
shift_right(line, i);
line[i] = ' ';
i++;
}
if (line[i + 1] != ' ') {
// Character after operator is not a space, insert a space
shift_right(line, i + 1);
line[i + 1] = ' ';
}
}
i++;
}
}
// Helper function to check if a character is an operator
int is_operator(char c) {
return (c == '=' || c == '+' || c == '-' || c == '*' || c == '/');
}
// Wrap lines longer than 80 characters so that there is at most
// 80 characters per line.
// All trailing lines should be indented with a tab character.
void wrap_long_lines(char *line) {
int length = strlen(line);
if (length <= 80) {
return;
}
int char_count = 0;
for (int i = 0; line[i] != '\0'; i++) {
if (char_count >= 80) {
// Find the last space before the 80th character by
// tracking back from the current index
int wrap_index = i;
while (wrap_index > 0 && line[wrap_index] != ' ') {
wrap_index--;
}
// Can assume there is always a space before the 80th character,
// so wrap_index will not be 0
// Shift the rest of the line to make space for a newline and tab
shift_right(line, wrap_index);
line[wrap_index] = '\n';
// The space after the newline can be replaced with a tab
line[wrap_index + 1] = '\t';
char_count = 0;
}
char_count++;
}
}
// Helper function to shift characters in a string to the right
// from index 'start' to the end of the string.
// This is useful for inserting characters into the string.
void shift_right(char *line, int start) {
int end = strlen(line);
for (int i = end; i >= start; i--) {
line[i + 1] = line[i];
}
}
// Helper function to shift characters in a string to the left
// from index 'start' to the end of the string.
// This is useful for removing characters from the string.
void shift_left(char *line, int start) {
int end = strlen(line);
for (int i = start; i < end; i++) {
line[i] = line[i + 1];
}
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
// Applies every linting rule in order.
void lint_line(char *line) {
collapse_whitespace(line);
add_operator_spacing(line);
wrap_long_lines(line);
}
// Helper function to remove the newline character from the end of a string
void remove_newline(char *line) {
// Find the newline or end of string
int i = 0;
while (line[i] != '\n' && line[i] != '\0') {
i++;
}
// Goto the last position in the array and replace with '\0'
// Note: will have no effect if already at null terminator
line[i] = '\0';
}
Exercise — individual:
Debugging Exercise -- Room Service
Download room_service.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity room_service
Before writing any code, your tutor will walk you through a short activity exploring the difference between functions that use pointers and functions that don't.
Your task is to fix any bugs in the starter code room_service.c so that the program works as intended.
The program should:
- Read in a number of snacks to deliver to Sasha and a number of snacks to deliver to Henry.
- Call
deliver()to add the snacks to each room. The function should remain as avoidfunction. - Print the new total number of snacks in each room.
Expected Output
dcc room_service.c -o room_service ./room_service Enter the number of snacks to deliver to Sasha's room: 3 Enter the number of snacks to deliver to Henry's room: 5 Sasha's room now has 5 snacks. Henry's room now has 9 snacks.
Assumptions/Restrictions/Clarifications
- No error checking is needed.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest room_service
room_service.c
// room_service.c
//
// This program was written by Holly Field (z5477436)
// on 16/07/26
//
// Delivers snacks to Sasha's and Henry's hotel rooms.
//
// This program should scan in a number of snacks to deliver to Sasha and
// a number of snacks to deliver to Henry, add them to the correct rooms,
// then print how many snacks are in each room.
//
// However, this program has a bug! Find and fix it so it works as
// intended.
#include <stdio.h>
// Deliver num_snacks snacks to the room at the given address.
void deliver(int *room, int num_snacks) {
*room += num_snacks;
}
int main(void) {
int sashas_room = 2;
int henrys_room = 4;
int num_snacks_for_sasha;
int num_snacks_for_henry;
printf("Enter the number of snacks to deliver to Sasha's room: ");
scanf("%d", &num_snacks_for_sasha);
printf("Enter the number of snacks to deliver to Henry's room: ");
scanf("%d", &num_snacks_for_henry);
deliver(&sashas_room, num_snacks_for_sasha);
deliver(&henrys_room, num_snacks_for_henry);
printf("Sasha's room now has %d snacks.\n", sashas_room);
printf("Henry's room now has %d snacks.\n", henrys_room);
return 0;
}
Exercise — individual:
Game Lag Reports
Download game_lags.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity game_lags
Your task is to complete game_lags.c using the provided starter code. Each command-line argument represents one recorded lag, given as its duration in milliseconds.
The program should:
- Prompt the user with
"Enter the maximum acceptable lag in milliseconds: "and read a single integer. Any recorded lag with a duration greater than or equal to this integer is considered a lag spike. - Count how many of the recorded lags are lag spikes.
- If there are no lag spikes, print
"No lag spikes recorded!\n"and return frommain. - Otherwise,
malloca new array to hold all the lag spikes. - Pass this array to the provided
average_lagandworst_lagfunctions to build a short report, printing:"Number of lag spikes: %d\n""Average lag spike duration: %.2f ms\n""Worst lag spike duration: %d ms\n"
- Free the array once you're done with it.
Examples
dcc game_lags.c -o game_lags ./game_lags 20 55 12 80 5 Enter the maximum acceptable lag in milliseconds: 30 Number of lag spikes: 2 Average lag spike duration: 67.50 ms Worst lag spike duration: 80 ms ./game_lags 5 10 15 Enter the maximum acceptable lag in milliseconds: 50 No lag spikes recorded!
Assumptions/Restrictions/Clarifications
- You may assume at least one lag duration will be provided as a command-line argument.
- You may assume every command-line argument is a valid non-negative integer.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest game_lags
game_lags.c
// game_lags.c
//
// Written by Holly Field (z5477436)
// on 16/07/26
//
// Builds a report of "lag spikes", which are the recorded frame lag durations
// (in milliseconds) that are at or above a given acceptable threshold.
#include <stdio.h>
#include <stdlib.h>
// Provided function prototypes
double average_lag(int *lags, int count);
int worst_lag(int *lags, int count);
// -----------------------------------------------------------------------------
int main(int argc, char *argv[]) {
int num_lags = argc - 1;
int goal;
printf("Enter the maximum acceptable lag in milliseconds: ");
scanf("%d", &goal);
// Count how many of the recorded lags are lag spikes (>= goal).
int num_spikes = 0;
// Start index from 1 since the 0th argument is the executable
for (int i = 1; i <= num_lags; i++) {
// Use atoi from stdlib to convert the character into an integer!
int duration = atoi(argv[i]);
if (duration >= goal) {
num_spikes++;
}
}
if (num_spikes == 0) {
printf("No lag spikes recorded!\n");
return 0;
}
// malloc an array big enough to hold exactly num_spikes integers, then
// fill it in with just the lag durations that are >= goal.
int *spikes = malloc(num_spikes * sizeof(int));
// copy the values into the array
int index = 0;
for (int i = 1; i <= num_lags; i++) {
int duration = atoi(argv[i]);
if (duration >= goal) {
spikes[index] = duration;
index++;
}
}
printf("Number of lag spikes: %d\n", num_spikes);
printf(
"Average lag spike duration: %.2f ms\n",
average_lag(spikes, num_spikes)
);
printf("Worst lag spike duration: %d ms\n", worst_lag(spikes, num_spikes));
free(spikes);
return 0;
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
// Returns the average of the values in lags, an array of count integers.
double average_lag(int *lags, int count) {
if (count == 0) {
return 0;
}
double sum = 0;
for (int i = 0; i < count; i++) {
sum += lags[i];
}
return sum / count;
}
// Returns the largest value in lags, an array of count integers.
int worst_lag(int *lags, int count) {
int worst = lags[0];
for (int i = 1; i < count; i++) {
if (lags[i] > worst) {
worst = lags[i];
}
}
return worst;
}
Exercise — individual:
Pottery Studio
Download pottery_studio.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity pottery_studio
Pottery has to pass through several stages before it's ready to sell. All pieces will start as soft, freshly-thrown greenware, through leather-hard and a bisque firing, to a final glaze firing that makes it ready for the shelf.
Your task is to complete the program pottery_studio.c using the provided starter code. The program simulates a small pottery studio, tracking pieces as they move through these stages using two linked lists: progress_pieces (pieces still being worked on) and pieces_for_sale (pieces that have finished glaze firing).
You should implement three functions:
create_piece: allocates and returns a newstruct ceramic_piecewith the given name.- All pieces will start at the
GREENWAREstage.
- All pieces will start at the
add_piece: creates a new piece with the given name and appends it to the end ofstudio->progress_pieces.fire_pieces: advances every piece instudio->progress_piecesforward by one stage- The stage progression is:
GREENWARE→LEATHER_HARD→BISQUE_FIRED→GLAZE_FIRED). - Any piece that reaches
GLAZE_FIREDduring this firing is finished, and should be removed fromprogress_piecesand appended to the end ofpieces_for_saleinstead.
- The stage progression is:
free_studio: frees every piece in bothprogress_piecesandpieces_for_sale, as well as thestudiostruct itself.
The provided print_studio, print_pieces, and stage_to_string functions handle all the output for you.
Example
dcc pottery_studio.c -o pottery_studio ./pottery_studio === Initial Studio === Pieces in progress: Tea Bowl GREENWARE Coffee Mug GREENWARE Pieces for sale: (none) === After Firing 1 === Pieces in progress: Tea Bowl LEATHER_HARD Coffee Mug LEATHER_HARD Pieces for sale: (none) === After Firing 2 === Pieces in progress: Tea Bowl BISQUE_FIRED Coffee Mug BISQUE_FIRED Vase LEATHER_HARD Pieces for sale: (none) === After Firing 3 === Pieces in progress: Vase BISQUE_FIRED Bowl LEATHER_HARD Pieces for sale: Tea Bowl GLAZE_FIRED Coffee Mug GLAZE_FIRED === After Firing 4 === Pieces in progress: Bowl BISQUE_FIRED Pieces for sale: Tea Bowl GLAZE_FIRED Coffee Mug GLAZE_FIRED Vase GLAZE_FIRED === After Firing 5 === Pieces in progress: (none) Pieces for sale: Tea Bowl GLAZE_FIRED Coffee Mug GLAZE_FIRED Vase GLAZE_FIRED Bowl GLAZE_FIRED
Assumptions/Restrictions/Clarifications
- No error checking is needed.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest pottery_studio
pottery_studio.c
// pottery_studio.c
//
// This program was written by Holly Field (z5477436)
// on 16/07/26
//
// Simulates pieces of pottery moving through the stages of firing in a
// pottery studio.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NAME_LENGTH 64
#define NUM_FIRINGS 5
enum stage {
GREENWARE,
LEATHER_HARD,
BISQUE_FIRED,
GLAZE_FIRED
};
struct ceramic_piece {
char name[MAX_NAME_LENGTH];
enum stage stage;
struct ceramic_piece *next;
};
struct studio {
struct ceramic_piece *progress_pieces;
struct ceramic_piece *pieces_for_sale;
};
// Student functions
void add_piece(struct studio *studio, char *name);
void fire_pieces(struct studio *studio);
void free_studio(struct studio *studio);
// Helper functions
struct ceramic_piece *create_piece(char *name);
void print_studio(struct studio *studio);
void print_pieces(struct ceramic_piece *head);
const char *stage_to_string(enum stage stage);
void move_to_sale(
struct studio *studio,
struct ceramic_piece *piece,
struct ceramic_piece *prev
);
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE THE MAIN FUNCTION ///////////////////////////////
int main(void) {
struct studio *studio = malloc(sizeof(struct studio));
studio->pieces_for_sale = NULL;
studio->progress_pieces = NULL;
add_piece(studio, "Tea Bowl");
add_piece(studio, "Coffee Mug");
printf("=== Initial Studio ===\n");
print_studio(studio);
for (int i = 1; i <= NUM_FIRINGS; i++) {
printf("\n=== After Firing %d ===\n", i);
fire_pieces(studio);
print_studio(studio);
// Add new pieces partway through, so not everything progresses
// through the stages together
if (i == 1) {
add_piece(studio, "Vase");
}
if (i == 2) {
add_piece(studio, "Bowl");
}
}
free_studio(studio);
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTIONS
// -----------------------------------------------------------------------------
// Creates a new ceramic piece by allocating and initialising memory
struct ceramic_piece *create_piece(char *name) {
struct ceramic_piece *new_piece = malloc(sizeof(struct ceramic_piece));
strcpy(new_piece->name, name);
new_piece->stage = GREENWARE;
new_piece->next = NULL;
return new_piece;
}
// Creates and appends a new piece with the given name to the
// studio's progress pieces.
void add_piece(struct studio *studio, char *name) {
struct ceramic_piece *new_piece = create_piece(name);
if (studio->progress_pieces == NULL) {
studio->progress_pieces = new_piece;
return;
}
struct ceramic_piece *curr = studio->progress_pieces;
while (curr->next != NULL) {
curr = curr->next;
}
curr->next = new_piece;
}
// Fires all progress pieces to the next stage
void fire_pieces(struct studio *studio) {
struct ceramic_piece *curr = studio->progress_pieces;
struct ceramic_piece *prev = NULL;
while (curr != NULL) {
if (curr->stage == GREENWARE) {
curr->stage = LEATHER_HARD;
prev = curr;
curr = curr->next;
} else if (curr->stage == LEATHER_HARD) {
curr->stage = BISQUE_FIRED;
prev = curr;
curr = curr->next;
} else if (curr->stage == BISQUE_FIRED) {
curr->stage = GLAZE_FIRED;
// Piece is ready for sale
move_to_sale(studio, curr, prev);
// update current
if (prev == NULL) {
curr = studio->progress_pieces;
} else {
curr = prev->next;
}
} else {
prev = curr;
curr = curr->next;
}
}
}
// Helper function to remove a piece from the progress list and add it to the
// sale list
void move_to_sale(
struct studio *studio,
struct ceramic_piece *piece,
struct ceramic_piece *prev
) {
// Remove from progress_pieces
if (prev == NULL) {
studio->progress_pieces = piece->next;
} else {
prev->next = piece->next;
}
// Add to pieces_for_sale
piece->next = NULL;
if (studio->pieces_for_sale == NULL) {
studio->pieces_for_sale = piece;
} else {
struct ceramic_piece *sale = studio->pieces_for_sale;
while (sale->next != NULL) {
sale = sale->next;
}
sale->next = piece;
}
}
// Frees all memory associated with the studio
void free_studio(struct studio *studio) {
struct ceramic_piece *curr;
curr = studio->progress_pieces;
while (curr != NULL) {
struct ceramic_piece *next = curr->next;
free(curr);
curr = next;
}
curr = studio->pieces_for_sale;
while (curr != NULL) {
struct ceramic_piece *next = curr->next;
free(curr);
curr = next;
}
free(studio);
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
const char *stage_to_string(enum stage stage) {
if (stage == GREENWARE) {
return "GREENWARE";
}
if (stage == LEATHER_HARD) {
return "LEATHER_HARD";
}
if (stage == BISQUE_FIRED) {
return "BISQUE_FIRED";
}
return "GLAZE_FIRED";
}
void print_pieces(struct ceramic_piece *head) {
if (head == NULL) {
printf(" (none)\n");
return;
}
while (head != NULL) {
printf(" %-15s %s\n",
head->name,
stage_to_string(head->stage));
head = head->next;
}
}
void print_studio(struct studio *studio) {
printf("Pieces in progress:\n");
print_pieces(studio->progress_pieces);
printf("\nPieces for sale:\n");
print_pieces(studio->pieces_for_sale);
}
Exercise — individual:
Mini Linter
Download mini_linter.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity mini_linter
Before splitting into groups, your tutor will live-code a small demo of mini_linter_comments.c, which strips any inline // comment from a line of code.
Each of the programs below will:
- Read lines of "code" from standard input, until
[Ctrl-D]is pressed. - For each line, a result will be printed depending on the role of the program, as outlined below.
1. Removing Inline Comments (Tutor Demo)
Complete remove_inline_comments so that it removes an inline // comment from line, if one is present.
// mini_linter_comments.c
//
// Written by YOUR-NAME (YOUR-ZID)
// on TODAYS-DATE
//
// Tutor demo: Removes inline comments from a line of code.
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 161
void remove_inline_comments(char *line);
void remove_newline(char *line);
// -----------------------------------------------------------------------------
int main(void) {
char line[MAX_LINE_LENGTH];
printf("Enter lines of code:\n");
while (fgets(line, MAX_LINE_LENGTH, stdin) != NULL) {
remove_newline(line);
remove_inline_comments(line);
printf("%s\n", line);
}
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTION
// -----------------------------------------------------------------------------
// Removes any inline comments from the provided line
void remove_inline_comments(char *line) {
// TODO: Follow along as your tutor demonstrates this function
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
// Helper function to remove the newline character from the end of a string
void remove_newline(char *line) {
int i = 0;
while (line[i] != '\n' && line[i] != '\0') {
i++;
}
line[i] = '\0';
}
Example
dcc mini_linter_comments.c -o mini_linter_comments ./mini_linter_comments Enter lines of code: int flag = 1; // this is a flag int flag = 1; int total = 0; int total = 0; [Ctrl-D]
2. Adding Missing Semicolons
Complete add_semicolon so that it adds a semicolon to the end of line, if one isn't already there.
// mini_linter_semicolon.c
//
// Written by YOUR-NAME (YOUR-ZID)
// on TODAYS-DATE
//
// Adds missing semicolons to lines of code.
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 161
void add_semicolon(char *line);
void remove_newline(char *line);
// -----------------------------------------------------------------------------
int main(void) {
char line[MAX_LINE_LENGTH];
printf("Enter lines of code:\n");
while (fgets(line, MAX_LINE_LENGTH, stdin) != NULL) {
remove_newline(line);
add_semicolon(line);
printf("%s\n", line);
}
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTION
// -----------------------------------------------------------------------------
// Adds any missing semicolon to the end of the provided line.
void add_semicolon(char *line) {
// TODO: Complete this function
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
// Helper function to remove the newline character from the end of a string
void remove_newline(char *line) {
int i = 0;
while (line[i] != '\n' && line[i] != '\0') {
i++;
}
line[i] = '\0';
}
Example
dcc mini_linter_semicolon.c -o mini_linter_semicolon ./mini_linter_semicolon Enter lines of code: int x = 5 int x = 5; int y = 10; int y = 10; [Ctrl-D]
3. Removing Trailing Spaces
Complete remove_trailing_spaces so that it removes any trailing spaces from the end of line.
// mini_linter_trailing_spaces.c
//
// Written by YOUR-NAME (YOUR-ZID)
// on TODAYS-DATE
//
// Removes trailing spaces from lines of code.
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 161
void remove_trailing_spaces(char *line);
void remove_newline(char *line);
// -----------------------------------------------------------------------------
int main(void) {
char line[MAX_LINE_LENGTH];
printf("Enter lines of code:\n");
while (fgets(line, MAX_LINE_LENGTH, stdin) != NULL) {
remove_newline(line);
remove_trailing_spaces(line);
printf("%s\n", line);
}
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTION
// -----------------------------------------------------------------------------
// Removes any trailing spaces from the provided line.
void remove_trailing_spaces(char *line) {
// TODO: Complete this function
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
// Helper function to remove the newline character from the end of a string
void remove_newline(char *line) {
int i = 0;
while (line[i] != '\n' && line[i] != '\0') {
i++;
}
line[i] = '\0';
}
Example
dcc mini_linter_trailing_spaces.c -o mini_linter_trailing_spaces ./mini_linter_trailing_spaces Enter lines of code: int y = 10; int y = 10; int z = 20; int z = 20; [Ctrl-D]
4. Checking Operator Whitespace
Complete missing_operand_whitespace so that it returns 1 if any of the operators +, -, *, /, % or = in line is missing whitespace on either side of it, and 0 otherwise.
// mini_linter_operator_whitespace.c
//
// Written by YOUR-NAME (YOUR-ZID)
// on TODAYS-DATE
//
// Detects operators without whitespace around them.
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 161
int missing_operand_whitespace(char *line);
void remove_newline(char *line);
// -----------------------------------------------------------------------------
int main(void) {
char line[MAX_LINE_LENGTH];
printf("Enter lines of code:\n");
while (fgets(line, MAX_LINE_LENGTH, stdin) != NULL) {
remove_newline(line);
if (missing_operand_whitespace(line)) {
printf("Missing whitespace around an operator!\n");
} else {
printf("No issues found.\n");
}
}
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTION
// -----------------------------------------------------------------------------
// Checks if any operands (* - + / % =) have any missing whitespace around them.
// Returns 1 if there is missing whitespace, and 0 otherwise.
int missing_operand_whitespace(char *line) {
// TODO: Complete this function
return 0;
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
// Helper function to remove the newline character from the end of a string
void remove_newline(char *line) {
int i = 0;
while (line[i] != '\n' && line[i] != '\0') {
i++;
}
line[i] = '\0';
}
Example
dcc mini_linter_operator_whitespace.c -o mini_linter_operator_whitespace ./mini_linter_operator_whitespace Enter lines of code: int total = a+b; Missing whitespace around an operator! int total =a + b; Missing whitespace around an operator! int total = a + b; No issues found. [Ctrl-D]
5. Splitting Long Lines
Complete split_long_line so that, if line is longer than 80 characters, it's split into two lines by replacing the closest space at or before position 80 with a newline character.
// mini_linter_split_line.c
//
// Written by YOUR-NAME (YOUR-ZID)
// on TODAYS-DATE
//
// Splits long lines over 80 characters.
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 161
void split_long_line(char *line);
void remove_newline(char *line);
// -----------------------------------------------------------------------------
int main(void) {
char line[MAX_LINE_LENGTH];
printf("Enter lines of code:\n");
while (fgets(line, MAX_LINE_LENGTH, stdin) != NULL) {
remove_newline(line);
split_long_line(line);
printf("%s\n", line);
}
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTION
// -----------------------------------------------------------------------------
// Splits long lines by replacing the closest whitespace before 80 characters
// with a newline character.
void split_long_line(char *line) {
// TODO: Complete this function
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
// Helper function to remove the newline character from the end of a string
void remove_newline(char *line) {
int i = 0;
while (line[i] != '\n' && line[i] != '\0') {
i++;
}
line[i] = '\0';
}
Example
dcc mini_linter_split_line.c -o mini_linter_split_line ./mini_linter_split_line Enter lines of code: int total = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16; int total = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16; short line; short line; [Ctrl-D]
Assumptions/Restrictions/Clarifications
- No error checking is required.
- You may assume no line will ever be longer than 160 characters.
missing_operand_whitespaceonly needs to check the operators+,-,*,/,%and=.- For
split_long_line, you may assume there will always be a space at or before position 80 inline. - All programs that are required to modify the provided
lineshould make these modifications in place. This meanslineitself should be modified, without creating any copies.
Exercise
(●◌◌)
:
Sea Creature Sanctuary
Download sea_creatures.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity sea_creatures
The ocean research centre needs a program to track the sea creatures its divers discover. Since creatures can be added and released at any time, they need a home in memory that isn't fixed in place. malloc lets you create exactly that!
Your task is to complete sea_creatures.c by implementing two functions:
create_creature: Creates a newstruct creatureusingmalloc, initialising itsnameandsizefields with the provided arguments, then returns a pointer to the new creature.release_creature: Frees the memory used by a creature when it is no longer needed.
Example
dcc sea_creatures.c -o sea_creatures --leak-check ./sea_creatures Enter creature name: Blue Whale Enter creature size: 2500 Creating creature... Creature details: Name: Blue Whale Size: 2500 Releasing creature...
Assumptions/Restrictions/Clarifications
- No error checking is required.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest sea_creatures
sea_creatures.c
// sea_creature.c
//
// Written by Holly Field (z5477436)
// on 17/07/26
//
// Introductory activity to learn malloc and free.
// Creates and releases sea creatures.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NAME_LENGTH 64
struct creature {
char name[MAX_NAME_LENGTH];
int size;
};
struct creature *create_creature(char name[], int size);
void release_creature(struct creature *creature);
void print_creature(struct creature *creature);
void remove_newline(char input[MAX_NAME_LENGTH]);
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE THE MAIN FUNCTION ///////////////////////////////
int main(void) {
char name[MAX_NAME_LENGTH];
int size;
printf("Enter creature name: ");
fgets(name, MAX_NAME_LENGTH, stdin);
remove_newline(name);
printf("Enter creature size: ");
scanf("%d", &size);
printf("\nCreating creature...\n");
struct creature *creature = create_creature(name, size);
printf("\nCreature details:\n");
print_creature(creature);
printf("\nReleasing creature...\n");
release_creature(creature);
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTIONS
// -----------------------------------------------------------------------------
// Creates and returns a pointer to a new creature by calling malloc
struct creature *create_creature(char name[], int size) {
struct creature *new_creature = malloc(sizeof(struct creature));
strcpy(new_creature->name, name);
new_creature->size = size;
return new_creature;
}
// Frees the creature by calling free
void release_creature(struct creature *creature) {
free(creature);
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE ANY OF THE CODE BELOW HERE //////////////////////
void print_creature(struct creature *creature) {
printf("Name: %s\n", creature->name);
printf("Size: %d\n", creature->size);
}
void remove_newline(char input[MAX_NAME_LENGTH]) {
int index = 0;
while (input[index] != '\n' && input[index] != '\0') {
index++;
}
input[index] = '\0';
}
Exercise
(●◌◌)
:
Debugging Exercise -- Broken Inventory
Download broken_inventory.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity broken_inventory
Your task is to find and fix the bugs in broken_inventory.c so that the inventory system works as intended, without crashing and without leaking memory.
HINT: There will be a bug in each of the functions:
create_item: creates a new item and returns a pointer to it.append_item: creates and appends a new item to the inventory and returns the updated head of the inventory list.use_item: "uses" an item that matches the given name by decreasing its quantity by 1. If the quantity reaches 0, the item is removed from the inventory.free_items: frees all memory associated with the inventory.
Debugging Tips
Some debugging tips for you:
dccoutput: as you run into issues, dcc will point you to where the errors are. It gives you the line number the issue is on along with some explanation, so make sure you read everything it tells you.- Not every bug is a compiler error: a program can compile with zero warnings and still crash, or leak memory. If your program runs but crashes partway through, trace through the code by hand to work out exactly which line causes it.
--leak-check: always compile with this flag for this activity. A leak won't crash your program or produce any visibly wrong output — it will only show up as a leak-check warning at the end of a run.- COMP1511 debugging guide
Examples
dcc broken_inventory.c -o broken_inventory --leak-check
./broken_inventory
== Starting inventory ==
====== Inventory ======
Potion x3
Sword x1
Using the Potion.
====== Inventory ======
Potion x2
Sword x1
Using the Sword.
====== Inventory ======
Potion x2
Assumptions/Restrictions/Clarifications
- You should not need to modify
mainorprint_inventory.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest broken_inventory
broken_inventory.c
// broken_inventory.c
//
// This program was written by Holly Field (z5477436)
// on 17/07/26
//
// Manages a simple game inventory as a linked list of items.
// The game keeps crashing! Find and fix the bugs below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NAME_LENGTH 20
struct item {
char name[MAX_NAME_LENGTH];
int quantity;
struct item *next;
};
struct item *create_item(char *name, int quantity);
struct item *append_item(struct item *inventory, char *name, int quantity);
struct item *use_item(struct item *inventory, char *name);
void print_inventory(struct item *inventory);
void free_inventory(struct item *inventory);
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE THE MAIN FUNCTION ///////////////////////////
int main(void) {
struct item *inventory = NULL;
inventory = append_item(inventory, "Potion", 3);
inventory = append_item(inventory, "Sword", 1);
printf("== Starting inventory ==\n");
print_inventory(inventory);
printf("\nUsing the Potion.\n");
inventory = use_item(inventory, "Potion");
print_inventory(inventory);
printf("\nUsing the Sword.\n");
inventory = use_item(inventory, "Sword");
print_inventory(inventory);
free_inventory(inventory);
return 0;
}
// ---------------------------------------------------------------------------
// BUGGY FUNCTIONS - FIX THESE
// ---------------------------------------------------------------------------
// Creates a new item and adds it to the front of the inventory list.
struct item *create_item(char *name, int quantity) {
// BUG FIX: new_item was a local (stack) variable, so its address
// became invalid the moment this function returned. Heap-allocate it
// with malloc instead, so it stays valid after we return.
struct item *new_item = malloc(sizeof(struct item));
strcpy(new_item->name, name);
new_item->quantity = quantity;
new_item->next = NULL;
return new_item;
}
// Adds a new item to the inventory and returns the updated inventory.
struct item *append_item(struct item *inventory, char *name, int quantity) {
struct item *new_item = create_item(name, quantity);
// BUG FIX: was not handling the case where inventory is NULL (empty list).
// This lead to a crash when trying to access current->next
if (inventory == NULL) {
return new_item;
}
struct item *current = inventory;
while (current->next != NULL) {
current = current->next;
}
current->next = new_item;
return inventory;
}
// Decreases the quantity of the item with the given name by 1.
// If the quantity reaches 0, the item is removed from the inventory.
struct item *use_item(struct item *inventory, char *name) {
struct item *current = inventory;
struct item *prev = NULL;
while (current != NULL && strcmp(current->name, name) != 0) {
prev = current;
current = current->next;
}
current->quantity--;
if (current->quantity <= 0) {
// BUG FIX: current was being freed before current->next
// was read, so the two lines below were reading from
// memory that had already been freed. Relink the list
// first, then free current afterwards.
if (prev == NULL) {
inventory = current->next;
} else {
prev->next = current->next;
}
free(current);
}
return inventory;
}
// Free all items in the inventory.
void free_inventory(struct item *inventory) {
struct item *current = inventory;
while (current != NULL) {
// BUG FIX: was only freeing the first item in the list
struct item *next = current->next;
free(current);
current = next;
}
}
// ---------------------------------------------------------------------------
// PROVIDED FUNCTIONS - DO NOT CHANGE THESE
// ---------------------------------------------------------------------------
void print_inventory(struct item *inventory) {
printf("====== Inventory ======\n");
struct item *current = inventory;
if (current == NULL) {
printf(" (empty)\n");
return;
}
while (current != NULL) {
printf(" %s x%d\n", current->name, current->quantity);
current = current->next;
}
}
Exercise
(●◌◌)
:
Calm Down!
Download calm_down.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity calm_down
Sometimes a message could really use fewer exclamation marks!!! Your job is to write a "calm down" filter that tones down an overly enthusiastic message.
Complete the program calm_down.c, which:
- Prompts the user to enter a string with
Enter a message:. - Scans in the string from stdin.
- Replaces every
'!'character with a'.', modifying the string in place. - Prints the modified string to stdout.
Example
dcc calm_down.c -o calm_down ./calm_down Enter a message: This is amazing!!! I can't believe it!! This is amazing... I can't believe it..
Assumptions/Restrictions/Clarifications
- No error checking is required.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest calm_down
calm_down.c
// calm_down.c
//
// Written by Holly Field (z5477436)
// on 17/07/26
//
// Calms down an overly enthusiastic message by replacing every '!' with
// a '.'.
#include <stdio.h>
#include <string.h>
#define MAX_LENGTH 200
void remove_newline(char input[MAX_LENGTH]);
// ----------------------------------------------------------------------------
int main(void) {
char message[MAX_LENGTH];
fgets(message, MAX_LENGTH, stdin);
remove_newline(message);
// Replace every '!' with a '.'
for (int i = 0; i < strlen(message); i++) {
if (message[i] == '!') {
message[i] = '.';
}
}
printf("%s\n", message);
return 0;
}
// ----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// ----------------------------------------------------------------------------
// Helper function to remove the newline character from the end of a string
void remove_newline(char input[MAX_LENGTH]) {
int length = strlen(input);
if (length > 0 && input[length - 1] == '\n') {
input[length - 1] = '\0';
}
}
Exercise
(●●◌)
:
Sum of Positive and Negative Numbers
Download sum_of_positive_negative.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity sum_of_positive_negative
Your task is to implement the function sum_of_positive_negative in the provided starter code sum_of_positive_negative.c.
The function should:
- Calculate the sum of all positive numbers in the linked list, and store it in
*positive_sum. - Calculate the sum of all negative numbers in the linked list, and store it in
*negative_sum.
The construction of the linked list from command line arguments has been provided for you.
Examples
dcc sum_of_positive_negative.c -o sum_of_positive_negative ./sum_of_positive_negative 1 -2 3 -4 5 LIST: 1 -> -2 -> 3 -> -4 -> 5 -> NULL Positive sum: 9 Negative sum: -6 ./sum_of_positive_negative 8 10 4 2 1 LIST: 8 -> 10 -> 4 -> 2 -> 1 -> NULL Positive sum: 25 Negative sum: 0 ./sum_of_positive_negative -5 -1 -3 -4 LIST: -5 -> -1 -> -3 -> -4 -> NULL Positive sum: 0 Negative sum: -13
Assumptions/Restrictions/Clarifications
- If the linked list is empty, both
positive_sumandnegative_sumshould be 0.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest sum_of_positive_negative
sum_of_positive_negative.c
// sum_of_positive_negative.c
//
// Written by Holly Field (z5477436)
// on 17/07/26
//
// Calculates the sum of positive and negative numbers in an array.
#include <stdio.h>
#include <stdlib.h>
// DO NOT CHANGE THIS STRUCT
struct node {
struct node *next;
int data;
};
// MODIFY THIS FUNCTION
// sum_of_positive_negative takes in the head of a linked list and calculates
// the sum of positive and negative numbers
void sum_of_positive_negative(
struct node *head,
int *positive_sum,
int *negative_sum
) {
*positive_sum = 0;
*negative_sum = 0;
struct node *current = head;
while (current != NULL) {
if (current->data > 0) {
*positive_sum += current->data;
} else if (current->data < 0) {
*negative_sum += current->data;
}
current = current->next;
}
}
////////////////////////////////////////////////////////////////////////
// DO NOT CHANGE THESE PROTOTYPES
////////////////////////////////////////////////////////////////////////
struct node *args_to_list(int argc, char *argv[]);
void print_list(struct node *head);
void free_list(struct node *head);
// DO NOT CHANGE THIS FUNCTION
int main(int argc, char *argv[]) {
struct node *head = args_to_list(argc, argv);
printf("LIST: ");
print_list(head);
int positive_sum;
int negative_sum;
sum_of_positive_negative(head, &positive_sum, &negative_sum);
printf("Positive sum: %d\n", positive_sum);
printf("Negative sum: %d\n", negative_sum);
free_list(head);
return 0;
}
// DO NOT CHANGE THIS FUNCTION
// Converts command-line args to a linked list of characters
struct node *args_to_list(int argc, char *argv[]) {
struct node *head = NULL;
for (int i = argc - 1; i >= 1; i--) {
struct node *new = malloc(sizeof(struct node));
new->data = atoi(argv[i]);
new->next = head;
head = new;
}
return head;
}
// DO NOT CHANGE THIS FUNCTION
// Prints a linked list of integers
void print_list(struct node *head) {
struct node *curr = head;
if (curr == NULL) {
printf("NULL\n");
return;
}
while (curr != NULL) {
printf("%d", curr->data);
if (curr->next != NULL) {
printf(" -> ");
}
curr = curr->next;
}
printf(" -> NULL\n");
}
void free_list(struct node *head) {
struct node *curr = head;
while (curr != NULL) {
struct node *to_free = curr;
curr = curr->next;
free(to_free);
}
}
Exercise
(●●◌)
:
Route Setter
Download route_setter.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity route_setter
Your task is to complete the program route_setter.c using the provided starter code.
A main function and a series of helper functions have been implemented for you. The program allows a climber to interactively build and analyse a bouldering route using commands. However, the functions that manipulate the linked list are currently incomplete.
You must implement the following functions:
-
append_hold- Add a new hold at the end of the route.
- The new hold should store the provided
new_hold_idanddifficulty.
-
insert_after_hold- Add a new hold into the route immediately after the hold with the given
hold_id. - The new hold should store the provided
new_hold_idanddifficulty.
- Add a new hold into the route immediately after the hold with the given
-
remove_hold- Remove the hold with the given
hold_idfrom the route. - Remember to free any memory that is no longer needed.
- Remove the hold with the given
-
find_crux- Find and return a pointer to the hardest hold in the route.
- The crux is the hold with the highest difficulty.
-
climber_progress- Given a climber's skill level, determine how many holds they can complete in order before encountering a hold that is too difficult.
- A climber can complete a hold if its difficulty is less than or equal to their skill level.
- Return the number of holds completed.
The provided create_hold, print_route, print_commands, and free_route functions handle creating, displaying, and freeing the linked list for you.
Example
dcc route_setter.c -o route_setter ./route_setter Welcome to Route Setter! a 1 2 a 2 4 a 3 7 a 4 3 p ==== Current Route ==== Hold 1 (difficulty 2) Hold 2 (difficulty 4) Hold 3 (difficulty 7) Hold 4 (difficulty 3) i 2 99 5 p ==== Current Route ==== Hold 1 (difficulty 2) Hold 2 (difficulty 4) Hold 99 (difficulty 5) Hold 3 (difficulty 7) Hold 4 (difficulty 3) r 3 p ==== Current Route ==== Hold 1 (difficulty 2) Hold 2 (difficulty 4) Hold 99 (difficulty 5) Hold 4 (difficulty 3) x Crux hold is 99 (difficulty 5) c 4 A climber with skill 4 can climb 2 holds before failing. q
Assumptions/Restrictions/Clarifications
- No error checking is required.
- You may assume that all ids are unique.
- You may assume that all commands are valid.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest route_setter
route_setter.c
// route_setter.c
//
// Written by Holly Field (z5477436)
// on 17/07/2026
//
// A linked list representation of a bouldering route.
#include <stdio.h>
#include <stdlib.h>
// Struct representing a hold in a climbing route
struct hold {
int hold_id;
int difficulty;
struct hold *next;
};
// Your function prototypes
struct hold *append_hold(struct hold *route, int new_hold_id, int difficulty);
struct hold *insert_after_hold(struct hold *route, int hold_id, int new_hold_id,
int difficulty);
struct hold *remove_hold(struct hold *route, int hold_id);
struct hold *find_crux(struct hold *route);
int climber_progress(struct hold *route, int skill);
// Provided function prototypes
struct hold *create_hold(int hold_id, int difficulty);
void print_route(struct hold *route);
void free_route(struct hold *route);
void print_commands();
void command_loop();
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE THE MAIN FUNCTION ///////////////////////////////
int main(void) {
// Create a sample route with 4 holds
printf("Welcome to Route Setter!\n");
command_loop();
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTIONS
// -----------------------------------------------------------------------------
// Append a new hold to the end of the route.
struct hold *append_hold(struct hold *route, int new_hold_id, int difficulty) {
struct hold *new_hold = create_hold(new_hold_id, difficulty);
if (route == NULL) {
return new_hold;
}
struct hold *current = route;
while (current->next != NULL) {
current = current->next;
}
current->next = new_hold;
return route;
}
// Insert a new hold after the hold with hold_id.
struct hold *insert_after_hold(struct hold *route, int hold_id, int new_hold_id,
int difficulty) {
struct hold *current = route;
while (current != NULL) {
if (current->hold_id == hold_id) {
struct hold *new_hold = create_hold(new_hold_id, difficulty);
new_hold->next = current->next;
current->next = new_hold;
return route;
}
current = current->next;
}
return route;
}
// Remove the hold with hold_id from the route.
struct hold *remove_hold(struct hold *route, int hold_id) {
if (route == NULL) {
return NULL;
}
if (route->hold_id == hold_id) {
struct hold *new_head = route->next;
free(route);
return new_head;
}
struct hold *current = route;
while (current->next != NULL) {
if (current->next->hold_id == hold_id) {
struct hold *to_remove = current->next;
current->next = to_remove->next;
free(to_remove);
return route;
}
current = current->next;
}
return route;
}
// Return a pointer to the hardest hold in the route.
struct hold *find_crux(struct hold *route) {
if (route == NULL) {
return NULL;
}
struct hold *crux = route;
struct hold *current = route->next;
while (current != NULL) {
if (current->difficulty > crux->difficulty) {
crux = current;
}
current = current->next;
}
return crux;
}
// Return how many holds a climber can complete.
int climber_progress(struct hold *route, int skill) {
int count = 0;
struct hold *current = route;
while (current != NULL) {
if (current->difficulty <= skill) {
count++;
current = current->next;
} else {
return count;
}
}
return count;
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
// DO NOT CHANGE ANY OF THE CODE BELOW HERE
// Creates a new hold and returns a pointer to it.
struct hold *create_hold(int hold_id, int difficulty) {
struct hold *new_hold = malloc(sizeof(struct hold));
new_hold->hold_id = hold_id;
new_hold->difficulty = difficulty;
new_hold->next = NULL;
return new_hold;
}
// Prints the entire route, one hold per line.
void print_route(struct hold *route) {
printf("==== Current Route ====\n");
if (route == NULL) {
printf("Route is empty.\n");
return;
}
while (route != NULL) {
printf("Hold %d (difficulty %d)\n", route->hold_id, route->difficulty);
route = route->next;
}
}
// Frees every hold in the route.
void free_route(struct hold *route) {
while (route != NULL) {
struct hold *next = route->next;
free(route);
route = next;
}
}
// Print the available commands for the user.
void print_commands() {
printf("Commands:\n");
printf(" a <new_id> <difficulty> - append hold to the route\n");
printf(" i <after_id> <new_id> <difficulty> - insert hold\n");
printf(" r <hold_id> - remove hold\n");
printf(" p - print route\n");
printf(" c <skill> - climber progress\n");
printf(" x - find crux\n");
printf(" q - quit\n\n");
}
// Command loop to process user input.
void command_loop() {
char command;
struct hold *route = NULL;
while (scanf(" %c", &command) == 1 && command != 'q') {
if (command == 'a') {
int new_id;
int difficulty;
scanf("%d %d", &new_id, &difficulty);
route = append_hold(route, new_id, difficulty);
} else if (command == 'i') {
int after_id;
int new_id;
int difficulty;
scanf("%d %d %d", &after_id, &new_id, &difficulty);
route = insert_after_hold(route, after_id, new_id, difficulty);
} else if (command == 'p') {
print_route(route);
} else if (command == 'r') {
int hold_id;
scanf("%d", &hold_id);
route = remove_hold(route, hold_id);
} else if (command == 'c') {
int skill;
scanf("%d", &skill);
printf(
"A climber with skill %d can climb %d holds before failing.\n",
skill, climber_progress(route, skill)
);
} else if (command == 'x') {
struct hold *crux = find_crux(route);
if (crux != NULL) {
printf(
"Crux hold is %d (difficulty %d)\n",
crux->hold_id, crux->difficulty
);
} else {
printf("No crux hold found.\n");
}
} else if (command == '?') {
print_commands();
}
}
free_route(route);
}
Exercise
(●●●)
:
Extension -- Chaotic Route Setter
Download chaotic_route_setter.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity chaotic_route_setter
Your task is to complete the program chaotic_route_setter.c by extending your solution to route_setter.c to implement a new shuffle route command.
The shuffle route command is called when the user types s into the command loop, with no additional arguments. When called, the program should:
- Split the climb into two halves: the first half and the second half.
- Recombine the two halves by zipping them together. This means alternating one hold from the first half, then one hold from the second half, repeating until both halves are exhausted.
For example, given the climb [1, 2, 3, 4, 5, 6], the first half is [1, 2, 3] and the second half is [4, 5, 6]. Zipping them together produces [1, 4, 2, 5, 3, 6].
As an extra challenge, attempt this task without allocating any new memory.
The provided starter code, chaotic_route_setter.c, is mostly the same as route_setter.c, but with some minor changes:
- The
command_loopfunction will need to be modified to recognise the shuffle route command. - There is an added function stub and prototype for
shuffle_routeto implement the shuffling logic. You may want to use this starter code, and copy over your function implementations fromroute_setter.c.
Example
dcc chaotic_route_setter.c -o chaotic_route_setter ./chaotic_route_setter Welcome to Route Setter! a 1 1 a 2 2 a 3 3 a 4 4 a 5 5 a 6 6 p ==== Current Route ==== Hold 1 (difficulty 1) Hold 2 (difficulty 2) Hold 3 (difficulty 3) Hold 4 (difficulty 4) Hold 5 (difficulty 5) Hold 6 (difficulty 6) s p ==== Current Route ==== Hold 1 (difficulty 1) Hold 4 (difficulty 4) Hold 2 (difficulty 2) Hold 5 (difficulty 5) Hold 3 (difficulty 3) Hold 6 (difficulty 6) q
Assumptions/Restrictions/Clarifications
- If a route contains an odd number of holds, the first half should contain one more hold than the second half.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest chaotic_route_setter
chaotic_route_setter.c
// chaotic_route_setter.c
//
// Written by Holly Field (z5477436)
// on 17/07/2026
//
// An extension of the route_setter activity that adds a new command to
// shuffle the route.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// Struct representing a hold in a climbing route
struct hold {
int hold_id;
int difficulty;
struct hold *next;
};
// Your function prototypes
struct hold *append_hold(struct hold *route, int new_hold_id, int difficulty);
struct hold *insert_after_hold(struct hold *route, int hold_id, int new_hold_id,
int difficulty);
struct hold *remove_hold(struct hold *route, int hold_id);
struct hold *find_crux(struct hold *route);
int climber_progress(struct hold *route, int skill);
struct hold *shuffle_route(struct hold *route);
// Provided function prototypes
struct hold *create_hold(int hold_id, int difficulty);
void print_route(struct hold *route);
void free_route(struct hold *route);
void print_commands();
void command_loop();
// -----------------------------------------------------------------------------
//////////////// DO NOT CHANGE THE MAIN FUNCTION ///////////////////////////////
int main(void) {
// Create a sample route with 4 holds
printf("Welcome to Route Setter!\n");
command_loop();
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTIONS
// -----------------------------------------------------------------------------
// Append a new hold to the end of the route.
struct hold *append_hold(struct hold *route, int new_hold_id, int difficulty) {
struct hold *new_hold = create_hold(new_hold_id, difficulty);
if (route == NULL) {
return new_hold;
}
struct hold *current = route;
while (current->next != NULL) {
current = current->next;
}
current->next = new_hold;
return route;
}
// Insert a new hold after the hold with hold_id.
struct hold *insert_after_hold(struct hold *route, int hold_id, int new_hold_id,
int difficulty) {
struct hold *current = route;
while (current != NULL) {
if (current->hold_id == hold_id) {
struct hold *new_hold = create_hold(new_hold_id, difficulty);
new_hold->next = current->next;
current->next = new_hold;
return route;
}
current = current->next;
}
return route;
}
// Remove the hold with hold_id from the route.
struct hold *remove_hold(struct hold *route, int hold_id) {
if (route == NULL) {
return NULL;
}
if (route->hold_id == hold_id) {
struct hold *new_head = route->next;
free(route);
return new_head;
}
struct hold *current = route;
while (current->next != NULL) {
if (current->next->hold_id == hold_id) {
struct hold *to_remove = current->next;
current->next = to_remove->next;
free(to_remove);
return route;
}
current = current->next;
}
return route;
}
// Return a pointer to the hardest hold in the route.
struct hold *find_crux(struct hold *route) {
if (route == NULL) {
return NULL;
}
struct hold *crux = route;
struct hold *current = route->next;
while (current != NULL) {
if (current->difficulty > crux->difficulty) {
crux = current;
}
current = current->next;
}
return crux;
}
// Return how many holds a climber can complete.
int climber_progress(struct hold *route, int skill) {
int count = 0;
struct hold *current = route;
while (current != NULL) {
if (current->difficulty <= skill) {
count++;
current = current->next;
} else {
return count;
}
}
return count;
}
// Shuffles the route by splitting it into two halves and zipping them together.
// If the route has an odd number of holds, the first half will have one more
// holds than the second half.
struct hold *shuffle_route(struct hold *route) {
if (route == NULL || route->next == NULL) {
return route;
}
// Find the size
int size = 0;
struct hold *current = route;
while (current != NULL) {
size++;
current = current->next;
}
// Find the last node of the first half
int first_half_size = (size + 1) / 2;
struct hold *first_tail = route;
for (int i = 1; i < first_half_size; i++) {
first_tail = first_tail->next;
}
// Split into two lists
struct hold *second = first_tail->next;
first_tail->next = NULL;
struct hold *first = route;
// Zip the two lists together
while (second != NULL) {
struct hold *first_next = first->next;
struct hold *second_next = second->next;
first->next = second;
second->next = first_next;
first = first_next;
second = second_next;
}
return route;
}
// HINT: You will need to edit the command loop to add the "shuffle" command.
// Command loop to process user input.
void command_loop() {
char command;
struct hold *route = NULL;
while (scanf(" %c", &command) == 1 && command != 'q') {
if (command == 'a') {
int new_id;
int difficulty;
scanf("%d %d", &new_id, &difficulty);
route = append_hold(route, new_id, difficulty);
} else if (command == 'i') {
int after_id;
int new_id;
int difficulty;
scanf("%d %d %d", &after_id, &new_id, &difficulty);
route = insert_after_hold(route, after_id, new_id, difficulty);
} else if (command == 'p') {
print_route(route);
} else if (command == 'r') {
int hold_id;
scanf("%d", &hold_id);
route = remove_hold(route, hold_id);
} else if (command == 'c') {
int skill;
scanf("%d", &skill);
printf(
"A climber with skill %d can climb %d holds before failing.\n",
skill, climber_progress(route, skill)
);
} else if (command == 'x') {
struct hold *crux = find_crux(route);
if (crux != NULL) {
printf(
"Crux hold is %d (difficulty %d)\n",
crux->hold_id, crux->difficulty
);
} else {
printf("No crux hold found.\n");
}
} else if (command == '?') {
print_commands();
} else if (command == 's') {
route = shuffle_route(route);
}
}
free_route(route);
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
// DO NOT CHANGE ANY OF THE CODE BELOW HERE
// Creates a new hold and returns a pointer to it.
struct hold *create_hold(int hold_id, int difficulty) {
struct hold *new_hold = malloc(sizeof(struct hold));
new_hold->hold_id = hold_id;
new_hold->difficulty = difficulty;
new_hold->next = NULL;
return new_hold;
}
// Prints the entire route, one hold per line.
void print_route(struct hold *route) {
printf("==== Current Route ====\n");
if (route == NULL) {
printf("Route is empty.\n");
return;
}
while (route != NULL) {
printf("Hold %d (difficulty %d)\n", route->hold_id, route->difficulty);
route = route->next;
}
}
// Frees every hold in the route.
void free_route(struct hold *route) {
while (route != NULL) {
struct hold *next = route->next;
free(route);
route = next;
}
}
// Print the available commands for the user.
void print_commands() {
printf("Commands:\n");
printf(" a <new_id> <difficulty> - append hold to the route\n");
printf(" i <after_id> <new_id> <difficulty> - insert hold\n");
printf(" r <hold_id> - remove hold\n");
printf(" p - print route\n");
printf(" c <skill> - climber progress\n");
printf(" x - find crux\n");
printf(" s - shuffle route\n");
printf(" q - quit\n\n");
}
Exercise
(●●●)
:
Extension -- Advanced Linter
Download advanced_linter.c here
Or, copy these file(s) to your CSE account using the following command:
1511 fetch-activity advanced_linter
This activity extends the mini linter exercise from the revision session.
Previously, your linter could perform a single modification to a line of code. Your task is now to implement a more capable linter by completing several string manipulation functions.
The program repeatedly reads lines of code from standard input until [CTRL-D] is entered.
For every line, the following functions should be applied in order:
collapse_whitespace- Replace consecutive spaces with a single space.
- It should not collapse any leading spaces (at the start of the line) as this may be valid indentation.
add_operator_spacing- Ensure every
=,+,-,*and/has a single space on either side.
- Ensure every
wrap_long_lines- If a line exceeds 80 characters, replace the last whitespace before the 80th character with a newline followed by a tab character (
'\n'then'\t'). - Continue wrapping until every line is at most 80 characters long.
- If a line exceeds 80 characters, replace the last whitespace before the 80th character with a newline followed by a tab character (
The provided print_result function will display the final linted line.
Example
dcc advanced_linter.c -o advanced_linter
./advanced_linter
Enter lines of code:
int x = 5;
int x = 5;
a =b+c*d/e-f
a = b + c * d / e - f
this is a deliberately very long line that should be wrapped multiple times because it contains far more than eighty characters before reaching the end of the sentence now we go again to show multi wrap this is a deliberately very long line that should be wrapped multiple times because it contains far more than eighty characters before reaching the end of the sentence
this is a deliberately very long line that should be wrapped multiple times
because it contains far more than eighty characters before reaching the end of the
sentence now we go again to show multi wrap this is a deliberately very long
line that should be wrapped multiple times because it contains far more than
eighty characters before reaching the end of the sentence
int total=apples+oranges+bananas+pears+grapes+watermelons+pineapples;
int total = apples + oranges + bananas + pears + grapes + watermelons +
pineapples;
[CTRL-D]
Assumptions/Restrictions/Clarifications
- You may assume every input line is at most 1000 characters long.
- You may assume that for
wrap_long_lines, there will always be a whitespace before the 80th character. - You may assume operators never appear at the beginning or end of a line.
- No error checking is required.
When you think your program is working,
you can use autotest
to run some simple automated tests:
1511 autotest advanced_linter
advanced_linter.c
// advanced_linter.c
//
// Written by Holly Field (z5477436)
// on 17/07/2026
//
// A mini linter that applies multiple formatting transformations to code lines.
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 1024
// Your function prototypes
void collapse_whitespace(char *line);
void add_operator_spacing(char *line);
void wrap_long_lines(char *line);
// Added helper function prototypes
void shift_right(char *line, int start);
void shift_left(char *line, int start);
int is_operator(char c);
// Provided function prototypes
void lint_line(char *line);
void remove_newline(char *line);
// -----------------------------------------------------------------------------
// DO NOT CHANGE THE MAIN FUNCTION
int main(void) {
char line[MAX_LINE_LENGTH];
printf("Enter lines of code:\n");
while (fgets(line, MAX_LINE_LENGTH, stdin) != NULL) {
remove_newline(line);
lint_line(line);
printf("%s\n", line);
}
return 0;
}
// -----------------------------------------------------------------------------
// YOUR FUNCTIONS
// -----------------------------------------------------------------------------
// Replace multiple spaces with a single space.
void collapse_whitespace(char *line) {
int i = 0;
int seen_first_non_space = 0;
while (line[i] != '\0') {
if (line[i] == ' ' && line[i + 1] == ' ' && seen_first_non_space) {
shift_left(line, i);
} else {
if (line[i] != ' ') {
seen_first_non_space = 1;
}
i++;
}
}
}
// Add spaces around operators (= + - * /).
void add_operator_spacing(char *line) {
int i = 0;
while (line[i] != '\0') {
// Check if the current character is an operator
if (is_operator(line[i])) {
if (i > 0 && line[i - 1] != ' ') {
// Character before operator is not a space, insert a space
shift_right(line, i);
line[i] = ' ';
i++;
}
if (line[i + 1] != ' ') {
// Character after operator is not a space, insert a space
shift_right(line, i + 1);
line[i + 1] = ' ';
}
}
i++;
}
}
// Helper function to check if a character is an operator
int is_operator(char c) {
return (c == '=' || c == '+' || c == '-' || c == '*' || c == '/');
}
// Wrap lines longer than 80 characters so that there is at most
// 80 characters per line.
// All trailing lines should be indented with a tab character.
void wrap_long_lines(char *line) {
int length = strlen(line);
if (length <= 80) {
return;
}
int char_count = 0;
for (int i = 0; line[i] != '\0'; i++) {
if (char_count >= 80) {
// Find the last space before the 80th character by
// tracking back from the current index
int wrap_index = i;
while (wrap_index > 0 && line[wrap_index] != ' ') {
wrap_index--;
}
// Can assume there is always a space before the 80th character,
// so wrap_index will not be 0
// Shift the rest of the line to make space for a newline and tab
shift_right(line, wrap_index);
line[wrap_index] = '\n';
// The space after the newline can be replaced with a tab
line[wrap_index + 1] = '\t';
char_count = 0;
}
char_count++;
}
}
// Helper function to shift characters in a string to the right
// from index 'start' to the end of the string.
// This is useful for inserting characters into the string.
void shift_right(char *line, int start) {
int end = strlen(line);
for (int i = end; i >= start; i--) {
line[i + 1] = line[i];
}
}
// Helper function to shift characters in a string to the left
// from index 'start' to the end of the string.
// This is useful for removing characters from the string.
void shift_left(char *line, int start) {
int end = strlen(line);
for (int i = start; i < end; i++) {
line[i] = line[i + 1];
}
}
// -----------------------------------------------------------------------------
// PROVIDED FUNCTIONS
// -----------------------------------------------------------------------------
// Applies every linting rule in order.
void lint_line(char *line) {
collapse_whitespace(line);
add_operator_spacing(line);
wrap_long_lines(line);
}
// Helper function to remove the newline character from the end of a string
void remove_newline(char *line) {
// Find the newline or end of string
int i = 0;
while (line[i] != '\n' && line[i] != '\0') {
i++;
}
// Goto the last position in the array and replace with '\0'
// Note: will have no effect if already at null terminator
line[i] = '\0';
}