Assignment 2 - Carriage Simulator
Welcome to CS: Carriage Simulator (or CS_CS for short)!
In order to address complaints about Light Rail, Transport NSW has hired you to make a program that can design and test train layouts, with the end goal being to increase passenger happiness.
Overview
Assignment Structure
This assignment will test your ability to create, use, manipulate and solve problems using linked lists. To do this, you'll be implementing a train as a linked list of carriages.
The following struct definition for a single carriage is provided for you in the starter code:
// A Train Carriage
struct carriage {
// carriage id in the form "N1002", unique, null terminated
char carriage_id[ID_SIZE];
// PASSENGER, BUFFET, RESTROOM, FIRST_CLASS
enum carriage_type type;
// Maximum number of passengers
int capacity;
// Current number of passengers
int occupancy;
struct carriage *next;
};
Purpose:
- Represents a single carriage, which can be connected to other carriages to form a train.
- Stores information about the carriage and its passengers.
Fields:
char carriage_id[ID_SIZE]
- The carriage's unique id, in the form of a null-terminated string of length
5
.
- The carriage's unique id, in the form of a null-terminated string of length
enum carriage_type type
- The type of carriage as an enum.
- Can be either
PASSENGER
,BUFFET
,RESTROOM
, orFIRST_CLASS
.
int capacity
- The maximum number of passengers that can fit inside the carriage.
int occupancy
- The number of passengers currently seated in the carriage.
occupancy
must always be less than or equal to thecapacity
. - Note that there is no standing capacity. We'll assume for this assignment that all passengers have a seat to themselves.
- The number of passengers currently seated in the carriage.
struct task *next
- The pointer to the next carriage in the train.
You may modify this struct if you wish, but you are not required to.
The following enum definition is also provided for you:
// The types of train carriages
enum carriage_type {INVALID_TYPE, PASSENGER, BUFFET, RESTROOM, FIRST_CLASS};
Purpose:
- Represents the different types of train carriages that we'll be dealing with in this assignment.
Values:
PASSENGER
- A regular passenger carriage.
BUFFET
- A train carriage with a built in restaurant.
- Sure to improve the travel experience of hungry passengers!
RESTROOM
- A carriage equipped with restroom facilities.
- Certainly good to have on a train, but maybe not the most pleasant to sit near.
FIRST_CLASS
- The most luxurious travel experience NSW has to offer.
- Fully equipped with all the amenities a passenger could ask for.
INVALID_TYPE
- Not an actual train carriage type.
- This usually represents if some error has occured when trying to scan in
enum carriage_types
.
From Stage 3 onwards, you will need to use an additional struct type: struct train
. This is further explained in Stage 3, and you won't need to worry about it until then.
Your tasks:
This assignment consists of four stages. Each stage builds on the work of the previous stage, and each stage has a higher complexity than its predecessor. You should complete the stages in order.
Stage 1.1 - Basic setup: Implement code to create a single struct carriage.
Stage 1.2 - Setup Command loop: Implement the Command loop.
In this stage, you will be setting up the program to read commands continuously until
CTRL-D
(similar to Assignment 1).Each command will start with a single unique character, and depending on the command, may be followed by some specific number of arguments relevant to that command.
Stage 1.3 onwards - Implement the commands:
Your bosses at Transport NSW have asked you to implement some different commands, which will allow them to create, modify and examine train designs.
How To Get Started
There are a few steps to getting started with Carriage Simulator.
- Create a new folder for your assignment work and move into it.
mkdir ass2 cd ass2
- Download the starter code (cs_cs.c) here or use this command on your CSE account to copy the file into your current directory:
cp -n /web/cs1511/23T1/activities/cs_cs/cs_cs.c .
- Run
1511 autotest cs_cs
to make sure you have correctly downloaded the file.
1511 autotest cs_cs
- Read through Stage 1.
Think about your solution, draw some diagrams to help you get started.
Start coding!
Reference Implementation
To help you understand the proper behaviour of the Carriage Simulator, we have provided a reference implementation. If you have any questions about the behaviour of your assignment, you can check and compare it to the reference implementation.
To run the reference implementation, use the following command:
1511 cs_cs
You might want to start by running the ?
command:
1511 cs_cs Enter command: ? =====================[ Carriage Simulator ]===================== ===============[ Usage Info ]=============== a [carriage_id] [type] [capacity] Add a carriage to the train p Print out all of the carriages in the train i [n] [carriage_id] [type] [capacity] Insert a carriage into the train at position `n` s [carriage_id] [n] Seat `n` passengers onto the train starting from carriage `carriage_id` d [carriage_id] [n] Remove `n` passengers from carriage `carriage_id` T Display the total number of passengers and empty seats on the train c [start_id] [end_id] Display the number of passengers and empty seats on between carriage `start_id` and carriage `end_id` m [source_id] [destination_id] [n] Move `n` passengers from one carrige to another, without kicking anyone off the train. h [carriage_id] Display the happiness of passengers in carriage `carriage_id` H Display the average happiness of all passengers on the train N Create a new empty train > Select the next train in the train list. < Select the previous train in the train list. P Display the train list. r [carriage_id] Remove carriage `carriage_id` from the selected train. R Remove the selected train. M Merge the selected train with the train after it. S [n] Split the current train into smaller trains. O Rearrange passengers on the selected train to optimise happiness. ? Show help ================================================================
Starter code:
The starter code contains some provided functions to help simplify some stages of the assignment. These functions have been fully implemented for you and should not need to be modified to complete the assignment.
These provided functions will be explained in the relevant stages of the assignment.
Please read the comments and the spec as we will suggest certain provided functions for you to use.
It also contains one stub function, create_carriage
, which you will need to complete in Stage 1.1.
Finally, the main
function contains some comments to help guide you through Stage 1.1 and Stage 1.2, as well as some printf
messages which run when the program starts and ends.
On program start:
Welcome to Carriage Simulator All aboard!
On program end:
Goodbye
Allowed C Features
In this assignment, you cannot use arrays, other than char
arrays, and cannot use the features explicitly banned in the Style Guide.
We strongly encourage you to complete the assessment using only features taught in lectures up to and including Weeks 8 and 9. The only C features you will need to get full marks in the assignment are:
int
,char
, anddouble
variables.- Enums.
- Structs.
- If statements.
- While and for loops.
- Your own functions.
- Pointers.
char
arrays/strings (you are not allowed to use arrays that are notchar
arrays).- Linked lists.
- Standard libraries:
stdio.h
,stdlib.h
, andstring.h
. - Good Code Style!
(Header comments, function comments, constants (#define
's), and whitespace and indentation.)
Using any other features will not increase your marks (and will make it more likely you make style mistakes that cost you marks).
If you choose to disregard this advice, you must still follow the Style Guide. You also may be unable to get help from course staff if you use features not taught in COMP1511.
Features that the Style Guide strongly discourages or bans will be penalised during marking.
Stage 1
For Stage 1 of this assignment, you will be implementing the command loop, as well as the commands to add carriages to a train.
Specifically, this will include:
- Implementing the
create_carriage
function. - Implementing the command loop, to scan commands until
CTRL-D
. - Adding Carriages to the end of a train.
- Printing out all carriages in the train.
- Checking that a carriage is valid.
By the end of this stage, your linked list of carriages will look something like:
Stage 1.1 - Creating a carriage
As you've probably found out by now, it can be really handy to have a function that malloc
s, initialises, and returns a single linked list node.
So in Stage 1.1, we will be implementing a function which does exactly that for a struct carriage
.
You'll find the following unimplemented function in the starter code:
struct carriage *create_carriage(
char id[ID_SIZE],
enum carriage_type type,
int capacity
) {
// STAGE 1.1
// TODO: malloc, initialise, and return a new carriage.
// hint: you will have to replace NULL in this return statement.
return NULL;
}
Your task is to complete this function, so that it:
- Malloc's a new
struct carriage
. - Copies the
id
,type
andcapacity
into the corresponding struct fields. - Initialises all other fields to some reasonable value.
- Returns the struct.
Assumptions
char id[ID_SIZE]
will always be 5 or less characters long (with a null terminator at the end).- No error handling is required for this stage.
Testing
There are no autotests for Stage 1.1.
Instead, you may want to double check your work by compiling your code using dcc
and make sure there are no warnings or errors.
You could also write some temporary testing code to check that your create_carriage
function works properly when it runs.
For example, you could copy the following testing code into your main function:
///////////////////////////// TESTING CODE /////////////////////////////
// create a struct carriage with
// id : "N1001"
// type : PASSENGER
// capacity : 20
struct carriage *test_carriage = create_carriage("N1001", PASSENGER, 20);
// print out all it's fields.
printf("carriage_id: %s\n", test_carriage->carriage_id);
printf("capacity: %d\n", test_carriage->capacity);
printf("occupancy: %d\n", test_carriage->occupancy);
printf("next field: %p\n", test_carriage->next);
if (test_carriage->type == PASSENGER) {
printf("type: passenger\n");
} else {
printf("not a passenger carriage\n");
}
///////////////////////////// TESTING CODE /////////////////////////////
This code just calls create_carriage
to malloc and initialise a struct carriage
, and then prints out all of its fields.
If you run it, it should print out something like:
carriage_id: N1001 capacity: 20 occupancy: 0 next field: (nil) type: passenger
Stage 1.2 - Implement the Command Loop
Now we'll be implementing the command loop, allowing your program to take in and perform different operations on the train.
From this stage onwards, your program should run in a loop, scanning in and executing commands until CTRL-D
. You should implement this command loop between the welcome and goodbye messages, so that your program runs in the following order:
First, print out the welcome message (the printf statement is included in the starter code).
Then run in a loop until
CTRL-D
, and:- Print out the prompt
Enter command:
; - Scan in a command character;
- Scan in any arguments following the command character and execute the command.
- Print out the prompt
After
CTRL-D
is pressed, the goodbye message should be printed (the printf statement is included in the starter code), and then your program should end.
Each command will start with a single unique character, and may be followed by a variable number of arguments depending on the command.
The unique character for each different command and the number and type of the command arguments are specified in the relevant stages of this assignment.
The first command you have to implement is the Help command, which is described below.
Command: Help
?
Description
When the character '?'
is entered into your program, it should run the Help command. This command doesn't read in any arguments following the initial command and should display a message which lists the different commands and their arguments.
The purpose of this command is to allow anyone using our program to easily look up the different commands.
A helper function: void print_usage(void)
has been provided to help you print and format this message.
You can find more information about it here: Provided helper function.
This is what it should look like when you run the Help command:
Because of the command loop, after the Help command has finished running, your program should print out
Enter command:
and then wait for the user to either enter another command, or press CTRL-D
to end the program.
Provided helper functions
A procedure,print_usage
, has been provided for you in the starter code which prints out the help message.
So all you need to do for this command is call the print_usage
procedure when the ?
character is scanned in.
Assumptions
- All commands will begin with a single unique character.
Some will be followed by a number of arguments, depending on the command. - You can assume that commands will always be given the correct number of arguments, so you do NOT need to check for this.
- You do not need to handle unknown commands.
- No error handling is required for this stage.
Examples
Stage 1.3 - Add carriages
Now it's time to start constructing your train!
When you run your program, the train (and your linked list representing the train) will start out completely empty.
It should look something like this:
To make your train more interesting (or even just a train at all), we need a way of adding empty carriages onto the end.
Command: Append carriage to train
a [carriage_id] [type] [capacity]
Description
The a
command takes in 3 arguments, a string of length 5 called carriage_id
, an enum carriage_type
called type
, and an integer capacity
.
Some helper functions have been provided to help you scan in these arguments:
void scan_id(char id[ID_SIZE])
enum carriage_type scan_type(void)
You can find more information about them here: Provided helper functions.
When the a
command is entered, your program should create a new carriage containing the carriage_id
, type
and capacity
, then append it to the end of the list of carriages (i.e. perform a tail insertion).
Finally, it should print out a message to confirm that the command was successful:Carriage: '[carriage_id]' attached!
You should replace [carriage_id]
in this message with the carriage_id
you scanned in.
For example, if we have just started the program, and we use the a
command once, it would look like:
Welcome to Carriage Simulator All aboard! Enter command: a N1001 passenger 10 Carriage: 'N1001' attached!
Our linked list should now look like this:
If we then run the a
command again:
Enter command: a N1002 buffet 20 Carriage: 'N1002' attached!
then our linked list should now look like:
Provided helper functions
Two helper functions have been provided for this stage:
void scan_id(char id[ID_SIZE])
:- Scans the
carriage_id
intochar id[ID_SIZE]
.
- Scans the
enum carriage_type scan_type(void)
:- Scans the
type
and returns it as anenum carriage_type
. - Returns
INVALID_TYPE
if thetype
did not correspond to one of the valid carriage types.
- Scans the
You should use these functions to help you scan in the carriage_id
and type
arguments.
Remember that the arguments must be scanned in the correct order, so your code to scan arguments will look something like the following:
...
// Create variables to scan arguments into
char id[ID_SIZE];
enum carriage_type type;
int capacity;
// We receive the arguments in the order: [carriage_id] [type] [capacity]
// 1. Scan id first
scan_id(id);
// 2. Then scan the carriage type
type = scan_type();
// 3. Then scan the capacity. We can just use scanf() for this
scanf(" %d", &capacity);
// We've scanned in all the arguments! Now we can use them in our code
...
Errors
There is no error handling required for this stage. You'll be adding this later in Stage 1.5.
Assumptions
carriage_id
will always be 5 or less characters long (with a null terminator at the end).type
will be entered as a lowercase string and automatically converted to the correctenum carriage_type
for you by thescan_type
function, when the function is used.Corresponding string enum carriage_type “passenger”
PASSENGER
"first_class"
FIRST_CLASS
“buffet"
BUFFET
“restroom”
RESTROOM
any invalid string INVALID_TYPE
Note that you don't need to worry about
INVALID_TYPE
until Stage 1.5.
Until then, you can assume that the returnedenum carriage_type
will never beINVALID_TYPE
.
Examples
Stage 1.4 - Printing out the train
Now we want to be able to display the train and all its carriages.
Command: Print train
p
Description
The p
command takes no arguments.
When the p
command is run, your program should print out all carriages in the train, from head to tail.
A function has been provided for you to format and print a single carriage:
Provided helper functions.
If there are no carriages in the train, the following message should be printed instead:This train is empty!
Provided helper functions
One helper function has been provided for this stage:
void print_carriage(struct carriage *carriage)
- Takes in a pointer to a single
struct carriage
and prints it in the correct format.
- Takes in a pointer to a single
This means you don't need to worry about copying the exact output format for the p
command. Instead, your code should loop through the linked list, and call the print_carriage
function for each carriage.
Errors/Assumption/Clarifications: N/A
Examples
Stage 1.5 - Handling Errors
By this stage, you should now be able to construct and display a train full of different carriages.
Well done!
You show the program so far to your bosses and they seem impressed.
There's one problem... You accidentally run the command a N0001 baffet -5
:
Enter command: a N0001 baffet -5 Carriage: 'N0001' attached! Enter command: p ---------\/--------- | N0001 | | (INVALID) | | Occupancy: 0/-5 | ---------||---------
ruh roh
That train carriage doesn't make much sense.
Its type is invalid, and what does a -5
capacity even mean??
On top of this, you remember from your extensive train-ing that each carriage id has to be unique! But right now there's nothing in your code stopping someone from adding 100 carriages all with the same id.
Your bosses now seem less impressed, and they politely ask you to fix these bugs.
So in this stage you will be modifying your code from Stage 1.3, to add some restrictions on what carriages can be added to a train.
Error Conditions:
When running the a
command, you scanned in the carriage_id
, type
and capacity
for the new carriage.
Now, if any one of the following conditions are met, then you should not append a new carriage to linked list. You should instead print out an error message:
If
type
(returned by thescan_type
function) isINVALID_TYPE
, the following error should be printed:ERROR: Invalid carriage type
If
capacity <= 0
orcapacity > 999
, the following error should be printed:ERROR: Capacity should be between 1 and 999
If there is already a carriage in the linked list that contains this
carriage_id
, the following error should be printed:ERROR: a carriage with id: '[carriage_id]' already exists in this train
This last condition might be a little harder than it seems. How can you check that none of the carriages in your list contain a particular
carriage_id
?
Clarifications
- If more than one error occurs, only the first error should be addressed by printing an error message. This is the same for all future commands.
Examples
Testing and Submission
Remember to do your own testing
Are you finished with this stage? If so, you should make sure to do the following:
- Run
1511 style
, and clean up any issues a human may have reading your code. Don't forget -- 20% of your mark in the assignment is based on style and readability! - Autotest for this stage of the assignment by running the
autotest-stage
command as shown below. - Remember -- give early, and give often. Only your last submission counts, but why not be safe and submit right now?
1511 style cs_cs.c 1511 autotest-stage 01 cs_cs give cs1511 ass2_cs_cs cs_cs.c