Project 0: American Bingo - Fall 2026

There are no late submission folders for Project 0. You must turn it in on-time to receive credit.

Due: Tuesday Sep 08, before 9:00 pm

Objectives

  • Review the procedures to access the GL servers and to compile programs on GL.
  • Review C++ memory management (allocating and deallocating memory dynamically).
  • Use Valgrind to check for memory leaks.
  • To practice writing unit tests.
  • To practice working in a Linux environment.
  • To practice analyzing and understanding a project requirements.

Introduction

In this project, you will complete a C++ class by writing a copy constructor, destructor, and assignment operator. Furthermore, you will write a tester class and a test program and use Valgrind to check that your program is free of memory leaks. Finally, you will submit your project files on GL. If you have submitted programs on GL using shared directories (instead of the submit command), then the submission steps should be familiar.

In this project we develop the core part of an app. The app will be used by multiple players to play American Bingo. This software module along with a proper user interface can provide the complete game experience.

The Bingo class enforces the rules for a bingo game and stores the state of a game. You are assigned the task to implement the Bingo class. An object of the Bingo class contains a digital bingo card. The class stores the state of the bingo card in a 2D array of the type Cell. The following figure presents multiple visualizations of an initial bingo card (left) and a winning card (right). The examples show the state of the game in a terminal. In this presentation the row numbers are provided to facilitate the debugging. The columns are specified with the word BINGO. The number 99 represents a blank cell. If the Bingo class during a game finds a drawn ball on the card it will replace the number by a 99 on the card. The figure on the right side presents the state of the game after multiple balls are drawn. On this card the column of O contains only blank cells which makes the card a winner.

The following figure represents the main data structure in this project. It is a 2d structure storing the state of the bingo card.

Assignment

Step 1: Create your working directory

Create a directory in your GL account to contain your project files. For example, cs341/proj0.

Step 2: Copy the project files

You can right-click on the file links on this page and save-as in your computer. Then, transfer the files to your GL account using SFTP.

For this project, you are provided with the skeleton .h and .cpp files, a sample driver, and the output of driver program:

  • bingo.h - The interface for Random, Cell and Bingo classes.
  • bingo.cpp - The skeleton for the Bingo class implementation.
  • driver.cpp - The sample driver/test program.
  • driver.pdf - The output of sample driver/test program.

Step 3: Complete the Bingo class

You need to implement the class according to the requirements. For the requirements please refer to the Specifications, The Rules, and Additional Requirements sections on this page.

Step 4: Test your code

You need to test your implementation properly and adequately. For a description of testing please refer to the Testing section on this page. Moreover, a sample test function has been provided in the file driver.cpp for your reference.

Step 5: Check for memory leaks

Run your test program using Valgrind. For example, assuming you have compiled mytest.cpp, producing the executable mytest.out, run the command

   valgrind mytest.out 

If there are no memory leaks, the end of the output should be similar to the following:

   ==8613== 
            ==8613== HEAP SUMMARY:
            ==8613==     in use at exit: 0 bytes in 0 blocks
            ==8613==   total heap usage: 14 allocs, 14 frees, 73,888 bytes allocated
            ==8613== 
            ==8613== All heap blocks were freed -- no leaks are possible
            ==8613== 
            ==8613== For lists of detected and suppressed errors, rerun with: -s
            ==8613== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
          

The important parts are “in use at exit: 0 bytes” and “no leaks are possible.” The last line is also important as memory errors can lead to leaks.

Step 6: Link your shared directory

Please take this step as soon as you receive the notification indicating the shared submission folders are ready. Leaving this step to the last minute can prevent you from submitting your work on time. Please note:
  * No other method of submission will be accepted due to such a problem.
  * No late submission will be accepted due to such a problem.

Follow the instructions on the Project Submission page to make a symbolic link to the shared directory in your home directory.

Step 7: Submit your files

See the “What to Submit” section, below.

Specifications

You must implement the following functions. These functions are the public interface of the project. The public interface will be used to test your project. You can use private helper functions in your design of this project. They won't be called directly for testing.

The class Bingo has multiple member variables as the following list:

  • m_card is 2D array which stores the information about every cell. Every cell is of the type Cell. The implementaion of the Cell class is provided. This member variable represents a bingo card in the game. The sizes of this data structure are specified by the member variables m_numRows and m_numCols.
  • m_helper is an array that stores the information about all cells in the card. In fact we are storing every cell twice in two different data structure. We do this for efficiency (running time). Everytime a ball is drawn we look into m_helper to check whether the ball number exists in the card. The index numbers of this data structures are the ball numbers. This allows us to directly access a cell's information, e.g. if the drawn ball exists in the card what are the row and column number.
  • m_trackRows and m_trackCols are arrays which can be used for keeping track of the number of empty cells in a row or in a column. An empty cell indicates that a ball with the same number has been drawn. After multiple ball drawing once there is a complete empty row or a complete empty column the game is finished.

Bingo::Bingo()
This is the default constructor. It creates an empty object. An empty object does not hold any memory. The constructor must initialize all member variables.
Bingo::Bingo(int rows, int columns, int min, int max)
This is the constructor. It initializes all member variables and it allocates required memory if neccessary. If the user passes invalid parameters such as a negative size the constructor creates an empty object. The parameters min and max specify the range of numbers that can be placed on a card. They also indicate the smallest and the largest ball numbers that can be drawn. There are certain requirements for the parameters that are passed to the constructor. If any of the requirements is not satisfied the constructor creates an empty object. For the requirements of these parameters one may refer to the section "The Game Rules" on this page.
Bingo::~Bingo()
This is the destructor and it deallocates the memory.
void Bingo::clear()
This function deallocates all memory and re-initializes all member variables to default values. It clears the current object to an empty object.
bool Bingo::initCard()
This function populates the card cells with random numbers. Moreover, it initializes other member variables that are used as helpers. Dumping the object after calling this function will render a randomly created card. The following figure presents the results of calling this function.

The return value of this function indicates whether the init operation has been successful. There may be some error handling required in this function. For example, before using memory we need to make sure whether required memory has been allocated.

bool Bingo::reCreateCard(int rows, int columns, int min, int max)
This function initializes a new card with randomly generated numbers. Generally we call this function after calling clear(). Therefore, this function needs to allocate required memory and initializes member variables. Then it initializes the card. Using this function along with clear() allows us to play multiple games with the same set of drawn balls. This would be a simulation of a physical bingo game when multiple players are playing together and at the same time if the same parameters as the constructor parameters are passed to this function. It also is possible to create a card with a new set of parameters.
The return value of this function indicates the success or failure of the operations. The parameters min and max specify the range of numbers that can be placed on a card. They also indicate the smallest and the largest ball numbers that can be drawn.
There are certain requirements for the parameters that are passed to this function. If any of the requirements is not satisfied the function does not modify the current object. In such a case the result would be an empty object since the function clear() was called first. For the requirements of these parameters one may refer to the section "The Game Rules" on this page.
vector<int> Bingo::drawBalls()
This function generates and returns a list containing randomly distributed ball numbers. There is no duplicate numbers in the list. One may use the RANDOM type SHUFFLE from the Random class for generating the list. The ball numbers are within the range of m_minBallVal and m_maxBallVal.
int Bingo::play(int numDraws, vector<int> rndBalls)
This function gets ball numbers from the vector rndBalls. If the ball number exists in the card this function replaces the corresponding cell in the card with an empty cell, i.e. EMPTYCELL. The function counts the number of replacements and at the end returns it. Among multiple players who are playing with the same rndBalls vector, the player who gets the lower count is the winner.

The parameter numDraws indicates the number of drawing allowed in the current play. The game stops if the number of drawing is reached. If the number of drawing is equal to the max ball number the game stops as soon as a row or a column wins. If the number of drawing is greater than max ball number or the size of ball vector is greater than the max ball number the game does not perform any drawing. Such a case would be an error case.

const Bingo & Bingo::operator=(const Bingo & rhs)
The assignment operator creates a deep copy of rhs. Reminder: a deep copy means the current object has the same information as rhs object, however, it has its own memory allocated. Moreover, an assignment operator needs protection against self-assignment. The new copy must be an exact copy of rhs.
void Bingo::dumpCard()
This function renders the card on the terminal at the time that it is called. The implementation of this function is provided. You do not need to modify this function. This function is for debugging purposes. Do not call this function in the test program that you'll submit.

The Game Rules

The following rules are part of the project requirements.

  • Since the game can be played with different critera the implementation must use the variables that provide such a generality. For example, the American version uses 75 balls and the British version uses 90 balls. Currently there are multiple const variables declared for the America version. The project implementation must use the variables properly so by changing them it is possible to convert the game to the British version. Therefore, the implementation should not use any hard coded value.
  • The card has 5 columns specified by the characters B, I, N, G, and O.
  • The number of rows on the card can vary in the range of 2 to 15.
  • The game initializes the card cells with numbers specified by the values in the range of min and max parameters that are passed to the constructor. This range also indicates the values on the balls. This range must be divisible by the number of columns on the game card which is 5.
    • The column B is filled with the first 20% of numbers in the range min to max. For example, if the range is specified by the constant variables MINVAL and MAXVAL, therefore, the numbers 11 to 25 will be used in the B column. Every number in the range 11 to 25 only appears once in this column, i.e. there is no duplicate.
    • The column I is filled with the second 20% of numbers in the range min to max. For example, if the range is specified by the constant variables MINVAL and MAXVAL, therefore, the numbers 26 to 40 will be used in the I column. Every number in the range 26 to 40 only appears once in this column, i.e. there is no duplicate.
    • The column N is filled with the third 20% of numbers in the range min to max. For example, if the range is specified by the constant variables MINVAL and MAXVAL, therefore, the numbers 41 to 55 will be used in the N column. Every number in the range 41 to 55 only appears once in this column, i.e. there is no duplicate.
    • The column G is filled with the fourth 20% of numbers in the range min to max. For example, if the range is specified by the constant variables MINVAL and MAXVAL, therefore, the numbers 56 to 70 will be used in the G column. Every number in the range 56 to 70 only appears once in this column, i.e. there is no duplicate.
    • The column O is filled with the fifth 20% of numbers in the range min to max. For example, if the range is specified by the constant variables MINVAL and MAXVAL, therefore, the numbers 71 to 85 will be used in the O column. Every number in the range 71 to 85 only appears once in this column, i.e. there is no duplicate.
  • The game draws balls in the range specified by the min and max parameters that are passed to the constructor. For example, the user may pass the constant variables MINVAL and MAXVAL indicating the range of 11 to 85 for the ball values. At every drawing, if there is a match on the card the game changes the matched cell to a value of EMPTYCELL. This indicates that the user places a chip on the card in a physical game.
  • The values for the min and max parameters are within the range of MINVAL and MAXVAL. For example, the user may decide to pass a range of 11 to 85. Another user may decide to pass a range of 11 to 60. The length of this range must be divisible by the number of columns which is 5.
  • As soon as all cells in a row or in a column are set to EMPTYCELL the game is finished and the number of hits to finish the game is reported.
  • Multiple playres can create cards and play with the same set of ball drawing.
  • The player who has the least number of hits is the winner.

Additional Requirements

  • The class declaration (Bingo) and provided function implementations in bingo.cpp may not be modified in any way. No additional libraries may be used.
  • You are not allowed to use STL template containers in the Bingo class except the one that is used in the provided function signatures.
  • You are allowed to use STL template containers in your test functions.
  • The Bingo class functions must be written in bingo.cpp; in particular, they must not be written “in-line.”
  • Private helper functions may be added, but must be declared in the private section of the Bingo class. There is a comment indicating where private helper fuction declarations should be written.
  • You must use the Random class to generate data. You can use the RANDOM type SHUFFLE to generate random numbers in a range without duplicates.
  • You must use nullptr to set pointers to a default value. Do not use NULL or zero or any other value for this purpose.
  • You should read through the coding standards for this course.

Testing

Important, points will be deducted for violating the following list:

  • The sample driver.cpp file will not be accepted as a test file.
  • The name of the test file must be mytest.cpp.
  • The mytest.cpp file contains the Tester class, and the main function.
  • The test cases must be implemented in the Tester class, and must be called in the main function.
  • Every test function returns true or false to indicate the pass or fail.
  • A dump() function is provided for debugging purposes, visual inspections will not be accepted as test cases.

You must write and submit a test program along with the implementation of the Bingo class. To test your Bingo class, you implement your test functions in the Tester class. The Tester class resides in your test file. You name your test file mytest.cpp. It is strongly recommended that you read the testing guidelines before writing test cases. A sample test program including Tester class, and sample test functions are provided in driver.cpp. You add your Tester class, test functions and test cases to mytest.cpp. You must write a separate function for every test case.

Any program should be adequately tested. Adequate testing includes testing the functionaly for normal cases, edge cases and error cases. The following list presents some examples for this project:

  • Trying to create a Bingo object with -5 rows and 5 columns is an error case.
  • Trying to create a Bingo object with 2 rows and 5 columns is an edge case.
  • Trying to create a Bingo object with 10 rows and 5 columns is a normal case.

The following presents a non-exhaustive list of tests for this project. Please note, you need to test your work adequately.

  • Test whether the constructor works correctly for an error case, e.g. it creates an empty object if the user tries to create an object with -5 rows and -10 columns.
  • Test whether the constructor works correctly for an normal case, e.g. it creates memory and initilizes all member variables to the proper values, if the user tries to create an object with 10 rows and 5 columns.
  • Test whether reCreateCard(...) works correctly for an error case, e.g. it creates an empty object if the user tries to create an object with -5 rows and -10 columns.
  • Test whether reCreateCard(...) works correctly for an normal case, e.g. it creates memory and initilizes all member variables to the proper values, if the user tries to create an object with 10 rows and 5 columns.
  • Test whether initCard() is working correctly for an error case, e.g. if it is called on an empty object, the object will stay an empty object after the call.
  • Test whether initCard() is working correctly for a normal case, e.g. the cells in m_card are initialized to the correct value, if the object has an 5x5 card. For example, the values in every column fall in the correct range.
  • Test whether play(...) is working correctly for an error case.
  • Test whether play(...) is working correctly for a normal case, e.g. set the card cells and the ball vector manually and check whether the function returns the expected result.
  • Test the assignment operator for an edge case, e.g. trying to assign an empty object to an object with regular data.
  • Test the assignment operator for a normal case.

The following test function is provided to you in driver.cpp in the Tester class as a sample test function. We recommend that you study the implementation of this function.

bool assignmentNormal(Bingo & lhs, Bingo & rhs)
This function tests whether the overloaded assignment operator works correctly for a normal case.

What to Submit

You must submit the following files to the proj0 submit directory:

  • bingo.h
  • bingo.cpp
  • mytest.cpp (Note: This file contains the declaration and implementation of your Tester class as well as all your test cases.)

If you followed the instructions in the Project Submission page to set up your directories, you can submit your code using the following command:

   cp bingo.h bingo.cpp mytest.cpp ~/cs341proj/proj0/

Grading Rubric

For details of grading rubric please refer to Project Grading Policy