Guest User

Chess Game

a guest
Feb 21st, 2023
112
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 16.43 KB | None | 0 0
  1. from collections import namedtuple
  2.  
  3. def chess_to_list(pos: namedtuple) -> tuple:  # position(4, 2) -> (6,3)
  4.   return (8 - pos.y, pos.x - 1)
  5.  
  6. def list_to_chess(pos: tuple) -> namedtuple:  # (6,3) -> position(4, 2)
  7.   return position(pos[1] + 1, 8 - pos[0])
  8.  
  9. class Piece:
  10.  
  11.   def __init__(self, colour, pos):
  12.     self.colour = colour
  13.     self.pos = pos
  14.  
  15.   def __str__(self):
  16.     return f"{self.type}"
  17.  
  18.   def __repr__(self):
  19.     return f"{self.repr_type}"
  20.  
  21.  
  22. class Pawn(Piece):
  23.   __slots__ = 'first_move', 'pinned', 'type', 'repr_type', 'direction'
  24.  
  25.   def __init__(self, colour, pos):
  26.     super().__init__(colour, pos)
  27.     self.first_move = 6 if self.colour == 'W' else 1
  28.     self.pinned = False
  29.     self.type = '♙' if self.colour == 'W' else '♟︎'
  30.     self.repr_type = 'wP' if self.colour == 'W' else 'bP'
  31.     self.direction = 1 if self.colour == 'W' else -1
  32.  
  33.   def legal_moves(self, board):
  34.     legal = []
  35.     current = chess_to_list(self.pos)
  36.     current_x = current[1]
  37.     current_y = current[0]
  38.     if current_y <= 7:
  39.       # d+2, check d3 and d4 == (6,3) check (5,3) and (4,3)
  40.       if board[current_y - self.direction][current_x] is None:
  41.         legal.append(list_to_chess((current_y - self.direction, current_x)))
  42.  
  43.         # Test Forward moves, but only if there is nothing one ahead of the pawn
  44.         if current_y == self.first_move:
  45.           if board[current_y - (2 * self.direction)][current_x] is None:
  46.             legal.append(
  47.               list_to_chess((current_y - (2 * self.direction), current_x)))
  48.  
  49.       # Test diagonal captures - Short Circuit Evaluation
  50.  
  51.       # Check to the Right
  52.       if current_x <= 6:
  53.         if (board[current_y - self.direction][current_x + 1]) is not None and (
  54.             board[current_y - self.direction][current_x +
  55.                                               1]).colour != self.colour:
  56.           legal.append(
  57.             list_to_chess((current_y - self.direction, current_x + 1)))
  58.  
  59.       # Check to the Left
  60.       if current_x <= 2:
  61.         if (board[current_y - self.direction][current_x - 1]) is not None and (
  62.             board[current_y - self.direction][current_x -
  63.                                               1]).colour != self.colour:
  64.           legal.append(
  65.             list_to_chess((current_y - self.direction, current_x + 1)))
  66.  
  67.     # test if move puts in check/keeps king in check, but causes error <---
  68.  
  69.     new_legal_moves = []
  70.     for move in legal:
  71.       g.board.move_piece(self.pos, move)
  72.       if not g.in_check(self.colour):
  73.         new_legal_moves.append(move)
  74.  
  75.       g.board.move_piece(move, self.pos)
  76.  
  77.     return new_legal_moves
  78.  
  79.     return legal
  80.  
  81. class Bishop(Piece):
  82.  
  83.   __slots__ = 'pinned', 'type', 'repr_type'
  84.  
  85.   def __init__(self, colour, pos):
  86.     super().__init__(colour, pos)
  87.     self.pinned = False
  88.     self.type = '♗' if self.colour == 'W' else '♝'
  89.     self.repr_type = 'wB' if self.colour == 'W' else 'bB'
  90.  
  91.   def legal_moves(self, board):
  92.     legal = []
  93.     current_x = self.pos.x
  94.     current_y = self.pos.y
  95.  
  96.     # Check up-right (Positive Gradient)
  97.     while current_y != 8 and current_x != 8:
  98.       current_y += 1
  99.       current_x += 1
  100.       test_pos = chess_to_list(position(current_x, current_y))
  101.  
  102.       if board[test_pos[0]][test_pos[1]] is None:
  103.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  104.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  105.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  106.         break
  107.       else:
  108.         break
  109.  
  110.     current_x = self.pos.x
  111.     current_y = self.pos.y
  112.  
  113.     # Check down-left (Positive Gradient)
  114.     while current_y != 1 and current_x != 1:
  115.       current_y -= 1
  116.       current_x -= 1
  117.       test_pos = chess_to_list(position(current_x, current_y))
  118.  
  119.       if board[test_pos[0]][test_pos[1]] is None:
  120.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  121.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  122.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  123.         break
  124.       else:
  125.         break
  126.  
  127.     current_x = self.pos.x
  128.     current_y = self.pos.y
  129.  
  130.     # Check up-left (Negative Gradient)
  131.     while current_y != 8 and current_x != 1:
  132.       current_y += 1
  133.       current_x -= 1
  134.       test_pos = chess_to_list(position(current_x, current_y))
  135.  
  136.       if board[test_pos[0]][test_pos[1]] is None:
  137.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  138.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  139.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  140.         break
  141.       else:
  142.         break
  143.  
  144.     current_x = self.pos.x
  145.     current_y = self.pos.y
  146.  
  147.     # Check down-right (Negative Gradient)
  148.     while current_y != 1 and current_x != 8:
  149.       current_y -= 1
  150.       current_x += 1
  151.       test_pos = chess_to_list(position(current_x, current_y))
  152.  
  153.       if board[test_pos[0]][test_pos[1]] is None:
  154.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  155.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  156.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  157.         break
  158.       else:
  159.         break
  160.  
  161.     return legal
  162.  
  163.  
  164. class Knight(Piece):
  165.  
  166.   __slots__ = 'pinned', 'type', 'repr_type'
  167.  
  168.   def __init__(self, colour, pos):
  169.     super().__init__(colour, pos)
  170.     self.pinned = False
  171.     self.type = '♘' if self.colour == 'W' else '♞'
  172.     self.repr_type = 'wN' if self.colour == 'W' else 'bN'
  173.  
  174.   def legal_moves(self, board):
  175.     legal = []
  176.     knight_moves = [(2, 1), (2, -1), (-2, 1), (-2, -1), (1, 2), (1, -2),
  177.                     (-1, 2), (-1, -2)]
  178.  
  179.     for move in knight_moves:
  180.       new_x = self.pos.x + move[0]
  181.       new_y = self.pos.y + move[1]
  182.  
  183.       if 1 <= new_x <= 7 and 1 <= new_y <= 7:
  184.         current = chess_to_list(position(new_x, new_y))
  185.  
  186.         target = board[current[0]][current[1]]
  187.  
  188.         if target is None or target.colour != self.colour:
  189.           legal.append(position(new_x, new_y))
  190.  
  191.     return legal
  192.  
  193.  
  194. class Rook(Piece):
  195.   __slots__ = 'has_moved', 'pinned', 'type', 'repr_type'
  196.  
  197.   def __init__(self, colour, pos):
  198.     super().__init__(colour, pos)
  199.     self.has_moved = False
  200.     self.pinned = False
  201.     self.type = '♖' if self.colour == 'W' else '♜'
  202.     self.repr_type = 'wR' if self.colour == 'W' else 'bR'
  203.  
  204.   def legal_moves(self, board):
  205.     legal = []
  206.     current = chess_to_list(self.pos)
  207.     current_x = current[1]
  208.     current_y = current[0]
  209.  
  210.     # Check Up
  211.     while current_y != 8:
  212.       current_y += 1
  213.       test_pos = chess_to_list(position(current_x, current_y))
  214.  
  215.       if board[test_pos[0]][test_pos[1]] is None:
  216.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  217.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  218.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  219.         break
  220.       else:
  221.         break
  222.  
  223.     current_x = self.pos.x
  224.     current_y = self.pos.y
  225.  
  226.     # Check Right
  227.     while current_x != 8:
  228.       current_x += 1
  229.       test_pos = chess_to_list(position(current_x, current_y))
  230.  
  231.       if board[test_pos[0]][test_pos[1]] is None:
  232.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  233.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  234.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  235.         break
  236.       else:
  237.         break
  238.  
  239.     current_x = self.pos.x
  240.     current_y = self.pos.y
  241.  
  242.     # Check Down
  243.     while current_y != 1:
  244.       current_y -= 1
  245.       test_pos = chess_to_list(position(current_x, current_y))
  246.  
  247.       if board[test_pos[0]][test_pos[1]] is None:
  248.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  249.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  250.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  251.         break
  252.       else:
  253.         break
  254.  
  255.     current_x = self.pos.x
  256.     current_y = self.pos.y
  257.  
  258.     # Check Left
  259.     while current_x != 1:
  260.       current_x -= 1
  261.       test_pos = chess_to_list(position(current_x, current_y))
  262.  
  263.       if board[test_pos[0]][test_pos[1]] is None:
  264.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  265.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  266.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  267.         break
  268.       else:
  269.         break
  270.  
  271.     return legal
  272.  
  273.  
  274. class Queen(Piece):
  275.  
  276.   __slots__ = 'pinned', 'type', 'repr_type'
  277.  
  278.   def __init__(self, colour, pos):
  279.     super().__init__(colour, pos)
  280.     self.pinned = False
  281.     self.type = '♕' if self.colour == 'W' else '♛'
  282.     self.repr_type = 'wQ' if self.colour == 'W' else 'bQ'
  283.  
  284.   def legal_moves(self, board):
  285.     legal = []
  286.     current_x = self.pos.x
  287.     current_y = self.pos.y
  288.  
  289.     # Check Rook Moves
  290.  
  291.     # Check Up
  292.     while current_y != 8:
  293.       current_y += 1
  294.       test_pos = chess_to_list(position(current_x, current_y))
  295.  
  296.       if board[test_pos[0]][test_pos[1]] is None:
  297.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  298.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  299.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  300.         break
  301.       else:
  302.         break
  303.  
  304.     current_x = self.pos.x
  305.     current_y = self.pos.y
  306.  
  307.     # Check Right
  308.     while current_x != 8:
  309.       current_x += 1
  310.       test_pos = chess_to_list(position(current_x, current_y))
  311.  
  312.       if board[test_pos[0]][test_pos[1]] is None:
  313.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  314.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  315.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  316.         break
  317.       else:
  318.         break
  319.  
  320.     current_x = self.pos.x
  321.     current_y = self.pos.y
  322.  
  323.     # Check Down
  324.     while current_y != 1:
  325.       current_y -= 1
  326.       test_pos = chess_to_list(position(current_x, current_y))
  327.  
  328.       if board[test_pos[0]][test_pos[1]] is None:
  329.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  330.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  331.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  332.         break
  333.       else:
  334.         break
  335.  
  336.     current_x = self.pos.x
  337.     current_y = self.pos.y
  338.  
  339.     # Check Left
  340.     while current_x != 1:
  341.       current_x -= 1
  342.       test_pos = chess_to_list(position(current_x, current_y))
  343.  
  344.       if board[test_pos[0]][test_pos[1]] is None:
  345.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  346.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  347.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  348.         break
  349.       else:
  350.         break
  351.  
  352.     # Check Bishop Moves
  353.  
  354.     # Check up-right (Positive Gradient)
  355.     while current_y != 8 and current_x != 8:
  356.       current_y += 1
  357.       current_x += 1
  358.       test_pos = chess_to_list(position(current_x, current_y))
  359.  
  360.       if board[test_pos[0]][test_pos[1]] is None:
  361.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  362.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  363.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  364.         break
  365.       else:
  366.         break
  367.  
  368.     current_x = self.pos.x
  369.     current_y = self.pos.y
  370.  
  371.     # Check down-left (Positive Gradient)
  372.     while current_y != 1 and current_x != 1:
  373.       current_y -= 1
  374.       current_x -= 1
  375.       test_pos = chess_to_list(position(current_x, current_y))
  376.  
  377.       if board[test_pos[0]][test_pos[1]] is None:
  378.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  379.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  380.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  381.         break
  382.       else:
  383.         break
  384.  
  385.     current_x = self.pos.x
  386.     current_y = self.pos.y
  387.  
  388.     # Check up-left (Negative Gradient)
  389.     while current_y != 8 and current_x != 1:
  390.       current_y += 1
  391.       current_x -= 1
  392.       test_pos = chess_to_list(position(current_x, current_y))
  393.  
  394.       if board[test_pos[0]][test_pos[1]] is None:
  395.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  396.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  397.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  398.         break
  399.       else:
  400.         break
  401.  
  402.     current_x = self.pos.x
  403.     current_y = self.pos.y
  404.  
  405.     # Check down-right (Negative Gradient)
  406.     while current_y != 1 and current_x != 8:
  407.       current_y -= 1
  408.       current_x += 1
  409.       test_pos = chess_to_list(position(current_x, current_y))
  410.  
  411.       if board[test_pos[0]][test_pos[1]] is None:
  412.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  413.       elif board[test_pos[0]][test_pos[1]].colour != self.colour:
  414.         legal.append(list_to_chess((test_pos[0], test_pos[1])))
  415.         break
  416.       else:
  417.         break
  418.  
  419.     return legal
  420.  
  421.  
  422. class King(Piece):
  423.  
  424.   __slots__ = 'can_castle', 'pinned', 'type', 'repr_type'
  425.  
  426.   def __init__(self, colour, pos):
  427.     super().__init__(colour, pos)
  428.     self.can_castle = True
  429.     self.pinned = False
  430.     self.type = '♔' if self.colour == 'W' else '♚'
  431.     self.repr_type = 'wK' if self.colour == 'W' else 'bK'
  432.  
  433.   def legal_moves(self, board):
  434.     legal = []
  435.     king_moves = [(0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1), (-1, 0),
  436.                   (-1, 1)]
  437.  
  438.     for move in king_moves:
  439.       new_x = self.pos.x + move[0]
  440.       new_y = self.pos.y + move[1]
  441.  
  442.       if 1 <= new_x <= 7 and 1 <= new_y <= 7:
  443.         current = chess_to_list(position(new_x, new_y))
  444.  
  445.         target = board[current[0]][current[1]]
  446.  
  447.         if target is None or target.colour != self.colour:
  448.           legal.append(position(new_x, new_y))
  449.  
  450.     return legal
  451.  
  452.  
  453. class Board:
  454.  
  455.   def __init__(self):
  456.     self.board = [[None for i in range(8)] for j in range(8)]
  457.  
  458.   def __str__(self):
  459.     board_str = ''
  460.     c = 8
  461.     for row in self.board:
  462.       board_str += f'{c} '
  463.       c -= 1
  464.       for piece in row:
  465.         if piece is None:
  466.           board_str += '- '
  467.         else:
  468.           board_str += f"{piece} "
  469.       board_str += '\n'
  470.  
  471.     board_str += '  a b c d e f g h'
  472.     return board_str
  473.  
  474.   def add_piece(self, piece, chess_coord):
  475.     list_coord = chess_to_list(chess_coord)
  476.     self.board[list_coord[0]][list_coord[1]] = piece
  477.  
  478.   def move_piece(self,
  479.                  start_coord: namedtuple,
  480.                  end_coord: namedtuple,
  481.                  captured=None) -> Piece | None:
  482.     start_list_coord = chess_to_list(start_coord)
  483.  
  484.     end_list_coord = chess_to_list(end_coord)
  485.  
  486.     piece = self.board[start_list_coord[0]][start_list_coord[1]]
  487.     print(f"{piece = !r}")
  488.     captured = self.board[end_list_coord[0]][end_list_coord[1]]
  489.  
  490.     self.board[start_list_coord[0]][start_list_coord[1]] = None
  491.     self.board[end_list_coord[0]][end_list_coord[1]] = piece
  492.     piece.pos = end_coord
  493.  
  494.     return captured
  495.  
  496.  
  497.  
  498. types = [Pawn, Bishop, Knight, Rook, Queen, King]
  499. colours = ['W', 'B']
  500. position = namedtuple("Position", ['x', 'y'])
  501.  
  502. pieces = [Pawn('W', position(x, 2)) for x in range(1, 9)
  503.           ] + [Pawn('B', position(x, 7)) for x in range(1, 9)] + [
  504.             Rook('W', position(1, 1)),
  505.             Rook('W', position(8, 1)),
  506.             Rook('B', position(1, 8)),
  507.             Rook('B', position(8, 8))
  508.           ] + [
  509.             Knight('W', position(2, 1)),
  510.             Knight('W', position(7, 1)),
  511.             Knight('B', position(2, 8)),
  512.             Knight('B', position(7, 8))
  513.           ] + [
  514.             Bishop('W', position(3, 1)),
  515.             Bishop('W', position(6, 1)),
  516.             Bishop('B', position(3, 8)),
  517.             Bishop('B', position(6, 8))
  518.           ] + [
  519.             Queen('W', position(4, 1)),
  520.             King('W', position(5, 1)),
  521.             Queen('B', position(4, 8)),
  522.             King('B', position(5, 8))
  523.           ]
  524.  
  525.  
  526. class Game:
  527.  
  528.   def __init__(self):
  529.     self.board = Board()
  530.  
  531.   def setup(self):
  532.     for piece in pieces:
  533.       chess_coord = piece.pos
  534.       self.board.add_piece(piece, chess_coord)
  535.  
  536.   def restart(self):
  537.     # Move pieces here at their default positions, so the game can be restarted
  538.     self.board.board = [[None for i in range(8)] for j in range(8)]
  539.     self.setup()
  540.  
  541.   def in_check(self, colour, checked=set()): # Have it so that you can't make a move if you are in check!!!!
  542.     king_pos = None
  543.     for piece in pieces:
  544.       if isinstance(piece, King) and piece.colour == colour:
  545.         king_pos = piece.pos
  546.         break
  547.  
  548.     return bool([piece for piece in pieces if king_pos in piece.legal_moves(self.board.board)])
  549.  
  550. # setup
  551. g = Game()
  552. g.setup()
  553.  
  554. # Testing g.in_check()
  555. g.in_check('W')
  556.  
Advertisement
Add Comment
Please, Sign In to add comment