本项目实现了一个简单的五子棋双人联机对战游戏,使用 Python 编程语言,结合 socket
模块实现网络通信,pygame
模块实现图形界面。玩家可以通过局域网连接,进行实时对战。
(应该是可以局域网的,不过我在将本地localhost改为主机ip地址时,拒绝连接,应该是防火墙的问题,觉得麻烦就没有测试了,如果有问题,可以提出,本文仅作为参考)
最后有源代码, 源码都在一个文件内, 导入相关package资源后,从终端先运行服务端,然后两个客户端就可以了
联机功能只实现得比较基础
此外AI对战有简单实现,只实现了附近落子和连成3个的时候会堵截,很容易赢,就不献丑了
1. 文本模式可以参考我之前的文章,比较简单,适合新手python五子棋项目(新手入门 文本模式入手)-CSDN博客
2. 图像模式,也可以参考另一篇博客,我也是参照这片文章写出来的图形界面,不过缺少AI对战和联机..Python Pygame制作简单五子棋游戏(详细代码+解释)_scratch五子棋游戏制作教程-CSDN博客
双人联机对战:支持两个玩家通过网络进行对战。
图形化界面:使用 pygame
绘制棋盘和棋子,提供友好的交互体验。
实时通信:通过 TCP 套接字实现玩家之间的实时消息传递。
简单易用:代码结构清晰,易于理解和扩展。
服务端负责接收客户端的连接请求,并转发玩家之间的操作消息。
import socket
import threading
clients = [] # 存储客户端连接
def handle_client(conn):
"""处理客户端连接"""
while True:
try:
data = conn.recv(1024).decode("utf-8") # 接收数据
if data:
print("收到数据:", data)
for client in clients: # 转发数据给其他客户端
if client != conn:
print("转发数据给其它客户端:", data)
client.send(data.encode("utf-8"))
except:
clients.remove(conn)
break
def start_server():
"""启动服务端"""
server = socket.socket()
server.bind(('0.0.0.0', 8800)) # 绑定地址和端口
server.listen(2) # 最大连接数
while len(clients) < 2: # 等待两个客户端连接
print("等待玩家连接...")
conn, addr = server.accept()
print(f"新链接来自:{addr}")
clients.append(conn)
threading.Thread(target=handle_client, args=(conn,)).start()
客户端负责与服务端通信,并通过图形界面展示游戏状态。
import socket
import threading
import queue
class NetworkClient:
"""网络客户端"""
def __init__(self, game_windows):
self.sock = socket.socket()
self.sock.connect(('localhost', 8800)) # 连接服务端
self.recv_thread = threading.Thread(target=self.recv_loop, daemon=True)
self.network_queue = queue.Queue()
self.recv_thread.start()
self.game_windows = game_windows
def send(self, data):
"""发送数据到服务端"""
print("发送数据:", data)
self.sock.send(data.encode("utf-8"))
def recv_loop(self):
"""接收服务端数据"""
while True:
try:
data = self.sock.recv(1024).decode("utf-8")
if data:
self.network_queue.put(data)
except ConnectionResetError:
break
def process_message(self):
"""处理接收到的消息"""
while not self.network_queue.empty():
msg = self.network_queue.get()
row, col = map(int, msg.split(','))
self.game_windows.handle_network_move(row, col)
使用 pygame
绘制棋盘和棋子,并处理玩家的交互操作。
import pygame
import sys
class GameWindow:
"""游戏窗口"""
CELL_SIZE = 48 # 格子大小
COLORS = {
'board': (238, 170, 0), # 棋盘颜色
'black': (0, 0, 0),
'white': (255, 255, 255)
}
def __init__(self, player_num):
pygame.init()
self.screen = pygame.display.set_mode((self.CELL_SIZE * 15, self.CELL_SIZE * 15))
pygame.display.set_caption(f"玩家{player_num}")
self.network = NetworkClient(self)
self.board = [['·' for _ in range(15)] for _ in range(15)]
self.current_player = "●"
def draw_board(self):
"""绘制棋盘"""
self.screen.fill(self.COLORS['board'])
for i in range(15):
pygame.draw.line(self.screen, self.COLORS['black'], (i * self.CELL_SIZE, 0), (i * self.CELL_SIZE, self.CELL_SIZE * 15))
pygame.draw.line(self.screen, self.COLORS['black'], (0, i * self.CELL_SIZE), (self.CELL_SIZE * 15, i * self.CELL_SIZE))
for i in range(15):
for j in range(15):
if self.board[i][j] == '●':
color = self.COLORS['black']
elif self.board[i][j] == '○':
color = self.COLORS['white']
else:
continue
center = (j * self.CELL_SIZE + self.CELL_SIZE // 2, i * self.CELL_SIZE + self.CELL_SIZE // 2)
pygame.draw.circle(self.screen, color, center, self.CELL_SIZE // 2 - 2)
pygame.display.update()
def handle_click(self, x, y):
"""处理鼠标点击"""
col = x // self.CELL_SIZE
row = y // self.CELL_SIZE
if 0 <= col < 15 and 0 <= row < 15 and self.board[col][row] == '·':
self.board[row][col] = self.current_player
if self.network:
self.network.send(f"{row},{col}")
self.current_player = '○' if self.current_player == '●' else '●'
def run(self):
"""运行游戏"""
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
x, y = pygame.mouse.get_pos()
self.handle_click(x, y)
if self.network:
self.network.process_message()
self.draw_board()
clock.tick(30)
通过命令行参数启动服务端或客户端:python + 文件地址 +
#启动
if __name__=='__main__':
if len(sys.argv)>1:#根据命令行参数启动服务器或客户端
if sys.argv[1]=="server":
start_server()
elif sys.argv[1]=="client1":
game=GameWindow(1)
game.run()
elif sys.argv[1]=="client2":
game=GameWindow(2)
game.run()
通过命令行参数启动服务端或客户端:python + 文件地址 + 服务端(或用户端1或用户端2) 例:
# 启动服务端
python 五子棋联机对战.py server
# 启动客户端1
python 五子棋联机对战.py client1
# 启动客户端2
python 五子棋联机对战.py client2
本项目实现了一个简单的五子棋双人联机对战游戏,适合初学者学习网络编程和图形界面开发。你可以在此基础上扩展更多功能,例如增加胜负判断、聊天功能等。
#!/usr/bin/python
# -*- coding:utf-8 -*-
import sys
import pygame
import socket
import threading
import queue
clients = [] # 设置客户端初始连接为空
def handle_client(conn):
while True:
try:
data = conn.recv(1024).decode("utf-8") # 设置接收数据的大小最大为1024字节
# recv()方法接收TCP消息,返回一个包含接收数据的缓冲区。
if data:
print("收到数据:", data)
for client in clients: # 遍历客户端
if client != conn: # 如果客户端不为自己本身 就发送数据给其它客户端
print("转发数据给其它客户端:", data)
client.send(data.encode("utf-8"))
except:
clients.remove(conn)
break
# 服务端
def start_server():
server = socket.socket()
server.bind(('0.0.0.0', 8800)) # 地址和端口号 第一个括号是bind的参数,第二个括号为元组
server.listen(2) # 最大连接数
while len(clients) < 2: # 如果客户端连接数小于2
print("等待玩家连接...")
conn, addr = server.accept() # accept方法接受连接并返回(conn, address),
# 其中conn是新的套接字对象,可以用来发送和接收数据,address是连接的客户端地址。
print(f"新链接来自:{addr}")
clients.append(conn) # 向客户端列表添加连接
print("当前连接数:", len(clients))
threading.Thread(target=handle_client, args=(conn,)).start()
# 为每个客户端连接创建一个线程,用于接收消息
# 解释target:要调用的可调用对象 args:传递给目标的参数元组 start:线程对象开始执行
# 客户端
class NetworkClient:
def __init__(self, game_windows): # self参数用于表示类的实例本身 允许访问操作对象属性和方法
# __init__方法是一个特殊的方法(init是单词初始化initialization的省略形式),
# 在使用类创建对象之后被执行,用于给新创建的对象初始化属性用。
self.sock = socket.socket() # 创建一个socket对象
self.sock.connect(('localhost', 8800)) # connect()方法:连接到服务器
self.recv_thread = threading.Thread(target=self.recv_loop, daemon=True)
# daemon参数的作用: 设置为True时,主线程结束时,子线程也会结束
self.network_queue = queue.Queue() # 消息队列
self.recv_thread.start() # 启动线程
self.game_windows = game_windows # 将游戏窗口传入,方便更新界面
def send(self, data):
print("发送数据:", data)
self.sock.send(data.encode("utf-8")) # 发送数据
def recv_loop(self): # 接收数据
while True:
try:
data = self.sock.recv(1024).decode("utf-8") # 接收数据
if data:
self.network_queue.put(data) # 将数据放入消息队列
except ConnectionResetError: # 连接重置错误
break
def process_message(self): # 处理消息
while not self.network_queue.empty(): # 如果消息队列不为空
msg = self.network_queue.get() # 获取消息
row, col = map(int, msg.split(','))
self.game_windows.handle_network_move(row, col) # 更新棋盘状态
# 图形界面
class GameWindow:
CELL_SIZE = 48 # 一个格子的大小
COLORS = { # 颜色定义
'board': (238, 170, 0), # 棋盘颜色
'black': (0, 0, 0),
'white': (255, 255, 255)
}
def __init__(self, player_num): # player_num是玩家编号,用于设置窗口标题
pygame.init() # 初始化
self.screen = pygame.display.set_mode((self.CELL_SIZE * 15, self.CELL_SIZE * 15)) # 设置窗口大小
pygame.display.set_caption(f"玩家{player_num}")
self.network = NetworkClient(self) # 创建网络客户端
self.board = [['·' for _ in range(15)] for _ in range(15)] # 初始化棋盘
self.current_player = "●" # 当前玩家
def draw_board(self):
# 填充屏幕背景 用之前电定义的COLOR的字典
self.screen.fill(self.COLORS['board']) # 设置为棋盘颜色
# 画棋盘
for i in range(15):
pygame.draw.line(self.screen, self.COLORS['black'],
(i * self.CELL_SIZE, 0),
(i * self.CELL_SIZE, self.CELL_SIZE * 15))
pygame.draw.line(self.screen, self.COLORS['black'],
(0, i * self.CELL_SIZE),
(self.CELL_SIZE * 15, i * self.CELL_SIZE))
for i in range(15):
for j in range(15):
if self.board[i][j] == '●':
color = self.COLORS['black']
elif self.board[i][j] == '○':
color = self.COLORS['white']
else:
continue
# 计算中心坐标 绘制圆圈(棋子)
center = (j * self.CELL_SIZE + self.CELL_SIZE // 2, # j为列数 i为行数
i * self.CELL_SIZE + self.CELL_SIZE // 2)
pygame.draw.circle(self.screen, color, center, self.CELL_SIZE // 2 - 2)
pygame.display.update() # 更新屏幕
def handle_click(self, x, y): # 处理鼠标点击事件
col = x // self.CELL_SIZE
row = y // self.CELL_SIZE
if 0 <= col < 15 and 0 <= row < 15 and self.board[col][row] == '·':
self.board[row][col]=self.current_player
if self.network:
self.network.send(f"{row},{col}")
self.current_player = '○' if self.current_player == '●' else '●'#(三元运算符) 切换玩家
def handle_network_move(self,row,col):#处理网络消息
if self.board[row][col]=='·':
self.board[row][col]=self.current_player
self.current_player='○' if self.current_player=='●' else '●'
self.draw_board() # 重新绘制棋盘
def run(self):
clock=pygame.time.Clock() # 创建一个时钟对象
while True:
for event in pygame.event.get():#获取事件
if event.type==pygame.QUIT:
pygame.quit()#退出
sys.exit()#退出
if event.type==pygame.MOUSEBUTTONDOWN:#处理鼠标点击事件
x,y=pygame.mouse.get_pos()
self.handle_click(x,y)
if self.network:
self.network.process_message()
self.draw_board()
clock.tick(30)#限制帧率
#启动
if __name__=='__main__':
if len(sys.argv)>1:#根据命令行参数启动服务器或客户端
if sys.argv[1]=="server":
start_server()
elif sys.argv[1]=="client1":
game=GameWindow(1)
game.run()
elif sys.argv[1]=="client2":
game=GameWindow(2)
game.run()
#启动方式
#终端输入以下命令 python 项目文件地址 服务端(或客户端1或客户端2) 例如:
#python 五子棋联机对战.py server
#python 五子棋联机对战.py client1
#python 五子棋联机对战.py client2