import queue grids = [] ################################################################################ grids.append('''\ OOOOOOOOOOOO OOO O OO O OO X O O # # O O O O O O # # O OO X OO OOX OO OO X OOO OOOOOOOOOOOO''') grids.append('''\ OOOOOOOOOOOO OOOOOOOOOOOO OOOOOOOOOOOO OOO OOO OO ## OO OO O # OO OO OO OO XX OO OOO OOO OOOOOOOOOOOO OOOOOOOOOOOO OOOOOOOOOOOO''') grids.append('''\ OOOOOOOOOOOO O O O O O # # O O O O O O O O O O X # O O O O O OOOOOOOOOOOO''') grids.append('''\ OOOOOOOOOOOO O O OO O # OO O OO O OO OOX OOO O OO O #OO O OO O O OOO OOOOOOOOOOOO OOOOOOOOOOOO''') grids.append('''\ OOOOOOOOOOOO O O O O O # O O # O O X O O O O O O # O O # O O O O O OOOOOOOOOOOO''') grids.append('''\ OOOOOOOOOOOO O O O O # O O OO O O O O O X O O O O # O O # O O O OOOOOOOOOOOO''') ################################################################################ def reader(grid): walls = set() blocks = set() targets = set() for y, line in enumerate(grid.split('\n')): for x, char in enumerate(line): if char == 'O': walls.add((y, x)) elif char == '#': blocks.add((y, x)) elif char == 'X': targets.add((y, x)) return walls, blocks, targets def worker(walls, blocks, targets): states = {frozenset(blocks)} jobs = queue.Queue() jobs.put((blocks, None)) while not jobs.empty(): job = jobs.get() # Pick a block to move. for block in job[0]: # Move up. offset = 1 temp = (block[0] - offset, block[1]) while temp not in walls and temp not in job[0]: offset += 1 temp = (block[0] - offset, block[1]) offset -= 1 # Check for movement. if offset: copy = set(job[0]) copy.remove(block) copy.add((block[0] - offset, block[1])) if copy not in states: if targets.issubset(copy): return (copy, job) states.add(frozenset(copy)) jobs.put((copy, job)) # Move down. offset = 1 temp = (block[0] + offset, block[1]) while temp not in walls and temp not in job[0]: offset += 1 temp = (block[0] + offset, block[1]) offset -= 1 # Check for movement. if offset: copy = set(job[0]) copy.remove(block) copy.add((block[0] + offset, block[1])) if copy not in states: if targets.issubset(copy): return (copy, job) states.add(frozenset(copy)) jobs.put((copy, job)) # Move left. offset = 1 temp = (block[0], block[1] - offset) while temp not in walls and temp not in job[0]: offset += 1 temp = (block[0], block[1] - offset) offset -= 1 # Check for movement. if offset: copy = set(job[0]) copy.remove(block) copy.add((block[0], block[1] - offset)) if copy not in states: if targets.issubset(copy): return (copy, job) states.add(frozenset(copy)) jobs.put((copy, job)) # Move right. offset = 1 temp = (block[0], block[1] + offset) while temp not in walls and temp not in job[0]: offset += 1 temp = (block[0], block[1] + offset) offset -= 1 # Check for movement. if offset: copy = set(job[0]) copy.remove(block) copy.add((block[0], block[1] + offset)) if copy not in states: if targets.issubset(copy): return (copy, job) states.add(frozenset(copy)) jobs.put((copy, job)) print(len(states), 'Unique States') print('No Solution Found!') return (blocks, None) def opener(walls, answer, targets): if answer[1] is not None: opener(walls, answer[1], targets) print(render(walls, answer[0], targets)) def render(walls, blocks, targets): box = {} for y, x in walls: if y not in box: box[y] = {} box[y][x] = 'O' for y, x in targets: box[y][x] = 'X' for y, x in blocks: box[y][x] = '#' max_y = max(box) max_x = 0 for y in box: max_x = max(max_x, max(box[y])) lines = [] for y in range(max_y + 1): line = '' for x in range(max_x + 1): line += box[y].get(x, ' ') lines.append(line) return '\n'.join(lines) ################################################################################ if __name__ == '__main__': walls, blocks, targets = reader(grids[-1]) answer = worker(walls, blocks, targets) opener(walls, answer, targets); input()