Beispiel #1
0
    def get_adj_coords(self, coord):
        """Returns a list of coordinates adjacent to coordinate coord"""
        adj_coords = []

        if not coord.x == 0:
            adj_coords.append(coord_class.Coordinate(coord.x - 1, coord.y))

        if not coord.x == self.num_rows - 1:
            adj_coords.append(coord_class.Coordinate(coord.x + 1, coord.y))

        if not coord.y == 0:
            adj_coords.append(coord_class.Coordinate(coord.x, coord.y - 1))

        if not coord.y == self.num_cols - 1:
            adj_coords.append(coord_class.Coordinate(coord.x, coord.y + 1))

        return adj_coords
Beispiel #2
0
        def generate_coord_boards():
            """Generates a 2D coordinate board and its transpose.

            These are used when verifying solutions and creating random boards.
            """
            self.coord_board = []

            for x in range(self.num_rows):
                coord_list = []
                for y in range(self.num_cols):
                    coord_list.append(coord_class.Coordinate(x, y))

                self.coord_board.append(coord_list)

            self.transpose_coord_board = [list(l) for l in zip(*self.coord_board)]
Beispiel #3
0
    def __init__(self, config):
        """Initializes the LightUpPuzzle class.

        Where config is a Config object for the light up puzzle problem.
        """
        def generate_coord_boards():
            """Generates a 2D coordinate board and its transpose.

            These are used when verifying solutions and creating random boards.
            """
            self.coord_board = []

            for x in range(self.num_rows):
                coord_list = []
                for y in range(self.num_cols):
                    coord_list.append(coord_class.Coordinate(x, y))

                self.coord_board.append(coord_list)

            self.transpose_coord_board = [
                list(l) for l in zip(*self.coord_board)
            ]

        def generate_random_board():
            """Randomly generates a solvable board.

            Solvable boards are generated by iteratively placing black squares (with probability
            dictated by the configuration file) and required bulbs around each square. Bulbs
            are also placed randomly around the board (not neighboring black squares). All bulbs are
            then removed, leaving a board with at least one solution.

            This function should only be called in __init__
            """
            self.black_squares = {}
            bulbs = set([])

            if int(self.config.settings["override_random_board_dimensions"]):
                self.num_rows = int(self.config.settings["override_num_rows"])
                self.num_cols = int(self.config.settings["override_num_cols"])

            else:
                min_dimension = int(
                    self.config.settings["min_random_board_dimension"])
                max_dimension = int(
                    self.config.settings["max_random_board_dimension"])

                self.num_rows = random.randint(min_dimension, max_dimension)
                self.num_cols = random.randint(min_dimension, max_dimension)

            generate_coord_boards()

            # Create a list of shuffled coordinates used in assigning black squares & bulbs
            shuffled_coords = []
            for row in self.coord_board:
                for coord in row:
                    shuffled_coords.append(coord)

            random.shuffle(shuffled_coords)

            # Assign black squares & bulbs to the board
            for coord in shuffled_coords:
                if not coord in bulbs:
                    if random.random() <= float(
                            self.config.settings["black_square_placement_prob"]
                    ):
                        # Place a black square
                        adj_coord_list = self.get_adj_coords(coord)
                        num_placed_bulbs = 0

                        # Compute the random max value for this black square
                        max_value = random.choices(
                            list(
                                range(
                                    0,
                                    int(self.config.
                                        settings["adj_value_dont_care"]) + 1)),
                            [
                                int(n) for n in self.config.
                                settings["black_square_value_weights"].split(
                                    ',')
                            ])[0]

                        if max_value == int(
                                self.config.settings["adj_value_dont_care"]):
                            # Always place a black square with value adj_value_dont_care
                            self.black_squares[coord] = max_value

                        else:
                            # Put a placeholder black square to ensure the maximum amount of bulbs can be placed
                            self.black_squares[coord] = int(
                                self.config.settings["adj_value_dont_care"])

                            # Place bulbs around the square, if allowed
                            for adj_coord in adj_coord_list:
                                if num_placed_bulbs < max_value and self.place_bulb(
                                        adj_coord, bulbs):
                                    num_placed_bulbs += 1

                            # Account for black square placements with value zero
                            if num_placed_bulbs == 0 and len([
                                    c for c in self.get_adj_coords(coord)
                                    if c in bulbs
                            ]):
                                # Place a adj_value_dont_care black square to preserve the bulb placement validity
                                self.black_squares[coord] = int(
                                    self.config.settings["adj_value_dont_care"]
                                )

                            else:
                                # Update the real black square value to match the number of adjacent bulbs
                                self.black_squares[coord] = num_placed_bulbs

                    elif random.random() <= float(
                            self.config.settings["bulb_placement_prob"]):
                        # Attempt to place a bulb
                        self.place_bulb(coord, bulbs)

        self.black_squares = {}
        self.config = config

        if int(self.config.settings["generate_uniform_random_puzzle"]):
            # Generate random initial board state
            generate_random_board()

        else:
            # Read initial board state
            with open(self.config.settings["input_file_path"],
                      'r') as input_file:
                # Read line 0 (number of columns)
                self.num_cols = int(input_file.readline())

                # Read line 1 (number of rows)
                self.num_rows = int(input_file.readline())

                # Read line 2 to eof (coordinates of black squares and their adjacency values)
                for row in input_file:
                    black_square_data = [int(i) for i in row.split()]
                    self.black_squares[coord_class.Coordinate(
                        black_square_data[1] - 1,
                        black_square_data[0] - 1)] = black_square_data[2]

            # Generate coordinate versions of the board
            generate_coord_boards()
Beispiel #4
0
    def visualize_pretty(self, bulbs):
        """Prints the board in a pretty way."""
        format = {
            0: "\x1b[0;37;40m0 \x1b[0m",
            1: "\x1b[0;37;40m1 \x1b[0m",
            2: "\x1b[0;37;40m2 \x1b[0m",
            3: "\x1b[0;37;40m3 \x1b[0m",
            4: "\x1b[0;37;40m4 \x1b[0m",
            5: "\x1b[0;37;40m  \x1b[0m",
            'LIT': "\x1b[1;33;43m  \x1b[0m",
            'BULB': "\x1b[1;33;43m[]\x1b[0m",
            'NOT_LIT': "\x1b[5;30;47m  \x1b[0m"
        }

        board = [['_' for col in range(self.num_cols)]
                 for row in range(self.num_rows)]
        # Create and populate set of shined squares
        self.shined_squares = set([])

        for bulb_coord in bulbs:
            # Create a list of adjacency lists - used for determining where the bulb shines
            adj_coord_lists = []

            adj_coord_lists.append(
                self.coord_board[bulb_coord.x][:bulb_coord.y]
                [::-1])  # Row from this column to the left
            adj_coord_lists.append(self.coord_board[bulb_coord.x]
                                   [bulb_coord.y +
                                    1:])  # Row from this column to the right
            adj_coord_lists.append(self.transpose_coord_board[
                bulb_coord.y][:bulb_coord.x][::-1])  # Column from this row up
            adj_coord_lists.append(self.transpose_coord_board[
                bulb_coord.y][bulb_coord.x + 1:])  # Column from this row down

            for coord_list in adj_coord_lists:
                for coord in coord_list:
                    if coord in self.black_squares:
                        break  # Shine cannot propagate any further
                    elif coord in bulbs:
                        # Redundant check for bulb on bulb shining
                        # Nullify the fitness of this board
                        self.shined_squares = set([])
                        return False
                    else:
                        self.shined_squares.add(coord)

        # Ensure bulbs count as shined squares
        for bulb_coord in bulbs:
            self.shined_squares.add(bulb_coord)

        tmp_coords = set([])
        for x in range(self.num_rows):
            for y in range(self.num_cols):
                tmp_coords.add(coord_class.Coordinate(x, y))

        # Print the board
        for coord, value in self.black_squares.items():
            board[coord.x][coord.y] = format[value]
            tmp_coords.remove(coord)

        for coord in self.shined_squares:
            board[coord.x][coord.y] = format['LIT']
            if coord in tmp_coords:
                tmp_coords.remove(coord)

        for coord in bulbs:
            board[coord.x][coord.y] = format['BULB']
            if coord in tmp_coords:
                tmp_coords.remove(coord)

        for coord in tmp_coords:
            board[coord.x][coord.y] = format['NOT_LIT']

        for row in board:
            for item in row:
                print(item, end='')

            print()

        print()
Beispiel #5
0
 def get_random_coord(self):
     """Returns a random coordinate ranging in the space (num_cols, num_rows)."""
     return coord_class.Coordinate(random.randint(0, self.num_rows - 1),
                                   random.randint(0, self.num_cols - 1))