mark
mark

Reputation: 27

python: Nesting

I am trying to write code that will display a shape with an odd width. Once the shape is completed it is placed inside an outer shape. The user will be able to input the characters used for the shape and the number of rows. I am hoping to produce a shape and, with a for loop, produce an outer shape.

*****       .           
 ***       ...        
  *       .....      
         .*****.        
        ...***...
       .....*.....        

this is my current code. I only know how to create a shape.

char = (input("Enter first charactrer"))

rows = int(input("enter the number of rows"))

for m in range(rows, 0, -1):
    for n in range(0, rows-m):
        print(end=" ")
    for n in range(0,m):
        print(char,end=" ")
    print()

Upvotes: 1

Views: 102

Answers (2)

Joe Iddon
Joe Iddon

Reputation: 20414

There is no need for some of the for-loops:

A useful feature in python is the way you can multiply a char by an int to get a string of that repeated char.

For example, if you do:

'c' * 10

you get:

'cccccccccc'

The reason this will be useful in your case is that you can use it to print the spaces and chars. This will be neater than for-loops as instead of printing a single chars over and over with no line breaks, you can instead print the full number of spaces as a string with one call.

To see what I mean, I wrote your original code using this:

char = input("enter the char ")
rows = int(input("enter the number of inner rows "))

for r in range(rows, 0, -1):
    print(' ' * 2 * (rows-r), end='')
    print((char+' ') * (2*r+1))

see how much neater it is? Also, you can notice that in the second print of each row for the chars, I used the calculation: 2*r +1. This is what you need to convert this pattern:

3. * * * *
2.  * * *
1.   * *
0.    *

to this one (as you requested):

3. *******
2.  *****
1.   ***
0.    *

you can see that for each row of the second one, the number of chars is 2 * the row +1. So row 3 has 7 chars, row 2 has 5 etc.

If you wanted to, as you have gotten rid of the unnecessary for-loops now, you could put both of the prints in one statement:

print(' ' * 2 * (rows-r) + (char+' ') * (2*r+1))

however, the first may be considered neater and clearer so you might want to stick with the separate statements.

Embedding the inner triangle in an outer one

First off for this, we will need to input two different chars for the inner and outer triangles. This will allow us to differentiate which is which.

I think that the best way to do this would be to keep the print statement which creates the spaces before the triangles as before, but modify how we draw the chars and their pattern.

This is what we want for an input of 4 rows:

0.        .
1.       ...
2.      .....
3.     .......
4.    .*******.
5.   ...*****...
6.  .....***.....
7. .......*.......

notice how it would be simplest to split the printing process in half. So for the top half just print a regular triangle, but for the bottom print one regular, one flipped and another regular.

To do this, we want to loop through each of the 8 rows and check when we are half way through. If past halfway, we want the calculations for each smaller triangle to think they are at the top of the triangle, i.e. reset the row count for them so when printing row 4, we calculate as if printing on row 0. To achieve this, once past halfway, we need to modulo the row variable by half of the total rows (the total rows now being not what is entered, but double the entered amount as you can see we have a triangle with 8 rows but an inputted row value of 4). This will give the intended effect of the triangles being half size of the total triangle.

Finally, one last thing to consider is that the center/inner triangle needs to be flipped as we have done before. As our row count is now going from 0 at the top downwards rather than the other way around, we need to change the calculation for that row to be rows - r so it is flipped.

Taking all this into account, the final code may look something like:

innerChar = input("enter the inner char ")
outerChar = input("enter the outer char ")

rows = int(input("enter the number of inner rows "))

for r in range(2 * rows):
    #print the white space on the left
    print(' ' * 2 * (2 * rows-r), end='')
    if r < rows:
        #top triangle
        print((outerChar+' ') * (2*r + 1))
    else:
        #working with the row modulo 2
        r %= rows
        #bottom three triangles
        print((outerChar+' ') * (2*r + 1), end='')
        print((innerChar+' ') * (2*(rows-r) - 1), end='')
        print((outerChar+' ') * (2*r + 1))

Upvotes: 2

PM 2Ring
PM 2Ring

Reputation: 55469

One way to make this easier is to not worry about the spaces at first. Just generate the non-space symbols of each row, and save them into a list. Then at the end, determine the width of the biggest row, and use the str.center method to print each row with the correct amount of space.

def tri(n, chars='*.'):
    ic, oc = chars
    outer = range(1, 2*n, 2)

    # top triangle chars
    rows = []
    for w in outer:
        rows.append(w * oc)

    # lower triangles chars
    inner = reversed(outer)
    for u, v in zip(outer, inner):
        a, b = u * oc, v * ic
        rows.append(a + b + a)

    # Get maximum row size
    width = len(rows[-1])

    #Print all the rows
    for row in rows:
        print(row.center(width))

# test

tri(5)

output

       .       
      ...      
     .....     
    .......    
   .*******.   
  ...*****...  
 .....***..... 
.......*.......

The characters to use are passed to tri as a two character string, with a default of '*' for the inner char (ic) and '.' for the outer char (oc). But if you want to use other chars you can pass them, like this:

tri(4, '@o') 

Upvotes: 0

Related Questions