Eugene
Eugene

Reputation: 97

How to print/output overlapping diamonds in python3

How would you output n overlapping diamonds with each diamond having a height of 2n-1. Here are the required outputs for:

n = 3

  *   *   *
 * * * * * *
* * * * * * *
 * * * * * *
  *   *   *

n = 4

   *     *     *     *
  * *   * *   * *   * *
 * * * * * * * * * * * *
* * * * * * * * * * * * *
 * * * * * * * * * * * *
  * *   * *   * *   * *
   *     *     *     *

n = 5

    *       *       *       *       *
   * *     * *     * *     * *     * *
  * * *   * * *   * * *   * * *   * * *
 * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * * * * * * * * * * * * *
  * * *   * * *   * * *   * * *   * * *
   * *     * *     * *     * *     * *
    *       *       *       *       *

I have tried some code using loops. I have managed to output each shape correctly w.r.t the input n. However, I could not display the diamonds on a 'single line' only on multiple lines like this:

n = 3

  *
 * *
* * *
 * *
  *
  *
 * *
* * *
 * *
  *
  *
 * *
* * *
 * *
  *

This is the code I used:

n = int(input()) #for number of diamonds per row
height = 2*n - 1

for j in range(1, n + 1): #for printing h no. of diamonds
    #from row 1 to middle row
    for row in range(1, (height + 1)//2 + 1):
        for spaces in range((height + 1)//2 - row): #print spaces per row
            print(" ", end = "")
        for stars in range((2*row) - 1): #print stars per row
            if stars % 2 == 0:
                print("*", end = "")
            else:
                print(" ", end = "")
        print()

    #from middle row to last row
    for row in range((height + 1)//2 + 1, height + 1):
        for spaces in range(row - (height + 1)//2):
            print(" ", end = "")
        for stars in range((height + 1 - row)*2 - 1):
            if stars % 2 == 0:
                print("*", end = "")
            else:
                print(" ", end = "")
        print()

Upvotes: 4

Views: 280

Answers (4)

quamrana
quamrana

Reputation: 39404

This is a way of printing the diamonds.

Each loop creates a line as a list of spaces and places part of each of the n diamonds in it before printing the line in one go:

n = int(input()) #for number of diamonds per row

for row in range(n):
    expanse = [' ']*(n+1)*(n-1)*2
    spaces_before = n-row-1
    stars = '* '*(row+1)
    for diamond in range(n):
        prefix = spaces_before + diamond*(n-1)*2
        expanse[prefix : prefix+len(stars)-1] = stars
    print(''.join(expanse))

for row in range(n-1):
    expanse = [' ']*(n+1)*(n-1)*2
    spaces_before = row+1
    stars = '* '*(n-row-1)
    for diamond in range(n):
        prefix = spaces_before + diamond*(n-1)*2
        expanse[prefix : prefix+len(stars)-1] = stars
    print(''.join(expanse))

The output is as per the question.

Update: Replaced inner loop with list slicing

Upvotes: 2

Lucas Wieloch
Lucas Wieloch

Reputation: 818

This is my answer to the proposed problem:

class Diamond(object):
    def __init__(self, size=3):
        self.size = size
        self.max_line_size = (size*2) + 1
        self.max_line = [self.one_line(size)]
        self.lines_below = map(self.one_line, range(size-1, 0, -1))
        self.lines_above = self.lines_below[::-1]
        self.lines = self.lines_above + self.max_line + self.lines_below

    def __str__(self):
        return "\n".join(self.lines)

    def one_line(self, num_of_diamonds):
        line_size = (num_of_diamonds * 2) + 1
        line = list()
        for i in range(1, line_size+1):
            el = "*" if (i % 2 == 0) else " "
            line.append(el)
        str_line = "".join(line)
        return  (" " * ((self.max_line_size - line_size)/2)) + str_line + (" " * ((self.max_line_size - line_size)/2))

class Diamonds(object):
    def __init__(self, size=3):
        self.size = size
        self.diamond = Diamond(size)

    def __str__(self):
        return "\n".join([i[:-1] + (i[2:-1]*(self.size-1)) for i in self.diamond.lines])

print(Diamonds(3))
print(Diamonds(4))
print(Diamonds(5))

I took a totally different approach from yours, but the main difference is in the method __str__ from Diamonds class. My approach printed the diamonds in one line because I am generating a big string with all lines already generated and concatenated with an endline before calling print. In your case it seems to me that you call print whenever you finish generating each line of each diamond, and print default behaviour is to endline after printing it's input.

Upvotes: 0

Aguy
Aguy

Reputation: 8059

How about this?

n = 4

for i in list(range(1, n+1)) + list(range(n-1, 0, -1)):
    rowpattern = (' '*(n-i) + '* '*(i) + ' '*(n-i)) * n
    print(rowpattern)

EDIT: Following comment below, this is more accurate:

n = 4

for i in list(range(1, n)) + list(range(n, 0, -1)):
    rowpattern = ' ' * (n-i) + ('* ' * (i if i!=n else i-1) +
        ' ' * (2 * (n - i) - 2)) * n + '*' *(1 if i==n else 0)
    print(rowpattern)

Upvotes: 2

Paul M.
Paul M.

Reputation: 10809

def print_diamonds(n):

    from functools import lru_cache

    number_of_peaks = n
    step_size = (n * 2) - 2

    @lru_cache(maxsize=n)
    def get_star_indecies(y):
        star_index_offset = 2 * y
        begin = star_index_offset
        end = (number_of_peaks * step_size) + begin

        star_indecies = {*range(begin, end, step_size)}
        if y == 0:
            return star_indecies
        else:
            return star_indecies | get_star_indecies(y-1)

    for iteration in range(n*2-1):
        y = -abs(iteration-(n-1))+(n-1)
        star_indecies = get_star_indecies(y)
        line_length = max(star_indecies) + 1
        space_offset = n-y-1
        space_padding = " " * space_offset
        line = space_padding
        for index in range(line_length):
            line += [" ", "*"][index in star_indecies]
        print(line)


def main():

    print_diamonds(5)

    return 0


if __name__ == "__main__":
    import sys
    sys.exit(main())

Upvotes: 0

Related Questions