Xây dựng và trực quan hóa trò chơi Sudoku bằng Pygame
Sudoku là một trò chơi giải đố sắp xếp số dựa trên logic, kết hợp. Mục tiêu là...
Category: Pygame
Bài viết này sẽ hướng dẫn và cung cấp cho bạn những ý tưởng cơ bản về cách thiết kế trò chơi Tic Tac Toe bằng thư viện pygame của Python. Pygame là một bộ mô-đun Python đa nền tảng được thiết kế để viết trò chơi điện tử. Nó bao gồm các thư viện đồ họa máy tính và âm thanh được thiết kế để sử dụng với ngôn ngữ lập trình Python. Hãy chia nhiệm vụ thành năm phần:
Nhập các thư viện cần thiết và thiết lập các biến toàn cục cần thiết.
Thiết kế chức năng hiển thị trò chơi, sẽ thiết lập nền tảng để các thành phần khác được hiển thị trên màn hình.
Thuật toán chính của thắng và hòa
Nhận thông tin đầu vào của người dùng và hiển thị "X" hoặc "O" ở đúng vị trí mà người dùng đã nhấp chuột.
Chạy vòng lặp vô hạn và bao gồm các phương thức đã xác định trong đó.
Lưu ý: Các tệp PNG cần thiết có thể được tải xuống bên dưới như sau:
modified_cover.png
X_modified.png
o_modified.png
Chúng ta sẽ sử dụng thư viện pygame, time và thư viện sys của Python. Thư viện time được sử dụng để theo dõi thời gian và phương thức sleep() mà chúng ta sẽ sử dụng trong mã. Hãy xem mã bên dưới.
# nhập các thư viện cần thiết
import pygame as pg
import sys
import time
from pygame.locals import *
# khai báo các biến toàn cục
# lưu giá trị 'x' hoặc 'o' dưới dạng ký tự
XO = 'x'
# lưu giá trị người chiến thắng
# tại bất kỳ thời điểm nào
winner = None
# kiểm tra xem trò chơi có hòa không
draw = None
# thiết lập chiều rộng cửa sổ trò chơi
width = 400
# thiết lập chiều cao cửa sổ trò chơi
height = 400
# thiết lập màu nền cho
# cửa sổ trò chơi
white = (255, 255, 255)
# màu của các đường kẻ trên bảng trắng,
# chia bảng thành 9 phần
line_color = (0, 0, 0)
# thiết lập bảng 3 * 3 trong canvas
board = [[None]*3, [None]*3, [None]*3]
Đây là phần khó hơn, có tầm quan trọng tối đa trong phát triển trò chơi. Chúng ta có thể sử dụng phương thức display.set_mode() để thiết lập cửa sổ hiển thị. Phương thức này có ba đối số, đối số đầu tiên là một bộ có (chiều rộng, chiều cao) của màn hình mà chúng ta muốn, hai đối số còn lại lần lượt là chiều sâu và fps. display.set_caption() , đặt chú thích trên thẻ tên của màn hình. pg.image.load() là một phương thức hữu ích để tải hình ảnh nền để tùy chỉnh màn hình. Phương thức này lấy tên tệp làm đối số cùng với phần mở rộng. Có một vấn đề nhỏ với image.load(), nó tải hình ảnh dưới dạng một đối tượng Python ở kích thước gốc của nó, có thể không được tối ưu hóa cùng với màn hình. Vì vậy, chúng ta sử dụng một phương thức khác trong pygame được gọi là pg.transform.scale() . Phương thức này có hai đối số, một là tên của đối tượng hình ảnh và đối số còn lại là một bộ có (chiều rộng, chiều cao) mà chúng ta muốn hình ảnh của mình được chia tỷ lệ theo. Cuối cùng, chúng ta hướng đến hàm đầu tiên, game_initiating_window() . Ngay dòng đầu tiên có hàm screen.blit() . Screen là hàm Python và blit là phương thức cho phép pygame hiển thị một thứ gì đó trên một thứ khác. Tại đây, đối tượng hình ảnh của chúng ta đã được hiển thị trên màn hình, ban đầu được đặt thành màu trắng. pg.display.update() là một hàm quan trọng khác trong phát triển trò chơi. Nó cập nhật màn hình hiển thị của cửa sổ khi được gọi. Pygame cũng cho phép chúng ta vẽ các đối tượng hình học như đường thẳng, hình tròn, v.v. Trong dự án này, chúng ta đã sử dụng phương thức pg.draw.line() có năm đối số, cụ thể là - (màn hình, màu đường thẳng, điểm bắt đầu, điểm kết thúc, chiều rộng) . Điều này liên quan đến một chút hình học tọa độ để vẽ các đường thẳng một cách chính xác. Điều này là không đủ. Tại mỗi lần cập nhật màn hình, chúng ta cần biết trạng thái trò chơi, cho dù đó là thắng hay thua. draw_status() giúp chúng ta hiển thị một cửa sổ 100pc khác ở cuối cửa sổ chính, cập nhật trạng thái tại mỗi lần nhấp của người dùng.
# khởi tạo cửa sổ pygame
pg.init()
# thiết lập số khung hình mỗi giây (fps)
fps = 30
# dùng để theo dõi thời gian
CLOCK = pg.time.Clock()
# tạo cửa sổ hiển thị
screen = pg.display.set_mode((width, height + 100), 0, 32)
# đặt tiêu đề cho cửa sổ trò chơi
pg.display.set_caption("My Tic Tac Toe")
# tải hình ảnh dưới dạng đối tượng Python
initiating_window = pg.image.load("modified_cover.png")
x_img = pg.image.load("X_modified.png")
y_img = pg.image.load("o_modified.png")
# thay đổi kích thước hình ảnh
initiating_window = pg.transform.scale(
initiating_window, (width, height + 100))
x_img = pg.transform.scale(x_img, (80, 80))
o_img = pg.transform.scale(y_img, (80, 80))
def game_initiating_window():
# hiển thị hình ảnh khởi đầu lên màn hình
screen.blit(initiating_window, (0, 0))
# cập nhật hiển thị
pg.display.update()
time.sleep(3)
screen.fill(white)
# vẽ các đường dọc
pg.draw.line(screen, line_color, (width / 3, 0), (width / 3, height), 7)
pg.draw.line(screen, line_color, (width / 3 * 2, 0),
(width / 3 * 2, height), 7)
# vẽ các đường ngang
pg.draw.line(screen, line_color, (0, height / 3), (width, height / 3), 7)
pg.draw.line(screen, line_color, (0, height / 3 * 2),
(width, height / 3 * 2), 7)
draw_status()
def draw_status():
# kích hoạt biến toàn cục draw
global draw
if winner is None:
message = XO.upper() + "'s Turn" # lượt đi của người chơi hiện tại
else:
message = winner.upper() + " won !" # thông báo người thắng
if draw:
message = "Game Draw !" # thông báo hòa
# tạo đối tượng font
font = pg.font.Font(None, 30)
# thiết lập thuộc tính font như
# màu sắc và kích thước
text = font.render(message, 1, (255, 255, 255))
# sao chép thông báo đã render lên bảng
# tạo một khối nhỏ phía dưới của cửa sổ hiển thị chính
screen.fill((0, 0, 0), (0, 400, 500, 100))
text_rect = text.get_rect(center=(width / 2, 500-50))
screen.blit(text, text_rect)
pg.display.update()
Thuật toán chính có cách tiếp cận trực tiếp. Người dùng có thể thắng theo hàng, theo cột và theo đường chéo. Vì vậy, bằng cách sử dụng mảng đa chiều, chúng ta có thể thiết lập các điều kiện một cách dễ dàng.
def check_win():
global board, winner, draw
# kiểm tra hàng thắng
for row in range(0, 3):
if((board[row][0] == board[row][1] == board[row][2]) and (board[row][0] is not None)):
winner = board[row][0]
pg.draw.line(screen, (250, 0, 0),
(0, (row + 1)*height / 3 - height / 6),
(width, (row + 1)*height / 3 - height / 6),
4)
break
# kiểm tra cột thắng
for col in range(0, 3):
if((board[0][col] == board[1][col] == board[2][col]) and (board[0][col] is not None)):
winner = board[0][col]
pg.draw.line(screen, (250, 0, 0), ((col + 1) * width / 3 - width / 6, 0),
((col + 1) * width / 3 - width / 6, height), 4)
break
# kiểm tra thắng đường chéo
if (board[0][0] == board[1][1] == board[2][2]) and (board[0][0] is not None):
# thắng đường chéo từ trái sang phải
winner = board[0][0]
pg.draw.line(screen, (250, 70, 70), (50, 50), (350, 350), 4)
if (board[0][2] == board[1][1] == board[2][0]) and (board[0][2] is not None):
# thắng đường chéo từ phải sang trái
winner = board[0][2]
pg.draw.line(screen, (250, 70, 70), (350, 50), (50, 350), 4)
# nếu tất cả ô đã điền và không ai thắng thì hòa
if(all([all(row) for row in board]) and winner is None):
draw = True
draw_status()
Phần này sẽ trình bày về hình ảnh hóa bảng và một chút về hình học tọa độ. drawXO() nhận hai đối số row và col. Trước hết, chúng ta phải thiết lập vị trí hình học chính xác để đặt ảnh của X và ảnh của O mà chúng ta đã lưu trữ dưới dạng hai đối tượng Python tương ứng là "x_img" và "y_img". Hãy xem mã để hiểu rõ hơn. user_click() là một hàm chúng tôi đã thiết kế để lấy dữ liệu đầu vào từ một cú nhấp chuột của người dùng. Hãy tưởng tượng, bạn đã nhấp vào một trong chín phần (các ô được chia bởi các đường chúng tôi đã vẽ theo chiều ngang và chiều dọc), hàm này sẽ xác định tọa độ của vị trí bạn đã nhấp. pg.mouse.get_pos() lấy tọa độ x và tọa độ y của cú nhấp chuột của người dùng và trả về một bộ. Tùy thuộc vào (x, y), chúng ta có thể xác định chính xác hàng và chính xác cột mà người dùng đã nhấp. Cuối cùng, khi chúng ta có hàng và cột, chúng ta truyền hai đối số này vào hàm drawXO(hàng, cột) để vẽ hình ảnh 'X' hoặc hình ảnh 'O' tại vị trí mong muốn của người dùng trên màn hình trò chơi.
def drawXO(row, col):
global board, XO
# với hàng đầu tiên, hình ảnh nên được dán ở tọa độ x cách lề trái 30px
if row == 1:
posx = 30
# với hàng thứ hai, hình ảnh nên được dán cách dòng đầu 1 khoảng (width / 3) + 30px
if row == 2:
posx = width / 3 + 30
# hàng thứ ba
if row == 3:
posx = width / 3 * 2 + 30
# với cột đầu tiên
if col == 1:
posy = 30
# với cột thứ hai
if col == 2:
posy = height / 3 + 30
# với cột thứ ba
if col == 3:
posy = height / 3 * 2 + 30
# cập nhật giá trị tương ứng trên bảng (x hoặc o)
board[row-1][col-1] = XO
if(XO == 'x'):
# dán ảnh X vào màn hình tại tọa độ (posy, posx)
screen.blit(x_img, (posy, posx))
XO = 'o' # đổi lượt chơi sang O
else:
# dán ảnh O vào màn hình tại tọa độ (posy, posx)
screen.blit(o_img, (posy, posx))
XO = 'x' # đổi lượt chơi sang X
# cập nhật màn hình
pg.display.update()
def user_click():
# lấy tọa độ chuột khi click
x, y = pg.mouse.get_pos()
# xác định cột (1-3) dựa trên vị trí chuột theo trục x
if(x < width / 3):
col = 1
elif (x < width / 3 * 2):
col = 2
elif(x < width):
col = 3
else:
col = None
# xác định hàng (1-3) dựa trên vị trí chuột theo trục y
if(y < height / 3):
row = 1
elif (y < height / 3 * 2):
row = 2
elif(y < height):
row = 3
else:
row = None
# sau khi xác định được hàng và cột, nếu ô đó còn trống thì vẽ X hoặc O
if(row and col and board[row-1][col-1] is None):
global XO
drawXO(row, col)
check_win()
Đây là bước quan trọng cuối cùng để chạy trò chơi vô hạn cho đến khi người dùng nhấp vào nút thoát . Trước khi chạy vòng lặp vô hạn, chúng ta cần thiết lập một hàm có thể đặt lại tất cả các giá trị và tham số toàn cục về giá trị ban đầu để bắt đầu lại trò chơi. reset_game() được sử dụng cho mục đích này. Nó đặt lại giá trị bảng thành giá trị 3 * 3 None một lần nữa và khởi tạo các tham số toàn cục. Trong quá trình phát triển trò chơi, mọi hành động của người chơi đều là một sự kiện . Cho dù người chơi nhấp vào cửa sổ hay nhấp vào biểu tượng thoát/đóng. Để lấy các sự kiện này dưới dạng một đối tượng, pygame có một phương thức tích hợp được sử dụng là pg.event.get() . Nếu kiểu sự kiện là "QUIT", chúng ta sử dụng thư viện sys của Python để thoát trò chơi. Nhưng nếu người dùng nhấn chuột, event.get() sẽ trả về "MOUSEBUTTONDOWN" và lệnh gọi đến user_click() của chúng ta tình cờ biết tọa độ chính xác của bảng nơi người dùng đã nhấp. Trong toàn bộ mã, chúng tôi đã sử dụng phương thức .sleep() để tạm dừng trò chơi trong một thời gian và làm cho trò chơi trở nên thân thiện và mượt mà hơn.
def reset_game():
global board, winner, XO, draw
time.sleep(3)
XO = 'x'
draw = False
game_initiating_window()
winner = None
board = [[None]*3, [None]*3, [None]*3]
game_initiating_window()
while(True):
for event in pg.event.get():
if event.type == QUIT:
pg.quit()
sys.exit()
elif event.type is MOUSEBUTTONDOWN:
user_click()
if(winner or draw):
reset_game()
pg.display.update()
CLOCK.tick(fps)
Mã đầy đủ:
# nhập các thư viện cần thiết
import pygame as pg
import sys
import time
from pygame.locals import *
# khai báo các biến toàn cục
# lưu giá trị 'x' hoặc 'o'
XO = 'x'
# lưu người thắng tại bất kỳ thời điểm nào
winner = None
# kiểm tra xem trận đấu có hòa không
draw = None
# đặt chiều rộng cửa sổ trò chơi
width = 400
# đặt chiều cao cửa sổ trò chơi
height = 400
# đặt màu nền của cửa sổ trò chơi
white = (255, 255, 255)
# màu của các đường kẻ trên bàn cờ trắng, chia bảng thành 9 phần
line_color = (0, 0, 0)
# thiết lập bàn cờ 3 * 3 trong canvas
board = [[None]*3, [None]*3, [None]*3]
# khởi tạo cửa sổ pygame
pg.init()
# đặt fps thủ công
fps = 30
# dùng để theo dõi thời gian
CLOCK = pg.time.Clock()
# tạo giao diện hiển thị
screen = pg.display.set_mode((width, height + 100), 0, 32)
# đặt tên cho cửa sổ trò chơi
pg.display.set_caption("My Tic Tac Toe")
# tải ảnh dưới dạng đối tượng python
initiating_window = pg.image.load("modified_cover.png")
x_img = pg.image.load("X_modified.png")
y_img = pg.image.load("o_modified.png")
# thay đổi kích thước ảnh
initiating_window = pg.transform.scale(
initiating_window, (width, height + 100))
x_img = pg.transform.scale(x_img, (80, 80))
o_img = pg.transform.scale(y_img, (80, 80))
def game_initiating_window():
# hiển thị lên màn hình
screen.blit(initiating_window, (0, 0))
# cập nhật màn hình
pg.display.update()
time.sleep(3)
screen.fill(white)
# vẽ các đường dọc
pg.draw.line(screen, line_color, (width / 3, 0), (width / 3, height), 7)
pg.draw.line(screen, line_color, (width / 3 * 2, 0),
(width / 3 * 2, height), 7)
# vẽ các đường ngang
pg.draw.line(screen, line_color, (0, height / 3), (width, height / 3), 7)
pg.draw.line(screen, line_color, (0, height / 3 * 2),
(width, height / 3 * 2), 7)
draw_status()
def draw_status():
# lấy biến toàn cục draw
global draw
if winner is None:
message = XO.upper() + " đến lượt"
else:
message = winner.upper() + " thắng!"
if draw:
message = "Hòa!"
# tạo đối tượng font
font = pg.font.Font(None, 30)
# thiết lập thuộc tính font như màu và độ rộng
text = font.render(message, 1, (255, 255, 255))
# sao chép nội dung ra màn hình
# tạo một khối nhỏ phía dưới hiển thị chính
screen.fill((0, 0, 0), (0, 400, 500, 100))
text_rect = text.get_rect(center=(width / 2, 500-50))
screen.blit(text, text_rect)
pg.display.update()
def check_win():
global board, winner, draw
# kiểm tra hàng ngang thắng
for row in range(0, 3):
if((board[row][0] == board[row][1] == board[row][2]) and (board[row][0] is not None)):
winner = board[row][0]
pg.draw.line(screen, (250, 0, 0),
(0, (row + 1)*height / 3 - height / 6),
(width, (row + 1)*height / 3 - height / 6),
4)
break
# kiểm tra cột thắng
for col in range(0, 3):
if((board[0][col] == board[1][col] == board[2][col]) and (board[0][col] is not None)):
winner = board[0][col]
pg.draw.line(screen, (250, 0, 0), ((col + 1) * width / 3 - width / 6, 0),
((col + 1) * width / 3 - width / 6, height), 4)
break
# kiểm tra đường chéo thắng
if (board[0][0] == board[1][1] == board[2][2]) and (board[0][0] is not None):
# thắng theo đường chéo trái sang phải
winner = board[0][0]
pg.draw.line(screen, (250, 70, 70), (50, 50), (350, 350), 4)
if (board[0][2] == board[1][1] == board[2][0]) and (board[0][2] is not None):
# thắng theo đường chéo phải sang trái
winner = board[0][2]
pg.draw.line(screen, (250, 70, 70), (350, 50), (50, 350), 4)
if(all([all(row) for row in board]) and winner is None):
draw = True
draw_status()
def drawXO(row, col):
global board, XO
# với hàng 1, ảnh được vẽ ở tọa độ x = 30 từ lề trái
if row == 1:
posx = 30
# với hàng 2, ảnh vẽ tại x = width/3 + 30
if row == 2:
posx = width / 3 + 30
if row == 3:
posx = width / 3 * 2 + 30
if col == 1:
posy = 30
if col == 2:
posy = height / 3 + 30
if col == 3:
posy = height / 3 * 2 + 30
# cập nhật giá trị trên bàn cờ
board[row-1][col-1] = XO
if(XO == 'x'):
# vẽ hình X tại vị trí tương ứng
screen.blit(x_img, (posy, posx))
XO = 'o'
else:
screen.blit(o_img, (posy, posx))
XO = 'x'
pg.display.update()
def user_click():
# lấy tọa độ chuột click
x, y = pg.mouse.get_pos()
# xác định cột (1-3)
if(x < width / 3):
col = 1
elif (x < width / 3 * 2):
col = 2
elif(x < width):
col = 3
else:
col = None
# xác định hàng (1-3)
if(y < height / 3):
row = 1
elif (y < height / 3 * 2):
row = 2
elif(y < height):
row = 3
else:
row = None
# sau khi có hàng và cột, tiến hành vẽ ảnh tại vị trí tương ứng
if(row and col and board[row-1][col-1] is None):
global XO
drawXO(row, col)
check_win()
def reset_game():
global board, winner, XO, draw
time.sleep(3)
XO = 'x'
draw = False
game_initiating_window()
winner = None
board = [[None]*3, [None]*3, [None]*3]
game_initiating_window()
while(True):
for event in pg.event.get():
if event.type == QUIT:
pg.quit()
sys.exit()
elif event.type is MOUSEBUTTONDOWN:
user_click()
if(winner or draw):
reset_game()
pg.display.update()
CLOCK.tick(fps)
Đầu ra:
Published on Jul 28, 2025
Sudoku là một trò chơi giải đố sắp xếp số dựa trên logic, kết hợp. Mục tiêu là điền các chữ số vào lưới 9x9 sao cho mỗi cột, mỗi hàng và mỗi ô lưới....
Thuật toán như Tìm kiếm Nhị phân có thể được hiểu dễ dàng bằng cách trực quan hóa. Bài viết này trình bày một chương trình trực quan hóa Thuật toá...
Thuật toán sắp xếp chèn có thể dễ dàng được hiểu bằng cách trực quan hóa. Trong bài viết này, một chương trình trực quan hóa thuật toán sắp xếp ch...
Thuật toán sắp xếp Heap có thể được hiểu dễ dàng bằng cách trực quan hóa. Bài viết này trình bày một chương trình trực quan hóa Thuật toán sắp xếp...
Thuật toán như Tìm kiếm Tam phân có thể được hiểu dễ dàng bằng cách trực quan hóa. Bài viết này trình bày một chương trình trực quan hóa Thuật toá...
Trong bài viết này, chúng ta sẽ xem cách hình dung thuật toán sắp xếp nổi bọt (bubble sort ) bằng PyGame. Cụ thể, khi khởi động ứng dụng PyGame,....