Exemple #1
0
    def test_black_and_white_with_empty_rows(self):
        """https://en.wikipedia.org/wiki/Nonogram#Example"""
        board = tested_board()
        solve(board)

        sol = board.cells
        columns, rows = clues(sol)

        assert columns == [
            [],
            [9],
            [9],
            [2, 2],
            [2, 2],
            [4],
            [4],
            [],
        ]

        assert rows == [
            [],
            [4],
            [6],
            [2, 2],
            [2, 2],
            [6],
            [4],
            [2],
            [2],
            [2],
            [],
        ]
Exemple #2
0
    def test_solve_board(self):
        columns, rows = read_example('w')

        board = BlackBoard(columns, rows)

        propagation.solve(board, methods='simpson')
        assert board.is_solved_full
Exemple #3
0
    def test_solve(self, board, stream):
        propagation.solve(board)
        board.draw()

        assert stream.getvalue().rstrip() == '\n'.join([
            '+---+---++---+---+---+---+---+---+---+---+',
            '| # | # ||   |   |   | 2 | 2 |   |   |   |',
            '|---+---++---+---+---+---+---+---+---+---|',
            '| # | # || 0 | 9 | 9 | 2 | 2 | 4 | 4 | 0 |',
            '|===+===++===+===+===+===+===+===+===+===|',
            '|   | 0 ||   |   |   |   |   |   |   |   |',
            '|---+---++---+---+---+---+---+---+---+---|',
            '|   | 4 ||   | # | # | # | # |   |   |   |',
            '|---+---++---+---+---+---+---+---+---+---|',
            '|   | 6 ||   | # | # | # | # | # | # |   |',
            '|---+---++---+---+---+---+---+---+---+---|',
            '| 2 | 2 ||   | # | # |   |   | # | # |   |',
            '|---+---++---+---+---+---+---+---+---+---|',
            '| 2 | 2 ||   | # | # |   |   | # | # |   |',
            '|---+---++---+---+---+---+---+---+---+---|',
            '|   | 6 ||   | # | # | # | # | # | # |   |',
            '|---+---++---+---+---+---+---+---+---+---|',
            '|   | 4 ||   | # | # | # | # |   |   |   |',
            '|---+---++---+---+---+---+---+---+---+---|',
            '|   | 2 ||   | # | # |   |   |   |   |   |',
            '|---+---++---+---+---+---+---+---+---+---|',
            '|   | 2 ||   | # | # |   |   |   |   |   |',
            '|---+---++---+---+---+---+---+---+---+---|',
            '|   | 2 ||   | # | # |   |   |   |   |   |',
            '|---+---++---+---+---+---+---+---+---+---|',
            '|   | 0 ||   |   |   |   |   |   |   |   |',
            '+---+---++---+---+---+---+---+---+---+---+',
        ])
Exemple #4
0
    def test_callbacks(self):
        # 'L' letter
        columns = [3, 1]
        rows = [1, 1, 2]

        board = BlackBoard(columns, rows)
        rows_updated = []
        cols_updated = []
        rounds = []

        board.on_row_update = lambda index, **kwargs: rows_updated.append(index)
        board.on_column_update = lambda index, **kwargs: cols_updated.append(index)
        board.on_solution_round_complete = lambda **kwargs: rounds.append(1)
        propagation.solve(board)

        # the solution will go like following:
        # 1. draw the lower '_' in L (row 2)
        # 2. the column 0 updated
        #   3. during that update the row 0 updated
        #     4. during that update the column 1 updated

        assert rows_updated == [2, 0]
        # draw the vertical '|' in L
        # and fill the spaces on the second column
        assert cols_updated == [0, 1]

        # it takes only one round to solve that
        assert sum(rounds) == 1
Exemple #5
0
    def test_basic(self):
        board = make_board(*Pbn.read(19407))
        assert board.has_blots
        assert board.rows_descriptions[1] == (BlottedBlock,)

        propagation.solve(board)
        assert board.is_solved_full
Exemple #6
0
    def test_several_solutions(self, stream):
        columns = [3, None, 1, 1]
        rows = [
            1,
            '1 1',
            '1 1',
        ]

        board = BlackBoard(columns, rows, renderer=AsciiRenderer, stream=stream)
        propagation.solve(board)
        board.draw()

        assert stream.getvalue().rstrip() == '\n'.join([
            '+---+---++---+---+---+---+',
            '| # | # || 3 | 0 | 1 | 1 |',
            '|===+===++===+===+===+===|',
            '|   | 1 || # |   |   |   |',
            '|---+---++---+---+---+---|',
            '| 1 | 1 || # |   | ? | ? |',
            '|---+---++---+---+---+---|',
            '| 1 | 1 || # |   | ? | ? |',
            '+---+---++---+---+---+---+',
        ])

        assert board.solution_rate * 3 == 2.0
Exemple #7
0
    def test_bold_lines(self, stream):
        """
        M letter
        """
        columns = [5, 1, 1, 1, 5]
        rows = ['1 1', '2 2', '1 1 1', '1 1', '1 1']

        renderer = AsciiRendererWithBold(stream=stream)
        renderer.BOLD_LINE_EVERY = 2
        board = BlackBoard(columns, rows, renderer=renderer)
        propagation.solve(board)
        board.draw()

        assert stream.getvalue().rstrip() == '\n'.join([
            '+---+---+---+++---+---++---+---++---+',
            '| # | # | # ||| 5 | 1 || 1 | 1 || 5 |',
            '|===+===+===+++===+===++===+===++===|',
            '|   | 1 | 1 ||| # |   ||   |   || # |',
            '|---+---+---+++---+---++---+---++---|',
            '|   | 2 | 2 ||| # | # ||   | # || # |',
            '|===+===+===+++===+===++===+===++===|',
            '| 1 | 1 | 1 ||| # |   || # |   || # |',
            '|---+---+---+++---+---++---+---++---|',
            '|   | 1 | 1 ||| # |   ||   |   || # |',
            '|===+===+===+++===+===++===+===++===|',
            '|   | 1 | 1 ||| # |   ||   |   || # |',
            '+---+---+---+++---+---++---+---++---+',
        ])
Exemple #8
0
    def test_smile(self):
        columns, rows = read_example('smile.txt')
        board = BlackBoard(columns, rows)

        propagation.solve(board)
        assert is_close(board.solution_rate, 0.6)
        # assert is_close(board.solution_rate, 407.0 / 625)

        Solver(board).solve()
        assert board.is_solved_full
Exemple #9
0
    def solve(self):
        """
        Solve the nonogram to the most with contradictions
        """

        board = self.board

        propagation.solve(board)
        if board.is_solved_full:
            board.set_finished()
            LOG.info('No need to solve with contradictions')
            return

        LOG.warning('Trying to solve using contradictions method')
        start = time.time()

        found_contradictions, best_candidates = self._solve_without_search(to_the_max=True)
        current_solution_rate = board.solution_rate
        LOG.warning('Contradictions (found %d): %f',
                    found_contradictions, current_solution_rate)

        if current_solution_rate < 1:
            # if stalled with sophisticated selection of cells
            # do the brute force search
            LOG.warning('Starting DFS (intelligent brute-force)')
            best_candidates = self.shrink_board(board, candidates=best_candidates)
            if board.is_colored:
                single_colored, color_mapping = board.reduce_to_single_color()
                if single_colored is not None:
                    LOG.warning('Replacing colored board with the '
                                'equivalent black and white: %r', color_mapping)

                    # from now we will search the black and white board
                    self.board = single_colored
                    best_candidates = self._fix_candidates_colors(best_candidates, color_mapping)

            self.search(best_candidates)
            board.restore_reduced()

            current_solution_rate = board.solution_rate
            LOG.warning('Search completed (depth reached: %d, solutions found: %d)',
                        self.depth_reached, len(board.solutions))

        if current_solution_rate != 1:
            LOG.warning('The nonogram is not solved full (with contradictions). '
                        'The rate is %.4f', current_solution_rate)

        board.set_finished()
        LOG.info('Full solution: %.6f sec', time.time() - start)
        for method, info in cache_info().items():
            size, hit_rate = info
            if size > 0:
                LOG.warning('%s cache: size=%d, hit rate=%.4f%%', method, size, hit_rate * 100.0)
Exemple #10
0
    def test_depth_search(self):
        board = make_board(*Pbn.read(3469))

        propagation.solve(board)
        assert board.solution_rate == 0
        assert len(board.solutions) == 0

        Solver(board, max_solutions=2, timeout=600).solve()
        assert board.is_solved_full
        assert len(board.solutions) == 1

        assert board.is_finished
Exemple #11
0
    def test_depth_search_colored(self, stream):
        renderer = BaseAsciiRenderer(stream=stream)
        board = make_board(*Pbn.read(5260), renderer=renderer)

        propagation.solve(board)
        assert board.solution_rate == 0.9375
        assert len(board.solutions) == 0

        Solver(board, max_solutions=2, timeout=600).solve()
        assert board.solution_rate == 0.9375
        assert len(board.solutions) == 2

        assert board.is_finished

        board.draw_solutions()
        example_solution = [
            '# # # # # #                   1 1                  ',
            '# # # # # #                   1 1                  ',
            '# # # # # #     1   1         1 1         1   1    ',
            '# # # # # #   1 1 1 1         1 1         1 1 1 1  ',
            '# # # # # #   6 1 2 1 2   1 2 1 1 2 1   2 1 2 1 6  ',
            '# # # # # # 1 1 1 1 1 2 0 1 2 1 1 2 1 0 2 1 1 1 1 1',
            '          1 X . . . . . . . . . . . . . . . . . . .',
            '      1 6 1 . X . . . . . % % % % % % . . . . . X .',
            '    1 1 1 1 . . X . . . . . % . . % . . . X . . . .',
            '      1 2 1 . . . . X . . . . % % . . . . . X . . .',
            '    1 1 1 1 . . . X . @ . . . . . . . . @ . . X . .',
            '        2 2 . . . . @ @ . . . . . . . . @ @ . . . .',
            '          0 . . . . . . . . . . . . . . . . . . . .',
            '        1 1 . % . . . . . . . . . . . . . . . . % .',
            '        2 2 . % % . . . . . . . . . . . . . . % % .',
            '1 1 1 1 1 1 . % . % . . . . . * % . . . . . % . % .',
            '1 1 1 1 1 1 . % . % . . . . . X * . . . . . % . % .',
            '        2 2 . % % . . . . . . . . . . . . . . % % .',
            '        1 1 . % . . . . . . . . . . . . . . . . % .',
            '          0 . . . . . . . . . . . . . . . . . . . .',
            '        2 2 . . . . @ @ . . . . . . . . @ @ . . . .',
            '    1 1 1 1 . X . . . @ . . . . . . . . @ . . X . .',
            '      1 2 1 . . X . . . . . . % % . . . . . X . . .',
            '    1 1 1 1 . . . . X . . . % . . % . . . . . . X .',
            '      1 6 1 . . . X . . . % % % % % % . . X . . . .',
            '          1 . . . . . . . . . . . . . . . . . . . X',
        ]

        actual_drawing = stream.getvalue().rstrip().split('\n')
        sol1, sol2 = actual_drawing[:26], actual_drawing[26:]

        # header
        assert sol1[:6] == sol2[:6] == example_solution[:6]

        # central
        assert sol1[11:21] == sol2[11:21] == example_solution[11:21]
Exemple #12
0
    def test_many_solutions(self):
        # source: https://en.wikipedia.org/wiki/Nonogram#Contradictions
        columns = [3, 1, 2, 2, '1 1', '1 1']
        rows = ['1 2', 1, 1, 3, 2, 2]

        board = BlackBoard(columns, rows)

        propagation.solve(board)
        assert board.solution_rate == 0

        Solver(board).solve()
        assert is_close(board.solution_rate, 7.0 / 9)
        assert len(board.solutions) == 2
Exemple #13
0
    def test_color(self):
        board = make_board(*Pbn.read(19647))
        assert board.has_blots
        assert board.rows_descriptions[15] == (
            (1, 2),
            (BlottedBlock, 8),
            (BlottedBlock, 8),
            (2, 2),
            (2, 2),
            (2, 8),
        )

        propagation.solve(board)
        assert board.is_solved_full
Exemple #14
0
    def test_colored(self):
        board = make_board(*Pbn.read(10282))

        propagation.solve(board)
        assert len(board.solutions) == 0

        Solver(board, max_solutions=2, timeout=600).solve()
        assert is_close(board.solution_rate, 0.91625)
        assert len(board.solutions) == 2

        assert len(board.solved_rows[0]) == 3
        assert len(board.solved_rows[1]) == 2
        assert len(board.solved_columns[0]) == 5
        assert len(board.solved_columns[1]) == 9
Exemple #15
0
    def test_various_modes(self):
        solutions = dict()

        for contradiction_mode in (False, True):
            stream = StringIO()
            board = tested_board(AsciiRenderer, stream=stream)
            start = time.time()

            propagation.solve(board,
                              contradiction_mode=contradiction_mode)
            solutions[contradiction_mode] = (
                stream.getvalue().rstrip(), time.time() - start)

        assert len(solutions) == 2
        assert len(set(v[0] for v in solutions.values())) == 1
Exemple #16
0
    def test_black_and_white(self):
        board = make_board(*Pbn.read(2903))

        propagation.solve(board)
        assert len(board.solutions) == 0

        Solver(board, max_solutions=2, timeout=600).solve()
        assert board.is_solved_full
        assert len(board.solutions) == 1

        assert len(board.solved_rows[0]) == 6
        assert len(board.solved_rows[1]) == 6
        assert len(board.solved_columns[0]) == 4
        assert len(board.solved_columns[1]) == 0

        assert board.is_finished
Exemple #17
0
    def test_chessboard(self):
        # The real chessboard could be defined like this
        #
        # `columns = rows = [[1, 1, 1, 1]] * 8`
        #
        # but it really slows down the test.
        #
        # So we just use simple 2x2 chessboard here
        # with the same effect on test coverage

        columns = rows = [[1, 1, 1, 1]] * 8
        board = BlackBoard(columns, rows)

        propagation.solve(board)
        assert board.solution_rate == 0

        Solver(board).solve()
        assert board.solution_rate == 0
Exemple #18
0
    def test_hello(self):
        columns, rows = read_example('hello.txt')

        stream = StringIO()
        board = BlackBoard(columns,
                           rows,
                           renderer=BaseAsciiRenderer,
                           stream=stream)
        propagation.solve(board)
        board.draw()

        assert stream.getvalue().rstrip() == '\n'.join([
            '# # # # # # # # #               1 1                          ',
            '# # # # # # # # #               1 1               1   1     5',
            '# # # # # # # # # 7 1 1 1 7 0 3 1 1 2 0 6 0 6 0 3 1 5 1 3 0 1',
            '            1 1 1 X . . . X . . . . . . . . . . . . . . . . X',
            '        1 1 1 1 1 X . . . X . . . . . . X . X . . . . . . . X',
            '    1 1 2 1 1 3 1 X . . . X . . X X . . X . X . . X X X . . X',
            '5 1 1 1 1 1 1 1 1 X X X X X . X . . X . X . X . X . X . X . X',
            '1 1 4 1 1 1 1 1 1 X . . . X . X X X X . X . X . X . X . X . X',
            '  1 1 1 1 1 1 1 1 X . . . X . X . . . . X . X . X . X . X . .',
            '    1 1 2 1 1 3 1 X . . . X . . X X . . X . X . . X X X . . X',
        ])
        assert board.is_solved_full
Exemple #19
0
    def propagate_change(self, cell_state):
        """
        Set the given color to given cell
        and try to solve the board with that new info.
        :type cell_state: CellState
        """
        board = self.board
        LOG.debug('Assume that (%i, %i) is %s', *tuple(cell_state))

        board.set_color(cell_state)

        return propagation.solve(
            board,
            row_indexes=(cell_state.row_index,),
            column_indexes=(cell_state.column_index,),
            contradiction_mode=True)
Exemple #20
0
    def test_black(self):
        board = make_board(*Pbn.read(20029))
        assert board.rows_descriptions[2] == (BlottedBlock, 1)

        propagation.solve(board)
        assert board.is_solved_full
Exemple #21
0
    def test_backtracking(self):
        board = make_board(*Pbn.read(4581))
        propagation.solve(board, methods=self.method_name())

        assert is_close(board.solution_rate, 0.75416666667)
        assert len(board.solutions) == 0
Exemple #22
0
    def test_small_table_solved(self, stream):
        b = self.one_row_table(2, stream)
        propagation.solve(b)
        b.draw()
        table = [line.strip() for line in stream.getvalue().split('\n')]

        svg_def = '''
            <svg baseProfile="full" height="45" version="1.1" width="60" xmlns="
            http://www.w3.org/2000/svg" xmlns:ev=
            "http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink">
                <defs>
                    <style type="text/css">
                        <![CDATA[
                            g.grid-lines line {
                            stroke-width: 1} g.grid-lines line.bold {
                            stroke-width: 2} g.header-clues text, g.side-clues text {
                            font-size: 9.000000} ]]>
                    </style>
                    <symbol id="color-True">
                        <rect fill="black" height="15" width="15" x="0" y="0" /></symbol>
                    <symbol id="space"><circle cx="0" cy="0" r="1.5" /></symbol>
                    <symbol fill="none" id="check" stroke="green">
                        <circle cx="50" cy="50" r="40" stroke-width="10" />
                        <polyline points="35,35 35,55 75,55" stroke-width="
                        12" transform="rotate(-45 50 50)" />
                    </symbol>
                </defs>

                <rect class="nonogram-thumbnail" height="15" width="15" x="0" y="0" />

                <rect class="nonogram-header" height="15" width="30" x="15" y="0" />
                <g class="header-clues">
                    <rect class="solved" height="15" width="15" x="15" y="0" />
                    <text x="27.75" y="10.5">1</text>
                    <rect class="solved" height="15" width="15" x="30" y="0" />
                    <text x="42.75" y="10.5">1</text>
                </g>

                <rect class="nonogram-side" height="15" width="15" x="0" y="15" />
                <g class="side-clues">
                    <rect class="solved" height="15" width="15" x="0" y="15" />
                    <text x="10.5" y="26.25">2</text>
                </g>

                <use x="0" xlink:href="#check" y="0" />

                <rect class="nonogram-grid" height="15" width="30" x="15" y="15" />
                <g class="space" />
                <g class="color-True">
                    <use x="15" xlink:href="#color-True" y="15" />
                    <use x="30" xlink:href="#color-True" y="15" />
                </g>
                <g class="grid-lines">
                    <line class="bold" x1="0" x2="45" y1="15" y2="15" />
                    <line class="bold" x1="0" x2="45" y1="30" y2="30" />
                    <line class="bold" x1="15" x2="15" y1="0" y2="30" />
                    <line x1="30" x2="30" y1="0" y2="30" />
                    <line class="bold" x1="45" x2="45" y1="0" y2="30" />
                </g>
            </svg>'''

        assert table[0] == '<?xml version="1.0" encoding="utf-8" ?>'
        assert table[1] == ''.join([
            line for line in [line.strip() for line in svg_def.split('\n')]
            if line
        ])
Exemple #23
0
    def test_color_solved(self, stream):
        b = make_board(*color_board_def(), renderer=SvgRenderer, stream=stream)
        propagation.solve(b)

        b.draw()
        table = [line.strip() for line in stream.getvalue().split('\n')]

        svg_def = '''
            <svg baseProfile="full" height="90" version="1.1" width="75" xmlns="
            http://www.w3.org/2000/svg" xmlns:ev=
            "http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink">
                <defs>
                    <style type="text/css">
                        <![CDATA[
                            g.grid-lines line {
                            stroke-width: 1} g.grid-lines line.bold {
                            stroke-width: 2} g.header-clues text, g.side-clues text {
                            font-size: 9.000000} ]]>
                    </style>

                    <symbol id="color-b">
                        <rect fill="blue" height="15" width="15" x="0" y="0" />
                    </symbol>
                    <symbol id="color-black">
                        <rect fill="#000" height="15" width="15" x="0" y="0" />
                    </symbol>
                    <symbol id="color-r">
                        <rect fill="red" height="15" width="15" x="0" y="0" />
                    </symbol>


                    <symbol id="x2-b-black">
                        <polygon fill="blue" points="0,0 0,15 15,0" />
                        <polygon fill="#000" points="0,15 15,0 15,15" />
                    </symbol>
                    <symbol id="x2-b-r">
                        <polygon fill="blue" points="0,0 0,15 15,0" />
                        <polygon fill="red" points="0,15 15,0 15,15" />
                    </symbol>
                    <symbol id="x2-b-white">
                        <polygon fill="blue" points="0,0 0,15 15,0" />
                        <polygon fill="#fff" points="0,15 15,0 15,15" />
                    </symbol>

                    <symbol id="x2-black-r">
                        <polygon fill="#000" points="0,0 0,15 15,0" />
                        <polygon fill="red" points="0,15 15,0 15,15" />
                    </symbol>
                    <symbol id="x2-black-white">
                        <polygon fill="#000" points="0,0 0,15 15,0" />
                        <polygon fill="#fff" points="0,15 15,0 15,15" />
                    </symbol>

                    <symbol id="x2-r-white">
                        <polygon fill="red" points="0,0 0,15 15,0" />
                        <polygon fill="#fff" points="0,15 15,0 15,15" />
                    </symbol>


                    <symbol id="x3-b-black-r">
                        <rect fill="blue" height="15" width="15" x="0" y="0" />
                        <polygon fill="#000" points="0,0 0,10.61 10.61,0" />
                        <polygon fill="red" points="15,4.39 4.39,15 15,15" />
                    </symbol>
                    <symbol id="x3-b-black-white">
                        <rect fill="blue" height="15" width="15" x="0" y="0" />
                        <polygon fill="#000" points="0,0 0,10.61 10.61,0" />
                        <polygon fill="#fff" points="15,4.39 4.39,15 15,15" />
                    </symbol>
                    <symbol id="x3-b-r-white">
                        <rect fill="blue" height="15" width="15" x="0" y="0" />
                        <polygon fill="red" points="0,0 0,10.61 10.61,0" />
                        <polygon fill="#fff" points="15,4.39 4.39,15 15,15" />
                    </symbol>
                    <symbol id="x3-black-r-white">
                        <rect fill="#000" height="15" width="15" x="0" y="0" />
                        <polygon fill="red" points="0,0 0,10.61 10.61,0" />
                        <polygon fill="#fff" points="15,4.39 4.39,15 15,15" />
                    </symbol>

                    <symbol id="space"><circle cx="0" cy="0" r="1.5" /></symbol>


                    <symbol fill="none" id="check" stroke="green">
                        <circle cx="50" cy="50" r="40" stroke-width="10" />
                        <polyline points="35,35 35,55 75,55" stroke-width="
                        12" transform="rotate(-45 50 50)" />
                    </symbol>
                </defs>

                <rect class="nonogram-thumbnail" height="30" width="15" x="0" y="0" />

                <rect class="nonogram-header" height="30" width="45" x="15" y="0" />
                <g class="header-clues">
                    <rect class="solved" height="30" width="15" x="15" y="0" />
                    <use x="15" xlink:href="#color-b" y="15" />
                    <text x="27.75" y="25.5">1</text>
                    <use x="15" xlink:href="#color-r" y="0" />
                    <text x="27.75" y="10.5">1</text>

                    <rect class="solved" height="30" width="15" x="30" y="0" />
                    <use x="30" xlink:href="#color-b" y="15" />
                    <text x="42.75" y="25.5">1</text>
                    <use x="30" xlink:href="#color-r" y="0" />
                    <text x="42.75" y="10.5">1</text>

                    <rect class="solved" height="30" width="15" x="45" y="0" />
                    <use x="45" xlink:href="#color-b" y="15" />
                    <text x="57.75" y="25.5">1</text>
                    <use x="45" xlink:href="#color-r" y="0" />
                    <text x="57.75" y="10.5">1</text>
                </g>

                <rect class="nonogram-side" height="45" width="15" x="0" y="30" />
                <g class="side-clues">
                    <rect class="solved" height="15" width="15" x="0" y="30" />
                    <use x="0" xlink:href="#color-r" y="30" />
                    <text x="10.5" y="41.25">3</text>

                    <rect class="solved" height="15" width="15" x="0" y="45" />

                    <rect class="solved" height="15" width="15" x="0" y="60" />
                    <use x="0" xlink:href="#color-b" y="60" />
                    <text x="10.5" y="71.25">3</text>
                </g>

                <use x="0" xlink:href="#check" y="0" />

                <rect class="nonogram-grid" height="45" width="45" x="15" y="30" />

                <g class="space">
                    <use x="22.5" xlink:href="#space" y="52.5" />
                    <use x="37.5" xlink:href="#space" y="52.5" />
                    <use x="52.5" xlink:href="#space" y="52.5" />
                </g>
                <g class="color-black" />
                <g class="x2-black-white" />
                <g class="color-r">
                    <use x="15" xlink:href="#color-r" y="30" />
                    <use x="30" xlink:href="#color-r" y="30" />
                    <use x="45" xlink:href="#color-r" y="30" />
                </g>
                <g class="x2-r-white" />
                <g class="x2-black-r" />
                <g class="x3-black-r-white" />
                <g class="color-b">
                    <use x="15" xlink:href="#color-b" y="60" />
                    <use x="30" xlink:href="#color-b" y="60" />
                    <use x="45" xlink:href="#color-b" y="60" />
                </g>
                <g class="x2-b-white" />
                <g class="x2-b-black" />
                <g class="x3-b-black-white" />
                <g class="x2-b-r" />
                <g class="x3-b-r-white" />
                <g class="x3-b-black-r" />

                <g class="grid-lines">
                    <line class="bold" x1="0" x2="60" y1="30" y2="30" />
                    <line x1="0" x2="60" y1="45" y2="45" />
                    <line x1="0" x2="60" y1="60" y2="60" />
                    <line class="bold" x1="0" x2="60" y1="75" y2="75" />
                    <line class="bold" x1="15" x2="15" y1="0" y2="75" />
                    <line x1="30" x2="30" y1="0" y2="75" />
                    <line x1="45" x2="45" y1="0" y2="75" />
                    <line class="bold" x1="60" x2="60" y1="0" y2="75" />
                </g>
            </svg>'''

        assert table[0] == '<?xml version="1.0" encoding="utf-8" ?>'
        svg_def = ''.join([
            line for line in [line.strip() for line in svg_def.split('\n')]
            if line
        ])
        assert table[1] == svg_def
Exemple #24
0
    def probe(self, cell_state, rollback=True, force=False):
        """
        Try to find if the given cell can be in an assumed state.
        If the contradiction is found, set the cell
        in an inverted state and propagate the changes if needed.
        If `rollback` then the solved board will restore to the previous state
        after the assumption was made.
        If `force`, try to solve it anyway, even if the cell is already solved
        (to rerun the line solver).

        Return the pair `(is_contradiction, new_info)` where

        is_contradiction: whether the assumption led to a contradiction

        new_info:
          a) when contradiction is found, it contains the state of the board
          before any assumptions was made. It will help further to determine
          which cells has changed on that probe.
          b) if no contradiction found, but `rollback` is False,
          then we do not restore the board and return the previous state also.
          c) otherwise it contains the number of solved cells for the partially
          solved board (if the assumption made is true)
        """
        board = self.board

        pos = cell_state.position
        assumption = cell_state.color
        # already solved
        if board.is_cell_solved(pos):
            if not force:
                return False, None

        if assumption not in board.cell_colors(pos):
            LOG.warning("The probe is useless: color '%s' already unset", assumption)
            return False, None

        save = board.make_snapshot()

        try:
            solved_cells = self.propagate_change(cell_state)
        except NonogramError:
            LOG.debug('Contradiction', exc_info=True)
            # rollback solved cells
            board.restore(save)

        else:
            if board.is_solved_full:
                self._add_solution()

            if rollback:
                board.restore(save)
                return False, solved_cells

            return False, save

        if USE_CONTRADICTION_RESULTS:
            before_contradiction = board.make_snapshot()
        else:
            before_contradiction = None

        pos = cell_state.position
        LOG.info('Found contradiction at (%i, %i)', *pos)
        try:
            board.unset_color(cell_state)
        except ValueError as ex:
            raise NonogramError(str(ex))

        # try to solve with additional info
        # solve with only one cell as new info
        propagation.solve(
            board,
            row_indexes=(pos.row_index,),
            column_indexes=(pos.column_index,))

        return True, before_contradiction
Exemple #25
0
 def _add_solution(self):
     # force to check the board
     propagation.solve(self.board, contradiction_mode=True)
     self.board.add_solution()
Exemple #26
0
 def test_repeat_solutions(self, board):
     propagation.solve(board)
     assert board.is_solved_full
     propagation.solve(board)
     assert board.is_solved_full