Hey all!

So I'm taking the Harvard CS50 course online in my free time to help me learn some coding / computer logic. I've worked through some of the problems in Problem Set 1, but am stuck on the Mario Pyramid example. For those unfamiliar with it, here's the prompt:

Write, in a file called mario.c in your ~/workspace/pset1 directory, a program that recreates this half-pyramid using hashes (#) for blocks. However, to make things more interesting, first prompt the user for the half-pyramid’s height, a non-negative integer no greater than 23. (The height of the half-pyramid pictured above happens to be 8.) If the user fails to provide a non-negative integer no greater than 23, you should re-prompt for the same again. Then, generate (with the help of printf and one or more loops) the desired half-pyramid. Take care to align the bottom-left corner of your half-pyramid with the left-hand edge of your terminal window, as in the sample output below, wherein underlined text represents some user’s input.

height: 8
##
###
####
#####
######
#######
########
#########

Note that the rightmost two columns of blocks must be of the same height. No need to generate the pipe, clouds, numbers, text, or Mario himself.

Now, I can get user input and make sure it is within the correct range (below 23 but greater than 0), but I am having a lot of trouble with the following:

• Working out the logic behind printing the correct number of spaces and hash marks. I can (obviously) figure out how the number of spaces/hash marks is related to each individual row, but not one formula to apply to every row (if that makes sense). This is obviously something I need to (and want to) work out on my own, but if someone could give me the best way to start, that'd be great.
• Figuring out the print process for printing multiple lines of text (hashes and spaces), each containing a different arrangement of characters based on the logic in the first bullet point.
• Probably related to the above bullet point, but I am really unsure of where to define my "rows" variable. If the user inputs "4" as the height, for example, there would be 4 rows (and five columns). But the logic behind the variables, aside from one for a hashmark and one for a space, is really frustrating me.

Here is the shell of code that I have right now. At the moment it is only telling the user whether or not his or her input is within the correct range, so I don't know if it's helpful.

Code:
#include <stdio.h>
#include <string.h>

int main(void)
{
int height;
int block;
int space;
int rows;

printf("Please input a height value for this half-pyramid.\n");
scanf("%i", &height);

if (height < 23 || height > 0)
{
printf("The value %i a valid height. \n", height);

}
else if (height > 23 || height < 0)
{
printf("The value %i an invalid height.\n", height);

}

return 0 ;
}

2. The description of the problem says the height should be "no greater than 23," meaning that 23 is an allowed height. Your code, as written, does not handle the case in which the user enters 23 as the height.
You don't need an else-if to handle the invalid height. It can simply be else.
Your if condition also fails to enforce any constraints at all. Literally all real numbers are less than 23 OR greater than zero.
The problem contradicts itself. It shows an example of what it should look like, but that example is in opposition to later, when it says "the rightmost two columns of blocks must be of the same height," unless the forum is eating whitespace, destroying the correct formatting, which is entirely possible.

You don't need a "formula" to apply to all rows. You can use a for loop, which is responsible for the rows, with two other for loops inside it, one for drawing the spaces, and the other for drawing blocks.

3. Another way that the input validation requirements are not being met in the code you posted:

>> If the user fails to provide a non-negative integer no greater than 23, you should re-prompt for the same again.

4. Originally Posted by Elkvis
The description of the problem says the height should be "no greater than 23," meaning that 23 is an allowed height. Your code, as written, does not handle the case in which the user enters 23 as the height.
You don't need an else-if to handle the invalid height. It can simply be else.
Your if condition also fails to enforce any constraints at all. Literally all real numbers are less than 23 OR greater than zero.
The problem contradicts itself. It shows an example of what it should look like, but that example is in opposition to later, when it says "the rightmost two columns of blocks must be of the same height," unless the forum is eating whitespace, destroying the correct formatting, which is entirely possible.

You don't need a "formula" to apply to all rows. You can use a for loop, which is responsible for the rows, with two other for loops inside it, one for drawing the spaces, and the other for drawing blocks.

Thanks for catching the mistake with the range of my condition--and I'm not sure why I put ||, I actually meant &&.

The forum messed with the formatting--in the code, the stairs of the pyramid will start lowest on the left and get higher as it moves right (it's reversed there, it would seem). But the intended point was that the top of the pyramid would not be one # but two ##. So the right-most column would be the same height as the one to its left.

As for the rows-maybe this is my being new to coding, but I'm just confused about defining the "rows". I know that the code would be something like ...

Code:
for(rows=0, rows < height, rows++)
But don't I need to define rows somewhere as referring to the the number of lines being printed? Right now, I only have "rows" defined as an integer, and have it initialized to 0 in the for() function. Where exactly is it supposed to be defined as the number of lines being printed?

Sorry if that is a dumb question.

5. Originally Posted by Matticus
Another way that the input validation requirements are not being met in the code you posted:

>> If the user fails to provide a non-negative integer no greater than 23, you should re-prompt for the same again.
Thanks for the reply! This is actually something I've had trouble figuring out. The cs50 class provides its own library with premade functions that are "training wheels," of sorts. I've used those functions for the most part, but in this case, their GetInt() function has that re-prompt built in. I've been wracking my brain for a while now to figure out how to get a loop to go back to the beginning of the loop if certain parameters are not met.

6. So the pyramid of height 8 should look like this:

Code:
.
##
###
####
#####
######
#######
########
#########
Correct? (Ignore the period. It's required to get the board to format it correctly.)

Originally Posted by mcavanaugh8
But don't I need to define rows somewhere as referring to the the number of lines being printed? Right now, I only have "rows" defined as an integer, and have it initialized to 0 in the for() function. Where exactly is it supposed to be defined as the number of lines being printed?
You don't need to do that with your "rows" variable. It literally exists as the counter for the loop. Height holds the number of lines being printed.

7. Originally Posted by mcavanaugh8
Thanks for the reply! This is actually something I've had trouble figuring out. The cs50 class provides its own library with premade functions that are "training wheels," of sorts. I've used those functions for the most part, but in this case, their GetInt() function has that re-prompt built in. I've been wracking my brain for a while now to figure out how to get a loop to go back to the beginning of the loop if certain parameters are not met.
You apparently know how "if()" statements work:

Code:
if( /* expression is true */ )
// do this
A loop (for instance, the "while()") works in a similar way:

Code:
while( /* expression is true */ )
// loop this
For input, I'd suggest a "do-while" loop, as the loop is guaranteed to run at least once (allowing values to be entered) before checking the condition.

The implementation of "GetInt()" shouldn't matter, as long as it gives you the value entered by the user. The "invalid input" message can be separate from the prompt in the "GetInt()" function.

I feel like two people giving advice about two separate things simultaneously might be confusing. I'd suggest focusing on what Elkvis is helping with, as that pertains more to program planning (which should come first).

8. Originally Posted by Elkvis
So the pyramid of height 8 should look like this:

Code:
.
##
###
####
#####
######
#######
########
#########
Correct? (Ignore the period. It's required to get the board to format it correctly.)

You don't need to do that with your "rows" variable. It literally exists as the counter for the loop. Height holds the number of lines being printed.
Yup, that is the correct formatting for the pyramid.

And ohh, that makes sense now--thanks! I think I've also figured out that the number of spaces is equal to height - rows, so I think some progress has been made there. But the nested for loops will probably take me some time to figure out.

9. Originally Posted by Matticus
You apparently know how "if()" statements work:

Code:
if( /* expression is true */ )
// do this
A loop (for instance, the "while()") works in a similar way:

Code:
while( /* expression is true */ )
// loop this
For input, I'd suggest a "do-while" loop, as the loop is guaranteed to run at least once (allowing values to be entered) before checking the condition.

The implementation of "GetInt()" shouldn't matter, as long as it gives you the value entered by the user. The "invalid input" message can be separate from the prompt in the "GetInt()" function.

I feel like two people giving advice about two separate things simultaneously might be confusing. I'd suggest focusing on what Elkvis is helping with, as that pertains more to program planning (which should come first).
I believe I was simply over-thinking the whole thing, assuming for some reason that I needed some kind of function that returned you to the start, rather than just using an if / else or while loop. Thanks! And you're right. I'm going to focus on the program planning and then get down to the grittier stuff.

10. Do you know why my compiler might tell me to put a semicolon at the end of my for() statement but when I add the semicolon, it asks me to take it away? This is incredibly frustrating lol.

11. You should post the exact error message, along with a simplified yet complete program that exhibits that error message. Also tell us what compiler you are using.

13. I'm using the CS50 IDE for the course. My code is as follows:

Code:
#include <stdio.h>
#include <string.h>

int main(void)
{
int height;
int blocks;
int space;
int rows;

printf("Please input a height value for this half-pyramid.\n");
scanf("%i", &height);

for(height <= 23 && height > 0);
{
printf("The value %i a valid height. Printing...\n", height);

for(rows = 0, rows < height, rows++);
{
printf("\n");

for(blocks = height, blocks = height + 1, blocks++);
{
printf("#");
}
}
}

for(height > 23 && height < 0);
{
printf("The value %i an invalid height.\n", height);
}

return 0 ;
}
And I get the following errors:

mario_test.c:14:39: error: expected ';' in 'for' statement specifier
for(height <= 23 && height > 0);
^
mario_test.c:14:39: error: expected ';' in 'for' statement specifier
mario_test.c:14:26: error: expression result unused [-Werror,-Wunused-value]
for(height <= 23 && height > 0);
~~~~~~~~~~~~ ^ ~~~~~~~~~~
mario_test.c:18:36: error: expression result unused [-Werror,-Wunused-value]
for(rows = 0, rows < height, rows++);
~~~~ ^ ~~~~~~
mario_test.c:18:52: error: expected ';' in 'for' statement specifier
for(rows = 0, rows < height, rows++);
^
mario_test.c:18:52: error: expected ';' in 'for' statement specifier
mario_test.c:23:75: error: expected ';' in 'for' statement specifier
for(blocks = height, blocks = height + 1, blocks++);
^
mario_test.c:23:75: error: expected ';' in 'for' statement specifier
mario_test.c:23:76: error: for loop has empty body [-Werror,-Wempty-body]
for(blocks = height, blocks = height + 1, blocks++);
^
mario_test.c:23:76: note: put the semicolon on a separate line to silence this warning
mario_test.c:18:53: error: for loop has empty body [-Werror,-Wempty-body]
for(rows = 0, rows < height, rows++);
^
mario_test.c:18:53: note: put the semicolon on a separate line to silence this warning
mario_test.c:30:38: error: expected ';' in 'for' statement specifier
for(height > 23 && height < 0);
^
mario_test.c:30:38: error: expected ';' in 'for' statement specifier
mario_test.c:30:25: error: expression result unused [-Werror,-Wunused-value]
for(height > 23 && height < 0);
~~~~~~~~~~~ ^ ~~~~~~~~~~
mario_test.c:14:40: error: for loop has empty body [-Werror,-Wempty-body]
for(height <= 23 && height > 0);
^
mario_test.c:14:40: note: put the semicolon on a separate line to silence this warning
mario_test.c:30:39: error: for loop has empty body [-Werror,-Wempty-body]
for(height > 23 && height < 0);
^
mario_test.c:30:39: note: put the semicolon on a separate line to silence this warning
If I take them away, I get:

mario_test.c:14:39: error: expected ';' in 'for' statement specifier
for(height <= 23 && height > 0)
^
mario_test.c:14:39: error: expected ';' in 'for' statement specifier
mario_test.c:18:36: error: expression result unused [-Werror,-Wunused-value]
for(rows = 0, rows < height, rows++)
~~~~ ^ ~~~~~~
mario_test.c:18:52: error: expected ';' in 'for' statement specifier
for(rows = 0, rows < height, rows++)
^
mario_test.c:18:52: error: expected ';' in 'for' statement specifier
mario_test.c:23:75: error: expected ';' in 'for' statement specifier
for(blocks = height, blocks = height + 1, blocks++)
^
mario_test.c:23:75: error: expected ';' in 'for' statement specifier
mario_test.c:14:26: error: expression result unused [-Werror,-Wunused-value]
for(height <= 23 && height > 0)
~~~~~~~~~~~~ ^ ~~~~~~~~~~
mario_test.c:30:38: error: expected ';' in 'for' statement specifier
for(height > 23 && height < 0);
^
mario_test.c:30:38: error: expected ';' in 'for' statement specifier
mario_test.c:30:25: error: expression result unused [-Werror,-Wunused-value]
for(height > 23 && height < 0);
~~~~~~~~~~~ ^ ~~~~~~~~~~
mario_test.c:30:39: error: for loop has empty body [-Werror,-Wempty-body]
for(height > 23 && height < 0);
^
mario_test.c:30:39: note: put the semicolon on a separate line to silence this warning

Thanks to both of you again, by the way.

14. Look closely at that "for" loop:
Code:
for(height > 23 && height < 0);
That is not how a for() loop is formatted (with or without the semicolon). Perhaps you meant for this to be an if() statement?

Edit: also look closely at the error message:
mario_test.c:14:39: error: expected ';' in 'for' statement specifier
for(height <= 23 && height > 0)
I suggest you review your documentation for for(;;) loops and if() statements.

Jim

15. In addition to Jim's comments, look at the body of the second "for()" loop. It only prints a message. If this loop is entered, you give it no chance to stop. That would be a good place to prompt for a new value...