CS140 -- Lab 3 (Spring 2021)


Inspiration

Connect Four (http://en.wikipedia.org/wiki/Connect_Four) is a simple game where two players take turns placing colored discs into a seven-column, six-row grid. Today, we will implement a simple version of Connect Four using composition.

This is a reworking of a popular lab of mine at my prior institution focused on developing simple classes, in a straightforward OOP style, to a simple game. Like all of our early labs this will also give you more experience with C++ and some of its powerful features.

Rubric

This lab is more difficult to test using scripts so the TAs will develop a framework they think is best and we'll post a corresponding rubric

Part 1: Column class

  1. First, we will develop a class called C4Col, which will be responsible for simply storing discs placed into columns. C4Col should contain three private data members: an integer storing the number of discs placed in the column (so far), an integer storing the maximum number of discs allowed (i.e., # of rows = 6), and a simple character array to store representations of each players' discs (more later). Remember: initialization must be done in the constructor, not the .h file

  2. Develop an interface (i.e, prototypes of the member functions for this class in your .h file) composed of a default constructor and the following member functions: int isFull(), which determines if the column is full (i.e., numDiscs == maxDiscs); char getDisc(int), which returns the requested element of the private character array (i.e., getDisc(0) will return Discs[0]); int getMaxDiscs(), which returns the maximum number of discs (i.e., number of rows); and void addDisc(char), which adds the character representing a disc to the next open slot in the Disc array (i.e., Discs[numDiscs++] = newDisc). Remember: developing an interface simply means adding function prototypes inside a new class .h file as we have done in lecture. Although there are hints on how to implement these, the functions go in a separate .cpp file (see below).

  3. Implement a default constructor for the C4Col class in your implementation file (.cpp). This default constructor will initialize the current and maximum number of discs to 0 and 6, respectively, and initialize the character array with ' ' characters.

    REMINDER 1: constructors have no type; C4Col::C4Col(){} is an empty constructor. All member functions should have a type, e.g., int getDisc(int)

    REMINDER 2: discs[ i ] = " "; is a compiler error... it must be ' ' in C++.

  4. Implement the member functions listed (and hinted at in the corresponding video) in your emerging .cpp file. If addDisc is called and the column is full, display a message but do nothing else. If the parameter given to getDisc is invalid, also display an error message to the user and do nothing. (hint: you should use isFull() in addDisc() for maximum code reusability)

Part 2: Developing a board class

  1. The second class will represent a Connect 4 board and contain data members that are C4Col objects. This concept is called composition.

  2. Develop the C4Board class starting with an interface (.h) that includes two private data members: an integer for the number of columns and an array of C4Col objects to represent the Connect 4 board (e.g. C4Col Board[100]; the constructor you developed in part 1 will be automatically run for all elements of this array). Next, add prototypes in your interface for a default constructor and two public member functions: void display(), which will display the current board in simple text; and void play(), which will allow two players to play a game. REMINDER: you will need to also include the header developed in part 1 here to declare C4Cols like this:

    #include "C4Col.h"

  3. Implement the member functions for this C4Board class. The default constructor should set the number of columns to be the row size of a Connect 4 board (n = 7). As mentioned above, constructors for the composed class (C4Col) are run automatically. Display() could contain a nested for loop that decreases from numRows - 1 to 0 in the outside loop and from 0 to numCols - 1 on the inside as:

    for (int i = board[0].getMaxDiscs() - 1; i >= 0; i--) {
        ...
        for (int j = 0; j < numCols; j++) 
          cout << board[j].getDisc(i) << " ";
        ...				       
    }
    

    NOTE: You must include "C4Col.h" and "C4Board.h" in this new .cpp file

  4. Add extra formatting to the above to make the board look proper including a separator character (e.g., '|') between columns, a number indicating which column is which for playing the game, and other enhancement (use your creativity here). Play() should display the current board (using display to maximize code reuse) and ask one of two players which column they would like to add their disc, or -1 to end the game. Use an 'X' character for player 1 discs and an ‘O’ character for player 2 discs (HINT: like tic-tac-tow, (turn % 2)+ 1 will give you the appropriate player if player 1 always goes first). Use addDisc from the C4Col class to complete the turn.

  5. Your main function/client program should be very simple. Declare a C4Board object (as we need at least one instantiated to use member functions), and then call its play member function at a minimum like this:

    /*  insert comments here */
    #include "C4Board.h"   // class definition for C4Board used below
    
    int main() {
      C4Board c4;   // instantiate an instance of a C4Board object
      c4.play();        // play game!!
    }

Part 3: Finishing up

  1. Finish your Connect4 game by adding a private member function (called a helper function in the text) that can determine if a player has won and, if they have, displays a congratulatory message. If you have trouble, please see the instructor or the TAs. HINT: This is similar to the 2-D array manipulation you did for Lab 1 (tic-tac-toe), but A[i][j] becomes A[i].getDisc(j) using composition.

  2. Kick it up a notch (5% extra credit): Develop a computer player that follows your move with a move of its own. Feel free to make this simple (choose a random non-full column) or as sophisticated of an AI as you’d like.

Testing your code prior to submission

To faciliate testing, you were previously asked to clone the course Github repository as follows:

git clone https://github.com/semrich/CS140-21.git cs140

For this assignment, update this clone by using the following:

git pull

We'll discuss this in class but note that your client must be named "main.cpp" and compilable using make. Unlike other assignments this term, you will get more credit for the structure of your code and following instructions versus solving unit tests. Please refer to the final rubric for details.


Submission

Having done a trial run on Lab 0/Challenge 1, and on Lab 1 (see Tom's comments on Piazza) we think it would be easiest for everyone to create a single .tar for this assignment. The command to do so is:

tar -cvf lab3.tar C4Col.h C4Col.cpp C4Board.h C4Board.cpp main.cpp
Because this assignment is more focused on C++ syntax/class design vs. solving specific problems, and since we checked for a very similar win condition in Lab 1, most of the to-be posted rubric will be on specific syntax/code items vs. solving scripts in advance.

Note: Although submission will be faciliated by Canvas, we will compile and test on EECS lab machines!

If you develop your solution elsewhere please make sure it works on the lab computers prior to the deadline.