text: 
  
  import baopig as bp
  from piece import Piece, PieceWidget, file_dict
  from pawn import Pawn
  from knight import Knight
  from bishop import Bishop
  from rook import Rook
  from queen import Queen
  from king import King
  
  
  class ChessBoard:
  
      def __init__(self, game):
  
          # Game is the turn, time, history and winning manager
          self.game = game
  
          # _squares is the board storage
          # If a square is empty, then _squares[square.id] returns 0
          # If a square is filled with a piece, it returns the piece
          self._squares = [0] * 80
  
          # All the pieces, captured and on the board ones
          self.pieces = ()
          self.set = {"w": (), "b": ()}
  
          # List of all the square
          # TODO : remove ? is it only used by assertions ?
          self.nbranks = 8
          self.existing_squares = ()
          for col in range(0, 71, 10):
              self.existing_squares += tuple(range(col+0, col+self.nbranks))
  
          # Creating pieces
          self.king = {}
          for square in range(1, 72, 10):
              Pawn(self, "b", square)
          # Knight(self, "b", 10), Knight(self, "b", 60)
          # Bishop(self, "b", 20), Bishop(self, "b", 50)
          # Rook(self, "b", 0), Rook(self, "b", 70)
          # Queen(self, "b", 30)
          # self.king["b"] = King(self, "b", 40)
          for square in range(6, 77, 10):
              Pawn(self, "w", square)
          # Knight(self, "w", 17), Knight(self, "w", 67)
          # Bishop(self, "w", 27), Bishop(self, "w", 57)
          # Rook(self, "w", 7), Rook(self, "w", 77)
          # Queen(self, "w", 37)
          # self.king["w"] = King(self, "w", 47)
  
          self.knight = {"b": (Knight(self, "b", 10), Knight(self, "b", 60)),
                         "w": (Knight(self, "w", 17), Knight(self, "w", 67))}
          self.bishop = {"b": (Bishop(self, "b", 20), Bishop(self, "b", 50)),
                         "w": (Bishop(self, "w", 27), Bishop(self, "w", 57))}
          self.rook = {"b": (Rook(self, "b", 0), Rook(self, "b", 70)),
                       "w": (Rook(self, "w", 7), Rook(self, "w", 77))}
          self.queen = {"b": (Queen(self, "b", 30),), "w": (Queen(self, "w", 37),)}
          self.king = {"b": King(self, "b", 40), "w": King(self, "w", 47)}
  
          # The display is done by a different object
          # Every board doesn't have a display (calculation boards for example)
          self.display = None
  
          # Used by displayed boards for calculations
          self.calculator = None
  
      def __getitem__(self, item):
  
          return self._squares[item]
  
      def __setitem__(self, key, value):
  
          if value is not 0:
  
              try:
                  index = self._squares.index(value)
                  self._squares[index] = 0
              except ValueError: pass
  
              assert isinstance(value, Piece), value
              value.square = key
          self._squares[key] = value
  
      def add(self, piece):
  
          assert isinstance(piece, Piece)
          self._squares[piece.square] = piece  # only time we directly use self._squares
          self.pieces += (piece,)
          self.set[piece.color] += (piece,)
  
      def get_position(self):
  
          return ChessBoardPosition(self, self.game.turn)
  
      @staticmethod
      def has_square(x, y):
  
          return 0 <= x < 8 and 0 <= y < 8
  
      def init_display(self, scene):
  
          if self.display is not None:
              raise PermissionError
  
          self.display = ChessBoardDisplay(self, scene)
          for piece in self.pieces:
              piece.init_display()
  
          self.calculator = ChessBoardCalculator(self)
  
      def move(self, piece, square, redo=None):
          """
          Moves a piece on a square, removes captured pieces
          Should only be called by the game
          :param piece: a Piece
          :param square: an integer
          :param redo: the move who is copied
          """
  
          assert piece.board is self
  
          old_square = piece.square
          captured_piece = self[square]
          rook_castle = None
          en_passant = None
  
          # En passant
          if captured_piece is 0 and piece.LETTER == "p":
              stepback = 1 if piece.color == "w" else -1
              piece_behind = self[square + stepback]
              if piece_behind is not 0 and piece_behind.color != piece.color and piece_behind.LETTER == "p":
                  last_move = self.game.history[-1]
                  if last_move.piece is piece_behind and abs(last_move.start_square - last_move.end_square) is 2:
                      self[piece_behind.square] = 0
                      captured_piece = piece_behind
                      en_passant = piece_behind.square
  
          # If it was a piece
          # if captured_piece is not 0:
          if isinstance(captured_piece, Piece):
              assert captured_piece.board is self
              try:
                  assert captured_piece.color != piece.color  # Chess pieces can't capture each other
              except AssertionError:
                  print(2)
              captured_piece.capture()
  
          # Castling
          if piece.LETTER == "k":
              if piece.castle_rights[2] is False:
                  file = square // 10
                  if file in (2, 6):
                      if file is 6:
                          rook_castle = self.move(self[square + 10], square - 10)
                      else:
                          rook_castle = self.move(self[square - 20], square + 10)
  
          # Memorizing the new position for the game
          self[old_square] = 0
          self[square] = piece
  
          if redo is not None and redo.promotion is not None:
              piece.go_to(square, redo.promotion)
          else:
              piece.go_to(square)
  
          # Return informations for the game history
          return MoveForHistoric(self.game, piece, old_square, square, captured_piece, rook_castle, en_passant)
  
      def undo(self, move):
  
          assert move.piece.board is self
  
          if move.rook_castle is not None:
              assert move.piece.LETTER == "k"
              self.undo(move.rook_castle)
  
          self[move.start_square] = move.piece
          self[move.end_square] = move.capture
          move.piece.undo(move)
  
          if move.en_passant is not None:
              assert move.capture.LETTER == "p"
              self[move.en_passant] = move.capture
          else:
              self[move.end_square] = move.capture
  
          if move.capture is not 0:
              assert move.capture.board is self
              move.capture.uncapture()
  
      def update_pieces_vm(self):
  
          for piece in self.pieces:
              piece.update_valid_moves()
  
          self.calculator.clone()
          for piece in self.set[self.game.turn]:
              cloned_piece = self.calculator.get_simulated_piece(piece)
              valid_moves = []  # moves who don't leave the king in check
              for move in piece.valid_moves:
                  history_element = self.calculator.move(cloned_piece, piece.square + move)
                  for enemy_cloned_piece in self.calculator.set[cloned_piece.enemy_color]:
                      enemy_cloned_piece.update_valid_moves()
                  if not self.calculator.king[piece.color].in_check:
                      valid_moves.append(move)
                  self.calculator.undo(history_element)
  
              piece.valid_moves = tuple(valid_moves)
  
  
  class ChessBoardPosition:
      """
      Saves a chess position in a final object
      """
  
      def __init__(self, board, turn):
  
          self.squares = [0] * 80
  
          for piece in board.pieces:
              if not piece.is_captured:
                  self.squares[piece.square] = piece.LETTER
          self.castle_rights = {"w": board.king["w"].castle_rights.copy(),
                                "b": board.king["b"].castle_rights.copy()}
          self.turn = turn
  
      def __eq__(self, other):
  
          return self.squares == other.squares and \
                  self.castle_rights == other.castle_rights and \
                  self.turn == other.turn
  
  
  class ChessBoardCalculator(ChessBoard):
  
      def __init__(self, board):
  
          ChessBoard.__init__(self, board.game)
  
          self.real_board = board
  
          self.pieces_correspondence = {}
          for i, piece in enumerate(board.pieces):
              self.pieces_correspondence[piece] = self.pieces[i]
  
      def clone(self):
  
          self._squares = [0] * 80
          for piece, clone_piece in self.pieces_correspondence.items():
              clone_piece.is_captured = piece.is_captured
              if not clone_piece.is_captured:
                  self[piece.square] = clone_piece
                  clone_piece.go_to(piece.square)
  
      def get_simulated_piece(self, piece):
  
          return self.pieces_correspondence[piece]
  
  
  class ChessBoardDisplay(bp.Zone):
  
      def __init__(self, board, scene):
  
          self.square_size = 80
  
          bp.Zone.__init__(self, scene, size=(self.square_size * 8, self.square_size * 8),
                           background_image=self.app.images["chessboard"], sticky="center")
  
          self.board = board
  
          # This grid layer contains the matermarks made for displaying the selected piece valid moves (vm_watermark)
          self.vm_watermarks_layer = bp.GridLayer(self, bp.Rectangle, weight=1, name="vm_watermarks_layer",
                                                  row_height=self.square_size, col_width=self.square_size,
                                                  nbcols=8, nbrows=8)
          # This layer contains the informative squares, as the selection square
          self.informative_squares_layer = bp.Layer(self, bp.Rectangle, weight=2, name="informative_squares_layer")
  
          # This grid layer contains the pieces, captured and on the board ones
          self.pieces_layer = bp.GridLayer(self, PieceWidget, name="pieces_layer", weight=3,
                                           row_height=self.square_size, col_width=self.square_size,
                                           nbcols=8, nbrows=8)
  
          # This rectangle highlights the selected piece
          self._selection_square = bp.Rectangle(self, width=self.square_size, height=self.square_size,
                                                color=(50, 250, 50))
          self._selection_square.hide()
  
          # Memory for the selected piece
          self.selected_piece = None
  
          # Dict referencing the square watermarks made for displaying a piece valid moves
          self.vm_watermarks = dict((i, VM_Watermark(self, i)) for i in self.board.existing_squares)
  
          # In-game displayed watermarks
          self.visible_vm_watermarks = []
  
          self.pawn_to_promote = None
          self.promotion_dialog = bp.Dialog(self.app, "Promotion time !",
                                            choices=("Queen", "Rook", "Bishop", "Knight"))
  
          def promote(ans):
              self.pawn_to_promote.promote(eval(ans))
          self.promotion_dialog.signal.ANSWERED.add_command(promote)
  
          self.orientation = "w"
  
      def flip(self):
  
          self.orientation = "b" if self.orientation == "w" else "w"
  
          assert self.selected_piece is None
  
          with bp.paint_lock:  # freezes the display durong the operation
  
              pws = tuple(self.children)
              swapped = []
              for pw in pws:
                  if isinstance(pw, PieceWidget):
                      if pw in swapped:
                          continue
                      try:
                          pw.piece.go_to(pw.piece.square)
                      except PermissionError:
                          pw2 = self.board[79 - pw.piece.square].widget
                          self.pieces_layer.swap(pw, pw2)
                          swapped.append(pw2)
  
      def select(self, widget):
  
          if widget.is_sleeping:
              return  # occurs when a piece is captured : the selection signal is emitted right after it falls asleep
  
          self._selection_square.move_at(widget.topleft)
          self._selection_square.show()
          self.selected_piece = widget
  
          if widget.piece.color != self.board.game.turn:
              return
  
          for move in widget.piece.valid_moves:
              square = widget.piece.square + move
              vm_watermark = self.vm_watermarks[square]
              vm_watermark.show()
              self.visible_vm_watermarks.append(vm_watermark)
  
      def unselect(self, widget):
  
          assert widget is self.selected_piece
          self.selected_piece = None
          self._selection_square.hide()
  
          for vm_watermark in self.visible_vm_watermarks:
              if vm_watermark.collidemouse():
                  self.board.game.move(widget.piece, vm_watermark.square)
              vm_watermark.hide()
          self.visible_vm_watermarks.clear()
  
  
  class VM_Watermark:
  
      def __init__(self, board, square):
  
          class W_VM_Watermark(bp.Rectangle):
  
              def __init__(self):
  
                  bp.Rectangle.__init__(self, board, col=square//10, row=square%10, color=(50, 250, 50, 50),
                                        width=80, height=80, layer=board.vm_watermarks_layer)
                  self.hide()
  
          self.board = board
          self.square = square
          self.widget = W_VM_Watermark()
  
          self.col = self.widget.col
          self.row = self.widget.row
  
          self.show = self.widget.show
          self.hide = self.widget.hide
          self.collidemouse = self.widget.collidemouse
  
  
  class MoveForHistoric:
  
      def __init__(self, game, piece, old_square, square, captured_piece, rook_castle, en_passant):
  
          self.piece = piece
          self.start_square = old_square
          self.end_square = square
          self.capture = captured_piece
          self.promotion = None
          self.rook_castle = rook_castle
          self.en_passant = en_passant
  
          n = ""
  
          # the name of the piece, not for pawns
          if self.piece.LETTER != "p":
              n += self.piece.LETTER.capitalize()
  
          # When two allied knights, rooks or queens could have jump on this square
          for thing in (("n", game.board.knight), ("r", game.board.rook), ("q", game.board.queen)):
              if self.piece.LETTER == thing[0]:
                  allies = []  # allies of same type who also could have make the move
                  for piece2 in thing[1][self.piece.color]:
                      if piece2 is not piece:
                          for move in piece2.valid_moves:
                              if self.end_square is piece2.square + move:
                                  allies.append(piece2)
                  if len(allies) is 1:
                      if self.start_square // 10 is allies[0].square // 10:
                          # They are on the same file, so we indiquate the rank
                          n += str(10 - self.start_square % 10)
                      else:
                          # They are not on the same file
                          n += file_dict[self.start_square // 10]
                  elif len(allies) > 1:
                      same_file = same_rank = False
                      for ally in allies:
                          if ally.file is self.start_square // 10:
                              same_file = True
                          if ally.rank is self.start_square % 10:
                              same_rank = True
                      if same_file is False:
                          # They are not on the same file
                          n += file_dict[self.start_square // 10]
                      elif same_rank is False:
                          # They are on the same file, but not on the same rank
                          n += str(10 - self.start_square % 10)
                      else:
                          # There is at least one on the same rank and one on the same file
                          n += file_dict[self.start_square // 10] + str(10 - self.start_square % 10)
  
          # NOTE : some very rare times, there is 3 queens who can go on the same square
          # does my notation method handles that ?
  
          if self.capture is not 0:
  
              # When a pawn captures something, we write its origin file
              if self.piece.LETTER == "p":
                  n += file_dict[self.start_square // 10]
              n += "x"
  
          n += self.piece.coordinates
          self.notation = n
  
      def __getitem__TOREMOVE(self, item):
  
          if item is 0:
              return self.piece
          if item is 1:
              return self.start_square
          if item is 2:
              return self.end_square
          if item is 3:
              return self.capture
          if item is 4:
              return self.promotion
          raise KeyError
  
  