COMP1911 23T2 Introduction to Programming

Objectives

In this lab, you will practise:

Preparation

Before the lab you should re-read the functions notes and accompanying examples

You should also read the first few slides in arrays and accompanying examples

Arrays are also covered in chapters 7.1 and 7.2 of Moffat.

You should also have read the lab assessment guide.

Getting Started

Login and run following commands inside a Unix terminal

Create a new directory for this lab called lab04 by typing:

mkdir lab04
Change to this directory by typing:
cd lab04

Exercise 1: Leap Year

Write a C program leapYearFunction.c that reads a year and then uses a function to calculate whether that year is a leap year (click here to see the algorithm). The function prototype must be int isLeapYear(int year).

Your function should return 0 if it is not a leap year, and 1 if it is a leap year.

Your leap year function must not print anything.

You should call this function from your main function, which is where you print the result.

Your function must be named isLeapYear.
It must exactly match the function prototype given above.

Match the examples below exactly

For example:

$ dcc -o leapYearFunction leapYearFunction.c
$ ./leapYearFunction
Enter year: 2017
2017 is not a leap year.
$ ./leapYearFunction
Enter year: 2016
2016 is a leap year.
$ ./leapYearFunction
Enter year: 2000
2000 is a leap year.
$ ./leapYearFunction
Enter year: 3000
3000 is not a leap year.
When you think you have leapYearFunction.c working use autotest to test it further:
1911 autotest lab04 leapYearFunction.c

Beware: autotest will not use your main function. It will call your isLeapYear function directly.

Sample solution for leapYearFunction.c
// Written 2018-03-09 by Andrew Bennett <andrew.bennett@unsw.edu.au>
// as a lab example for COMP1511.

#include <stdio.h>

int isLeapYear (int year);

int main (void) {

    // Scan in the year.
    int year;
    printf("Enter year: ");

    scanf("%d", &year);

    // We've moved all of the logic that was here into a function.
    if (isLeapYear(year) == 1) {
        printf("%d is a leap year.\n", year);
    } else {
        printf("%d is not a leap year.\n", year);
    }

    return 0;
}

int isLeapYear (int year) {
    int leapYear = 0;
    if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)) {
        leapYear = 1;
    } 
    return leapYear;
}



Exercise 2: Arrays and Runtime Errors

For this lab question, you will run some experiments and discuss your results and conclusions in a blog post.

Make a blog post called "Arrays and Runtime Errors"

To make a Blog

  1. Go to the COMP1911 Home Page (On WebCMS)
  2. Log in with your zid and zpass
  3. Click on the speech bubble icon at the bottom of the side navigation panel near your name.
  4. Click on "Create Post"
  5. Select the "Course Staff" option in the Viewable by selection box.

Background Information

Once we start programming with arrays it is easy to accidentally create runtime errors. (We have seen runtime errors before such as divide by zero errors).

Runtime errors mean that your code compiles, but when you run it something will cause the program to stop execution and 'crash'. There may also be other times where errors are more subtle and your program runs and does NOT 'crash' but you do not get the output you want.

Make a file named badArray1.c and copy and paste the following code into it and save it

#include <stdio.h>
// This is a bad program
int main(void) {
    int i;
    int numbers[] = {2,4,6,8};
    
    i = 0;
       
    while(i <= 4){
        printf("%d\n",numbers[i]);
        i = i + 1;
    }    
    
    i = i*1000;
    
    numbers[i] = 3;
    return 0;
}

We are now going to compile and then execute (if it compiles) this program using different compilers and different compiler options and note whether you see any of the following:

  1. Compile time error: ie the code does not compile
  2. Runtime error: the code 'crashes' and does not fully execute until completion
  3. Incorrect output : the code prints wrong/garbage output

and try to explain the results you get.

Note: In some cases you may get incorrect output AND a crash. The compiler options you should use are:

Repeat the proccess for the following program badArray2.c:

#include <stdio.h>

#define SIZE 10

// This is a bad program
int main(void) {
    int numbers[SIZE];
    int i;
    i = 0;
    
    //initialise array
    while(i < SIZE){
        numbers[i] = i;
        i = i + 2;
    }    
    
    //print array
    i = 0;
    while(i < SIZE){
        printf("%d: %d\n",i, numbers[i]);
        i = i + 1;
    }  
       
    return 0;
}

Discuss the differences between the errors in both programs. Did you come to any conclusions about the different types of errors dcc finds compared to gcc?

Program 1:
  • All of:
    gcc -o badArray1 badArray1.c
    clang -o badArray1 badArray1.c
    gcc -Wall -Werror -o badArray1 badArray1.c

    Incorrect output:

    2
    4
    6
    8
    1336077680
    Segmentation fault
    
    Note: the following give the same errors/output:
    gcc -Wall -Wextra -Werror -o badArray1 badArray1.c
    (even more error checking)
    clang -Wall -Wextra -Weverything -Werror -o badArray1 badArray1.c
    (check for every error possible) (not in gcc)
    gcc -Wall -Wextra -Wpedantic -Werror -o badArray1 badArray1.c
    (check for complience issues with the C standard)
  • gcc -Wall -O -o badArray1 badArray1.c
    (same as -O1 (level 1 optimizations) (default is level 0))

    Compile error:

    badArray1.c: In function ‘main’:
    badArray1.c:10:9: warning: iteration 4 invokes undefined behavior [-Waggressive-loop-optimizations]
             printf("%d\n",numbers[i]);
             ^~~~~~~~~~~~~~~~~~~~~~~~~
    badArray1.c:9:10: note: within this loop
         while(i <= 4){
              ^
    
    Incorrect output:
    2
    4
    6
    8
    0
    
    Note: the following give the same errors/output:
    gcc -Wall -O3 -o badArray1 badArray1.c
    (level 3 optimizations (highest level)) Note: the following gives different output:
    gcc -Wall -Os -o badArray1 badArray1.c
    (optimize for size)
  • dcc -o badArray1 badArray1.c

    Compile error:

    badArray1.c: In function ‘main’:
    badArray1.c:10:9: warning: iteration 4 invokes undefined behavior [-Waggressive-loop-optimizations]
             printf("%d\n",numbers[i]);
             ^~~~~~~~~~~~~~~~~~~~~~~~~
    badArray1.c:9:10: note: within this loop
         while(i <= 4){
              ^
    
    Runtime error: uninitialized variable accessed.
    2
    4
    6
    8
    
    badArray1.c:10:23: runtime error - index 4 out of bounds for type 'int [4]'
    
    dcc explanation: You are using an illegal array index: 4
      Valid indices for an array of size 4 are 0..3
      Make sure the size of your array is correct.
      Make sure your array indices are correct.
    
    Execution stopped in main() in badArray1.c at line 10:
    
        int i;
        int numbers[] = {2,4,6,8};
    
        i = 0;
    
        while(i <= 4){
    -->     printf("%d\n",numbers[i]);
            i = i + 1;
        }
    
    Values when execution stopped:
    
    i = 4
    numbers = {2, 4, 6, 8}
    numbers[i] = <uninitialized value>
    
This program went out of the bounds of the array twice.
Once it went over just by one and attempted to read the value at index 4.
Then it attempted to write way way beyond the bounds of the array at index 5000.
3 out of the 5 options allowed us to continue while reading just outside of the array.
None of them seemed to let me continue to write at an index 5000.
However once I removed the error on line 9, gcc -Wall -Werror -O let me write at index 5000.



Program 2:

  • gcc -Wall -Werror -o badArray2 badArray2.c

    Incorrect output: prints out 0 values at all odd indexes

    Note: so does -O2 and -O3

  • gcc -Wall -O -o badArray2 badArray2.c

    Incorrect output: prints out garbage values at all odd indexes

    Note: so does -Os

  • dcc -o badArray2 badArray2.c

    Runtime error: uninitialized variable accessed.

    0: 0
    
    Runtime error: uninitialized variable accessed.
    
    Execution stopped in main() in badArray2.c at line 19:
    
            i = i + 2;
        }
    
        // print array
        i = 0;
        while (i < SIZE) {
    -->     printf("%d: %d\n", i, numbers[i]);
            i = i + 1;
        }
    
    Values when execution stopped:
    
    i = 1
    numbers = {0, <uninitialized value>, 2, <uninitialized value>, 4, <uninitialized value>, ...}
    numbers[i] = <uninitialized value>
    

This program did not go outside the bounds of its array, however it had not initialised any of the memory at odd indexes, so was using memory with 'garbage' values from within its array. Only dcc was able to detect this at runtime.

badArray1.c accessed memory outside the bounds of its array. badArray2.c accessed memory from inside the bounds of the array that was not yet initialised.

Lesson: Going outside the bounds of an array and accessing uninitialised memory even if it is within our own array gives us unpredictable behavioursand is easy to do and can be hard to find. dcc is our friend as it try to let us know when and where we do this.

Exercise 3: Vector Match

Write a program called vectorMatch.c that reads 2 vectors of 10 positive integers the prints a count of how many times the 2 vectors have the same value in the same position.

It must be possible by changing a single #define to change the length of the vectors your program reads. You must use arrays, not 20 separate variables

Your program can assume it is given correct input (20 positive integers).

Your program must match the examples below perfectly.

$ ./vectorMatch
Enter vector 1 of 10 positive numbers: 42 1 2 3 4 5 6 7 8 9
Enter vector 2 of 10 positive numbers: 42 1 2 3 4 5 6 7 8 9
Vectors match in 10 positions.
$ ./vectorMatch
Enter vector 1 of 10 positive numbers: 42 1 2 3 4 5 6 7 8 9
Enter vector 2 of 10 positive numbers: 3 42 1 2 3 4 5 6 7 8
Vectors match in 0 positions.
$ ./vectorMatch
Enter vector 1 of 10 positive numbers: 4 3 2 1 8 7 6 9 9 9
Enter vector 2 of 10 positive numbers: 2 4 2 6 8 7 6 5 4 3
Vectors match in 4 positions.

You must create and use a function to read the values into your array.
You may create and use other functions too.

Hint: remember to compile with dcc to get a clear message immediately if any array reference is wrong when you run your program. For example:

$ dcc vectorMatch.c -o vectorMatch

As usual autotest is available to test your program.

$ 1911 autotest lab04 vectorMatch
Sample solution for vectorMatch.c.
#include <stdio.h>

#define VECTOR_LENGTH 10

void readInput(int numbers[],int size);
int findMatches(int numbers1[], int numbers2[],int size);

int main(void) {
    int vector1[VECTOR_LENGTH];
    int vector2[VECTOR_LENGTH];
    int nMatches;

    printf("Enter vector 1 of %d positive numbers: ", VECTOR_LENGTH);
    readInput(vector1,VECTOR_LENGTH);

    printf("Enter vector 2 of %d positive numbers: ", VECTOR_LENGTH);
    readInput(vector2,VECTOR_LENGTH);

    nMatches = findMatches(vector1,vector2,VECTOR_LENGTH);
    printf("Vectors match in %d positions.\n", nMatches);
    return 0;
}

void readInput(int numbers[],int size){
    int i = 0;
    while ( i < size ) {
        scanf("%d", &numbers[i]);
        i = i + 1;
    }
}

int findMatches(int numbers1[], int numbers2[],int size){
    int nMatches = 0;
    int i = 0;
    while ( i < size ) {
        if (numbers1[i] == numbers2[i]) {
            nMatches = nMatches + 1;
        }
        i = i + 1;
    }
    return nMatches;
}

Exercise 4: Noughts and Crosses - Input

Write a program called tttBoard.c that reads in a 3x3 noughts and crosses (Tic-Tac-Toe) board, stores it in a 2D array and then prints it out. You can assume that the user enters valid input only. You should assume that 0 stands for noughts and 1 for crosses and 2 for an empty square. You can assume that the input is valid.

Your program should behave like the following:

$ ./tttBoard
Please enter the board:
0 1 0
0 1 1
2 0 2
Here is the board:

O X O
O X X
. O .

Hint

Start off with something like the code below and implement readBoard with a scanf inside a nested loop and printBoard as a printf inside a nested loop. You should store the board as 2d array of ints.
#include <stdio.h>

#define SIZE 3

void readBoard(int board[SIZE][SIZE]);
void printBoard(int board[SIZE][SIZE]);

int main(void) {
   int i, j;
   int board[SIZE][SIZE];
   ....
   return 0;
}

For a first step when you print the board out, just try and print out the ints like below.

Please enter the board:
0 1 0
0 1 1
2 0 2
Here is the board:

0 1 0
0 1 1
2 0 2
Then modify your printing code to print it out with O, X and . instead of the 0,1 and 2s.
Sample solution for tttBoard.c.
#include <stdio.h>

#define SIZE 3
#define NOUGHT 0
#define CROSS 1
#define EMPTY 2

void readBoard(int board[SIZE][SIZE]);
void printBoard(int board[SIZE][SIZE]);


int main(int argc, char *argv[]) {
    int board[SIZE][SIZE];

    printf("Please enter the board:\n");
    readBoard(board);
   

    printf("Here is the board:\n\n");
    printBoard(board);
    
    printf("\n");
  
    return 0;
}

void readBoard(int board[SIZE][SIZE]){
    int i,j;
    i = 0;
    while (i < SIZE) {
        j = 0;
        while (j < SIZE) {
            scanf("%d",&board[i][j]);
            j = j + 1;
        }
        i = i + 1;
    }

}
void printBoard(int board[SIZE][SIZE]){
    int i,j;
    i = 0;
    while (i < SIZE) {
        j = 0;
        while (j < SIZE) {
            if (board[i][j] == NOUGHT) {
                printf("O ");
            } else if (board[i][j] == CROSS) {
                printf("X ");
            } else {
                printf(". ");
            }
           
            j = j + 1;
        }
        printf("\n");
        i = i + 1;
    }
}


As usual autotest is available to test your program.
$ 1911 autotest lab04 tttBoard

(Optional) Challenge Exercise 1: Noughts and Crosses - Detecting a Winner

Make a copy of tttBoard.c and call it tttCheck.c. Now add code to tttCheck.c to check to see if there is a winner and print a suitable message.

To do this you must write a function with the following prototype

int winner(int board[SIZE][SIZE]);

You should #define constants to represent the winner, such as NOUGHT, CROSS, NO_WINNER and DRAW and return these values from your function.

You may write and use additional functions if you require.

There is a winner if there are 3 X's or 3 O's in a row or a column or diagonal. It is a draw if there is no winner and there are no empty positions left on the board.

Match the output below exactly. For example:

$ ./tttCheck
Please enter the board:
0 1 0
0 1 1
2 0 2
Here is the board:

O X O
O X X
. O .

There are no winners
$ ./tttCheck
Please enter the board:
0 1 0
0 1 1
1 0 0
Here is the board:

O X O
O X X
X O O

It is a draw
$ ./tttCheck
Please enter the board:
0 0 2
1 1 1
0 0 1
Here is the board:

O O .
X X X
O O X

Crosses win
$ ./tttCheck
Please enter the board:
0 1 1
0 1 1
0 0 2
Here is the board:

O X X
O X X
O O .

Noughts win
$ ./tttCheck
Please enter the board:
2 1 0
2 0 1
0 1 2
Here is the board:

. X O
. O X
O X .

Noughts win

You can assume there are not 2 winners as this would not happen in a real game.

Your code to check for a winner must use loops.

Your code to check for a winner must not depend on the board (array) being of size 3.

It must use a constant for the board size (e.g. SIZE).

As usual autotest is available to test your program.

$ 1911 autotest lab04 tttCheck
Sample solution for tttCheck.c
#include <stdio.h>

#define FALSE 0
#define TRUE 1

#define SIZE 3
#define NO_PLAYER -1
#define NOUGHT 0
#define CROSS 1
#define EMPTY 2
#define DRAW 2

void readBoard(int board[SIZE][SIZE]);
void printBoard(int board[SIZE][SIZE]);
void printCell(int cell);
int winner(int board[SIZE][SIZE]);
void printWinner(int winner);

int main(int argc, char *argv[]) {
    int board[SIZE][SIZE];
    int winningPlayer;

    printf("Please enter the board:\n");
    readBoard(board);

    printf("Here is the board:\n\n");
    printBoard(board);
    printf("\n");

    // Find and print the winner
    winningPlayer = winner(board);

    printWinner(winningPlayer);
    
    return 0;
}

// This function reads in a SIZExSIZE
// array of ints that represents a board
// No error checking is performed in this function.
void readBoard(int board[SIZE][SIZE]){
    int i, j;
    i = 0;
    while (i < SIZE) {
        j = 0;
        while (j < SIZE) {
            scanf("%d",&board[i][j]);
            j = j + 1;
        }
        i = i + 1;
    }
}

// This function prints out the SIZExSIZE board
void printBoard(int board[SIZE][SIZE]){
    int i, j;
    i = 0;
    while (i < SIZE) {
        j = 0;
        while (j < SIZE) {
            printCell(board[i][j]);
            j = j + 1;
        }
        printf("\n");
        i = i + 1;
    }
}

// This function prints out the appropriate
// visual representation of a given cell from
// the board. This function does not perform
// any error checking
void printCell(int cell){
    if (cell == NOUGHT) {
        printf("O ");
    } else if (cell == CROSS) {
        printf("X ");
    } else {
        printf(". ");
    }
}

// This function returns NOUGHT, CROSS or NO_PLAYER
// depending on whether there is a winner with SIZE pieces in
// a row or a column
int winner(int board[SIZE][SIZE]){
    int i, j, identicalSquares;
    int emptySquares = 0;
    
    i = 0;
    while (i < SIZE) {
        // check row for a winner
        // by counting how many squares
        // match first in row
        if (board[i][0] != EMPTY) {
            identicalSquares = 1;
            j = 1;
            while (j < SIZE ) {
                if (board[i][0] == board[i][j]) {
                    identicalSquares = identicalSquares + 1;
                } else if (board[i][j] == EMPTY){
                    emptySquares = 1;
                }
                j = j + 1;
            }
            if (identicalSquares == SIZE) {
                return board[i][0];
            }
        //We have found an emptySquare
        } else {
            emptySquares = 1;
        }

        // check column for a winner
        // by counting how many squares
        // match first in column

        if (board[0][i] != EMPTY) {
            identicalSquares = 1;
            j = 1;
            while (j < SIZE) {
                if (board[0][i] == board[j][i]) {
                    identicalSquares = identicalSquares + 1;
                }
                j = j + 1;
            }
            if (identicalSquares == SIZE) {
                return board[0][i];
            }
            
        }
        
        i = i + 1;
    }


    // check left diagonal for a winner
    // by counting how many squares
    // match first in diagonal
    if (board[0][0] != EMPTY) {
        identicalSquares = 1;
        i = 1;
        while ( i < SIZE ) {
            if (board[0][0] == board[i][i]) {
                identicalSquares = identicalSquares + 1;
            }
            i = i + 1;
        }
        if (identicalSquares == SIZE) {
            return board[0][0];
        }
    }

    // check right diagonal for a winner
    // by counting how many squares
    // match first in diagonal
    if (board[0][SIZE - 1] != EMPTY) {
        identicalSquares = 1;
        i = 1;
        while (i < SIZE) {
            if (board[0][SIZE - 1] == board[i][SIZE - 1 - i]) {
                identicalSquares = identicalSquares + 1;
            }
            i = i + 1;
        }
        if (identicalSquares == SIZE) {
            return board[0][SIZE - 1];
        }
    }
    if(emptySquares == 0){
        return DRAW;
    } else {
        return NO_PLAYER;
    }
}

void printWinner(int winner){
    if (winner == NOUGHT) {
        printf("Noughts win\n");
    } else if (winner == CROSS)  {
        printf("Crosses win\n");
    } else if (winner == DRAW) {
        printf("It is a draw\n");
    } else {
        printf("There are no winners\n");
    }
}

(Optional) Challenge Exercise 2: Box Fun

Credits: This was taken from a COMP1511 lab

For this challenge, make a program called boxFun.c which reads in a number and then draws that many square boxes inside each other using the character #.

You are only permitted to use C language features covered in weeks 1-3 lectures. In particular, you are not permitted to use array(s).

For example:

$ ./boxFun
How many boxes: 1
###
# #
###
$ ./boxFun
How many boxes: 2
#######
#     #
# ### #
# # # #
# ### #
#     #
#######
$ ./boxFun
How many boxes: 5
###################
#                 #
# ############### #
# #             # #
# # ########### # #
# # #         # # #
# # # ####### # # #
# # # #     # # # #
# # # # ### # # # #
# # # # # # # # # #
# # # # ### # # # #
# # # #     # # # #
# # # ####### # # #
# # #         # # #
# # ########### # #
# #             # #
# ############### #
#                 #
###################
As usual autotest is available to test your program.
$ 1911 autotest lab04 boxFun
Sample solution for boxFun.c
#include <stdio.h>

int main(void){
    int n;
    printf("How many boxes: ");
    scanf("%d",&n);
    int size = 4*n-1;
    int i=0;
    while(i < size){
        int j = 0;
        while(j < size){
            if(i == 0 || j == 0 || 
               i == size-1 || j == size -1 || 
               
               (i % 2 == 0 && j >= i  && j <= size-i-1) ||
               (j % 2 == 0 && i>=j  && i <= size-j-1)  ||
               (i % 2 == 0 && i>=j && j > size-i-1) ||
               (j % 2 == 0 && j>=i && i > size-j-1) ){
                printf("#");
            }else {
                printf(" ");
            }
	    j++;
	}
        i++;
        printf("\n");
    }
    return 0;
}

(Optional) Challenge Exercise 3: Spiral

Credits: This was taken from a COMP1511 lab

Note: This is very difficult to solve. This is an optional challenge. You do not need to attempt this to get A for the lab.

Write a program called spiral.c that reads an integer n from standard input. and prints an nxn pattern of asterisks and dashes in the shape of a spiral. You can assume n is odd and >= 5.

You are only permitted to use C language features covered in weeks 1-3 lectures. In particular, you are not permitted to use array(s).

Make your program match the examples below exactly.

$ ./spiral
Enter size: 7
*******
------*
*****-*
*---*-*
*-***-*
*-----*
*******
$ ./spiral
Enter size: 9
*********
--------*
*******-*
*-----*-*
*-***-*-*
*-*---*-*
*-*****-*
*-------*
*********
$ ./spiral
Enter size: 17
*****************
----------------*
***************-*
*-------------*-*
*-***********-*-*
*-*---------*-*-*
*-*-*******-*-*-*
*-*-*-----*-*-*-*
*-*-*-***-*-*-*-*
*-*-*-*---*-*-*-*
*-*-*-*****-*-*-*
*-*-*-------*-*-*
*-*-*********-*-*
*-*-----------*-*
*-*************-*
*---------------*
*****************

As usual autotest is available to test your program.
$ 1911 autotest lab04 spiral
Sample solution for spiral.c
// Prints an ascii spiral given a size >= 5
// Written 19/3/2017
// by Evan Kohilas (z5114986@unsw.edu.au)

/*
 * Example for size = 17
 * row, section: ... : formula
 *  0, 0: ***************** :"*-"x0 + "*"x17 + "-*"x0
 *  1, 0: ----------------* :"*-"x0 + "-"x15 + "-*"x1
 *  2, 1: ***************-* :"*-"x0 + "*"x15 + "-*"x1
 *  3, 1: *-------------*-* :"*-"x1 + "-"x11 + "-*"x2
 *  4, 2: *-***********-*-* :"*-"x1 + "*"x11 + "-*"x2
 *  5, 2: *-*---------*-*-* :"*-"x2 + "-"x7  + "-*"x3
 *  6, 3: *-*-*******-*-*-* :"*-"x2 + "*"x7  + "-*"x3
 *  7, 3: *-*-*-----*-*-*-* :"*-"x3 + "-"x3  + "-*"x4
 *  8, 4: *-*-*-***-*-*-*-* :"*-"x3 + "*"x3  + "-*"x4
 *  9, 5: *-*-*-*---*-*-*-* :"*-"x4 + "-"x1  + "-*"x4
 * 10, 5: *-*-*-*****-*-*-* :"*-"x3 + "*"x5  + "-*"x3
 * 11, 6: *-*-*-------*-*-* :"*-"x3 + "-"x5  + "-*"x3
 * 12, 6: *-*-*********-*-* :"*-"x2 + "*"x9  + "-*"x2
 * 13, 7: *-*-----------*-* :"*-"x2 + "-"x9  + "-*"x2
 * 14, 7: *-*************-* :"*-"x1 + "*"x13 + "-*"x1
 * 15, 8: *---------------* :"*-"x1 + "-"x13 + "-*"x1
 * 16, 8: ***************** :"*-"x0 + "*"x17 + "-*"x0
 */

#include <stdio.h>

int main(void){

    int size;
    printf("Enter size: ");
    scanf("%d", &size);

    int i = 0;

    int offset;
    if (size % 4 == 3){
        // if down spiral
        offset = 1;
    } else if (size % 4 == 1){
        // if up spiral
        offset = 0;
    } else {
        printf("WARNING: INVALID SIZE\n");
        offset = 0;
    }

    int mid = size/2 + offset;
    int mid_section = size/4 + offset;
    int max_section = size/2;

    int row = 0;
    while (row < size){

        int section;
        if (row < mid) {
            section = row/2;
        } else if (row == mid){
            section = mid_section;
        } else if (row > mid) {
            section = (row + 1)/2;
        } else {
            printf("WARNING: UNKNOWN CASE\n");
            section = 0;
        }

        if (row == 0){
            // first row

            i = 0;
            while (i < size){
                printf("*");
                i += 1;
            }

        } else if (row == 1){
            // second row

            i = 0;
            while (i < (size - 1)){
                printf("-");
                i += 1;
            }
            printf("*");

        } else if ((section < mid_section) && (row % 2 == 0)) {
            // even rows before middle

            //left
            i = 0;
            while (i < (section - 1)) {
                printf("*-");
                i += 1;
            }

            //middle
            i = 0;
            while (i < ((size - 2) - ((section - 1) * 4))) {
                printf("*");
                i += 1;
            }

            //right
            i = 0;
            while (i < section) {
                printf("-*");
                i += 1;
            }

        } else if ((section < mid_section) && (row % 2 != 0)) {
            // odd rows before middle

            //left
            i = 0;
            while (i < section) {
                printf("*-");
                i += 1;
            }

            //middle
            i = 0;
            while (i < ((size - 2) - (section * 4))) {
                printf("-");
                i += 1;
            }

            //right
            i = 0;
            while (i < (section + 1)) {
                printf("-*");
                i += 1;
            }

        } else if (section == mid_section) {
            // middle row

            //left
            i = 0;
            while (i < (section - 1)) {
                printf("*-");
                i += 1;
            }

            //middle
            printf("***");

            //right
            i = 0;
            while (i < (section - offset)) {
                printf("-*");
                i += 1;
            }

        } else if ((section > mid_section) && (row % 2 != 0)) {
            // even rows after middle

            //left
            i = 0;
            while (i < (max_section - section + 1)) {
                printf("*-");
                i += 1;
            }

            //middle
            i = 0;
            while (i < ((section - mid_section - 1)*4 + 1 + offset*2)) {
                printf("-");
                i += 1;
            }

            //right
            i = 0;
            while (i < (max_section - section + 1)) {
                printf("-*");
                i += 1;
            }

        } else if ((section > mid_section) && (row % 2 == 0)) {
            // odd rows after middle

            //left
            i = 0;
            while (i < (max_section - section)) {
                printf("*-");
                i += 1;
            }

            //middle
            i = 0;
            while (i < ((section - mid_section)*4 + 1 + offset*2)) {
                printf("*");
                i += 1;
            }

            //right
            i = 0;
            while (i < (max_section - section)) {
                printf("-*");
                i += 1;
            }

        } else {
            // unknown cases
            printf(">>> WARNING: UNKNOWN ROW CASE <<<");
        }

        row += 1;
        printf("\n");
    }

    return 0;

}

(Optional) Challenge Exercise 4: Noughts and Crosses Move

Now make a copy of your tttCheck.c and name it tttMove.c that instead of printing out "There are no winners" prompts the user to make a move. You must read in the players move, check that the move is valid and then print out the updated board. You can assume that it is Noughts turn. A valid move is two integers between 0 and 2 inclusive that represent a row and column on the board that is currently empty.

$ ./tttMove
Please enter the board:
2 2 2
2 0 1
0 1 2
Here is the board:

. . .
. O X
O X .

Please enter your move Noughts: 0 2
Here is the board:

. . O
. O X
O X .

Noughts win
$ ./tttMove
Please enter the board:
2 2 2
2 0 1
0 1 2
Here is the board:

. . .
. O X
O X .

Please enter your move Noughts: 2 0
Invalid Move
$ ./tttMove
Please enter the board:
2 2 2
2 0 1
0 1 2
Here is the board:

. . .
. O X
O X .

Please enter your move Noughts: 0 3
Invalid Move
Sample solution for tttMove.c
#include <stdio.h>

#define FALSE 0
#define TRUE 1

#define SIZE 3
#define NO_PLAYER -1
#define NOUGHT 0
#define CROSS 1
#define EMPTY 2
#define DRAW 2

void readBoard(int board[SIZE][SIZE]);
void printBoard(int board[SIZE][SIZE]);
void printCell(int cell);
int winner(int board[SIZE][SIZE]);
void printWinner(int winner);
int makeMove(int board[SIZE][SIZE]);

int main(int argc, char *argv[]) {
    int board[SIZE][SIZE];
    int winningPlayer;
    int rowMove, colMove;
    

    printf("Please enter the board:\n");
    readBoard(board);

    printf("Here is the board:\n\n");
    printBoard(board);
    printf("\n");

    // Find and print the winner
    winningPlayer = winner(board);

    if(winningPlayer != NO_PLAYER){
        printWinner(winningPlayer);
    } else {
        if(makeMove(board)){
            printf("Here is the board:\n\n");
            printBoard(board);
            printf("\n");
            winningPlayer = winner(board);
            printWinner(winningPlayer);
        } else {
            printf("Invalid Move\n");
        }
    }
    return 0;
}

// This function reads in a SIZExSIZE
// array of ints that represents a board
// No error checking is performed in this function.
void readBoard(int board[SIZE][SIZE]){
    int i, j;
    i = 0;
    while (i < SIZE) {
        j = 0;
        while (j < SIZE) {
            scanf("%d",&board[i][j]);
            j = j + 1;
        }
        i = i + 1;
    }
}

// This function prints out the SIZExSIZE board
void printBoard(int board[SIZE][SIZE]){
    int i, j;
    i = 0;
    while (i < SIZE) {
        j = 0;
        while (j < SIZE) {
            printCell(board[i][j]);
            j = j + 1;
        }
        printf("\n");
        i = i + 1;
    }
}


// This function prints out the appropriate
// visual representation of a given cell from
// the board. This function does not perform
// any error checking
void printCell(int cell){
    if (cell == NOUGHT) {
        printf("O ");
    } else if (cell == CROSS) {
        printf("X ");
    } else {
        printf(". ");
    }
}

// This function returns NOUGHT, CROSS or NO_PLAYER
// depending on whether there is a winner with SIZE pieces in
// a row or a column
int winner(int board[SIZE][SIZE]){
    int i, j, identicalSquares;
    int emptySquares = 0;
    
    i = 0;
    while (i < SIZE) {
        // check row for a winner
        // by counting how many squares
        // match first in row
        if (board[i][0] != EMPTY) {
            identicalSquares = 1;
            j = 1;
            while (j < SIZE ) {
                if (board[i][0] == board[i][j]) {
                    identicalSquares = identicalSquares + 1;
                } else if (board[i][j] == EMPTY){
                    emptySquares = 1;
                }
                j = j + 1;
            }
            if (identicalSquares == SIZE) {
                return board[i][0];
            }
        //We have found an emptySquare
        } else {
            emptySquares = 1;
        }

        // check column for a winner
        // by counting how many squares
        // match first in column

        if (board[0][i] != EMPTY) {
            identicalSquares = 1;
            j = 1;
            while (j < SIZE) {
                if (board[0][i] == board[j][i]) {
                    identicalSquares = identicalSquares + 1;
                }
                j = j + 1;
            }
            if (identicalSquares == SIZE) {
                return board[0][i];
            }
            
        }
        
        i = i + 1;
    }


    // check left diagonal for a winner
    // by counting how many squares
    // match first in diagonal
    if (board[0][0] != EMPTY) {
        identicalSquares = 1;
        i = 1;
        while ( i < SIZE ) {
            if (board[0][0] == board[i][i]) {
                identicalSquares = identicalSquares + 1;
            }
            i = i + 1;
        }
        if (identicalSquares == SIZE) {
            return board[0][0];
        }
    }

    // check right diagonal for a winner
    // by counting how many squares
    // match first in diagonal
    if (board[0][SIZE - 1] != EMPTY) {
        identicalSquares = 1;
        i = 1;
        while (i < SIZE) {
            if (board[0][SIZE - 1] == board[i][SIZE - 1 - i]) {
                identicalSquares = identicalSquares + 1;
            }
            i = i + 1;
        }
        if (identicalSquares == SIZE) {
            return board[0][SIZE - 1];
        }
    }
    if(emptySquares == 0){
        return DRAW;
    } else {
        return NO_PLAYER;
    }
}


void printWinner(int winner){
    if (winner == NOUGHT) {
        printf("Noughts win\n");
    } else if (winner == CROSS)  {
        printf("Crosses win\n");
    } else if (winner == DRAW) {
        printf("It is a draw\n");
    } else {
        printf("There are no winners\n");
    }
}

//Returns 1 if move was made
//Returns 0 if move was invalid
int makeMove(int board[SIZE][SIZE]){
    int row, col;
    int validMove = 1;
    printf("Please enter your move Noughts: ");
    
    if(scanf("%d %d",&row,&col) != 2){
        validMove = 0;
    } else {
        if(row >= 0 && row < SIZE && 
           col >= 0 && col < SIZE && board[row][col] == EMPTY){
        
            board[row][col] = NOUGHT;
        } else {
            validMove = 0;
        }
    }
    return validMove;
}

Just for fun

Change your code so it does not read in a board and always starts with an empty board. Alternate turns for noughts and crosses until the game is over. You now have a working 2 player noughts and crosses game.

Just for even more fun

You could even try to make your own AI player and alternate between user input and a function that decides on and makes a move! To start with you could just try to write a function that goes through each row and column and always chooses the first empty square. Then once that is working, you could try and make it smarter.

Submission/Assessment

When you are satisfied with your work, ask your tutor to assess it. You are to submit it electronically by typing (run this command in your lab04 directory):
give cs1911 lab04 leapYearFunction.c vectorMatch.c tttBoard.c tttCheck.c boxFun.c spiral.c tttMove.c
Submit advanced exercises only if you attempt the advanced exercise.

Remember the lab assessment guidelines - if you don't finish the exercises you can finish them in your own time, submit them by 19:59:59 Sunday using give and ask ask tutor to assess them at the start of the following lab.

You can also just run the autotests without submitting by typing

1911 autotest lab04