Movatterモバイル変換


[0]ホーム

URL:


How to Make a Chess Game with Pygame in Python

Learn how you can build a chess game from scratch with Python classes and pygame library in Python.
  · 23 min read · Updated may 2023 ·Game Development

Want to code faster? OurPython Code Generator lets you create Python scripts with just a few clicks. Try it now!

The chess game is a pretty cool project idea for intermediate Python programmers. It's good practice for making apps with GUIs while getting good at using classes. In this tutorial, you learn about:

  • Using the basics ofpygame.
  • Learn how to code a chess game with Python classes.

Related:How to Make a Hangman Game in Python.

Installation and Setting Up

Before we start coding, let's first install thepygame module in the terminal:

$ pip install pygame

Once we installed thepygame, let's move into setting up our environment by making the py files and folder we're using in this order:

> python-chess    > data        > classes            > pieces                /* Bishop.py                /* King.py                /* Knight.py                /* Pawn.py                /* Queen.py                /* Rook.py            /* Board.py            /* Piece.py            /* Square.py        > imgs    /* main.py

Move the images of the chess icons you'll use in thepython/data/imgs/ directory. Make sure your image files are named[color's 1st letter]_[piece name].png just like this:

If you don't have chess icons, you can use minehere.

Coding the Game

And now we're done setting up; we can start coding now. Our chess game has two main code parts; creating the board and creating the pieces. The board will mainly focus on square positions and game rules, while the pieces focus on the piece they represent and the moves it has.

Making the Board

Let's start by making theSquare class. TheSquare class creates, colors, position, and draw each chess tile inside our game window:

# /* Square.pyimport pygame# Tile creatorclass Square:    def __init__(self, x, y, width, height):        self.x = x        self.y = y        self.width = width        self.height = height        self.abs_x = x * width        self.abs_y = y * height        self.abs_pos = (self.abs_x, self.abs_y)        self.pos = (x, y)        self.color = 'light' if (x + y) % 2 == 0 else 'dark'        self.draw_color = (220, 208, 194) if self.color == 'light' else (53, 53, 53)        self.highlight_color = (100, 249, 83) if self.color == 'light' else (0, 228, 10)        self.occupying_piece = None        self.coord = self.get_coord()        self.highlight = False        self.rect = pygame.Rect(            self.abs_x,            self.abs_y,            self.width,            self.height        )    # get the formal notation of the tile    def get_coord(self):        columns = 'abcdefgh'        return columns[self.x] + str(self.y + 1)    def draw(self, display):        # configures if tile should be light or dark or highlighted tile        if self.highlight:            pygame.draw.rect(display, self.highlight_color, self.rect)        else:            pygame.draw.rect(display, self.draw_color, self.rect)        # adds the chess piece icons        if self.occupying_piece != None:            centering_rect = self.occupying_piece.img.get_rect()            centering_rect.center = self.rect.center            display.blit(self.occupying_piece.img, centering_rect.topleft)

The first thing we're gonna do is to make a class for making chessSquare. Let's start by adding the__init__() function to get the square'swidth,height,x for a row, andy for the column.

With this basic information, we can fulfill other variables using them. As you see above, we haveself.x andself.y while we also haveself.abs_x andself.abs_y.self.abs_x andself.abs_y dictates where the chess tile is assigned to be drawn inside the window, and we compile them both inself.abs_pos.

Theself.color tells the square tile should be light colored if it is divisible by 2 or instead dark if not, while theself.draw_color tells the color configuration for light and dark. We also haveself.highlight_color which we use to highlight the tiles with the possible movement of a piece if it was selected. Theself.rect configures the width, height, and location (usingself.abs_x andself.abs_y) of a square or tile.

Theget_coord() returns the name of the tile depending on itsx andy based on the real board. Letters symbolize rows, and the number symbolizes columns. Like "a1", it is the bottom leftmost tile in a chess board.

Thedraw(), executes the configurations we did by drawing the tile in the canvas, in the color it was assigned. The secondif statement tells that if the square has a piece in this position, you should access its icon and place it inside the tile.

Now we have a class for making a square. Let's make another class for handling tiles and the whole board.

# /* Board.pyimport pygamefrom data.classes.Square import Squarefrom data.classes.pieces.Rook import Rookfrom data.classes.pieces.Bishop import Bishopfrom data.classes.pieces.Knight import Knightfrom data.classes.pieces.Queen import Queenfrom data.classes.pieces.King import Kingfrom data.classes.pieces.Pawn import Pawn# Game state checkerclass Board:    def __init__(self, width, height):        self.width = width        self.height = height        self.tile_width = width // 8        self.tile_height = height // 8        self.selected_piece = None        self.turn = 'white'        self.config = [            ['bR', 'bN', 'bB', 'bQ', 'bK', 'bB', 'bN', 'bR'],            ['bP', 'bP', 'bP', 'bP', 'bP', 'bP', 'bP', 'bP'],            ['','','','','','','',''],            ['','','','','','','',''],            ['','','','','','','',''],            ['','','','','','','',''],            ['wP', 'wP', 'wP', 'wP', 'wP', 'wP', 'wP', 'wP'],            ['wR', 'wN', 'wB', 'wQ', 'wK', 'wB', 'wN', 'wR'],        ]        self.squares = self.generate_squares()        self.setup_board()    def generate_squares(self):        output = []        for y in range(8):            for x in range(8):                output.append(                    Square(x,  y, self.tile_width, self.tile_height)                )        return output    def get_square_from_pos(self, pos):        for square in self.squares:            if (square.x, square.y) == (pos[0], pos[1]):                return square    def get_piece_from_pos(self, pos):        return self.get_square_from_pos(pos).occupying_piece

In making the whole chess board, it's important to know first what is thewidth andheight of the game window so we can divide it into 8 rows with 8 columns to identify our tiles' exact size.

Theself.config represents the chessboard configuration with a 2D list having our pieces with their default position. Below it, we configure theself.squares with a value calling ourself.generate_squares() for making chess tiles and putting them all in a list.

Now let's create the other parts of theBoard, including theself.setup_board() we called above.

    def setup_board(self):        for y, row in enumerate(self.config):            for x, piece in enumerate(row):                if piece != '':                    square = self.get_square_from_pos((x, y))                    # looking inside contents, what piece does it have                    if piece[1] == 'R':                        square.occupying_piece = Rook(                            (x, y), 'white' if piece[0] == 'w' else 'black', self                        )                    # as you notice above, we put `self` as argument, or means our class Board                    elif piece[1] == 'N':                        square.occupying_piece = Knight(                            (x, y), 'white' if piece[0] == 'w' else 'black', self                        )                    elif piece[1] == 'B':                        square.occupying_piece = Bishop(                            (x, y), 'white' if piece[0] == 'w' else 'black', self                        )                    elif piece[1] == 'Q':                        square.occupying_piece = Queen(                            (x, y), 'white' if piece[0] == 'w' else 'black', self                        )                    elif piece[1] == 'K':                        square.occupying_piece = King(                            (x, y), 'white' if piece[0] == 'w' else 'black', self                        )                    elif piece[1] == 'P':                        square.occupying_piece = Pawn(                            (x, y), 'white' if piece[0] == 'w' else 'black', self                        )

Thesetup_board() creates each piece, and puts them in their respective place by mapping theself.config with the whole board. If the current value ofpiece inself.config is an empty string or'', then the tile must be empty, and if not, it will access its respective tile position through the current value ofx andy. Eachpiece fromself.config will be identified according to the capital letter it has, except theKnight.

If we got'N', then it's aKnight if'P', then it's aPawn. If'R', then it's aRook, 'B' forBishop, and so on. After we configure the letters, we'll overwrite the currentsquare.occupying_piece with a value of their piece class with the color depending on the first value of thepiece string. As you noticed here and in the other statement:

                    if piece[1] == 'R':                        square.occupying_piece = Rook(                            (x, y), 'white' if piece[0] == 'w' else 'black', self

We put aself as an argument for theRook class. That means we put our current class,Board, as an argument.

We need a function that detects each click in our game. So let's makehandle_click() in ourBoard class:

    def handle_click(self, mx, my):        x = mx // self.tile_width        y = my // self.tile_height        clicked_square = self.get_square_from_pos((x, y))        if self.selected_piece is None:            if clicked_square.occupying_piece is not None:                if clicked_square.occupying_piece.color == self.turn:                    self.selected_piece = clicked_square.occupying_piece        elif self.selected_piece.move(self, clicked_square):            self.turn = 'white' if self.turn == 'black' else 'black'        elif clicked_square.occupying_piece is not None:            if clicked_square.occupying_piece.color == self.turn:                self.selected_piece = clicked_square.occupying_piece

Thehandle_click() accepts the x (mx) and y (my) coordinates of where you click inside the game window as an argument. Thex andy variables inside this function compute what row and column you clicked, then we pass its outcomes toclicked_square to get the square or the tile.

This configuration can now receive our every click inside the game window. The following if/else statements process our click if we're making a move or just clicking around.

It all works once you clicked somewhere inside the game window so let's assume that you're playing the game using a white piece and you've already clicked. If we haven't selected any piece yet, it will look as if the tile you clicked has a piece, and if it's your colors turn, and if yes, it will be yourself.selected_piece.

With the help of other classes, your piece's possible move will be highlighted in the game. After selecting a piece's move, it will convert theself.turn into the next player's piece color.

Now that we have a selected piece and chosen a move, it will make the move. I'll explain the other moving later as we make the classes for that piece.

Let's add another feature for theBoard class; we are adding functions that check if a player is in check or checkmate.

    # check state checker    def is_in_check(self, color, board_change=None): # board_change = [(x1, y1), (x2, y2)]        output = False        king_pos = None        changing_piece = None        old_square = None        new_square = None        new_square_old_piece = None        if board_change is not None:            for square in self.squares:                if square.pos == board_change[0]:                    changing_piece = square.occupying_piece                    old_square = square                    old_square.occupying_piece = None            for square in self.squares:                if square.pos == board_change[1]:                    new_square = square                    new_square_old_piece = new_square.occupying_piece                    new_square.occupying_piece = changing_piece        pieces = [            i.occupying_piece for i in self.squares if i.occupying_piece is not None        ]        if changing_piece is not None:            if changing_piece.notation == 'K':                king_pos = new_square.pos        if king_pos == None:            for piece in pieces:                if piece.notation == 'K' and piece.color == color:                        king_pos = piece.pos        for piece in pieces:            if piece.color != color:                for square in piece.attacking_squares(self):                    if square.pos == king_pos:                        output = True        if board_change is not None:            old_square.occupying_piece = changing_piece            new_square.occupying_piece = new_square_old_piece        return output

For every move we make, theis_in_check() function will be called, whenever theboard_change is not empty.

In the first iteration, it locates the position of the old tile, passes its current piece inchanging_piece, and empty that tile while in the second iteration, it catches the new tile position and passes its current piece tonew_square_old_piece and give it a new piece fromchanging_piece.

Once ourchanging_piece is not empty, it'll try to identify if it's aKing by getting itsself.notation. If so, it'll override theking_pos and give it the value ofnew_square.pos.

Note: Theself.notation is a variable from the pieces' class, that serves as an identification containing their letter symbols.

The next thing we'll try to identify is what the enemy piece does to do the check to our player'sKing, where we check starting byif piece.color != color.

        for piece in pieces:            if piece.color != color:                for square in piece.attacking_squares(self):                    if square.pos == king_pos:                        output = True

With the following code above, we can iterate through enemy pieces and check theirattacking_squares, which gets all the possible moves of a piece. If apiece has a position inattacking_squares the same value asking_pos, which means one of the players is checked, so we set theoutput toTrue. Theoutput tells if aKing is in check or not, so we have to return it.

Now let's make theis_in_checkmate() function for identifying if we have a winner yet:

    # checkmate state checker    def is_in_checkmate(self, color):        output = False        for piece in [i.occupying_piece for i in self.squares]:            if piece != None:                if piece.notation == 'K' and piece.color == color:                    king = piece        if king.get_valid_moves(self) == []:            if self.is_in_check(color):                output = True        return output

Once we get theKing the same color as the argument we passed, it'll try to see if it has any moves left. If none, then it'll check if the player is in check. If that's the case, then it will return the value of theoutput which isTrue, which means the side of the color we passed is checkmate.

Now we have all the board configurations; it's time to add the final function for theBoard class which is thedraw() function:

    def draw(self, display):        if self.selected_piece is not None:            self.get_square_from_pos(self.selected_piece.pos).highlight = True            for square in self.selected_piece.get_valid_moves(self):                square.highlight = True        for square in self.squares:            square.draw(display)

This function highlights all the possible moves of a piece once selected while it is its color's turn.

Learn also: How to Make a Tetris Game using PyGame in Python.

Making the Pieces

Now we're finished withBoard class, let's make another class for pieces inPiece.py.

Let's start by adding a function that gets all the available moves and a checker if the next player got checked by the previous player:

# /* Piece.pyimport pygameclass Piece:    def __init__(self, pos, color, board):        self.pos = pos        self.x = pos[0]        self.y = pos[1]        self.color = color        self.has_moved = False    def get_moves(self, board):        output = []        for direction in self.get_possible_moves(board):            for square in direction:                if square.occupying_piece is not None:                    if square.occupying_piece.color == self.color:                        break                    else:                        output.append(square)                        break                else:                    output.append(square)        return output

Theget_moves() gets all the available moves of the current player, including attacking the enemy piece. If an opponent piece is in range of a piece's move, the piece can capture it where its range will limit on these opponent's piece tile position throughoutput.append(square) thenbreak unless the piece isKnight which can move in an 'L-shape'.

    def get_valid_moves(self, board):        output = []        for square in self.get_moves(board):            if not board.is_in_check(self.color, board_change=[self.pos, square.pos]):                output.append(square)        return output

Before proceeding for our current player in making a move, theget_valid_moves() checks first if the last player does a move that checked our current player. And if not, then it will return the available moves.

To make the pieces work, we're adding amove() function that handles every move we make on the board:

    def move(self, board, square, force=False):        for i in board.squares:            i.highlight = False        if square in self.get_valid_moves(board) or force:            prev_square = board.get_square_from_pos(self.pos)            self.pos, self.x, self.y = square.pos, square.x, square.y            prev_square.occupying_piece = None            square.occupying_piece = self            board.selected_piece = None            self.has_moved = True            # Pawn promotion            if self.notation == ' ':                if self.y == 0 or self.y == 7:                    from data.classes.pieces.Queen import Queen                    square.occupying_piece = Queen(                        (self.x, self.y),                        self.color,                        board                    )            # Move rook if king castles            if self.notation == 'K':                if prev_square.x - self.x == 2:                    rook = board.get_piece_from_pos((0, self.y))                    rook.move(board, board.get_square_from_pos((3, self.y)), force=True)                elif prev_square.x - self.x == -2:                    rook = board.get_piece_from_pos((7, self.y))                    rook.move(board, board.get_square_from_pos((5, self.y)), force=True)            return True        else:            board.selected_piece = None            return False    # True for all pieces except pawn    def attacking_squares(self, board):        return self.get_moves(board)

It takesboard andsquare as arguments. If the tile we select to move our chosen piece is inself.get_valid_moves(), the move is valid to execute. To make it happen, themove() function will get the current square usingboard.get_square_from_pos(self.pos) and save it inprev_square and get its positionssquare.pos,square.x,square.y and save it inself.pos,self.x, andself.y for further use.

Then the function will empty theprev_square, and the piece (self - the current chess piece class) will be moved to thesquare.occupying_piece.

Chess has cool features; some of them are castling and pawn promotion, and that's what we do next.

If the notation of the piece we've just moved in is' ', which is a pawn, and it reaches row 0 (for white pawns) or row 7 (for black pawns), the pawn will be replaced by another queen of the same color.

And if the piece's notation is'K' and then moved 2 tiles to the left or right, it means the player's move is casting.

Making a Class for Each Piece

Now that we've finished theSquare,Board, andPiece classes, it's time to create different classes for every piece type. Each piece will have the mainPiece class as its parent class:

# /* Pawn.pyimport pygamefrom data.classes.Piece import Piececlass Pawn(Piece):    def __init__(self, pos, color, board):        super().__init__(pos, color, board)        img_path = 'data/imgs/' + color[0] + '_pawn.png'        self.img = pygame.image.load(img_path)        self.img = pygame.transform.scale(self.img, (board.tile_width - 35, board.tile_height - 35))        self.notation = ' '    def get_possible_moves(self, board):        output = []        moves = []        # move forward        if self.color == 'white':            moves.append((0, -1))            if not self.has_moved:                moves.append((0, -2))        elif self.color == 'black':            moves.append((0, 1))            if not self.has_moved:                moves.append((0, 2))        for move in moves:            new_pos = (self.x, self.y + move[1])            if new_pos[1] < 8 and new_pos[1] >= 0:                output.append(                    board.get_square_from_pos(new_pos)                )        return output    def get_moves(self, board):        output = []        for square in self.get_possible_moves(board):            if square.occupying_piece != None:                break            else:                output.append(square)        if self.color == 'white':            if self.x + 1 < 8 and self.y - 1 >= 0:                square = board.get_square_from_pos(                    (self.x + 1, self.y - 1)                )                if square.occupying_piece != None:                    if square.occupying_piece.color != self.color:                        output.append(square)            if self.x - 1 >= 0 and self.y - 1 >= 0:                square = board.get_square_from_pos(                    (self.x - 1, self.y - 1)                )                if square.occupying_piece != None:                    if square.occupying_piece.color != self.color:                        output.append(square)        elif self.color == 'black':            if self.x + 1 < 8 and self.y + 1 < 8:                square = board.get_square_from_pos(                    (self.x + 1, self.y + 1)                )                if square.occupying_piece != None:                    if square.occupying_piece.color != self.color:                        output.append(square)            if self.x - 1 >= 0 and self.y + 1 < 8:                square = board.get_square_from_pos(                    (self.x - 1, self.y + 1)                )                if square.occupying_piece != None:                    if square.occupying_piece.color != self.color:                        output.append(square)        return output    def attacking_squares(self, board):        moves = self.get_moves(board)        # return the diagonal moves         return [i for i in moves if i.x != self.x]

Here is our code for thePawn pieces, whether it is black or white. As you notice, we haveget_moves() andattacking_square() functions here in thePawn class, just like the functions we have in thePiece class but given with a different script. It is because pawn pieces are basically allowed to move 1 step at a time away from their team position. A pawn also has 3 possible moves; the pawn can move up to 2 tiles from its starting position only, can move 1 step forward at a time, and can capture a piece 1 diagonally step at a time.

As we noticed, we have another function which is theget_possible_moves(). As of its name, it gets all the possible moves of a piece base on the current state of the board.

Now let's move to do the other codes for other pieces.

Code forKnight.py:

# /* Kinght.pyimport pygamefrom data.classes.Piece import Piececlass Knight(Piece):    def __init__(self, pos, color, board):        super().__init__(pos, color, board)        img_path = 'data/imgs/' + color[0] + '_knight.png'        self.img = pygame.image.load(img_path)        self.img = pygame.transform.scale(self.img, (board.tile_width - 20, board.tile_height - 20))        self.notation = 'N'    def get_possible_moves(self, board):        output = []        moves = [            (1, -2),            (2, -1),            (2, 1),            (1, 2),            (-1, 2),            (-2, 1),            (-2, -1),            (-1, -2)        ]        for move in moves:            new_pos = (self.x + move[0], self.y + move[1])            if (                new_pos[0] < 8 and                new_pos[0] >= 0 and                 new_pos[1] < 8 and                 new_pos[1] >= 0            ):                output.append([                    board.get_square_from_pos(                        new_pos                    )                ])        return output

Code forBishop.py:

# /* Bishop.pyimport pygamefrom data.classes.Piece import Piececlass Bishop(Piece):    def __init__(self, pos, color, board):        super().__init__(pos, color, board)        img_path = 'data/imgs/' + color[0] + '_bishop.png'        self.img = pygame.image.load(img_path)        self.img = pygame.transform.scale(self.img, (board.tile_width - 20, board.tile_height - 20))        self.notation = 'B'    def get_possible_moves(self, board):        output = []        moves_ne = []        for i in range(1, 8):            if self.x + i > 7 or self.y - i < 0:                break            moves_ne.append(board.get_square_from_pos(                (self.x + i, self.y - i)            ))        output.append(moves_ne)        moves_se = []        for i in range(1, 8):            if self.x + i > 7 or self.y + i > 7:                break            moves_se.append(board.get_square_from_pos(                (self.x + i, self.y + i)            ))        output.append(moves_se)        moves_sw = []        for i in range(1, 8):            if self.x - i < 0 or self.y + i > 7:                break            moves_sw.append(board.get_square_from_pos(                (self.x - i, self.y + i)            ))        output.append(moves_sw)        moves_nw = []        for i in range(1, 8):            if self.x - i < 0 or self.y - i < 0:                break            moves_nw.append(board.get_square_from_pos(                (self.x - i, self.y - i)            ))        output.append(moves_nw)        return output

Code forRook.py:

# /* Rook.pyimport pygamefrom data.classes.Piece import Piececlass Rook(Piece):    def __init__(self, pos, color, board):        super().__init__(pos, color, board)        img_path = 'data/imgs/' + color[0] + '_rook.png'        self.img = pygame.image.load(img_path)        self.img = pygame.transform.scale(self.img, (board.tile_width - 20, board.tile_height - 20))        self.notation = 'R'    def get_possible_moves(self, board):        output = []        moves_north = []        for y in range(self.y)[::-1]:            moves_north.append(board.get_square_from_pos(                (self.x, y)            ))        output.append(moves_north)        moves_east = []        for x in range(self.x + 1, 8):            moves_east.append(board.get_square_from_pos(                (x, self.y)            ))        output.append(moves_east)        moves_south = []        for y in range(self.y + 1, 8):            moves_south.append(board.get_square_from_pos(                (self.x, y)            ))        output.append(moves_south)        moves_west = []        for x in range(self.x)[::-1]:            moves_west.append(board.get_square_from_pos(                (x, self.y)            ))        output.append(moves_west)        return output

Code forQueen.py:

# /* Queen.pyimport pygamefrom data.classes.Piece import Piececlass Queen(Piece):    def __init__(self, pos, color, board):        super().__init__(pos, color, board)        img_path = 'data/imgs/' + color[0] + '_queen.png'        self.img = pygame.image.load(img_path)        self.img = pygame.transform.scale(self.img, (board.tile_width - 20, board.tile_height - 20))        self.notation = 'Q'    def get_possible_moves(self, board):        output = []        moves_north = []        for y in range(self.y)[::-1]:            moves_north.append(board.get_square_from_pos(                (self.x, y)            ))        output.append(moves_north)        moves_ne = []        for i in range(1, 8):            if self.x + i > 7 or self.y - i < 0:                break            moves_ne.append(board.get_square_from_pos(                (self.x + i, self.y - i)            ))        output.append(moves_ne)        moves_east = []        for x in range(self.x + 1, 8):            moves_east.append(board.get_square_from_pos(                (x, self.y)            ))        output.append(moves_east)        moves_se = []        for i in range(1, 8):            if self.x + i > 7 or self.y + i > 7:                break            moves_se.append(board.get_square_from_pos(                (self.x + i, self.y + i)            ))        output.append(moves_se)        moves_south = []        for y in range(self.y + 1, 8):            moves_south.append(board.get_square_from_pos(                (self.x, y)            ))        output.append(moves_south)        moves_sw = []        for i in range(1, 8):            if self.x - i < 0 or self.y + i > 7:                break            moves_sw.append(board.get_square_from_pos(                (self.x - i, self.y + i)            ))        output.append(moves_sw)        moves_west = []        for x in range(self.x)[::-1]:            moves_west.append(board.get_square_from_pos(                (x, self.y)            ))        output.append(moves_west)        moves_nw = []        for i in range(1, 8):            if self.x - i < 0 or self.y - i < 0:                break            moves_nw.append(board.get_square_from_pos(                (self.x - i, self.y - i)            ))        output.append(moves_nw)        return output

Code forKing.py:

# /* King.pyimport pygamefrom data.classes.Piece import Piececlass King(Piece):    def __init__(self, pos, color, board):        super().__init__(pos, color, board)        img_path = 'data/imgs/' + color[0] + '_king.png'        self.img = pygame.image.load(img_path)        self.img = pygame.transform.scale(self.img, (board.tile_width - 20, board.tile_height - 20))        self.notation = 'K'    def get_possible_moves(self, board):        output = []        moves = [            (0,-1), # north            (1, -1), # ne            (1, 0), # east            (1, 1), # se            (0, 1), # south            (-1, 1), # sw            (-1, 0), # west            (-1, -1), # nw        ]        for move in moves:            new_pos = (self.x + move[0], self.y + move[1])            if (                new_pos[0] < 8 and                new_pos[0] >= 0 and                 new_pos[1] < 8 and                 new_pos[1] >= 0            ):                output.append([                    board.get_square_from_pos(                        new_pos                    )                ])        return output    def can_castle(self, board):        if not self.has_moved:            if self.color == 'white':                queenside_rook = board.get_piece_from_pos((0, 7))                kingside_rook = board.get_piece_from_pos((7, 7))                if queenside_rook != None:                    if not queenside_rook.has_moved:                        if [                            board.get_piece_from_pos((i, 7)) for i in range(1, 4)                        ] == [None, None, None]:                            return 'queenside'                if kingside_rook != None:                    if not kingside_rook.has_moved:                        if [                            board.get_piece_from_pos((i, 7)) for i in range(5, 7)                        ] == [None, None]:                            return 'kingside'            elif self.color == 'black':                queenside_rook = board.get_piece_from_pos((0, 0))                kingside_rook = board.get_piece_from_pos((7, 0))                if queenside_rook != None:                    if not queenside_rook.has_moved:                        if [                            board.get_piece_from_pos((i, 0)) for i in range(1, 4)                        ] == [None, None, None]:                            return 'queenside'                if kingside_rook != None:                    if not kingside_rook.has_moved:                        if [                            board.get_piece_from_pos((i, 0)) for i in range(5, 7)                        ] == [None, None]:                            return 'kingside'    def get_valid_moves(self, board):        output = []        for square in self.get_moves(board):            if not board.is_in_check(self.color, board_change=[self.pos, square.pos]):                output.append(square)        if self.can_castle(board) == 'queenside':            output.append(                board.get_square_from_pos((self.x - 2, self.y))            )        if self.can_castle(board) == 'kingside':            output.append(                board.get_square_from_pos((self.x + 2, self.y))            )        return output

Let's finish the game by adding code inmain.py that runs our whole game:

import pygamefrom data.classes.Board import Boardpygame.init()WINDOW_SIZE = (600, 600)screen = pygame.display.set_mode(WINDOW_SIZE)board = Board(WINDOW_SIZE[0], WINDOW_SIZE[1])def draw(display):display.fill('white')board.draw(display)pygame.display.update()if __name__ == '__main__':running = Truewhile running:mx, my = pygame.mouse.get_pos()for event in pygame.event.get():# Quit the game if the user presses the close buttonif event.type == pygame.QUIT:running = Falseelif event.type == pygame.MOUSEBUTTONDOWN:        # If the mouse is clickedif event.button == 1:board.handle_click(mx, my)if board.is_in_checkmate('black'): # If black is in checkmateprint('White wins!')running = Falseelif board.is_in_checkmate('white'): # If white is in checkmateprint('Black wins!')running = False# Draw the boarddraw(screen)

As you see above, we hadscreen andboard variable, which has pretty similar arguments but not really.

Thescreen handles the rendering of the chess board on the screen so we can see what's happening in theboard. The codepygame.display.set_mode(WINDOW_SIZE) creates the game window.

While we use theboard for making and handling tiles, tile positions, and what piece a chess square has. As you remember, in theBoard class code, we give it two arguments: the game window's length and width.

To keep the game running, we give it awhile loop that runs as long as the value ofrunning isTrue.

Themx, my = pygame.mouse.get_pos() locates the current position of your mouse as long as it's inside the game window. If you addprint(mx, my) below this code, you'll see the current mouse position, and its value changes every time you hover it inside the window.

Theevent.type == pygame.MOUSEBUTTONDOWN catches every click you make. To identify if a player is making a move, every time it catches a player doing a click, the current position of the mouse we get frompygame.mouse.get_pos() will be sent inBoard.handle_click(), and process your click back there.

Ok, now let's try this game. If it's working so in your terminal, move to the directory where ourMain.py file was saved, then run theMain.py. Once you run the file, the game will start immediately:

Start clicking on the pieces that can move, and you'll see available moves:

Conclusion

To simplify it, always remember that the chess game has two main parts, the board, and the pieces.

The board is in charge of every tile's name & position and the rules of the game, while the piece classes take care of moves and attacks for every piece.

To make the board, you should have aSquare class that creates the handling chess tiles that also mind the piece it contains, and another class calledBoard, which contains the game rules. We also need to do class for every chess piece, from thePawn to theKing. And that's how you make a chess game with Python using only classes andpygame!

You can check the complete codehere.

Here is a list of other pygame tutorials:

Happy coding ♥

Found the article interesting? You'll love ourPython Code Generator! Give AI a chance to do the heavy lifting for you. Check it out!

View Full Code Auto-Generate My Code
Sharing is caring!



Read Also


How to Make a Snake Game in Python
How to Make a Planet Simulator with PyGame in Python
How to Make a Checkers Game with Pygame in Python

Comment panel

    Got a coding query or need some guidance before you comment? Check out thisPython Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!





    Ethical Hacking with Python EBook - Topic - Top


    Join 50,000+ Python Programmers & Enthusiasts like you!



    Tags


    New Tutorials

    Popular Tutorials


    Ethical Hacking with Python EBook - Topic - Bottom






    Claim your Free Chapter!

    Download a Completely Free Practical Python PDF Processing Chapter.

    See how the book can help you build handy PDF Processing tools!



    [8]ページ先頭

    ©2009-2025 Movatter.jp