diff --git a/main.py b/main.py index 78ef184..0730604 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,5 @@ import itertools +from copy import deepcopy from itertools import combinations, combinations_with_replacement OP_PLUS = "PLUS" @@ -24,7 +25,7 @@ OP_LAMBDAS = { } BOX_H_LABELS = 'ABCDEF' -BOX_V_LABELS = '123456' +BOX_V_LABELS = '654321' class Block(object): @@ -33,7 +34,7 @@ class Block(object): self.operation = operation self.result = result - self.solutions = [] + self.solutions = set() self.verify() @@ -86,20 +87,49 @@ class Block(object): self.generate_hypotheses(grid_size) for hyp in self.generate_hypotheses(grid_size): for perm in itertools.permutations(hyp): - sol = list(zip(self.boxes, perm)) - self.solutions.append(sol) + sol = tuple(zip(self.boxes, perm)) + self.solutions.add(sol) def __repr__(self): return 'Block {}'.format(self.boxes) +def translate_box_to_rc(box): + return BOX_V_LABELS.index(box[1]), BOX_H_LABELS.index(box[0]) + + +def has_line_integrity(line): + existing_values = set() + for value in line: + if value in existing_values: + return False + if value != 0: + existing_values.add(value) + return True + + +def fill_grid(grid, solution): + box_values = [] + for box_value in solution: + box_values.append(box_value) + for box, value in box_values: + br, bc = translate_box_to_rc(box) + grid[br][bc] = value + return grid + + class Game(object): def __init__(self, grid_size, blocks): self.blocks = blocks self.grid_size = grid_size - self.grid = [[0] * grid_size] * grid_size self.verify() + for block in self.blocks: + block.generate_solutions(self.grid_size) + + def init_grid(self): + return [[0] * self.grid_size for _ in range(self.grid_size)] + def verify(self): found = set() for block in self.blocks: @@ -116,34 +146,52 @@ class Game(object): rv.add(block) return rv - def solve(self): - for block in self.blocks: - block.generate_solutions(self.grid_size) - # row_permutations = itertools.permutations(1, self.grid_size + 1) - for row_id, row in enumerate(self.grid): - for col_id, cell in enumerate(row): - self.grid[row_id][col_id] = self.generate_possible_cell_values(row_id, col_id) + def check_grid(self, grid): + for row in grid: + if not has_line_integrity(row): + return False + for col_id in range(self.grid_size): + col = [grid[r][col_id] for r in range(self.grid_size)] + if not has_line_integrity(col): + return False + return True + + def solve(self, grid, current_block_id=0): + block = self.blocks[current_block_id] + for solution in block.solutions: + prev_grid = deepcopy(grid) + grid = fill_grid(grid, solution) + if not self.check_grid(grid): + grid = prev_grid + else: + if current_block_id == len(self.blocks) - 1: + return grid + sol = self.solve(deepcopy(grid), current_block_id + 1) + if sol: + return sol def main(): game = Game(grid_size=6, blocks=[ - Block(boxes=['A6', 'A5'], operation=OP_MINUS, result=2), - Block(boxes=['B6', 'C6', 'C5'], operation=OP_MULTIPLY, result=20), - Block(boxes=['D6', 'D5', 'E5'], operation=OP_PLUS, result=11), - Block(boxes=['E6', 'F6', 'F5'], operation=OP_MULTIPLY, result=18), - Block(boxes=['A4', 'B4', 'B5'], operation=OP_PLUS, result=9), - Block(boxes=['C4', 'D4'], operation=OP_MINUS, result=5), - Block(boxes=['E4', 'F4', 'E3', 'F3'], operation=OP_MULTIPLY, result=80), - Block(boxes=['D3', 'C3'], operation=OP_MINUS, result=1), - Block(boxes=['A3', 'B3', 'B2'], operation=OP_PLUS, result=12), - Block(boxes=['C2', 'D2'], operation=OP_DIVIDE, result=2), - Block(boxes=['A1', 'A2'], operation=OP_PLUS, result=5), - Block(boxes=['B1'], operation=OP_NONE, result=6), - Block(boxes=['C1', 'D1', 'E1'], operation=OP_MULTIPLY, result=60), - Block(boxes=['F1', 'E2', 'F2'], operation=OP_MULTIPLY, result=9), + Block(boxes=['A6', 'B6', 'C6', 'D6'], operation=OP_MULTIPLY, result=120), + Block(boxes=['E6', 'E5', 'E4', 'F4'], operation=OP_PLUS, result=17), + Block(boxes=['F6', 'F5'], operation=OP_PLUS, result=5), + Block(boxes=['A5', 'B5'], operation=OP_MINUS, result=1), + Block(boxes=['C5', 'C4'], operation=OP_DIVIDE, result=2), + Block(boxes=['D5', 'D4'], operation=OP_PLUS, result=3), + Block(boxes=['A4', 'A3'], operation=OP_PLUS, result=5), + Block(boxes=['B4', 'B3', 'C3'], operation=OP_MULTIPLY, result=30), + Block(boxes=['D3', 'D2', 'E2'], operation=OP_MULTIPLY, result=15), + Block(boxes=['B2', 'C2', 'C1'], operation=OP_MULTIPLY, result=10), + Block(boxes=['A2'], operation=OP_NONE, result=6), + Block(boxes=['A1', 'B1'], operation=OP_DIVIDE, result=3), + Block(boxes=['D1', 'E1'], operation=OP_MINUS, result=1), + Block(boxes=['F1', 'F2', 'F3', 'E3'], operation=OP_PLUS, result=16), ]) - game.solve() + grid = game.solve(grid=game.init_grid()) + for line in grid: + print(line) if __name__ == '__main__':