Movatterモバイル変換


[0]ホーム

URL:


How to Make a Checkers Game with Pygame in Python

Learn how to build a checkers game from scratch with the help of Pygame library in Python.
  · 21 min read · Updated may 2023 ·Game Development

Before we get started, have you tried our newPython Code Assistant? It's like having an expert coder at your fingertips. Check it out!

Checkers is a classic two-player board game that has been enjoyed by people of all ages for generations. It is a strategy game that requires players to move their pieces across the board, capturing their opponent's pieces and ultimately trying to reach the other end of the board with one of their pieces to become a king.

In this article, we will be making a Checkers game in Python. Our goal is to provide an overview of the game's codebase, breaking it down into several classes that work together to provide the game's functionality. We will cover the installation and setup process, the main class, the game class, the board class, the tile class, the piece class, the pawn class, and the king class.

We also have a tutorial onmaking a chess game, make sure to check it out if you're interested!

Our focus will be on explaining the code in a way that is easy to understand for new programmers. So, without further ado, let's get started!

Table of Contents:

Setup and Installation

Before we can begin working on our game, we need to make sure we have all the necessary tools installed on our computer. The first thing we need to do is to make sure we have Python installed. You can download Python from theofficial website.

Also, we need to installthe Pygame library. Pygame is a set of Python modules that allow us to create games and multimedia applications. To install Pygame, open up the command prompt/terminal and type the following command:

$ pip install pygame

We can now create the directory for our Checkers game. Open up your file explorer and navigate to the directory where you want to create your game, and create the"Checkers" folder.

Inside theCheckers directory, we need to create several Python files. Namely"Main.py", "Game.py", "Board.py", "Tile.py", "Piece.py", "Pawn.py", and "King.py".

Finally, we need to create a folder named"images" inside theCheckers directory. This folder will contain the images for our game pieces. Inside the folder, place the following image files:black-pawn.png,black-king.png,red-pawn.png, andred-king.png. You canaccess the pictures here.

The structure of our game should look like this:

TheMain class

TheMain class is the starting point of our game. It sets up the game window, initializes the game objects, and runs the game loop.

First, we import the necessary modules and classes from the other files in our Checkers game. Then, we initialize Pygame with thepygame.init() function:

# /* Main.pyimport pygamefrom Board import Boardfrom Game import Gamepygame.init()

TheCheckers class has an__init__() method that takes in ascreen parameter which initializes several class attributes, includingscreen,running, andFPS.screen represents the game window,running is a boolean that determines whether the game is still running, andFPS is a Pygame clock that limits the game to a certain frame rate.

class Checkers:    def __init__(self, screen):        self.screen = screen        self.running = True        self.FPS = pygame.time.Clock()

Below the__init__() function, add two more functions, and name them,_draw() andmain().

The_draw() method is a helper method that takes in aboard parameter and draws the game board onto the screen using theboard.draw() method. It then updates the display usingpygame.display.update().

    def _draw(self, board):        board.draw(self.screen)        pygame.display.update()

Themain() method is the main game loop. It takes inwindow_width andwindow_height parameters, which are the dimensions of the game window.board_size is set to 8, which represents the size of the game board.tile_width andtile_height are calculated based on the size of the window and the size of the board:

    def main(self, window_width, window_height):        board_size = 8        tile_width, tile_height = window_width // board_size, window_height // board_size        board = Board(tile_width, tile_height, board_size)        game = Game()        while self.running:            game.check_jump(board)            for self.event in pygame.event.get():                if self.event.type == pygame.QUIT:                    self.running = False                if not game.is_game_over(board):                    if self.event.type == pygame.MOUSEBUTTONDOWN:                        board.handle_click(self.event.pos)                else:                    game.message()                    self.running = False            self._draw(board)            self.FPS.tick(60)

board is then initialized with theBoard class, passing in thetile_width,tile_height, andboard_size parameters.game is also initialized with theGame class.

Thewhile loop runs as long asself.running isTrue. Within the loop, we callgame.check_jump(board) to check for any available jumps on the board. We then loop through the events in the Pygame event queue usingpygame.event.get(). If the event type ispygame.QUIT (when the exit button was clicked), we setself.running toFalse to exit the game.

If the game is not over, we check if the user has clicked on a tile on the board usingboard.handle_click(). If the game is over, we display a message usinggame.message() and exit the game loop by settingself.running toFalse.

We then call the_draw() method to draw the game board on the screen and update the display usingpygame.display.update(). Finally, we useself.FPS.tick(60) to limit the game to 60 frames per second.

Below the class, let's call the game with themain() function.

if __name__ == "__main__":    window_size = (640, 640)    screen = pygame.display.set_mode(window_size)    pygame.display.set_caption("Checkers")    checkers = Checkers(screen)    checkers.main(window_size[0], window_size[1])

We set thewindow_size to(640, 640), create a Pygame display screen with this size usingpygame.display.set_mode(window_size), and set the window caption to "Checkers" usingpygame.display.set_caption("Checkers"). Then, we create an instance of theCheckers class calledcheckers, passing in thescreen as a parameter. Then, we call themain() method oncheckers, passing in thewindow_size dimensions. This code sets up the Pygame display screen, creates an instance of theCheckers class, and starts the game loop by calling themain() method.

TheGame class

TheGame class contains methods for checking if the game is over, checking if there is a jump available, and displaying the winner of the game:

# /* Game.pyclass Game:    def __init__(self):        self.winner = None

The__init__() method initializes thewinner variable toNone.

    # checks if both colors still has a piece    def check_piece(self, board):        red_piece = 0        black_piece = 0        for y in range(board.board_size):            for x in range(board.board_size):                tile = board.get_tile_from_pos((x, y))                if tile.occupying_piece != None:                    if tile.occupying_piece.color == "red":                        red_piece += 1                    else:                        black_piece += 1        return red_piece, black_piece

Thecheck_piece() method iterates over each tile on the board and checks if it contains an occupying piece. If it does, it adds to thered_piece count if the piece color is "red", or to theblack_piece count if the piece color is "black". It then returns a tuple containing thered_piece andblack_piece counts.

    def is_game_over(self, board):        red_piece, black_piece = self.check_piece(board)        if red_piece == 0 or black_piece == 0:            self.winner = "red" if red_piece > black_piece else "black"            return True        else:            return False

Theis_game_over() method calls thecheck_piece() method to get the current piece count for each color. If one color has no pieces left, the other color is declared the winner, and the method returnsTrue. Otherwise, the method returnsFalse.

    def check_jump(self, board):        piece = None        for tile in board.tile_list:            if tile.occupying_piece != None:                piece = tile.occupying_piece                if len(piece.valid_jumps()) != 0 and board.turn == piece.color:                    board.is_jump = True                    break                else:                    board.is_jump = False        if board.is_jump:            board.selected_piece = piece            board.handle_click(piece.pos)        return board.is_jump

Thecheck_jump() method checks if there is a piece that can make a jump. If there is, it sets theis_jump attribute of the board toTrue, sets theselected_piece to the first piece that can make a jump, and returnsTrue. If there isn't, it sets theis_jump attribute of the board toFalse and returnsFalse. Since we're creating the traditional Checkers where jump moves can't be skipped, thecheck_jump() will force the users to jump.

    def message(self):        print(f"{self.winner} Wins!!")

The message method simply prints out the winner of the game.

TheBoard class

TheBoard class defines the behavior of a checkers game board and how it responds to user input. It has an__init__() method that initializes the board with the specifiedtile_width,tile_height, andboard_size. It also initializes various variables such asselected_piece,turn, andis_jump:

# /* Board.pyimport pygamefrom Tile import Tilefrom Pawn import Pawnclass Board:    def __init__(self,tile_width, tile_height, board_size):        self.tile_width = tile_width        self.tile_height = tile_height        self.board_size = board_size        self.selected_piece = None        self.turn = "black"        self.is_jump = False        self.config = [            ['', 'bp', '', 'bp', '', 'bp', '', 'bp'],            ['bp', '', 'bp', '', 'bp', '', 'bp', ''],            ['', 'bp', '', 'bp', '', 'bp', '', 'bp'],            ['', '', '', '', '', '', '', ''],            ['', '', '', '', '', '', '', ''],            ['rp', '', 'rp', '', 'rp', '', 'rp', ''],            ['', 'rp', '', 'rp', '', 'rp', '', 'rp'],            ['rp', '', 'rp', '', 'rp', '', 'rp', '']        ]        self.tile_list = self._generate_tiles()        self._setup()

Theself.config contains the starting setup of the board for our game. Thetile_list calls the_generate_tiles() method that creates each tile for the board, and the_setup() method sets the starting position of pawns in the game base onconfig.

    def _generate_tiles(self):        output = []        for y in range(self.board_size):            for x in range(self.board_size):                output.append(                    Tile(x,  y, self.tile_width, self.tile_height)                )        return output    def get_tile_from_pos(self, pos):        for tile in self.tile_list:            if (tile.x, tile.y) == (pos[0], pos[1]):                return tile

The_generate_tiles() method generates a list of tiles using the specified tile width, tile height, and board size. Theget_tile_from_pos() method returns the tile object at a given position.

    def _setup(self):        for y_ind, row in enumerate(self.config):            for x_ind, x in enumerate(row):                tile = self.get_tile_from_pos((x_ind, y_ind))                if x != '':                    if x[-1] == 'p':                        color = 'red' if x[0] == 'r' else 'black'                        tile.occupying_piece = Pawn(x_ind, y_ind, color, self)

The_setup() method sets up the initial board configuration by iterating through theself.config list, which represents the starting positions of the pieces, and setting theoccupying_piece attribute of the corresponding tile object to aPawn object.

    def handle_click(self, pos):        x, y = pos[0], pos[-1]        if x >= self.board_size or y >= self.board_size:            x = x // self.tile_width            y = y // self.tile_height        clicked_tile = self.get_tile_from_pos((x, y))        if self.selected_piece is None:            if clicked_tile.occupying_piece is not None:                if clicked_tile.occupying_piece.color == self.turn:                    self.selected_piece = clicked_tile.occupying_piece        elif self.selected_piece._move(clicked_tile):            if not self.is_jump:                self.turn = 'red' if self.turn == 'black' else 'black'            else:                if len(clicked_tile.occupying_piece.valid_jumps()) == 0:                    self.turn = 'red' if self.turn == 'black' else 'black'        elif clicked_tile.occupying_piece is not None:            if clicked_tile.occupying_piece.color == self.turn:                self.selected_piece = clicked_tile.occupying_piece

Thehandle_click() method takes a position (pos) as an argument, which represents the pixel coordinates of the location on the game board that were clicked by the player.

First, the method extracts thex andy coordinates from thepos argument. If the coordinates are outside the board size, the method calculates which tile was clicked based on the position of the click relative to the size of each tile. Next, the method retrieves theTile object that was clicked by calling theget_tile_from_pos() method, passing in the (x,y) coordinates of the clicked tile.

If there is noselected_piece currently, the method checks if the clicked tile has aPawn object on it and whether thatPawn object belongs to the current player's turn. If there is aPawn object on the clicked tile and it belongs to the current player's turn, theselected_piece attribute of theBoard object is set to thatPawn object.

If there is aselected_piece already, the method attempts to move thePawn object to the clicked tile by calling the_move() method of thePawn object, passing in theTile object as an argument. If the move is successful, theturn attribute of theBoard object is updated to reflect the next player's turn.

If the move is a jump, theis_jump attribute of theBoard object is set toTrue. The method then checks if there are any more valid jumps available for the samePawn object, by calling thevalid_jumps() method of thePawn object. If there are no more valid jumps, theturn attribute of theBoard object is updated to reflect the next player's turn.

If the clicked tile does have aPawn object on it and belongs to the current player's turn, theselected_piece attribute of theBoard object is set to thePawn object on the clicked tile.

    def draw(self, display):        if self.selected_piece is not None:            self.get_tile_from_pos(self.selected_piece.pos).highlight = True            if not self.is_jump:                for tile in self.selected_piece.valid_moves():                    tile.highlight = True            else:                for tile in self.selected_piece.valid_jumps():                    tile[0].highlight = True        for tile in self.tile_list:            tile.draw(display)

Thedraw() method is used to draw the board and the pieces. It is called to update the display with any changes made by thehandle_click() method. If a piece is selected, the tile it is on and its valid moves or jumps are highlighted.

TheTile class

TheTile class represents a single tile on the game board.

# /* Tile.pyimport pygameclass Tile:    def __init__(self, x, y, tile_width, tile_height):        self.x = x        self.y = y        self.pos = (x, y)        self.tile_width = tile_width        self.tile_height = tile_height        self.abs_x = x * tile_width        self.abs_y = y * tile_height        self.abs_pos = (self.abs_x, self.abs_y)        self.color = 'light' if (x + y) % 2 == 0 else 'dark'        self.draw_color = (220, 189, 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.tile_width,            self.tile_height        )

The__init__() method initializes the object's properties, including thex andy coordinates of the tile, itswidth andheight, its absolute position (abs_x andabs_y) and position tuple (abs_pos).

Thecolor property is determined by whether the sum of the tile'sx andy coordinates are even or odd. If it is even, the tile is a light color, otherwise, it is dark. Thedraw_color andhighlight_color properties are tuples representing RGB values for the tile's fill color and highlight color, respectively.

Theoccupying_piece property is set toNone by default but can be assigned aPiece object if there is a piece occupying the tile.

Let's also have theget_coord() anddraw() methods:

    def get_coord(self):        columns = 'abcdefgh'        return columns[self.x] + str(self.y + 1)    def draw(self, display):        if self.highlight:            pygame.draw.rect(display, self.highlight_color, self.rect)        else:            pygame.draw.rect(display, self.draw_color, self.rect)        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)

Theget_coord() method returns a string representing the tile's coordinate in standard chess notation, using the lettered columns and numbered rows.

Thedraw() method draws the tile on the screen usingpygame.draw.rect, using thedraw_color property as the fill color. If the highlight property is set toTrue, the tile is drawn with thehighlight_color instead. If there is anoccupying_piece, the piece's image is blitted onto the center of the tile usingpygame.Surface.blit(). The image is first centered using theget_rect() andcenter properties of the piece's image.

ThePiece class

ThePiece class is a parent class for all checker pieces in the game. It contains common attributes and methods that are shared among all pieces such as theposition of the piece on the board, itscolor, and the ability to move to certain tiles on the board based on the game rules.

# /* Piece.pyimport pygameclass Piece:    def __init__(self, x, y, color, board):        self.x = x        self.y = y        self.pos = (x, y)        self.board = board        self.color = color

For thePawn andKing classes specifically, they override some of the methods of thePiece class to account for the specific rules that apply to these pieces in the game of checkers. For example, thevalid_moves() method of thePawn class returns the valid moves that a pawn can make on the board based on the game rules for a pawn (can move or jump forward/against their starting side only). Similarly, thevalid_moves() method of theKing class returns the valid moves that a king can make on the board based on the game rules for a king (can move or jump forward or backward).

In addition, thePawn class includes a specific implementation for pawn promotion, which occurs when a pawn reaches the opposite end of the board. TheKing class does not require a specific implementation for promotion since kings are already the highest-ranking pieces in the game.

Here inPiece class, we also define themove() method for both pieces.

    def _move(self, tile):        for i in self.board.tile_list:            i.highlight = False        # ordinary move/s        if tile in self.valid_moves() and not self.board.is_jump:            prev_tile = self.board.get_tile_from_pos(self.pos)            self.pos, self.x, self.y = tile.pos, tile.x, tile.y            prev_tile.occupying_piece = None            tile.occupying_piece = self            self.board.selected_piece = None            self.has_moved = True            # Pawn promotion            if self.notation == 'p':                if self.y == 0 or self.y == 7:                    from King import King                    tile.occupying_piece = King(                        self.x, self.y, self.color, self.board                    )            return True        # jump move/s        elif self.board.is_jump:            for move in self.valid_jumps():                if tile in move:                    prev_tile = self.board.get_tile_from_pos(self.pos)                    jumped_piece = move[-1]                    self.pos, self.x, self.y = tile.pos, tile.x, tile.y                    prev_tile.occupying_piece = None                    jumped_piece.occupying_piece = None                    tile.occupying_piece = self                    self.board.selected_piece = None                    self.has_moved = True                    # Pawn promotion                    if self.notation == 'p':                        if self.y == 0 or self.y == 7:                            from King import King                            tile.occupying_piece = King(                                self.x, self.y, self.color, self.board                            )                    return True        else:            self.board.selected_piece = None            return False

The_move() method takes in atile object representing the new position for the piece. It first checks if the new position is a valid move for the piece by calling thevalid_moves() method. If the move is valid and no jump is occurring, the method updates the position of the piece and theoccupying_piece attribute of the relevant tiles to reflect the new position of the piece. If the piece is a pawn and reaches the last row, it will be promoted to a king.

If a jump is occurring, the_move() method checks if the new position is a valid jump move by calling thevalid_jumps() method. If the move is valid, the method updates the position of the piece, theoccupying_piece attribute of the relevant tiles, and removes thejumped_piece (opponent's piece that caused the jump). The method checks if the pawn has reached the last row and promotes it to a king if necessary.

If the move is not valid, the method simply returnsFalse.

ThePawn class

ThePawn class is a subclass of thePiece class, pawns are the pieces used from the start of the game. The class has an__init__() method that calls the parent's__init__() method to initialize the object'sx andy position,color, andboard. It also loads an image for the pawn and assigns it a notation of 'p':

# /* Pawn.pyimport pygamefrom Piece import Piececlass Pawn(Piece):    def __init__(self, x, y, color, board):        super().__init__(x, y, color, board)        img_path = f'images/{color}-pawn.png'        self.img = pygame.image.load(img_path)        self.img = pygame.transform.scale(self.img, (board.tile_width, board.tile_height))        self.notation = 'p'

Let's add three more methods; the_possible_moves(), thevalid_moves(), and thevalid_jumps() methods:

    def _possible_moves(self):        # (x, y) move for left and right        if self.color == "red":            possible_moves = ((-1, -1), (+1, -1))         else:            possible_moves = ((-1, +1), (+1, +1))        return possible_moves

The_possible_moves() method returns a tuple of possible moves that a pawn can make in the form of(x, y) coordinates for left and right directions. The possible moves depend on the color of the pawn. For example, a red pawn can move left and right in the (-1, -1) and (+1, -1) directions respectively.

    def valid_moves(self):        tile_moves = []        moves = self._possible_moves()        for move in moves:            tile_pos = (self.x + move[0], self.y + move[-1])            if tile_pos[0] < 0 or tile_pos[0] > 7 or tile_pos[-1] < 0 or tile_pos[-1] > 7:                pass            else:                tile = self.board.get_tile_from_pos(tile_pos)                if tile.occupying_piece == None:                    tile_moves.append(tile)        return tile_moves

Thevalid_moves() method checks all possible moves for a pawn on the board and returns a list of valid moves. It does so by iterating through the possible moves and checking if each move results in a valid tile position on the board that does not contain any occupying piece. If the tile position is valid and empty, it appends the tile object to the list of valid moves.

    def valid_jumps(self):        tile_jumps = []        moves = self._possible_moves()        for move in moves:            tile_pos = (self.x + move[0], self.y + move[-1])            if tile_pos[0] < 0 or tile_pos[0] > 7 or tile_pos[-1] < 0 or tile_pos[-1] > 7:                pass            else:                tile = self.board.get_tile_from_pos(tile_pos)                if self.board.turn == self.color:                    if tile.occupying_piece != None and tile.occupying_piece.color != self.color:                        next_pos = (tile_pos[0] + move[0], tile_pos[-1] + move[-1])                        next_tile = self.board.get_tile_from_pos(next_pos)                        if next_pos[0] < 0 or next_pos[0] > 7 or next_pos[-1] < 0 or next_pos[-1] > 7:                            pass                        else:                            if next_tile.occupying_piece == None:                                tile_jumps.append((next_tile, tile))        return tile_jumps

Thevalid_jumps() method returns a list of tuples that represents a valid jump move for the pawn. It checks for jumps in a similar way tovalid_moves() but also checks for a tile position with an opposing piece. If such a tile position exists, it checks the next position in the direction of the jump to ensure that it is empty. If the next position is empty, it appends a tuple containing the current tile and the tile that contains the opponent's piece to the list of valid jumps.

TheKing class

TheKing class is very similar to thePawn class in terms of its structure and methods. However, the_possible_moves() method now returns all the diagonal directions, as a king can move diagonally in any direction.

Thevalid_moves() method returns a list of tiles that the king can move to, which are tiles that are empty and within the boundaries of the board. Thevalid_jumps() method returns a list of tuples, where each tuple contains two tiles, representing a jump that the king can make over an opposing piece. The implementation ofvalid_jumps() is very similar to that of thePawn class, with the only difference being that theKing can jump over opposing pieces in any diagonal direction:

# /* King.pyimport pygamefrom Piece import Piececlass King(Piece):    def __init__(self, x, y, color, board):        super().__init__(x, y, color, board)        img_path = f'images/{color}-king.png'        self.img = pygame.image.load(img_path)        self.img = pygame.transform.scale(self.img, (board.tile_width, board.tile_height))        self.notation = 'k'    def _possible_moves(self):        possible_moves = ((-1, -1), (+1, -1), (-1, +1), (+1, +1))        return possible_moves    def valid_moves(self):        tile_moves = []        moves = self._possible_moves()        for move in moves:            tile_pos = (self.x + move[0], self.y + move[-1])            if tile_pos[0] < 0 or tile_pos[0] > 7 or tile_pos[-1] < 0 or tile_pos[-1] > 7:                pass            else:                tile = self.board.get_tile_from_pos(tile_pos)                if tile.occupying_piece == None:                    tile_moves.append(tile)        return tile_moves    def valid_jumps(self):        tile_jumps = []        moves = self._possible_moves()        for move in moves:            tile_pos = (self.x + move[0], self.y + move[-1])            if tile_pos[0] < 0 or tile_pos[0] > 7 or tile_pos[-1] < 0 or tile_pos[-1] > 7:                pass            else:                tile = self.board.get_tile_from_pos(tile_pos)                if self.board.turn == self.color:                    if tile.occupying_piece != None and tile.occupying_piece.color != self.color:                        next_pos = (tile_pos[0] + move[0], tile_pos[-1] + move[-1])                        next_tile = self.board.get_tile_from_pos(next_pos)                        if next_pos[0] < 0 or next_pos[0] > 7 or next_pos[-1] < 0 or next_pos[-1] > 7:                            pass                        else:                            if next_tile.occupying_piece == None:                                tile_jumps.append((next_tile, tile))        return tile_jumps

And now the game is done! You can try the game by running theMain.py on your terminal:

$ python Main.py

Here are some of the game snapshots:

Starting the game:

Pawn's move:

Pawn's Jump:

Pawn Promotion:

King's moves:

King's Jump:

Conclusion

In this tutorial, we've explored how to implement a checkers game in Python using the Pygame library. We've covered how to create the game board, pieces, and their movements, as well as the basic rules of the game.

Playing checkers is an enjoyable way to pass the time and test your strategy and critical thinking skills. We hope this article has been helpful in understanding the basics of checkers programming in Python. See you on the next one!

We also havea chess game tutorial, if you want to build one!

Get the complete codehere.

Here is a list of other pygame tutorials:

Happy coding ♥

Just finished the article? Why not take your Python skills a notch higher with ourPython Code Assistant? Check it out!

View Full Code Convert My Code
Sharing is caring!



Read Also


How to Build a Tic Tac Toe Game in Python
How to Make a Chess Game with Pygame in Python
How to Make a Tetris Game using 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

    CodingFleet - 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