From fbc133b8a9a18a0419103b91b06b58e7bb6d625d Mon Sep 17 00:00:00 2001 From: Daniel Tsvetkov Date: Sat, 9 Apr 2022 12:25:33 +0200 Subject: [PATCH] humanized grid as entry --- main.py | 122 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 94 insertions(+), 28 deletions(-) diff --git a/main.py b/main.py index 1aa0743..5b385a6 100644 --- a/main.py +++ b/main.py @@ -2,11 +2,16 @@ import itertools from copy import deepcopy from itertools import combinations, combinations_with_replacement -OP_PLUS = "PLUS" -OP_MINUS = "MINUS" -OP_MULTIPLY = "MULTIPLY" -OP_DIVIDE = "DIVIDE" -OP_NONE = "NONE" +# up right down left +DIRECTIONS = [(0, -1), (1, 0), (0, 1), (-1, 0)] +OP_PLUS = "+" +OP_MINUS = "-" +OP_MULTIPLY = "x" +OP_DIVIDE = "/" +OP_NONE = "#" + +BOX_H_LABELS = 'ABCDEFGHI' +BOX_V_LABELS = '123456789' def lambda_mul(x): @@ -24,12 +29,13 @@ OP_LAMBDAS = { OP_NONE: lambda x: x[0], } -BOX_H_LABELS = 'ABCDEFGHI' -BOX_V_LABELS = '123456789' - def translate_box_to_rc(box, grid_size): - return BOX_V_LABELS.index(box[1]), BOX_H_LABELS[grid_size-1::-1].index(box[0]) + return BOX_V_LABELS.index(box[1]), BOX_H_LABELS[grid_size - 1::-1].index(box[0]) + + +def translate_rc_to_box(row_id, col_id, grid_size): + return BOX_H_LABELS[col_id] + BOX_V_LABELS[grid_size - row_id - 1] def has_line_integrity(line): @@ -183,28 +189,88 @@ class Game(object): return sol -def main(): - game = Game(grid_size=6, - blocks=[ - 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), - ]) - grid = game.solve(grid=init_grid(game.grid_size)) +def walk_grid_to_boxes(grid, row_id, col_id, initial_cell, boxes): + """Recursively walk through the boxes in a grid going up,left,down,right to construct + a contiguous block of same operations.""" + grid_size = len(grid) + for v_offset, h_offset in DIRECTIONS: + new_row_id, new_col_id = row_id + h_offset, col_id + v_offset + # avoid wrap-around + if not (0 <= new_row_id < grid_size and 0 <= new_col_id < grid_size): + continue + box = translate_rc_to_box(new_row_id, new_col_id, grid_size) + if box in boxes: + continue + cell = grid[new_row_id][new_col_id].strip() + if cell == initial_cell: + boxes.append(box) + walk_grid_to_boxes(grid, new_row_id, new_col_id, initial_cell, boxes) + return boxes + + +def construct_blocks(grid): + """Reads a grid of operations and converts them to contiguous blocks.""" + grid_size = len(grid) + blocks, used_boxes = [], [] + for row_id, row in enumerate(grid): + assert len(row) == grid_size + for col_id, cell in enumerate(row): + box = translate_rc_to_box(row_id, col_id, grid_size) + if box in used_boxes: + continue + cell = cell.strip() + operation = cell[-1] + assert operation in OP_LAMBDAS.keys() + value = int(cell[:-1]) + boxes = walk_grid_to_boxes(grid, row_id, col_id, cell, [box]) + used_boxes += boxes + blocks.append(Block(boxes=boxes, operation=operation, result=value)) + return grid_size, blocks + + +def print_grid(grid): for line in grid: print(line) +def solve_game(grid: list): + grid_size, blocks = construct_blocks(grid) + game = Game(grid_size=grid_size, blocks=blocks) + grid = game.solve(grid=init_grid(game.grid_size)) + print_grid(grid) + + +def main(): + """Construct a game using the +-x/ symbols for operations and the number before that. + Whitespace around is meaningless and can be used to arrange the matrix visually.""" + solve_game([ + ['120x', '120x', '120x', '120x', '17+', '5+ '], + ['1- ', '1- ', '2/ ', '3+ ', '17+', '5+ '], + ['5+ ', '30x ', '2/ ', '3+ ', '17+', '17+'], + ['5+ ', '30x ', '30x ', '15x ', '16+', '16+'], + ['6# ', '10x ', '10x ', '15x ', '15x', '16+'], + ['3/ ', '3/ ', '10x ', '1- ', '1- ', '16+'], + ]) + + # Alternatively construct a Game object more explicitly: + # game = Game(grid_size=6, + # blocks=[ + # 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), + # ]) + + if __name__ == '__main__': main()