''' Copyright (c) Paulo Pinto <paulo@cod.pt> Sample solution of the challenge at: reddit.com/3ltee2 In short: this: * *** ****** should become this: A / \ A A +---+ A / \ / \| |/ \ / \ +---+ +---+ A / \| o |/ \ +-------+ +---+ | o | | o | +-----------------------+ Works well with different aspects, and deals with floating/overlapping blocks. Try, for instance (add a space on the empty line to delay the construction): *** *** * ****** *** **** ********* ** ''' import random, sys aspect = (4, 2) # block size class Grid(object): 'Very simple sparce table implementation' emptyblock = ' ' def __init__(self): self.data = {} self.minx = 0 self.maxx = 0 self.miny = 0 self.maxy = 0 def __setitem__(self, pair, c): x, y = pair self.minx = min(self.minx, x) self.maxx = max(self.maxx, x) self.miny = min(self.miny, y) self.maxy = max(self.maxy, y) self.data[(x, y)] = c def __getitem__(self, pair): try: return self.data[pair] except KeyError: return Grid.emptyblock def empty(self, x, y): return self[(x, y)] == Grid.emptyblock def normalized(self): 'return a new translated grid starting on the origin (0,0)' newgrid = Grid() for x in range(self.minx, self.maxx + 1): for y in range(self.miny, self.maxy + 1): if not self.empty(x, y): newgrid[(x - self.minx, y - self.miny)] = self[(x, y)] return newgrid def show(self): for y in range(self.miny, self.maxy + 1): print(''.join(self[(x, y)] for x in range(self.minx, self.maxx + 1))) def parse(lines): 'parses an input lines into a grid, trimming the borders' g = Grid() for y, line in enumerate(lines): for x, block in enumerate(line): if block == '*': g[(x, y)] = '*' return g.normalized() def build_house(lines): # parse lines, determine door position bw, bh = aspect blue = parse(lines) # blueprints: ' ':empty, *:wall, d:door, r:roof grid = Grid() doory = blue.maxy doorx = random.choice([x for x in range(blue.maxx + 1) if not blue.empty(x, doory)]) blue[(doorx, blue.maxy)] = 'd' def puttext(x, y, text): for dy, line in enumerate(text.split('\n')): for dx, c in enumerate(line): grid[(x + dx, y + dy)] = c def roof(x, y, width): for dx in range(width//2): puttext(x + dx, y - dx, '/' + ' ' * (width-dx*2-2) + '\\') if width % 2 == 1: grid[(x + width//2, y-width//2)] = 'A' # draw blocks slab = '+' + '-' * (bw - 1) + '+' wall = '+' + '\n|' * (bh - 1) + '\n+' for y in range(blue.maxy + 1): for x in range(blue.maxx + 1): if blue[(x,y)] in ('*','d'): if not blue[(x,y - 1)] in ('*','d'): puttext(x * bw, y * bh, slab) # determine roof width, marking tiles for roofwidth in range(blue.maxx - x + 2): if blue.empty(x + roofwidth, y - 1) and not blue.empty(x + roofwidth, y): blue[(x + roofwidth, y - 1)] = 'r' else: break if roofwidth > 0: roof(x * bw + 1, y * bh - 1, roofwidth * bw - 1) if blue.empty(x,y+1): puttext(x * bw, (y + 1) * bh, slab) if blue.empty(x-1,y): puttext(x * bw, y * bh, wall) if blue.empty(x+1,y): puttext((x+1) * bw, y * bh, wall) if blue[(x, y)] == '*' and random.randint(1, 2) == 1: puttext(x * bw + bw//2, y * bh + bh//2, 'o') puttext(doorx * bw + 1, doory * bh, '\n|' * (bh - 1)) puttext((doorx + 1) * bw - 1, doory * bh, '\n|' * (bh - 1)) grid.show() print('blueprints (use * for blocks and space for indenting, empty line to build):') lines = [] while True: line = sys.stdin.readline().rstrip('\n') if any(c for c in line if c not in ('*', ' ')): print('bye') break elif line: lines.append(line) elif len(lines): build_house(lines) lines = [] print('')