def clone(compressed, filename): contents = None with open(filename) as f: contents = f.read() puzzle, name, reverse_id_map = load(contents) board = puzzle.board revealed = puzzle.revealed constraints = puzzle.og_constraints num = len(board) board.sort( key=lambda c: c[0] in revealed) # praise be to Python's stable sort if not compressed: return dict( num=num, board=board, revealed=revealed, constraints=constraints, ) else: replace_cells(board, revealed, constraints, compressed) puzzle = Puzzle(board, revealed, constraints) result = puzzle.solve() scored = score(result, 'seqnum') title = f'Cloned "{name}" with score {scored}' tile_text = 'CLO' data = extract(contents) num_revealed = 0 for index, node in enumerate(data['nodes']): if index in revealed: node['revealed'] = True num_revealed += 1 else: node['has_mine'] = compressed[index - num_revealed] == '*' node['secret'] = compressed[index - num_revealed] == '?' return dict( title=title, tile_text=tile_text, scored=scored, nodes=data['nodes'], columns=data['columns'], colors=data['colors'], )
def cl_corner_bite_render(compressed, size): num, board, revealed, constraints, _ = cl_corner_bite(size) tile_size = 10 points = '-{s},-{s},{s},-{s},{s},{s},-{s},{s}'.format(s=0.96 * tile_size / 2) nodes = [] columns = [] for i in range(num): board[i][1] = compressed[i] nodes.append( dict( id=i, neighbors=board[i][2], position=((i % size) * tile_size, (i // size) * tile_size), has_mine=compressed[i] == '*', secret=compressed[i] == '?', revealed=i in revealed, points=points, )) for j in range(size): # horizontal column hints constraints[2 * j][0] = sum( [compressed[i] == '*' for i in constraints[2 * j][1]]) columns.append( dict( ids=constraints[2 * j][1], text_location=(-tile_size, j * tile_size), )) # vertical column hints constraints[2 * j + 1][0] = sum( [compressed[i] == '*' for i in constraints[2 * j + 1][1]]) columns.append( dict( ids=constraints[2 * j + 1][1], text_location=(j * tile_size, -tile_size), )) constraints[-1][0] = compressed.count('*') result = Puzzle(board, revealed, constraints).solve() scored = score(result, 'seqnum') title = f'CL Corner Bite {size}x{size} with score {scored}' tile_text = 'CoB' return dict( title=title, tile_text=tile_text, nodes=nodes, columns=columns, scored=scored, )
def test_conformsToRuleThreeIsolatedWhiteTile(): p = Puzzle( ((2, 1, 3), (1, 1, 2), (3, 2, 1)), True) p.markBlack(1, 1) p.markBlack(2, 0) p.markBlack(2, 2) assert(not p.conformsToRuleThree())
def test_conformsToRuleOneMultipleMarked(): p = Puzzle( ((2, 1), (1, 1)), False) p.markBlack(1, 1) assert(p.conformsToRuleOne())
def test_conformsToRuleOneNoneMarked(): p = Puzzle( ((2, 1), (1, 1)), False) assert(not p.conformsToRuleOne())
def test_getMarkedNum(): p = Puzzle( ((2, 1), (1, 1)), True) assert(p.getNum(0, 0) == 2)
def L_shape_grid(compressed, size, depth): """ 33 13 2114 2244 """ size, depth = int(size), int(depth) side_length = 2 * size * 2**depth points = '-{s},-{s},{s},-{s},{s},{s},-{s},{s}'.format(s=0.96 / 2) cindex = 0 board = [] revealed = [] constraints = [] nodes = [] columns = [] colors = [ dict(ids=[], color='RED', is_dark=False), dict(ids=[], color='ORANGE', is_dark=False), dict(ids=[], color='GREEN', is_dark=False), dict(ids=[], color='BLUE', is_dark=False), ] id_map = dict() for y in range(side_length): for x in range(side_length): if x < side_length // 2 - 1 and y < side_length // 2 - 1: continue else: id_map[(x, y)] = len(id_map) pos_to_id = lambda x, y: id_map.get((x, y)) for y in range(side_length): for x in range(side_length): if x < side_length // 2 - 1 and y < side_length // 2 - 1: continue cell_id = pos_to_id(x, y) neighbors = [] for dy in range(-1, 2): for dx in range(-1, 2): if dx == dy == 0: continue neighbor_id = pos_to_id(x + dx, y + dy) if neighbor_id: neighbors.append(neighbor_id) if x < side_length // 2 and y < side_length // 2: what = '.' revealed.append(cell_id) else: what = compressed[cindex] if compressed else '' cindex += 1 temp_x, temp_y, threshold = x, y, side_length // 4 for i in range(depth): if threshold <= temp_x < 3 * threshold and threshold <= temp_y < 3 * threshold: if i == depth - 1: colors[0]['ids'].append(cell_id) else: temp_x -= threshold temp_y -= threshold elif temp_x < threshold * 2: if i == depth - 1: colors[1]['ids'].append(cell_id) else: temp_x, temp_y = temp_y - threshold * 2, threshold * 2 - temp_x - 1 elif temp_y < threshold * 2: if i == depth - 1: colors[2]['ids'].append(cell_id) else: temp_x, temp_y = threshold * 2 - temp_y - 1, temp_x - threshold * 2 else: if i == depth - 1: colors[3]['ids'].append(cell_id) else: temp_x -= threshold * 2 temp_y -= threshold * 2 threshold //= 2 board.append([cell_id, what, neighbors]) if compressed: nodes.append( dict( id=cell_id, neighbors=neighbors, position=(x, y), has_mine=what == '*', secret=what == '?', revealed=cell_id in revealed, points=points, )) constraints.append([ sum([c[1] == '*' for c in board]), [c[0] for c in board if c[0] not in revealed] ]) for color in colors: constraints.append( [sum(board[n][1] == '*' for n in color['ids']), color['ids']]) num = 3 * side_length**2 // 4 board.sort( key=lambda c: c[0] in revealed) # praise be to Python's stable sort if not compressed: return dict( num=num, board=board, revealed=revealed, constraints=constraints, ) else: result = Puzzle(board, revealed, constraints).solve() scored = score(result, 'seqnum') title = f'L-shape {size}-{depth} with score {scored}' tile_text = 'L' return dict( title=title, tile_text=tile_text, nodes=nodes, columns=columns, colors=colors, scored=scored, )
# puzzle = Puzzle(g, t, buffer_size=4) # # print(puzzle) # # print('='*50) # # # for g, t in [(g3, t3), (g4, t4)]: # puzzle = Puzzle(g, t, buffer_size=4) # # print(puzzle) # # print('='*50) # # for g, t in [(g5, t5)]: # puzzle = Puzzle(g, t, buffer_size=6) # # print(puzzle) # # print('='*50) for g, t in [(g6, t6)]: puzzle = Puzzle(g, t, buffer_size=4) print(puzzle) f = puzzle.plot_solution() f.show() print('=' * 50)
def test_conformsToRuleTwoHorizontallyAdjacentMarks(): p = Puzzle( ((2, 1), (1, 1)), True) p.markBlack(0, 0) p.markBlack(0, 1) assert(not p.conformsToRuleTwo())
def test_conformsToRuleTwoNonAdjacentMarks(): p = Puzzle( ((2, 1), (1, 1)), True) p.markBlack(1, 1) p.markBlack(0, 0) assert(p.conformsToRuleTwo())
def test_conformsToRuleTwoSingleMark(): p = Puzzle( ((2, 1), (1, 1)), True) p.markBlack(1, 1) assert(p.conformsToRuleTwo())
def test_conformsToRuleTwoUnmarked(): p = Puzzle( ((2, 1), (1, 1)), False) assert(p.conformsToRuleTwo())
def test_getUnmarkedNum(): p = Puzzle( ((2, 1), (1, 1)), True) assert(int(p.getNum(0, 1)) == 1)
def test_findMostRestrictedCompleted(): b = ( (1, 2, 3), (1, 1, 3), (2, 3, 3) ) p = Puzzle(b , False) possibles = [ [ [[], [], []], [ [ [1, 0], [1, 1]], [], [] ], [ [], [], [ [2, 1], [2, 2] ] ] ], [ [ [ [0, 0], [1, 0] ], [], [] ], [ [], [], [] ], [ [], [], [ [0, 2], [1, 2], [2, 2] ] ] ] ] findMostRestricted(p, possibles) findMostRestricted(p, possibles) # These should be Black assert(p.isBlack(1, 0)) assert(p.isBlack(0, 2)) # These should be now marked White assert(p.isWhite(0, 0)) assert(p.isWhite(1, 2)) assert(p.isWhite(2, 1)) # These should still be adjacent assert(p.isMarkedAdjacent(1, 1)) # The other three tiles should have remained marked White assert(p.isWhite(0, 1)) assert(p.isWhite(2, 0)) # Solve the puzzle fully findMostRestricted(p, possibles) # This should now be marked White assert(p.isWhite(1, 1)) # Check that the puzzle was solved assert(p.isSolved())
def test_markBlack(): p = Puzzle( ((2, 1), (1, 1)), True) p.markBlack(1, 1) assert(p.isBlack(1, 1))
def test_findMostRestricted(): b = ( (1, 2, 3), (1, 1, 3), (2, 3, 3) ) p = Puzzle(b , False) possibles = [ [ [[], [], []], [ [ [1, 0], [1, 1]], [], [] ], [ [], [], [ [2, 1], [2, 2] ] ] ], [ [ [ [0, 0], [1, 0] ], [], [] ], [ [], [], [] ], [ [], [], [ [0, 2], [1, 2], [2, 2] ] ] ] ] possibles = findMostRestricted(p, possibles) # The first iteration should just mark bottom-right element as black assert(p.isBlack(2, 2)) # All other adjacent numbers should be marked as adjacent assert(p.isMarkedAdjacent(0, 0)) assert(p.isMarkedAdjacent(0, 2)) assert(p.isMarkedAdjacent(1, 0)) assert(p.isMarkedAdjacent(1, 1)) assert(p.isMarkedAdjacent(1, 2)) assert(p.isMarkedAdjacent(2, 1)) # The other two tiles should remain marked White assert(p.isWhite(0, 1)) assert(p.isWhite(2, 0))
def test_conformsToRuleThreeEmpty(): p = Puzzle( ((2, 1), (1, 1)), False) assert(p.conformsToRuleThree())
def load(contents, verbose=False): id_map = dict() nodes = [] total_mines = 0 initial_revealed = [] # Not parsing arbitrary XML here! matches = re.finditer('<NODE>.*?</NODE>', contents, re.DOTALL) for match in matches: node = match.group(0) node_id = re.search('<ID>(.*?)</ID>', node).group(1) neighbor_ids = re.search('<EDGES>(.*?)</EDGES>', node).group(1).split(',') revealed = bool(re.search('<REVEALED>[Tt]rue</REVEALED>', node)) has_mine = bool(re.search('<HAS_MINE>[Tt]rue</HAS_MINE>', node)) secret = bool(re.search('<SECRET>[Tt]rue</SECRET>', node)) if neighbor_ids == ['']: # no neighbors neighbor_ids = [] if revealed: initial_revealed.append(node_id) if has_mine: total_mines += 1 node_info = dict( node_id=node_id, neighbor_ids=neighbor_ids, revealed=revealed, has_mine=has_mine, secret=secret, ) id_map[node_id] = len(nodes) nodes.append(node_info) # Hack for 94: Gridlock III (that the dev had to do too) if re.search('<ID>(.*?)</ID>', contents).group(1) == '2256502332117638': initial_revealed = '0,96,33,66,3,35,99,36,37,69,6,39,9,46,26,90,60,93,30,63'.split( ',') board = [] revealed = [id_map[revealed_id] for revealed_id in initial_revealed] constraints = [] for node in nodes: cell = [ id_map[node['node_id']], '*' if node['has_mine'] else '?' if node['secret'] else '.', [id_map[neighbor_id] for neighbor_id in node['neighbor_ids']], ] board.append(cell) constraints.append([total_mines, list(range(len(nodes)))]) gray_mines = [total_mines, set(range(len(nodes)))] hints = re.finditer('<(COLUMN_)?HINT>.*?</(COLUMN_)?HINT>', contents, re.DOTALL) for hint in hints: mapped_ids = [] mine_count = 0 ids = re.search('<IDS>(.*?)</IDS>', hint.group(0)).group(1).split(',') for hint_id in ids: hint_id = id_map[hint_id] mapped_ids.append(hint_id) mine_count += nodes[hint_id]['has_mine'] constraints.append([mine_count, mapped_ids]) if 'COLUMN' not in hint.group(0): gray_mines[0] -= mine_count gray_mines[1].difference_update(set(mapped_ids)) if total_mines != gray_mines[0]: gray_mines[1] = sorted(gray_mines[1]) constraints.append(gray_mines) if verbose: print('board:') for cell in board: print(' ', cell) print('revealed:', revealed) print('constraints:') for constraint in constraints: print(' ', constraint) name = re.search('<TITLE>(.+?)</TITLE>', contents).group(1) reverse_id_map = [node['node_id'] for node in nodes] return Puzzle(board, revealed, constraints, verbose=verbose), name, reverse_id_map
def test_getRows(): p = Puzzle( ((2, 1), (1, 1)), True) assert(p.getRows() == 2)
def holey_render(compressed, size): num, board, revealed, constraints, _ = holey(size) size = 2 * size + 1 tile_size = 10 points = '-{s},-{s},{s},-{s},{s},{s},-{s},{s}'.format(s=0.96 * tile_size / 2) nodes = [] columns = [] mapped = dict() for i in range(size**2): c = compressed[i] if i < len(compressed) else '.' board[i][1] = c mapped[board[i][0]] = c nodes.append( dict( id=board[i][0], neighbors=board[i][2], position=((board[i][0] % size) * tile_size, (board[i][0] // size) * tile_size), has_mine=c == '*', secret=c == '?', revealed=board[i][0] in revealed, points=points, )) for j in range(size // 2 + 1): # horizontal column hints constraints[2 * j][0] = sum( [mapped[i] == '*' for i in constraints[2 * j][1]]) columns.append( dict( ids=constraints[2 * j][1], text_location=(-tile_size, 2 * j * tile_size), )) # vertical column hints constraints[2 * j + 1][0] = sum( [mapped[i] == '*' for i in constraints[2 * j + 1][1]]) columns.append( dict( ids=constraints[2 * j + 1][1], text_location=(2 * j * tile_size, -tile_size), )) constraints[-1][0] = compressed.count('*') result = Puzzle(board, revealed, constraints).solve() scored = score(result, 'seqnum') title = f'Holey {size}x{size} with score {scored}' tile_text = 'HOL' return dict( title=title, tile_text=tile_text, nodes=nodes, columns=columns, scored=scored, )
def test_conformsToRuleThreeSingleMarked(): p = Puzzle( ((2, 1), (1, 1)), True) p.markBlack(0, 0) assert(p.conformsToRuleThree())
def test_conformsToRuleThreeDiagonallyDivided(): p = Puzzle( ((2, 1), (1, 1)), True) p.markBlack(0, 0) p.markBlack(1, 1) assert(not p.conformsToRuleThree())
from flask import Flask, render_template, get_template_attribute from solver import Puzzle, Solver from detector import get_pattern_img, get_black_thresh from puzzle import get_board_from_img, add_piece_edges_to_grid app = Flask(__name__) pattern_img = get_pattern_img() # TODO: get only the string of the board from img, # do everything else via the solver board = get_board_from_img(pattern_img, get_black_thresh()) raw_board = get_board_from_img(pattern_img, get_black_thresh(), False) puzzle = Puzzle(board['red_count'], board['black_count']) app.jinja_env.globals.update(get_piece=puzzle.get_piece) pieces = list(puzzle.get_pieces()) solver = Solver(board, pieces, puzzle) # piece_sets = puzzle.get_piece_sets() pieces_registry = puzzle.get_pieces_registry() pieces_map = {} for name, piece in pieces_registry.items(): size = piece['size'] name = piece['name'] dev = piece['deviation']