Section 2: Arrays and Structs

You can find the code for this section here.

Discussion

DISCUSSION A. Why does the following C program compile, although the type of nums, int[], and type of numbers, int*, do not match?

int add_three_nums(int* numbers) {
  int sum = 0;
  for (int i = 0; i < 3; i++) {
    sum = sum + *(numbers + i);
  }
  
  return sum;
}

int main() {
  int nums[3];
  nums[0] = 19240;
  nums[1] = 12645;
  nums[2] = 912831;

  int result = add_three_nums(nums);
  printf("The result is: %d\n", result);

  return 0;
}

Question 1: Tic-Tac-Toe Debugging

In this section, you'll work on a C program that plays the game of Tic-Tac-Toe. One feature we've already started coding is a function that initializes the gameboard. This function reads in user input describing the size of the gameboard, allocates space on the heap for the board using malloc, and initializes the newly-allocated heap memory.

Here are some data structure definitions that this program uses:

// Enum for aliasing game grid contents (X or O)
enum TTTSymbol { X, O };

// Struct to contstruct the game board out of (using an array of these structs)
struct TTTCell {
  // Indicates if a player filled in this cell (0 is empty, 1 is filled)
  int is_filled;
  // The symbol of the player that filled in this cell (if non-empty)
  enum TTTSymbol symbol;
};

Unfortunately, nobody writes perfect code on their first try (including the course staff), and the game implementation has bugs! Help us by using GDB to find and fix the bugs in initialize_board. You can assume that the data structure definition above is correct.

The buggy implementation of initialize_board is below:

/* Function initialize_board
 * 
 * Arguments:   dims, integer pointer to board dimensions (initialized by this function)
 * Returns:     TTTCell pointer to the allocated board region (allocated on heap with malloc)
 * Description: Extracts board dimensions from STDIN. Mallocs a board with the inputted
 *              dimensions, initializes it, and returns it to the user.
 */
struct TTTCell* initialize_board(int* dims) {
  // Extract how big the board should be
  while (*dims <= 0) {
    printf("Enter dimensions for square board. For example: 3 means a 3 row, 3 col board.\n");

    // Read dimensions for board from user
    char buffer[BUFSIZE];
    int bytes_read = read(STDIN_FILENO, buffer, BUFSIZE - 1);
    buffer[bytes_read] = '\0';
    dims = atoi(buffer);

    // Error check for nonexistant board dimensions
    if (dims <= 0) {
      printf("Invalid board dimensions. Enter new dimensions.\n");
    }
  }

  // Allocate space for the board (note the dereference for dims)
  struct TTTCell* board = (struct TTTCell*)malloc(sizeof(struct TTTCell) * (*dims));
  
  // Initialize the board here!
  for (int x = 0; x < dims; x++) {
    for (int y = 0; y < dims; y++) {
      board[(x * (*dims)) + y].symbol = X;
    }
  }

  // Note that the dimensions of the board are also returned since we update the original reference
  return board;
}

Question 2: Even More Tic-Tac-Toe Debugging

After spraying pesticides on the bugs in initialize_board, the course staff wants to finish the Tic-Tac-Toe game. They got partway through but had to stop and prepare for section. Unfortunately, they left behind even more bugs in the update_board function!

The update_board function prompts the user to input a move location, extracts the location of their move using the extract_numbers function you debugged previously in section 1, and updates the board data accordingly. Use GDB to fix the bugs in update_board!

The buggy implementation of update_board is below:

/* Function update_board
 * 
 * Arguments:   board, TTTCell pointer to the game board
 *              curr_player, TTTSymbol indicating current player (X or O)
 *              dims, dimensions of square game board
 * Returns:     None
 * Description: Extracts move location from STDIN and performs error bounds checking.
 *              Updates board metadata with the inputted move
 */
void update_board(struct TTTCell* board, enum TTTSymbol curr_player, int dims) {
  int move_row = -1;
  int move_col = -1;
  int valid_move = -1;
  char buffer[BUFSIZE]; 

  // Extract move location
  while (valid_move == -1) {
    printf("Enter move location. For example, 0x0 is 0 row, 0 col.\n");
    // Read into buffer and place null byte to terminate string
    int bytes_read = read(STDIN_FILENO, buffer, BUFSIZE - 1);
    buffer[bytes_read] = '\0';
    valid_move = extract_numbers(buffer, &move_row, &move_col);

    if (valid_move == -1) {
      printf("Invalid move location. Enter new move.\n");
    }
  }

  // Update the board
  board[(move_col * dims) + move_col].is_filled = 1;
  board[(move_col * dims) + move_col].symbol = curr_player;
}