Week 09 Laboratory Sample Solutions

Objectives

  • More complex linked lists
  • Practice working with lists of lists

Preparation

Before the lab you should re-read the relevant lecture slides and their accompanying examples.

Getting Started

Exercise — in pairs:
Check whether a Linked List is in Increasing Order

Download list_increasing.c here, or copy it to your CSE account using the following command:

cp -n /web/cs1511/20T1/activities/list_increasing/list_increasing.c .

Your task is to add code to this function in list_increasing.c:

int increasing(struct node *head) {

    // PUT YOUR CODE HERE (change the next line!)
    return 42;

}
increasing is given one argument, head, which is the pointer to the first node in a linked list.

Add code to increasing so that its returns 1 if the list is in increasing order - the value of each list element is larger than the element before.

For example if the linked list contains these 8 elements:

1, 7, 8, 9, 13, 19, 21, 42

increasing should return 1 because is is increasing order

Testing

list_increasing.c also contains a main function which allows you to test your increasing function.

This main function:

  • converts the command-line arguments to a linked list
  • assigns a pointer to the first node in the linked list to head
  • calls list_increasing(head)
  • prints the result.

Do not change this main function. If you want to change it, you have misread the question.

Your list_increasing function will be called directly in marking. The main function is only to let you test your list_increasing function

Here is how you use main function allows you to test list_increasing:

dcc list_increasing.c -o list_increasing
./list_increasing 1 2 4 8 16 32 64 128 256
1
./list_increasing 2 4 6 5 8 9
0
./list_increasing 13 15 17 17 18 19
0
./list_increasing 2 4
1
./list_increasing 42
1
./list_increasing
1

Assumptions/Restrictions/Clarifications.

increasing should return a single integer.

increasing should not change the linked list it is given. Your function should not change the next or data fields of list nodes.

increasing should not use arrays.

increasing should not call malloc.

increasing should not call scanf (or getchar or fgets).

You can assume the linked list only contains positive integers.

increasing should not print anything. It should not call printf.

Do not change the supplied main function. It will not be tested or marked.

New! You can run an automated code style checker using the following command:
1511 style list_increasing.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest list_increasing

When you are finished working on this exercise, you and your lab partner must both submit your work by running give:

give cs1511 lab09_list_increasing list_increasing.c

Note, even though this is a pair exercise, you both must run give from your own account before Friday 19 April 20:00 to obtain the marks for this lab exercise.

Sample solution for list_increasing.c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

struct node {
    struct node *next;
    int          data;
};

int increasing(struct node *head);
struct node *strings_to_list(int len, char *strings[]);

int main(int argc, char *argv[]) {
    // create linked list from command line arguments
    struct node *head = strings_to_list(argc - 1, &argv[1]);

    int result = increasing(head);
    printf("%d\n", result);

    return 0;
}

// return 1 if values in a linked list are in increasing order,
// return 0, otherwise

int increasing(struct node *head) {
    // If the list is empty, it's considered increasing, so return 1.
    if (head == NULL) {
        return 1;
    }

    // Assume that it is increasing, and look for evidence
    // that proves otherwise.
    int is_increasing = 1;

    struct node *curr = head;
    while (curr->next != NULL) {
        // If this one is not less than the next one,
        // the list definitely isn't increasing
        // (since these two nodes are out of order).
        if (curr->data >= curr->next->data)  {
            is_increasing = 0;
        }
        curr = curr->next;
    }

    // At this point, if is_increasing is still 1, we didn't find
    // any nodes that were out of order.
    //
    // However, if we did find any nodes that were out of order,
    // we set it to 0 in the loop above.
    //
    // So, is_increasing contains the answer to return.
    return is_increasing;
}


// create linked list from array of strings
struct node *strings_to_list(int len, char *strings[]) {
    struct node *head = NULL;
    for (int i = len - 1; i >= 0; i = i - 1) {
        struct node *n = malloc(sizeof (struct node));
        assert(n != NULL);
        n->next = head;
        n->data = atoi(strings[i]);
        head = n;
    }
    return head;
}
Alternative solution for list_increasing.c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

struct node {
    struct node *next;
    int          data;
};

int increasing(struct node *head);
struct node *strings_to_list(int len, char *strings[]);

int main(int argc, char *argv[]) {
    // create linked list from command line arguments
    struct node *head = strings_to_list(argc - 1, &argv[1]);

    int result = increasing(head);
    printf("%d\n", result);

    return 0;
}

// return 1 if values in a linked list are in increasing order,
// return 0, otherwise

int increasing(struct node *head) {
    if (head == NULL) {
        return 1;
    }

    struct node *p = head;
    while (p->next != NULL) {
        if (p->data >= p->next->data)  {
            return 0;
        }
        p = p->next;
    }

    return 1;
}


// create linked list from array of strings
struct node *strings_to_list(int len, char *strings[]) {
    struct node *head = NULL;
    for (int i = len - 1; i >= 0; i = i - 1) {
        struct node *n = malloc(sizeof (struct node));
        assert(n != NULL);
        n->next = head;
        n->data = atoi(strings[i]);
        head = n;
    }
    return head;
}
Alternative solution for list_increasing.c
#include <stdio.h>
#include <assert.h>

struct node {
    struct node *next;
    int          data;
};

// return 1 if values in a linked list in increasing order
// recursive solution

int increasing(struct node *head) {
    if (head == NULL || head->next == NULL) {
        return 1;
    } else if (head->data >= head->next->data)  {
        return 0;
    } else {
        return increasing(head->next);
    }
}

Exercise — in pairs:
Delete First Element from a Linked List

Download list_delete_first.c here, or copy it to your CSE account using the following command:

cp -n /web/cs1511/20T1/activities/list_delete_first/list_delete_first.c .

Your task is to add code to this function in list_delete_first.c:

//
// Delete the first node in list.
// The deleted node is freed.
// The head of the list is returned.
//
struct node *delete_first(struct node *head) {

    // PUT YOUR CODE HERE (change the next line!)
    return NULL;
}
Note list_delete_first.c uses the following familiar data type:
struct node {
    struct node *next;
    int          data;
};
delete_first is given one argument, head, which is the pointer to the first node in the linked list.

Add code to delete_first so that it deletes the first node from list.

delete_first should return a pointer to the new first node in the list.

If the list is now empty delete_first should return NULL.

delete_first should call free to free the memory of the node it deletes.

For example if the linked list contains these 8 elements:

16, 7, 8, 12, 13, 19, 21, 12

delete_first should return a pointer to a list with these elements:

7, 8, 12, 13, 19, 21, 12

Hint: this is a simple task requiring only a few lines of code.

Testing

list_delete_first.c also contains a main function which allows you to test your delete_first function. It converts command-line arguments to a linked list, calls delete_first, and then prints the result.

Do not change this main function. If you want to change it, you have misread the question.

Your delete_first function will be called directly in marking. The main function is only to let you test your delete_first function

Here is how you the main function allows you to test delete_first:

cp -n /web/cs1511/20T1/activities/list_delete_first/list_delete_first.c .
dcc list_delete_first.c -o list_delete_first
./list_delete_first 16 7 8 12 13 19 21 12
[7, 8, 12, 13, 19, 21, 12]
./list_delete_first 2 4 6 2 4 6
[4, 6, 2, 4, 6]
./list_delete_first 42
[]
./list_delete_first
[]

Assumptions/Restrictions/Clarifications.

delete_first should call free to free the memory for the node it deletes

delete_first should not change the data fields of list nodes.

delete_first should not use arrays.

delete_first should not call malloc.

delete_first should not call scanf (or getchar or fgets).

delete_first should not print anything. It should not call printf.

Do not change the supplied main function. It will not be tested or marked.

New! You can run an automated code style checker using the following command:
1511 style list_delete_first.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest list_delete_first

When you are finished working on this exercise, you and your lab partner must both submit your work by running give:

give cs1511 lab09_list_delete_first list_delete_first.c

Note, even though this is a pair exercise, you both must run give from your own account before Friday 19 April 20:00 to obtain the marks for this lab exercise.

Sample solution for list_delete_first.c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

struct node {
    struct node *next;
    int          data;
};

struct node *delete_first(struct node *head);
struct node *strings_to_list(int len, char *strings[]);
void print_list(struct node *head);

int main(int argc, char *argv[]) {
    // create linked list from command line arguments
    struct node *head = strings_to_list(argc - 1, &argv[1]);

    struct node *new_head = delete_first(head);
    print_list(new_head);

    return 0;
}

// Delete the first node in list.
// The deleted node is freed.
// The head of the list is returned.

struct node *delete_first(struct node *head) {
    if (head == NULL) {
        // list is empty no node to delete
        return NULL;
    }
    struct node *new_head = head->next;
    free(head);
    return new_head;
}

// create linked list from array of strings
struct node *strings_to_list(int len, char *strings[]) {
    struct node *head = NULL;
    for (int i = len - 1; i >= 0; i = i - 1) {
        struct node *n = malloc(sizeof (struct node));
        assert(n != NULL);
        n->next = head;
        n->data = atoi(strings[i]);
        head = n;
    }
    return head;
}

// print linked list
void print_list(struct node *head) {
    printf("[");

    for (struct node *n = head; n != NULL; n = n->next) {
        // If you're getting an error here,
        // you have returned an invalid list
        printf("%d", n->data);
        if (n->next != NULL) {
            printf(", ");
        }
    }
    printf("]\n");
}

Exercise — in pairs:
Delete First Element Containing A Value from a Linked List

Download list_delete_contains.c here, or copy it to your CSE account using the following command:

cp -n /web/cs1511/20T1/activities/list_delete_contains/list_delete_contains.c .

Your task is to add code to this function in list_delete_contains.c:

//
// Delete the first node in the list containing the value `value`.
// The deleted node is freed.
// If no node contains `value`, the list is not changed.
// The head of the list is returned.
//
struct node *delete_contains(int value, struct node *head) {

    // PUT YOUR CODE HERE (change the next line!)
    return NULL;

}
Note list_delete_contains.c uses the following familiar data type:
struct node {
    struct node *next;
    int          data;
};
delete_contains is given two argument, value and head. value is an int. head is the pointer to the first node in a linked list.

Add code to delete_contains so that it deletes the first node in the linked list that whose data field equals value.

If value does not occur in the linked list, the list should not be changed.

If value occurs more than once in the linked list, only the first occurrence should be deleted.

delete_contains should return a pointer to the new list.

If the list is now empty delete_contains should return NULL.

delete_contains should call free to free the memory of the node it deletes.

For example if value is 12 and the linked list contains these 8 elements:

16, 7, 8, 12, 13, 19, 21, 12

delete_contains should return a pointer to a list with these elements:

16, 7, 8, 13, 19, 21, 12

Testing

list_delete_contains.c also contains a main function which allows you to test your delete_contains function.

This main function:

  • converts the command-line arguments to a linked list
  • assigns a pointer to the first node in the linked list to head
  • reads a single integer from standard input and assigns it to value
  • calls delete_contains(value, head)
  • prints the result.

Do not change this main function. If you want to change it, you have misread the question.

Your delete_contains function will be called directly in marking. The main function is only to let you test your delete_contains function

cp -n /web/cs1511/20T1/activities/list_delete_contains/list_delete_contains.c .
dcc list_delete_contains.c -o list_delete_contains
./list_delete_contains 16 7 8 12 13 19 21 12
12
[16, 7, 8, 13, 19, 21, 12]
./list_delete_contains 16 7 8 12 13 19 21 12
42
[16, 7, 8, 12, 13, 19, 21, 12]
./list_delete_contains 4 6 2 4 6
2
[4, 6, 4, 6]
./list_delete_contains 42
42
[]
./list_delete_contains
42
[]

Assumptions/Restrictions/Clarifications.

delete_contains should call free to free the memory for the node it deletes

delete_first should not change the data fields of list nodes.

delete_contains should not use arrays.

delete_contains should not call malloc.

delete_contains should not call scanf (or getchar or fgets).

delete_contains should not print anything. It should not call printf.

Do not change the supplied main function. It will not be tested or marked.

New! You can run an automated code style checker using the following command:
1511 style list_delete_contains.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest list_delete_contains

When you are finished working on this exercise, you and your lab partner must both submit your work by running give:

give cs1511 lab09_list_delete_contains list_delete_contains.c

Note, even though this is a pair exercise, you both must run give from your own account before Friday 19 April 20:00 to obtain the marks for this lab exercise.

Sample solution for list_delete_contains.c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

struct node {
    struct node *next;
    int          data;
};

struct node *delete_contains(int value, struct node *head);
struct node *strings_to_list(int len, char *strings[]);
void print_list(struct node *head);

int main(int argc, char *argv[]) {
    int value;
    scanf("%d", &value);
    // create linked list from command line arguments
    struct node *head = strings_to_list(argc - 1, &argv[1]);

    struct node *new_head = delete_contains(value, head);
    print_list(new_head);

    return 0;
}


// Delete the first node in the list containing i
// The deleted node is freed.
// If no node contains i, the list is not changed
// The head of the list is returned.

struct node *delete_contains(int value, struct node *head) {
    if (head == NULL) {
        // list is empty no node to delete
        return NULL;
    } else if (head->data == value) {
        // deleting the first node
        struct node *new_head = head->next;
        free(head);
        return new_head;
    } else if (head->next == NULL) {
        // first node is the only node
        // first node is definitely not value
        return head;
    }

    struct node *n = head;
    // find node before first node containing i
    while (n->next->next != NULL && n->next->data != value) {
        n = n->next;
    }

    if (n->next->data == value) {
        struct node *new_next = n->next->next;
        free(n->next);
        n->next = new_next;
    }
    return head;
}

// create linked list from array of strings
struct node *strings_to_list(int len, char *strings[]) {
    struct node *head = NULL;
    int i = len - 1;
    while (i >= 0) {
        struct node *n = malloc(sizeof (struct node));
        assert(n != NULL);
        n->next = head;
        n->data = atoi(strings[i]);
        head = n;
        i -= 1;
    }   
    return head;
}

// print linked list
void print_list(struct node *head) {
    printf("[");    
    struct node *n = head;
    while (n != NULL) {
        // If you're getting an error here,
        // you have returned an invalid list
        printf("%d", n->data);
        if (n->next != NULL) {
            printf(", ");
        }
        n = n->next;
    }
    printf("]\n");
}

Exercise — in pairs:
Determine how many elements are the same in two lists

Download list_intersection_size.c here, or copy it to your CSE account using the following command:

cp -n /web/cs1511/20T1/activities/list_intersection_size/list_intersection_size.c .

Your task is to add code to this function in list_intersection_size.c:

// return the number of values which occur in both linked lists
// no value is repeated in either list
int intersection_size(struct node *head1, struct node *head2) {

    // PUT YOUR CODE HERE (change the next line!)
    return 42;

}
Note list_intersection_size.c uses the following familiar data type:
struct node {
    struct node *next;
    int          data;
};
intersection_size is given two arguments, head1 and head2, which are pointers to the first node of linked lists.

Add code to intersection_size so that its returns the number of values that occur in both linked list.

Assume no value occurs more than once in either linked list.

For example, if the two lists contain these values:

3, 1, 4
2, 7, 1, 8, 3

intersection_size should return 2, because these 2 elements occur in both lists:

1, 3

Testing

list_intersection_size.c also contains a main function which allows you to test your intersection_size function.

This main function:

  • uses a command line argument of "-" to separate the values for two linked lists.
  • converts the command-line arguments before the "-" to a linked list
  • assigns a pointer to the first node in the linked list to head1
  • converts the command-line arguments after the "-" to a linked list
  • assigns a pointer to the first node in the linked list to head2
  • calls intersection_size(head1, head2)
  • prints the result.

Do not change this main function. If you want to change it, you have misread the question.

Your intersection_size function will be called directly in marking. The main function is only to let you test your intersection_size function

Here is how the main function allows you to test intersection_size:

cp -n /web/cs1511/20T1/activities/list_intersection_size/list_intersection_size.c .
dcc list_intersection_size.c -o list_intersection_size
./list_intersection_size 3 1 4 - 2 7 1 8 3
2
./list_intersection_size 16 7 8 12 - 13 19 21 12
1
./list_intersection_size 2 4 6 - 2 4 6
3
./list_intersection_size 3 5 7 11 13 - 15 17 19 23 29
0
./list_intersection_size 1 2 3 4 - 3 2 1
3
./list_intersection_size - 1 2 3 4
0
./list_intersection_size 4 3 2 1 -
0
./list_intersection_size -
0

Assumptions/Restrictions/Clarifications.

intersection_size should return a single integer.

No value will occur more than once in either linked list.

intersection_size should not change the linked lists it is given. Your function should not change the next or data fields of list nodes.

intersection_size should not use arrays.

intersection_size should not call malloc.

intersection_size should not call scanf (or getchar or fgets).

intersection_size should not print anything. It should not call printf.

Do not change the supplied main function. It will not be tested or marked.

New! You can run an automated code style checker using the following command:
1511 style list_intersection_size.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest list_intersection_size

When you are finished working on this exercise, you and your lab partner must both submit your work by running give:

give cs1511 lab09_list_intersection_size list_intersection_size.c

Note, even though this is a pair exercise, you both must run give from your own account before Friday 19 April 20:00 to obtain the marks for this lab exercise.

Sample solution for list_intersection_size.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>

struct node {
    struct node *next;
    int          data;
};

int intersection_size(struct node *head1, struct node *head2);
int member(int i, struct node *head);
struct node *strings_to_list(int len, char *strings[]);

int main(int argc, char *argv[]) {
    // create two linked lists from command line arguments
    int dash_arg = argc - 1;
    while (dash_arg > 0 && strcmp(argv[dash_arg], "-") != 0) {
        dash_arg = dash_arg - 1;
    }
    struct node *head1 = strings_to_list(dash_arg - 1, &argv[1]);
    struct node *head2 = strings_to_list(argc - dash_arg - 1, &argv[dash_arg + 1]);

    int result = intersection_size(head1, head2);
    printf("%d\n", result);

    return 0;
}

// return the number of values which occur in both linked lists
// no value is repeated in either list
int intersection_size(struct node *head1, struct node *head2) {
    int num_both = 0;
    struct node *p = head1;
    while (p != NULL) {
        if (member(p->data, head2)) {
            num_both = num_both + 1;
        }
        p = p->next;
    }
    return num_both;
}

// return 1 if i occurs in list, 0 otherwise
int member(int i, struct node *head) {
    struct node *p = head;
    while (p != NULL) {
        if (p->data == i) {
            return 1;
        }
        p = p->next;
    }
    return 0;
}


// create linked list from array of strings
struct node *strings_to_list(int len, char *strings[]) {
    struct node *head = NULL;
    for (int i = len - 1; i >= 0; i = i - 1) {
        struct node *n = malloc(sizeof (struct node));
        assert(n != NULL);
        n->next = head;
        n->data = atoi(strings[i]);
        head = n;
    }
    return head;
}
Alternative solution for list_intersection_size.c
#include <stdio.h>

struct node {
    struct node *next;
    int          data;
};

// return 1 if i  occurs in list, 0 otherwise
int member(int i, struct node *head) {
    if (head == NULL) {
        return 0;
    } if (head->data == i) {
        return 1;
    } else {
        return member(i, head->next);
    }
}

// return the number of values which occur in both linked lists
// no value is repeated in either list
// cute, recursive solution
int intersection_size(struct node *head1, struct node *head2) {
    if (head1 == NULL) {
        return 0;
    } else {
        return member(head1->data, head2) + intersection_size(head1->next, head2);
    }
}

Challenge Exercise — individual:
Determine whether a list of lists contains a diagonal line of identical values

Download lists_diagonal.c here, or copy it to your CSE account using the following command:

cp -n /web/cs1511/20T1/activities/lists_diagonal/lists_diagonal.c .

Your task is to add code to this function in lists_diagonal.c:

// Treat the linked lists like they're a 2D array
// and return 1 if the first element is repeated
// diagonally through the lists
int has_diagonal(struct list_node *head) {
    return 0;
}
lists_diagonal is written using the following structs that cannot be changed:

struct node {
    int data;
    struct node *next;
};

struct list_node {
    struct node *my_list;
    struct list_node *next;
};

The node struct is a normal linked list node.

The list_node struct is used to make a linked list where each element contains a list of nodes.

has_diagonal should take a pointer to the head of a list_node list and return a 1 if it finds a diagonal or a 0 if it doesn't. A diagonal in this exercise means that the first number in the first list is the same as the second number in the second list and the third number in the third list and so on.

For example if the list of lists looks like this:

list_node 0 contains the list {5, 0, 0}
list_node 1 contains the list {0, 5, 0}
list_node 2 contains the list {0, 0, 5}

has_diagonal should return 1.

However, if the list of lists looks like this:

list_node 0 contains the list {5, 0, 0, 0}
list_node 1 contains the list {0, 4, 0, 0}
list_node 2 contains the list {0, 0, 5, 0}
list_node 3 contains the list {0, 0, 0, 5}

has_diagonal should return 0, because the 2nd element of the second list does not equal the value of the first element of the first list.

Assumptions/Restrictions/Clarifications.

struct node and struct list_node cannot be edited. They must be used as they are.

You may not use arrays in this solution. Arrays are not necessary to complete this task.

You can assume that you'll never receive an empty list of list_nodes.

You can assume that all lists of nodes are also not empty.

You can assume that there will always be the same number of nodes in each list and that will be the same number of list_nodes. That is to say, the 2D grid formed by the lists will always be square. Your submitted file may contain a main function. It will not be tested or marked.

New! You can run an automated code style checker using the following command:
1511 style lists_diagonal.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest lists_diagonal

When you are finished working on this exercise, you must submit your work by running give:

give cs1511 lab09_lists_diagonal lists_diagonal.c

You must run give before Friday 19 April 20:00 to obtain the marks for this lab exercise. Note that this is an individual exercise, the work you submit with give must be entirely your own.

Sample solution for lists_diagonal.c
#include <stdio.h>
#include <stdlib.h>

// Do not edit these structs. You may use them exactly as
// they are but you cannot make changes to them

// A node in a linked list
struct node {
    int data;
    struct node *next;
};

// a list_node in a linked list. Each list_node
// contains a list of nodes.
struct list_node {
    struct node *my_list;
    struct list_node *next;
};

// Treat the linked lists like they're a 2D array
// and return 1 if the first element is repeated
// diagonally through the lists
int has_diagonal(struct list_node *head) {
    int has = 1;
    int i = 0;
    // assuming that we're never getting an empty first list
    int number = head->my_list->data;
    while (head != NULL) {
        int j = 0;
        struct node *n = head->my_list;
        while (n != NULL) {
            if (j == i && n->data != number) {
                // we're at the diagonal and they're not equal
                has = 0;
            }
            n = n->next;
            j++;            
        }
        i++;
        head = head->next;
    }
    return has;
}

// This helper function is for the main below and will
// have no effect on your has_diagonal. It does not
// need to be modified.
struct node *make_list(int a, int b, int c);

// This is a main function which could be used
// to test your has_diagonal function.
// It will not be marked.
// Only your has_diagonal function will be marked.
//
// It's recommended to change the int values in this
// main to test whether your has_diagonal is working.
int main(void) {
    struct list_node *head = malloc(sizeof (struct list_node));
    struct list_node *l = head;
    
    // create the first list
    l->my_list = make_list(5, 0, 0);
    
    // create the second list
    l->next = malloc(sizeof (struct list_node));
    l = l->next;
    l->my_list = make_list(0, 5, 0);
    
    // create the third list
    l->next = malloc(sizeof (struct list_node));
    l = l->next;
    l->my_list = make_list(0, 0, 5);
    l->next = NULL;
    
    printf("The result of has_diagonal is: %d\n", has_diagonal(head));
    
    return 0;
}

struct node *make_list(int a, int b, int c) {
    struct node *head = malloc(sizeof (struct node));
    struct node *n = head;
    n->data = a;
    n->next = malloc(sizeof (struct node));
    n = n->next;
    n->data = b;
    n->next = malloc(sizeof (struct node));
    n = n->next;
    n->data = c;
    n->next = NULL;
    
    return head;
}

Challenge Exercise — individual:
Play the Game of Chairs. Win or die.

Download musical_chairs.c here, or copy it to your CSE account using the following command:

cp -n /web/cs1511/20T1/activities/musical_chairs/musical_chairs.c .

Your task is to add code to this function in musical_chairs.c:

// Make music for a certain number of turns.
// Each turn of music makes the players move
// one chair along the list.
// After they've moved that many times, the
// first chair in the list is removed, along
// with the person sitting in it.
struct chair *make_music(int turns, struct chair *chairs) {
    // IMPLEMENT THIS FUNCTION
    return chairs;
}

Welcome to the Game of Chairs, where you either win or have your memory freed.

musical_chairs is written using the following structs that cannot be changed:

// player in the game of chairs
struct player {
    char name[MAX_NAME_LENGTH];
};

// A node in a linked list of chairs
struct chair {
    struct player *sitting;
    struct chair *next;
};

The chair struct is a linked list node.

The player struct represents a player that can sit on a chair (represented by the chair's pointer aiming at the player).

make_music is given a pointer to a chair, which is the first element in a list of chairs. It is also given an int turns which represents how many turns of movement there will be before the music stops.

Like the game of Musical Chairs, this program will have players move along the linked list, changing which chair they're sitting in.

In make_music, every player moves turns spaces along the linked list. Anyone who moves off the end of the linked list, should then move to the head of the list, so the players will end up rotating through the list as if it's a loop. This would be similar to if the next of the last chair was aimed at the first chair.

Once all the players have finished moving, the head of the list of chairs is removed. This means both that chair and the player sitting in it are removed from the game.

make_music should then print out the name of the player that was removed.

Be careful to make sure you free all memory used in this game!

For example if a list of chairs called thrones looks like this:

throne points at the player named "Spoiler Alert"
throne points at the player named "Eddard Stark"
throne points at the player named "Joffrey Baratheon"
throne points at the player named "Cersei Lannister"
throne points at the player named "Robert Baratheon"

Then the following function is called:

make_music(3, thrones);

The output would be:

Joffrey Baratheon

and the resulting linked list would look like this:

(throne pointed at "Joffrey Baratheon" but was removed)
throne points at the player named "Cersei Lannister"
throne points at the player named "Robert Baratheon"
throne points at the player named "Spoiler Alert"
throne points at the player named "Eddard Stark"

In this list, all the players have moved down 3 chairs and are now sitting in different chairs. Anyone that moved past the end of the chairs was moved back to the top of the list of chairs.

Assumptions/Restrictions/Clarifications.

struct player and struct chair cannot be edited. They must be used as they are.

The be_seated function will help you create chairs. It cannot be edited and must be used as it is.

You may not use arrays in this solution. Arrays are not necessary to complete this task.

You must free all memory used in your program. Use dcc --leak-check if you need to check for memory leaks. Autotest will also check your code for leaks Your submitted file may contain a main function. It will not be tested or marked.

New! You can run an automated code style checker using the following command:
1511 style musical_chairs.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest musical_chairs

When you are finished working on this exercise, you must submit your work by running give:

give cs1511 lab09_musical_chairs musical_chairs.c

You must run give before Friday 19 April 20:00 to obtain the marks for this lab exercise. Note that this is an individual exercise, the work you submit with give must be entirely your own.

Sample solution for musical_chairs.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_NAME_LENGTH 100

// Do not edit these structs. You may use them exactly as
// they are but you cannot make changes to them

// player in the game of chairs
struct player {
    char name[MAX_NAME_LENGTH];
};

// A node in a linked list of chairs
struct chair {
    struct player *sitting;
    struct chair *next;
};

// Move the players along to the next chair
// The chairs are acting as a loop, so if 
// a player moves off the end of the list,
// they'll end up back at the start
void move_players(struct chair *chairs) {
    struct chair *c = chairs;
    struct player *unseated = NULL;
    while (c != NULL) {
        struct player *moving = c->sitting;
        c->sitting = unseated;
        unseated = moving;
        c = c->next;
    }
    // the last unseated then goes back to the
    // first chair
    if (chairs != NULL) {
        chairs->sitting = unseated;
    }
}

// Make music for a certain number of turns
// Each turn of music makes the players move
// one chair along the list.
// After they've moved that many times, the
// first chair in the list is removed, along
// with the person sitting in it
struct chair *make_music(int turns, struct chair *chairs) {
    int i = 0;
    while (i < turns) {
        move_players(chairs);
        i++;
    }
    // remove the head!
    struct chair *beheaded = chairs;
    printf("%s\n", beheaded->sitting->name);
    chairs = chairs->next;
    free(beheaded->sitting);
    free(beheaded);
    
    return chairs;
}

// This helper function is only for this main, but
// it may help you to both understand and test this exercise.
// You may use this function for testing, but
// YOU CANNOT CHANGE THIS FUNCTION
struct chair *be_seated(char name[MAX_NAME_LENGTH], struct chair *heir) {
    struct chair *c = malloc(sizeof(struct chair));
    c->sitting = malloc(sizeof(struct player));
    strcpy(c->sitting->name, name);
    c->next = heir;
    return c;
}

// This is a main function which could be used
// to test your make_music function.
// It will not be marked.
// Only your make_music function will be marked.
int main(int argc, char * argv[]) {
    struct chair *thrones = be_seated("Robert Baratheon", NULL);
    thrones = be_seated("Cersei Lannister", thrones);
    thrones = be_seated("Joffrey Baratheon", thrones);
    thrones = be_seated("Eddard Stark", thrones);
    thrones = be_seated("Spoiler Alert", thrones);
        
    thrones = make_music(1, thrones);
    thrones = make_music(3, thrones);
    thrones = make_music(0, thrones);
    thrones = make_music(2, thrones);

    free(thrones->sitting);
    free(thrones);
        
    return 0;
}

Submission

When you are finished each exercises make sure you submit your work by running give.

You can run give multiple times. Only your last submission will be marked.

Don't submit any exercises you haven't attempted.

If you are working at home, you may find it more convenient to upload your work via give's web interface.

Remember you have until Week 9 Sunday 20:00 to submit your work.

You cannot obtain marks by e-mailing lab work to tutors or lecturers.

You check the files you have submitted here

Automarking will be run by the lecturer several days after the submission deadline for the test, using test cases that you haven't seen: different to the test cases autotest runs for you.

(Hint: do your own testing as well as running autotest)

After automarking is run by the lecturer you can view it here the resulting mark will also be available via via give's web interface

Lab Marks

When all components of a lab are automarked you should be able to view the the marks via give's web interface or by running this command on a CSE machine:

1511 classrun -sturec