Create a triangle type pattern

Write a C++ program that asks the user to enter a number of rows to be printed. It should then display for the first row one asterisk preceded by periods. The second row should display two asterisks preceded by periods and so on until all the rows have been printed as entered by the user.

A sample run would be like so:

Enter number of rows: 5
....*
...**
..***
.****
*****

I've seen many varied questions surrounding this particular problem.

Here's a solution, firstly, just the logic of the code for clarity, not concerning ourselves with error trapping incorrect keyboard input. The object is to understand the reasoning of the logic of addressing the problem at hand.

// nestedloop.cpp
// 29 Nov, 2007.

#include <iostream>
using namespace std;

int main()
{
    int rows, c;
    cout << "Enter the number of rows: ";
    cin >> rows;
    cin.get();
    for (int r = 1; r <= rows; r++)
    {
        for (c = 1; c <= rows - r; c++)
            cout << ".";

        for (; c <= rows; c++)
            cout << "*";
        cout << endl;
    }
    // exit routine
    cout << "\n\n...Press 'ENTER' key to EXIT...\n";
    cin.get();
    return 0;
}

Copy and paste the above into your compiler of choice, compile and run it. As you can see, it works.

The guts of the solution is that an outer loop is first created, that relating to the rows. Within this loop the columns are accounted for and for this particular problem the columns loop is split into two, one dealing with the periods and the other dealing with the asterisks. Now trying to explain the sequence of the variables concerned will take forever. I reckon the code alone should clearly explain what is happening.

I mentioned earlier that this code doesn't account for incorrect data input, try it, enter a non numeric for the number of rows. The program doesn't work.

The following pages discuss enhancements to the program.

Enhancement # 1 - Trap non numeric input

Okay, lets enhance the asterisk triangle program so that it only accepts numeric input. That is, if a user types in a non numeric then the program should re-request correct input until a number is entered.

Remove these lines:

cin >> rows;
cin.get();

and replace with:

while (!(cin >> rows) ) // checking against cin fail state
{
    cout << "Must enter an integer: ";
    cin.clear();        // reset error state back to false
    cin.get();          // flush out next character
}

An understanding of the cin object is required for this snippet. As the variable rows is defined as an integer type, attempting to assign a non integer to this type will set the cin objects error flag to true. The expression (cin >> rows) returns a boolean value (true or false) relating to the error state of the expression. We can include this error state as a conditional part of a while loop. while loops only continue whilst a condition is true and therefore in this instance we need to check for when not true which means false. Confusing, I know.

Compile and run the program. Enter a single non numeric character. You'll now note that the program asks you to input only an integer. Entering an integer is the only way to exit the loop and continue through to the end.

We're not done yet. There's still a problem or two. Enter as input a multiple non numeric characters, ie 'steve'. Rediculous, I know, as it's not in context but then are users ever in context. You'll note that the loop cycles five times, each time displaying the same message like so:

Enter the number of rows: steve
Must enter an integer: Must enter an integer: Must enter an
integer: Must enter an integer: Must enter an integer:

Not very pretty but this can be cleaned up. What happened? After hitting the 'ENTER' key inputting 'steve' each character was processed by the while loop as each character created an error state in the cin object.

Enter as input 'steve10'. The same effect as above but this time 10 rows are also printed. As soon as the while loop arrived at the digit one (1), it correctly established the integer number ten (10), which no longer did an error state exist and the rows were printed. This obviously is not a desired implementation. It can be fixed.

Replace the while loop with the following snippet.

   while (!(cin >> rows))
    {
        cout << "Must enter an integer: ";
        cin.clear();              // clear cin error state
        while (cin.get() != '\n') // clear out non newline characters
            continue;
    }

As you can see we've added another while loop. This effectively removes all characters left in the input buffer, including numbers if present. At the end of this process the newline character still remains in the buffer. Remember though, that basic cin >> rows skips over newline characters, so is not an issue in our example.

As the code stands now, it still doesn't account for incorrect data entry as regards entries like '50prime'. The number 50 is considered valid input and the remaining 'prime' is ignored. Again, this should not be allowed. We'll address that component a little later.

Enhancement # 2 - Valid Input Range

To further enhance the asterisk triangle program the numeric input needs to be restricted to a certain value range. Such a valid range would be 1 through 79. Entering anything greater than 79 and all the visuals go out the window.

Replace the entire while loop with the following while snippet.

   while (!(cin >> rows) or (rows < 1 or rows > 79))
    {
        cout << "Must enter an integer (1 - 79): ";
        cin.clear();              // clear cin error state, if exists
        while (cin.get() != '\n') // clear out non newline characters
            continue;
    }

In reality, only the while statement needed to be replaced as the nothing drastically needed to be changed in the body of the loop. As you can see I did change the cout statement to better inform a user of what is required, a number between 1 - 79.

The while statement could have been written like so:

while (!(cin >> rows) || (rows < 1 || rows > 79))

The || is the same as or and, though not used in this example, && is the same as and.

You will note that the program now detects incorrect data entry, that is any data entry commencing with a non numeric and any numeric entry not falling within the range of 1 through 79.

As the code stands now, what keyboard input doesn't it correctly trap? Entries such as '5prime' or '5.5' are not processed. Both of these entries are considered valid input as the first character is a numeric. Enter as input '80prime' or '80.5' and both are considered outside the valid value range. I'll discuss this in the next article.

Enhancement # 3 - Trap number followed by non numeric

As you know, the Asterisk Triangle Program has one remaining flaw and it's a flaw that should be addressed.

Presently, if a user enters as input 50prime the number '50' is accepted as valid and the remaining 'prime' is ignored. If a user enters as input 50.5 the number '50' is accepted and the remaining '.5' is ignored. Ideally we need to trap this and force a true integer to be entered.

Replace this line of code

while (!(cin >> rows) or (rows < 1 or rows > 79))

with this

while (!(cin >> rows) or (rows < 1 or rows > 79 or cin.peek() != '\n'))

You'll note that a third condition has been added to the while statement. That being cin.peek() != '\n'. What this is saying is,

"if the next character in the input buffer is not a newline character".

We know that after the execution of cin >> rows that if a true number was entered then all that is left in the input buffer is a newline ('\n') character. The peek() function merely checks for the next character in the input buffer. It doesn't remove the character from the input buffer. An entry such as '50prime' will cause the expression cin.peek() != '/n' to evaluate to true, as it detects the letter 'p', which in turn the body of the while loop is entered.

Here is the complete program code.

// trianglerows-5.cpp
// 30 Apr, 2008.

#include <iostream>
using namespace std;

int main()
{
    int rows, c;
    cout << "Enter the number of rows (1 - 79): ";
    while (!(cin >> rows) or (rows < 1 or rows > 79 or cin.peek() != '\n'))
    {
        cout << "Must enter an integer (1 - 79): ";
        cin.clear();              // clear cin error state, if exists
        while (cin.get() != '\n') // clear out non newline characters
            continue;
    }
    for (int r = 1; r <= rows; r++)
    {
        for (c = 1; c <= rows - r; c++)
            cout << ".";

        for (; c <= rows; c++)
            cout << "*";
        cout << endl;
    }
    // exit routine
    cout << "\n\n...Press 'ENTER' key to EXIT...\n";
    while (cin.get() != '\n')
        continue;                   // clear out non newline characters
    cin.get();                      // user to hit 'ENTER' key
    return 0;
}

I think I've covered all aspects of validating against incorrect data input.

Please note that there are other ways to accept keyboard input and different methods to validate against incorrect keyboard input. Feel free to make suggestions.