Skip to content

Commit 9cf9a1d

Browse files
committed
add checkers game tutorial
1 parent c0cc01f commit 9cf9a1d

File tree

14 files changed

+375
-0
lines changed

14 files changed

+375
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -278,5 +278,6 @@ This is a repository of all the tutorials of [The Python Code](https://www.thepy
278278
- [How to Build a Tic Tac Toe Game in Python](https://www.thepythoncode.com/article/make-a-tic-tac-toe-game-pygame-in-python). ([code](gui-programming/tictactoe-game))
279279
- [How to Build a GUI Language Translator App in Python](https://www.thepythoncode.com/article/build-a-gui-language-translator-tkinter-python). ([code](gui-programming/gui-language-translator))
280280
- [How to Make an Image Editor in Python](https://www.thepythoncode.com/article/make-an-image-editor-in-tkinter-python). ([code](gui-programming/image-editor))
281+
- [How to Make a Checkers Game with Pygame in Python](https://www.thepythoncode.com/article/make-a-checkers-game-with-pygame-in-python). ([code](gui-programming/checkers-game))
281282

282283
For any feedback, please consider pulling requests.
+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import pygame
2+
from Tile import Tile
3+
from Pawn import Pawn
4+
5+
class Board:
6+
def __init__(self,tile_width, tile_height, board_size):
7+
self.tile_width = tile_width
8+
self.tile_height = tile_height
9+
self.board_size = board_size
10+
self.selected_piece = None
11+
12+
self.turn = "black"
13+
self.is_jump = False
14+
15+
self.config = [
16+
['', 'bp', '', 'bp', '', 'bp', '', 'bp'],
17+
['bp', '', 'bp', '', 'bp', '', 'bp', ''],
18+
['', 'bp', '', 'bp', '', 'bp', '', 'bp'],
19+
['', '', '', '', '', '', '', ''],
20+
['', '', '', '', '', '', '', ''],
21+
['rp', '', 'rp', '', 'rp', '', 'rp', ''],
22+
['', 'rp', '', 'rp', '', 'rp', '', 'rp'],
23+
['rp', '', 'rp', '', 'rp', '', 'rp', '']
24+
]
25+
26+
self.tile_list = self._generate_tiles()
27+
self._setup()
28+
29+
def _generate_tiles(self):
30+
output = []
31+
for y in range(self.board_size):
32+
for x in range(self.board_size):
33+
output.append(
34+
Tile(x, y, self.tile_width, self.tile_height)
35+
)
36+
return output
37+
38+
def get_tile_from_pos(self, pos):
39+
for tile in self.tile_list:
40+
if (tile.x, tile.y) == (pos[0], pos[1]):
41+
return tile
42+
43+
def _setup(self):
44+
for y_ind, row in enumerate(self.config):
45+
for x_ind, x in enumerate(row):
46+
tile = self.get_tile_from_pos((x_ind, y_ind))
47+
if x != '':
48+
if x[-1] == 'p':
49+
color = 'red' if x[0] == 'r' else 'black'
50+
tile.occupying_piece = Pawn(x_ind, y_ind, color, self)
51+
52+
def handle_click(self, pos):
53+
x, y = pos[0], pos[-1]
54+
if x >= self.board_size or y >= self.board_size:
55+
x = x // self.tile_width
56+
y = y // self.tile_height
57+
clicked_tile = self.get_tile_from_pos((x, y))
58+
59+
if self.selected_piece is None:
60+
if clicked_tile.occupying_piece is not None:
61+
if clicked_tile.occupying_piece.color == self.turn:
62+
self.selected_piece = clicked_tile.occupying_piece
63+
elif self.selected_piece._move(clicked_tile):
64+
if not self.is_jump:
65+
self.turn = 'red' if self.turn == 'black' else 'black'
66+
else:
67+
if len(clicked_tile.occupying_piece.valid_jumps()) == 0:
68+
self.turn = 'red' if self.turn == 'black' else 'black'
69+
elif clicked_tile.occupying_piece is not None:
70+
if clicked_tile.occupying_piece.color == self.turn:
71+
self.selected_piece = clicked_tile.occupying_piece
72+
73+
def draw(self, display):
74+
if self.selected_piece is not None:
75+
self.get_tile_from_pos(self.selected_piece.pos).highlight = True
76+
if not self.is_jump:
77+
for tile in self.selected_piece.valid_moves():
78+
tile.highlight = True
79+
else:
80+
for tile in self.selected_piece.valid_jumps():
81+
tile[0].highlight = True
82+
83+
for tile in self.tile_list:
84+
tile.draw(display)

gui-programming/checkers-game/Game.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
class Game:
2+
3+
def __init__(self):
4+
self.winner = None
5+
6+
# checks if both colors still has a piece
7+
def check_piece(self, board):
8+
red_piece = 0
9+
black_piece = 0
10+
for y in range(board.board_size):
11+
for x in range(board.board_size):
12+
tile = board.get_tile_from_pos((x, y))
13+
if tile.occupying_piece != None:
14+
if tile.occupying_piece.color == "red":
15+
red_piece += 1
16+
else:
17+
black_piece += 1
18+
return red_piece, black_piece
19+
20+
def is_game_over(self, board):
21+
red_piece, black_piece = self.check_piece(board)
22+
if red_piece == 0 or black_piece == 0:
23+
self.winner = "red" if red_piece > black_piece else "black"
24+
return True
25+
else:
26+
return False
27+
28+
def check_jump(self, board):
29+
piece = None
30+
for tile in board.tile_list:
31+
if tile.occupying_piece != None:
32+
piece = tile.occupying_piece
33+
if len(piece.valid_jumps()) != 0 and board.turn == piece.color:
34+
board.is_jump = True
35+
break
36+
else:
37+
board.is_jump = False
38+
if board.is_jump:
39+
board.selected_piece = piece
40+
board.handle_click(piece.pos)
41+
return board.is_jump
42+
43+
def message(self):
44+
print(f"{self.winner} Wins!!")

gui-programming/checkers-game/King.py

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import pygame
2+
from Piece import Piece
3+
4+
class King(Piece):
5+
def __init__(self, x, y, color, board):
6+
super().__init__(x, y, color, board)
7+
img_path = f'images/{color}-king.png'
8+
self.img = pygame.image.load(img_path)
9+
self.img = pygame.transform.scale(self.img, (board.tile_width, board.tile_height))
10+
self.notation = 'k'
11+
12+
def _possible_moves(self):
13+
possible_moves = ((-1, -1), (+1, -1), (-1, +1), (+1, +1))
14+
return possible_moves
15+
16+
def valid_moves(self):
17+
tile_moves = []
18+
moves = self._possible_moves()
19+
for move in moves:
20+
tile_pos = (self.x + move[0], self.y + move[-1])
21+
if tile_pos[0] < 0 or tile_pos[0] > 7 or tile_pos[-1] < 0 or tile_pos[-1] > 7:
22+
pass
23+
else:
24+
tile = self.board.get_tile_from_pos(tile_pos)
25+
if tile.occupying_piece == None:
26+
tile_moves.append(tile)
27+
return tile_moves
28+
29+
def valid_jumps(self):
30+
tile_jumps = []
31+
moves = self._possible_moves()
32+
for move in moves:
33+
tile_pos = (self.x + move[0], self.y + move[-1])
34+
if tile_pos[0] < 0 or tile_pos[0] > 7 or tile_pos[-1] < 0 or tile_pos[-1] > 7:
35+
pass
36+
else:
37+
tile = self.board.get_tile_from_pos(tile_pos)
38+
if self.board.turn == self.color:
39+
if tile.occupying_piece != None and tile.occupying_piece.color != self.color:
40+
next_pos = (tile_pos[0] + move[0], tile_pos[-1] + move[-1])
41+
next_tile = self.board.get_tile_from_pos(next_pos)
42+
if next_pos[0] < 0 or next_pos[0] > 7 or next_pos[-1] < 0 or next_pos[-1] > 7:
43+
pass
44+
else:
45+
if next_tile.occupying_piece == None:
46+
tile_jumps.append((next_tile, tile))
47+
return tile_jumps

gui-programming/checkers-game/Main.py

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import pygame
2+
from Board import Board
3+
from Game import Game
4+
5+
pygame.init()
6+
7+
class Checkers:
8+
def __init__(self, screen):
9+
self.screen = screen
10+
self.running = True
11+
self.FPS = pygame.time.Clock()
12+
13+
def _draw(self, board):
14+
board.draw(self.screen)
15+
pygame.display.update()
16+
17+
def main(self, window_width, window_height):
18+
board_size = 8
19+
tile_width, tile_height = window_width // board_size, window_height // board_size
20+
board = Board(tile_width, tile_height, board_size)
21+
game = Game()
22+
while self.running:
23+
game.check_jump(board)
24+
25+
for self.event in pygame.event.get():
26+
if self.event.type == pygame.QUIT:
27+
self.running = False
28+
29+
if not game.is_game_over(board):
30+
if self.event.type == pygame.MOUSEBUTTONDOWN:
31+
board.handle_click(self.event.pos)
32+
else:
33+
game.message()
34+
self.running = False
35+
36+
self._draw(board)
37+
self.FPS.tick(60)
38+
39+
40+
if __name__ == "__main__":
41+
window_size = (640, 640)
42+
screen = pygame.display.set_mode(window_size)
43+
pygame.display.set_caption("Checkers")
44+
45+
checkers = Checkers(screen)
46+
checkers.main(window_size[0], window_size[1])

gui-programming/checkers-game/Pawn.py

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import pygame
2+
from Piece import Piece
3+
4+
class Pawn(Piece):
5+
def __init__(self, x, y, color, board):
6+
super().__init__(x, y, color, board)
7+
img_path = f'images/{color}-pawn.png'
8+
self.img = pygame.image.load(img_path)
9+
self.img = pygame.transform.scale(self.img, (board.tile_width, board.tile_height))
10+
self.notation = 'p'
11+
12+
def _possible_moves(self):
13+
# (x, y) move for left and right
14+
if self.color == "red":
15+
possible_moves = ((-1, -1), (+1, -1))
16+
else:
17+
possible_moves = ((-1, +1), (+1, +1))
18+
return possible_moves
19+
20+
def valid_moves(self):
21+
tile_moves = []
22+
moves = self._possible_moves()
23+
for move in moves:
24+
tile_pos = (self.x + move[0], self.y + move[-1])
25+
if tile_pos[0] < 0 or tile_pos[0] > 7 or tile_pos[-1] < 0 or tile_pos[-1] > 7:
26+
pass
27+
else:
28+
tile = self.board.get_tile_from_pos(tile_pos)
29+
if tile.occupying_piece == None:
30+
tile_moves.append(tile)
31+
return tile_moves
32+
33+
def valid_jumps(self):
34+
tile_jumps = []
35+
moves = self._possible_moves()
36+
for move in moves:
37+
tile_pos = (self.x + move[0], self.y + move[-1])
38+
if tile_pos[0] < 0 or tile_pos[0] > 7 or tile_pos[-1] < 0 or tile_pos[-1] > 7:
39+
pass
40+
else:
41+
tile = self.board.get_tile_from_pos(tile_pos)
42+
if self.board.turn == self.color:
43+
if tile.occupying_piece != None and tile.occupying_piece.color != self.color:
44+
next_pos = (tile_pos[0] + move[0], tile_pos[-1] + move[-1])
45+
next_tile = self.board.get_tile_from_pos(next_pos)
46+
if next_pos[0] < 0 or next_pos[0] > 7 or next_pos[-1] < 0 or next_pos[-1] > 7:
47+
pass
48+
else:
49+
if next_tile.occupying_piece == None:
50+
tile_jumps.append((next_tile, tile))
51+
return tile_jumps
+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import pygame
2+
3+
class Piece:
4+
def __init__(self, x, y, color, board):
5+
self.x = x
6+
self.y = y
7+
self.pos = (x, y)
8+
self.board = board
9+
self.color = color
10+
11+
def _move(self, tile):
12+
for i in self.board.tile_list:
13+
i.highlight = False
14+
15+
if tile in self.valid_moves() and not self.board.is_jump:
16+
prev_tile = self.board.get_tile_from_pos(self.pos)
17+
self.pos, self.x, self.y = tile.pos, tile.x, tile.y
18+
19+
prev_tile.occupying_piece = None
20+
tile.occupying_piece = self
21+
self.board.selected_piece = None
22+
self.has_moved = True
23+
24+
# Pawn promotion
25+
if self.notation == 'p':
26+
if self.y == 0 or self.y == 7:
27+
from King import King
28+
tile.occupying_piece = King(
29+
self.x, self.y, self.color, self.board
30+
)
31+
return True
32+
33+
elif self.board.is_jump:
34+
for move in self.valid_jumps():
35+
if tile in move:
36+
prev_tile = self.board.get_tile_from_pos(self.pos)
37+
jumped_piece = move[-1]
38+
self.pos, self.x, self.y = tile.pos, tile.x, tile.y
39+
40+
prev_tile.occupying_piece = None
41+
jumped_piece.occupying_piece = None
42+
tile.occupying_piece = self
43+
self.board.selected_piece = None
44+
self.has_moved = True
45+
46+
# Pawn promotion
47+
if self.notation == 'p':
48+
if self.y == 0 or self.y == 7:
49+
from King import King
50+
tile.occupying_piece = King(
51+
self.x, self.y, self.color, self.board
52+
)
53+
return True
54+
else:
55+
self.board.selected_piece = None
56+
return False
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# [How to Make a Checkers Game with Pygame in Python](https://www.thepythoncode.com/article/make-a-checkers-game-with-pygame-in-python)
2+
To run this:
3+
- `pip3 install -r requirements.txt`
4+
- `python3 Main.py`

gui-programming/checkers-game/Tile.py

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import pygame
2+
3+
class Tile:
4+
def __init__(self, x, y, tile_width, tile_height):
5+
self.x = x
6+
self.y = y
7+
self.pos = (x, y)
8+
self.tile_width = tile_width
9+
self.tile_height = tile_height
10+
self.abs_x = x * tile_width
11+
self.abs_y = y * tile_height
12+
self.abs_pos = (self.abs_x, self.abs_y)
13+
14+
self.color = 'light' if (x + y) % 2 == 0 else 'dark'
15+
self.draw_color = (220, 189, 194) if self.color == 'light' else (53, 53, 53)
16+
self.highlight_color = (100, 249, 83) if self.color == 'light' else (0, 228, 10)
17+
18+
self.occupying_piece = None
19+
self.coord = self.get_coord()
20+
self.highlight = False
21+
self.rect = pygame.Rect(
22+
self.abs_x,
23+
self.abs_y,
24+
self.tile_width,
25+
self.tile_height
26+
)
27+
28+
def get_coord(self):
29+
columns = 'abcdefgh'
30+
return columns[self.x] + str(self.y + 1)
31+
32+
def draw(self, display):
33+
if self.highlight:
34+
pygame.draw.rect(display, self.highlight_color, self.rect)
35+
else:
36+
pygame.draw.rect(display, self.draw_color, self.rect)
37+
38+
if self.occupying_piece != None:
39+
centering_rect = self.occupying_piece.img.get_rect()
40+
centering_rect.center = self.rect.center
41+
display.blit(self.occupying_piece.img, centering_rect.topleft)
Loading
Loading
Loading
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pygame

0 commit comments

Comments
 (0)