Creating a Checkers Game with Python

Table of Contents

  1. Introduction
  2. Prerequisites
  3. Setup
  4. Creating the Board
  5. Placing the Pieces
  6. Moving the Pieces
  7. Validating Moves
  8. Capturing Opponent’s Pieces
  9. Game Loop
  10. Conclusion

Introduction

In this tutorial, we will be creating a checkers game using Python. Checkers is a classic board game played between two players on an 8x8 board. The goal is to capture all of your opponent’s pieces or block them from making any more moves. By the end of this tutorial, you will have a working checkers game that you can play against a friend or computer opponent.

Prerequisites

To follow along with this tutorial, you should have a basic understanding of Python programming concepts such as variables, functions, loops, and conditionals. Familiarity with object-oriented programming (OOP) will also be helpful.

Setup

Before we begin coding, let’s set up our development environment. We will be using Python 3 for this project.

  1. Install Python 3 on your system if you haven’t already. You can download it from the official Python website: https://www.python.org/downloads/

  2. Create a new directory for your checkers game project.

  3. Open your terminal or command prompt and navigate to the project directory.

  4. Create a virtual environment by running the following command:

    python3 -m venv venv
    
  5. Activate the virtual environment:

    • On macOS and Linux:

      source venv/bin/activate
      
    • On Windows:

      venv\Scripts\activate
      
  6. Now, we can proceed with creating our checkers game!

Creating the Board

Let’s start by creating the game board. We will represent the board using a 2D list, where each element of the list represents a square on the board. We will use the following symbols to represent different types of squares:

  • . for empty squares
  • B for black pieces
  • W for white pieces

Create a new file called checkers.py and add the following code: ```python # checkers.py

class CheckersGame:
    def __init__(self):
        self.board = [['.' for _ in range(8)] for _ in range(8)]

    def display_board(self):
        for row in self.board:
            print(' '.join(row))
``` Here, we define a `CheckersGame` class with an `__init__` method that initializes the game board as an 8x8 grid with empty squares.

The display_board method prints the current state of the board to the console.

Let’s write some code to test our board creation. Add the following code to the bottom of checkers.py: ```python # checkers.py

if __name__ == '__main__':
    game = CheckersGame()
    game.display_board()
``` Save the file and run it with the following command:
```bash
python checkers.py
``` You should see an empty 8x8 board displayed in the console.

Placing the Pieces

Now that we have our game board, let’s add the ability to place the pieces on the board. Black pieces will be represented by B and white pieces by W. We will define a starting setup where each player has 12 pieces.

Modify the __init__ method of the CheckersGame class as follows: ```python # checkers.py

class CheckersGame:
    def __init__(self):
        self.board = [['.' for _ in range(8)] for _ in range(8)]
        self.place_pieces()

    def display_board(self):
        for row in self.board:
            print(' '.join(row))

    def place_pieces(self):
        for row in range(3):
            for col in range(8):
                # Place black pieces in the top three rows
                if (row + col) % 2 == 1:
                    self.board[row][col] = 'B'

        for row in range(5, 8):
            for col in range(8):
                # Place white pieces in the bottom three rows
                if (row + col) % 2 == 1:
                    self.board[row][col] = 'W'
``` We have added a new method `place_pieces` that places the black and white pieces on the board according to the starting setup. The black pieces are placed in the top three rows, and the white pieces are placed in the bottom three rows.

Let’s run the game again to see the pieces on the board: bash python checkers.py You should now see the black and white pieces arranged in their starting positions on the board.

Moving the Pieces

Now, let’s implement the ability to move the pieces on the board. We will allow players to move their pieces diagonally forward one square at a time. We will represent the pieces with the following symbols:

  • b for black pieces
  • w for white pieces

Modify the CheckersGame class as follows: ```python # checkers.py

class CheckersGame:
    def __init__(self):
        self.board = [['.' for _ in range(8)] for _ in range(8)]
        self.place_pieces()
        self.current_player = 'B'

    def display_board(self):
        for row in self.board:
            print(' '.join(row))

    def place_pieces(self):
        for row in range(3):
            for col in range(8):
                # Place black pieces in the top three rows
                if (row + col) % 2 == 1:
                    self.board[row][col] = 'B'

        for row in range(5, 8):
            for col in range(8):
                # Place white pieces in the bottom three rows
                if (row + col) % 2 == 1:
                    self.board[row][col] = 'W'

    def make_move(self, start_pos, end_pos):
        start_row, start_col = start_pos
        end_row, end_col = end_pos

        piece = self.board[start_row][start_col]

        if piece == '.':
            print("No piece at the starting position.")
            return

        if self.current_player == 'B' and piece != 'b':
            print("Invalid move. It's not your turn.")
            return

        if self.current_player == 'W' and piece != 'w':
            print("Invalid move. It's not your turn.")
            return

        if piece == 'b' and end_row <= start_row:
            print("Invalid move. Pieces can only move forward.")
            return

        if piece == 'w' and end_row >= start_row:
            print("Invalid move. Pieces can only move forward.")
            return

        if abs(start_row - end_row) != 1 or abs(start_col - end_col) != 1:
            print("Invalid move. Pieces can only move diagonally one square at a time.")
            return

        self.board[start_row][start_col] = '.'
        self.board[end_row][end_col] = piece

        self.current_player = 'W' if self.current_player == 'B' else 'B'
``` We have added a `make_move` method to the `CheckersGame` class. This method takes two parameters: `start_pos` and `end_pos`, which represent the starting and ending positions of the move. The method performs several checks to validate the move:
  • It checks if there is a piece at the starting position.
  • It checks if it’s the current player’s turn.
  • It checks if the piece is moving in the correct direction.
  • It checks if the move is diagonal and one square at a time.

If all the checks pass, the method updates the board to reflect the move and switches the current player.

To test the movement of pieces, add the following code to the bottom of checkers.py: ```python # checkers.py

if __name__ == '__main__':
    game = CheckersGame()
    game.display_board()

    print("Black player's move:")
    game.make_move((2, 1), (3, 2))
    game.display_board()

    print("White player's move:")
    game.make_move((5, 2), (4, 3))
    game.display_board()
``` Save the file and run it again:
```bash
python checkers.py
``` You should see the board before and after each move.

Validating Moves

Currently, our move validation is limited to basic checks. We need to implement additional logic to handle more complex moves such as capturing opponent’s pieces and enforcing mandatory captures.

We will update the make_move method to handle capturing opponent’s pieces. If a piece can capture an opponent’s piece, the move will be considered valid. We will use the following symbols to represent captured pieces:

  • B for captured black pieces
  • W for captured white pieces

Modify the CheckersGame class as follows: ```python # checkers.py

class CheckersGame:
    def __init__(self):
        self.board = [['.' for _ in range(8)] for _ in range(8)]
        self.place_pieces()
        self.current_player = 'B'

    def display_board(self):
        for row in self.board:
            print(' '.join(row))

    def place_pieces(self):
        for row in range(3):
            for col in range(8):
                # Place black pieces in the top three rows
                if (row + col) % 2 == 1:
                    self.board[row][col] = 'B'

        for row in range(5, 8):
            for col in range(8):
                # Place white pieces in the bottom three rows
                if (row + col) % 2 == 1:
                    self.board[row][col] = 'W'

    def make_move(self, start_pos, end_pos):
        start_row, start_col = start_pos
        end_row, end_col = end_pos

        piece = self.board[start_row][start_col]

        if piece == '.':
            print("No piece at the starting position.")
            return

        if self.current_player == 'B' and piece != 'b':
            print("Invalid move. It's not your turn.")
            return

        if self.current_player == 'W' and piece != 'w':
            print("Invalid move. It's not your turn.")
            return

        if piece == 'b' and end_row <= start_row:
            print("Invalid move. Pieces can only move forward.")
            return

        if piece == 'w' and end_row >= start_row:
            print("Invalid move. Pieces can only move forward.")
            return

        if abs(start_row - end_row) != 1 or abs(start_col - end_col) != 1:
            # Check for capture
            if abs(start_row - end_row) == 2 and abs(start_col - end_col) == 2:
                middle_row = (start_row + end_row) // 2
                middle_col = (start_col + end_col) // 2
                middle_piece = self.board[middle_row][middle_col]

                if middle_piece != '.' and piece.lower() != middle_piece.lower():
                    # Capture the opponent's piece
                    opponent_piece = 'B' if middle_piece == 'W' else 'W'
                    self.board[middle_row][middle_col] = opponent_piece

                    self.board[start_row][start_col] = '.'
                    self.board[end_row][end_col] = piece

                    self.current_player = 'W' if self.current_player == 'B' else 'B'

                    return

            print("Invalid move. Pieces can only move diagonally one square at a time.")
            return

        self.board[start_row][start_col] = '.'
        self.board[end_row][end_col] = piece

        self.current_player = 'W' if self.current_player == 'B' else 'B'
``` We have added a check for capturing an opponent's piece. If the move is diagonal and two squares away in both row and column, we calculate the position of the middle square and check if there is an opponent's piece in that square. If there is, we capture the piece by changing it to the corresponding opponent's piece.

Let’s test the capturing of opponent’s pieces. Update the code at the bottom of checkers.py as follows: ```python # checkers.py

if __name__ == '__main__':
    game = CheckersGame()
    game.display_board()

    print("Black player's move:")
    game.make_move((2, 1), (3, 2))
    game.display_board()

    print("White player's move (capturing):")
    game.make_move((5, 2), (3, 0))
    game.display_board()
``` Save the file and run it again:
```bash
python checkers.py
``` You should see the captured piece on the board after the white player's move.

Game Loop

Now that we have implemented the basic gameplay mechanics, let’s add a game loop that allows players to take turns until the game is over. We will also add logic to check if a player has won the game.

Modify the CheckersGame class as follows: ```python # checkers.py

class CheckersGame:
    def __init__(self):
        self.board = [['.' for _ in range(8)] for _ in range(8)]
        self.place_pieces()
        self.current_player = 'B'

    def display_board(self):
        for row in self.board:
            print(' '.join(row))

    def place_pieces(self):
        for row in range(3):
            for col in range(8):
                # Place black pieces in the top three rows
                if (row + col) % 2 == 1:
                    self.board[row][col] = 'B'

        for row in range(5, 8):
            for col in range(8):
                # Place white pieces in the bottom three rows
                if (row + col) % 2 == 1:
                    self.board[row][col] = 'W'

    def make_move(self, start_pos, end_pos):
        # move logic...

    def get_winner(self):
        black_pieces = 0
        white_pieces = 0

        for row in range(8):
            for col in range(8):
                if self.board[row][col].lower() == 'b':
                    black_pieces += 1
                elif self.board[row][col].lower() == 'w':
                    white_pieces += 1

        if black_pieces == 0:
            return 'White'

        if white_pieces == 0:
            return 'Black'

        return None

    def play_game(self):
        while True:
            self.display_board()

            print("Current player:", "Black" if self.current_player == 'B' else "White")

            start_pos = tuple(map(int, input("Enter the starting position (row, col): ").split(',')))
            end_pos = tuple(map(int, input("Enter the ending position (row, col): ").split(',')))

            self.make_move(start_pos, end_pos)

            winner = self.get_winner()
            if winner:
                print("Game over. The winner is:", winner)
                break
``` We have added a `get_winner` method that counts the remaining black and white pieces on the board and determines the winner based on the counts. If one of the players has no remaining pieces, the other player is declared the winner. If there are pieces of both players remaining, the game continues.

The play_game method implements the game loop. It displays the current state of the board, prompts the current player for their move, and checks for a winner after each move. If there is a winner, the loop breaks and the winner is announced.

To start the game, update the code at the bottom of checkers.py as follows: ```python # check