def possibleMoves(self, start): """Return list of possible moves. Each item is itself a list of moves in one direction. """ row, col = position(start) directions = [] # Go round the clock starting with the two top moves if row < 6: if col > 0: directions.append([squarify(row + 2, col - 1)]) if col < 7: directions.append([squarify(row + 2, col + 1)]) # Try the two right moves if col < 6: if row < 7: directions.append([squarify(row + 1, col + 2)]) if row > 0: directions.append([squarify(row - 1, col + 2)]) # Try the two bottom moves if row > 1: if col < 7: directions.append([squarify(row - 2, col + 1)]) if col > 0: directions.append([squarify(row - 2, col - 1)]) # Try the two left moves if col > 1: if row > 0: directions.append([squarify(row - 1, col - 2)]) if row < 7: directions.append([squarify(row + 1, col - 2)]) return directions
def possibleMoves(self, start): """Return list of possible moves. Each item is itself a list of moves in one direction. """ row, col = position(start) directions = [] # Go round the clock starting at the top left if row < 7: if col > 0: directions.append([squarify(row + 1, col - 1)]) directions.append([squarify(row + 1, col)]) if col < 7: directions.append([squarify(row + 1, col + 1)]) # Try to move right if col < 7: directions.append([squarify(row, col + 1)]) # Try the three bottom moves if row > 0: if col < 7: directions.append([squarify(row - 1, col + 1)]) directions.append([squarify(row - 1, col)]) if col > 0: directions.append([squarify(row - 1, col - 1)]) # Try to move left if col > 0: directions.append([squarify(row, col - 1)]) return directions
def attackMoves(self, start): """Return list of square that this pawn can attack. Also return the matching squares that can be an en passant target. """ row, col = position(start) directions = [] if self.colour == WHITE and row < 7: if col > 0: target = squarify(row+1, col-1) en_passant = squarify(row, col-1) directions.append((target, en_passant)) if col < 7: target = squarify(row+1, col+1) en_passant = squarify(row, col+1) directions.append((target, en_passant)) if self.colour == BLACK and row > 0: if col > 0: target = squarify(row-1, col-1) en_passant = squarify(row, col-1) directions.append((target, en_passant)) if col < 7: target = squarify(row-1, col+1) en_passant = squarify(row, col+1) directions.append((target, en_passant)) return directions
def possibleMoves(self, start): """Return list of possible moves. Each item is itself a list of moves in one direction. """ row, col = position(start) directions = [] # Go round the clock starting with the two top moves if row < 6: if col > 0: directions.append([squarify(row+2, col-1)]) if col < 7: directions.append([squarify(row+2, col+1)]) # Try the two right moves if col < 6: if row < 7: directions.append([squarify(row+1, col+2)]) if row > 0: directions.append([squarify(row-1, col+2)]) # Try the two bottom moves if row > 1: if col < 7: directions.append([squarify(row-2, col+1)]) if col > 0: directions.append([squarify(row-2, col-1)]) # Try the two left moves if col > 1: if row > 0: directions.append([squarify(row-1, col-2)]) if row < 7: directions.append([squarify(row+1, col-2)]) return directions
def possibleMoves(self, start): """Return list of possible moves. Each item is itself a list of moves in one direction. """ row, col = position(start) directions = [] # Go round the clock starting at the top left if row < 7: if col > 0: directions.append([squarify(row+1, col-1)]) directions.append([squarify(row+1, col)]) if col < 7: directions.append([squarify(row+1, col+1)]) # Try to move right if col < 7: directions.append([squarify(row, col+1)]) # Try the three bottom moves if row > 0: if col < 7: directions.append([squarify(row-1, col+1)]) directions.append([squarify(row-1, col)]) if col > 0: directions.append([squarify(row-1, col-1)]) # Try to move left if col > 0: directions.append([squarify(row, col-1)]) return directions
def diagonalMoves(self, startrow, startcol): """List possible diagonal moves. """ directions = [] # Go to the top left. row = startrow col = startcol direction = [] while row < 7 and col > 0: row += 1 col -= 1 direction.append(squarify(row, col)) if len(direction) > 0: directions.append(direction) # Go to the top right. row = startrow col = startcol direction = [] while row < 7 and col < 7: row += 1 col += 1 direction.append(squarify(row, col)) if len(direction) > 0: directions.append(direction) # Go to the bottom right. row = startrow col = startcol direction = [] while row > 0 and col < 7: row -= 1 col += 1 direction.append(squarify(row, col)) if len(direction) > 0: directions.append(direction) # Go to the bottom left. row = startrow col = startcol direction = [] while row > 0 and col > 0: row -= 1 col -= 1 direction.append(squarify(row, col)) if len(direction) > 0: directions.append(direction) return directions
def attackMoves(self, start): """Return list of square that this pawn can attack. Also return the matching squares that can be an en passant target. """ row, col = position(start) directions = [] if self.colour == WHITE and row < 7: if col > 0: target = squarify(row + 1, col - 1) en_passant = squarify(row, col - 1) directions.append((target, en_passant)) if col < 7: target = squarify(row + 1, col + 1) en_passant = squarify(row, col + 1) directions.append((target, en_passant)) if self.colour == BLACK and row > 0: if col > 0: target = squarify(row - 1, col - 1) en_passant = squarify(row, col - 1) directions.append((target, en_passant)) if col < 7: target = squarify(row - 1, col + 1) en_passant = squarify(row, col + 1) directions.append((target, en_passant)) return directions
def findPiece(self, symbol, colour): """Find a piece with this symbol and colour. Find white rooks. >>> board = ChessBoard() >>> board.findPiece('R', WHITE) ['A1', 'H1'] Lowercase is also acceptable. >>> board.findPiece('r', WHITE) ['A1', 'H1'] Give me all black pawns. >>> board.findPiece('P', BLACK) ['A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7'] """ symbol = symbol.upper() results = [] for row in range(self.nrows): for col in range(self.ncols): if self[row, col].symbol == symbol and \ self[row, col].colour == colour: results.append(position.squarify(row, col)) return results
def value(self): """Return value of current board. Positive: white is in a better position Negative: black is in a better position An empty chess board is worth nothing >>> board = ChessBoard(EMPTY_CHESS_BOARD) >>> board.value() 0 In the beginning white and black should be equal. White begins first though, so maybe we want to give him a small advantage later on. >>> board = ChessBoard() >>> board.value() 0 On our castling board, black and white are equal. >>> board = ChessBoard(CASTLING_BOARD) >>> board.value() 0 Let's inspect our end game board. >>> board = ChessBoard(ENDGAME_BOARD) >>> board.printPieces() KQ----N-P------- kqr-b---pp------ The white knight and black bishop even out, but black has a rook and a pawn more. >>> board.value() -6 Let's inspect our end promotion board. >>> board = ChessBoard(PROMOTION_BOARD) >>> board.printPieces() K-R---N-PPPP---- k-r-----ppp----- White has a knight and a pawn more. >>> board.value() 4 """ total = 0 for row in range(self.nrows): for col in range(self.ncols): square = position.squarify(row, col) total += self.pieceWorth(square) return total
def sideWaysMoves(self, row, col, direction=1): """List possible sideways moves. """ moves = [] count = 0 while count < CHESS_COLS: count += 1 col += direction if 0 <= col < CHESS_COLS: moves.append(squarify(row, col)) else: break return moves
def upDownMoves(self, row, col, max=None, direction=1): """List possible forward moves. """ moves = [] count = 0 max = max or self.max_moves or CHESS_ROWS while count < max: count += 1 row += direction if 0 <= row < CHESS_ROWS: moves.append(squarify(row, col)) else: break return moves
def hint(self, depth=0): """Give a hint for the current player. Our hints are pretty lousy currently. >>> board = ChessBoard(HINT_BOARD) >>> board.hint() {'start': 'D7', 'target': 'A7', 'value': 5} """ options = [] for row in range(self.nrows): for col in range(self.ncols): if self[row, col].colour == self.player: friend = position.squarify(row, col) moves = self.calcMovesForPiece(friend) moves.extend([x[0] for x in self.specialAttackMoves(friend)]) for move in moves: board = copy.deepcopy(self) try: board.move(friend, move) except position.PromotionException: continue if depth > 0: value = board.hint(depth-1)['value'] if depth == 0: value = board.value() options.append({'start': friend, 'target': move, 'value': value}) if self.player == WHITE: fun = max else: fun = min if len(options) == 0: return None best_value = fun([x['value'] for x in options]) best_move = [x for x in options if x['value'] == best_value] return best_move[0]
def isAttacked(self, ownColour, square): """Is the square, owned by ownColour, attacked? - Mostly used for the King of course. Let's start with a castling board. >>> board = ChessBoard(CASTLING_BOARD) The white and black kings are safe in their starting positions. >>> board.isAttacked(WHITE, 'E1') False >>> board.isAttacked(BLACK, 'E8') False They are even safe in the squares that they would normally use for castling. >>> for square in ('D1', 'C1', 'F1', 'G1'): ... if board.isAttacked(WHITE, square): ... print True >>> for square in ('D8', 'C8', 'F8', 'G8'): ... if board.isAttacked(BLACK, square): ... print True They are not safe when they are at the enemy's last line, being attacked by the opposite rooks. >>> board.isAttacked(WHITE, 'B8') True >>> board.isAttacked(BLACK, 'D1') True Now we add some pieces to the board and see what effect they have. First we add a black knight on E3. That makes D1 and F1 unsafe. >>> board['E3'] = piece.Knight(BLACK) >>> for square in ('C1', 'E1', 'G1'): ... if board.isAttacked(WHITE, square): ... print True >>> board.isAttacked(WHITE, 'D1') True >>> board.isAttacked(WHITE, 'F1') True We will attack black with a pawn right in front of the King. The King should be safe in his current spot, but he cannot move one square to the left or the right. >>> board['E7'] = piece.Pawn(WHITE) >>> for square in ('C8', 'E8', 'G8'): ... if board.isAttacked(BLACK, square): ... print True >>> board.isAttacked(BLACK, 'D8') True >>> board.isAttacked(BLACK, 'F8') True """ king = piece.King(ownColour) for row in range(self.nrows): for col in range(self.ncols): if self[row, col].isEnemy(king): attacker = position.squarify(row, col) moves = self.calcMovesForPiece(attacker, unconditional=True) if square in moves: return True # Take a list of squares (without their en passant # friends) that can be attacked by this piece, # presumably a Pawn if anything ends up in this # list. special = [sq for (sq, ep) in self[attacker].attackMoves(attacker)] if square in special: return True return False
def isMate(self): """Check to see if the player is mate. That is: if no legel moves can be made. Still can be checkmate or stalemate. We have a nice board for checking this. At first there is no mate. >>> board = ChessBoard(MATE_BOARD) >>> board.isMate() False >>> board.isCheckMate() False >>> board.isStaleMate() False White plays and wins. >>> board.move('E5', 'F7') >>> board.player == BLACK True >>> board.isMate() True >>> board.isCheckMate() True >>> board.isStaleMate() False If we look at this from White's point of view, there is no mate. >>> board.switchPlayers() >>> board.isMate() False >>> board.isCheckMate() False >>> board.isStaleMate() False Let's try again, but now with black starting. >>> board = ChessBoard(MATE_BOARD) >>> board.switchPlayers() >>> board.isMate() False >>> board.isCheckMate() False >>> board.isStaleMate() False Black thinks he is smart in getting rid of the white knight. >>> board.move('D6', 'E5') >>> board.isMate() True >>> board.isCheckMate() False >>> board.isStaleMate() True Oops, that was a stalemate. Let's give black another chance by cheating. >>> board.switchPlayers() >>> board.move('G3', 'G2') >>> board.isMate() True >>> board.isCheckMate() True >>> board.isStaleMate() False """ for row in range(self.nrows): for col in range(self.ncols): if self[row, col].colour == self.player: friend = position.squarify(row, col) moves = self.calcMovesForPiece(friend) if len(moves) > 0: return False # Take a list of squares (without their en passant # friends) that can be attacked by this piece, # presumably a Pawn if anything ends up in this # list. special = self.specialAttackMoves(friend) if len(special) > 0: return False return True