示例#1
0
    def __init__(self, height, width, piece_counts):
        """Assigns board size and create piece abstractions"""
        self.height = height
        self.width = width

        self.threat_cache = ThreatCache(height, width)

        self.pieces = []
        # Sorted list of pieces based on how many squares will they cover
        for piece in ['Q', 'R', 'B', 'K', 'N']:
            self.pieces += [piece for _ in range(piece_counts[piece])]

        self.elapsed_time = None  # Problem resolution time in float seconds

        # Unique list of simple string solutions
        self.raw_solutions = set()
        # List of pretty printed solutions (only showing up to MAX_SOLUTIONS)
        self.solutions = []
示例#2
0
class ChessPlayer(object):
    """Create all possible solutions for the given pieces and board size"""
    MAX_SOLUTIONS = 15

    def __init__(self, height, width, piece_counts):
        """Assigns board size and create piece abstractions"""
        self.height = height
        self.width = width

        self.threat_cache = ThreatCache(height, width)

        self.pieces = []
        # Sorted list of pieces based on how many squares will they cover
        for piece in ['Q', 'R', 'B', 'K', 'N']:
            self.pieces += [piece for _ in range(piece_counts[piece])]

        self.elapsed_time = None  # Problem resolution time in float seconds

        # Unique list of simple string solutions
        self.raw_solutions = set()
        # List of pretty printed solutions (only showing up to MAX_SOLUTIONS)
        self.solutions = []

    def run(self):
        """
        Starts the backtracking algorithm process from top to down,
        setting the initial state
        """
        start = time.clock()
        free_positions = set()
        assigned_positions = ''
        # Generate all board position coordinates as tuples
        for i in range(self.height):
            for j in range(self.width):
                assigned_positions += '-'
                free_positions.add((i, j))
        # Start backtracking algorithm
        self._solve(free_positions, set(), list(assigned_positions), 0)
        self.elapsed_time = time.clock() - start

    def _solve(self, free_positions, occupied_positions, assigned_positions,
               piece_index):
        """
        Iterates through the search tree recursively, from the root down,
        in depth-first order, until it finds a valid solution or it runs
        out of elegible chess positions
        """
        if piece_index == len(self.pieces):
            # Found solution as there are no more pieces to assign
            self.raw_solutions.add(''.join(assigned_positions))
            return

        piece = self.pieces[piece_index]  # Retrieve next chess piece
        for free_position in free_positions:
            # Retrieve all positions current piece threatens in this slot
            threatened_positions = set(
                self.threat_cache.get_threats(piece, free_position))
            if threatened_positions & occupied_positions:
                # Current piece threatens a previously placed one: backtrack
                continue

            occupied_copy = occupied_positions.copy()
            assigned_copy = assigned_positions[:]

            # Occupy that slot for further iterations
            occupied_copy.add(free_position)
            # Add new position to the current piece
            assigned_copy[
                free_position[0] * self.width + free_position[1]] = piece

            # Recursive call with updated free, occupied and assigned slots,
            # for the next piece index
            self._solve(free_positions - threatened_positions - occupied_copy,
                        occupied_copy, assigned_copy, piece_index + 1)

    def draw_boards(self):
        """Concatenates all tracked unique solution strings"""
        n_solutions = len(self.raw_solutions)
        output = "\nSolutions:\n\n"

        if n_solutions > self.MAX_SOLUTIONS:
            # Show only up to MAX_SOLUTIONS number of solutions
            output += ("(only showing up to {} unique solutions)\n"
                       "").format(self.MAX_SOLUTIONS)
            limit = self.MAX_SOLUTIONS
        else:
            limit = n_solutions

        for _ in range(limit):
            solution = self._generate_board(self.raw_solutions.pop())
            self.solutions.append(solution)
            output += "{}\n".format(solution)

        output += "{} solutions found in {} seconds\n".format(
            n_solutions, self.elapsed_time)
        return output

    def _generate_board(self, layout):
        """
        Creates a pretty formatted string from a dictionary of piece types
        assigned to one or more positions
        """
        output = "* " * (self.width + 1) + "*\n"
        for i in range(0, self.width * self.height, self.width):
            output += "*{}*\n".format(
                layout[i:i+self.width].replace('', ' '))
        output += "* " * (self.width + 1) + "*\n"
        return output