导语

每次都写单机游戏自嗨好像没啥意思,这次我们来写个支持联机对战的游戏吧,支持局域网联机对战的五子棋小游戏他来了!

相关文件

关注小编,私信小编领取游戏源码的哟!!当然别忘了一键三连哈!!!

开发环境

安装Python并添加到环境变量,pip安装需要的相关模块即可。

原理介绍

这里简单介绍下原理吧,代码主要用PyQt5写的,pygame只用来播放一些音效。首先,设计并实现个游戏主界面:

代码实现如下:

'''游戏开始界面'''
class gameStartUI(QWidget):def __init__(self, parent=None, **kwargs):super(gameStartUI, self).__init__(parent)self.setFixedSize(760, 650)self.setWindowTitle('五子棋————彳余大胆')self.setWindowIcon(QIcon(cfg.ICON_FILEPATH))# 背景图片palette = QPalette()palette.setBrush(self.backgroundRole(), QBrush(QPixmap(cfg.BACKGROUND_IMAGEPATHS.get('bg_start'))))self.setPalette(palette)# 按钮# --人机对战self.ai_button = PushButton(cfg.BUTTON_IMAGEPATHS.get('ai'), self)self.ai_button.move(250, 200)self.ai_button.show()self.ai_button.click_signal.connect(self.playWithAI)# --联机对战self.online_button = PushButton(cfg.BUTTON_IMAGEPATHS.get('online'), self)self.online_button.move(250, 350)self.online_button.show()self.online_button.click_signal.connect(self.playOnline)'''人机对战'''def playWithAI(self):self.close()self.gaming_ui = playWithAIUI(cfg)self.gaming_ui.exit_signal.connect(lambda: sys.exit())self.gaming_ui.back_signal.connect(self.show)self.gaming_ui.show()'''联机对战'''def playOnline(self):self.close()self.gaming_ui = playOnlineUI(cfg, self)self.gaming_ui.show()

会pyqt5的应该都可以写出这样的界面,没啥特别的,记得把人机对战和联机对战两个按钮触发后的信号分别绑定到人机对战和联机对战的函数上就行。
然后分别来实现人机对战和联机对战就行了。

主要的代码实现如下:

'''人机对战'''
class playWithAIUI(QWidget):back_signal = pyqtSignal()exit_signal = pyqtSignal()send_back_signal = Falsedef __init__(self, cfg, parent=None, **kwargs):super(playWithAIUI, self).__init__(parent)self.cfg = cfgself.setFixedSize(760, 650)self.setWindowTitle('五子棋——彳余大胆')self.setWindowIcon(QIcon(cfg.ICON_FILEPATH))# 背景图片palette = QPalette()palette.setBrush(self.backgroundRole(), QBrush(QPixmap(cfg.BACKGROUND_IMAGEPATHS.get('bg_game'))))self.setPalette(palette)# 按钮self.home_button = PushButton(cfg.BUTTON_IMAGEPATHS.get('home'), self)self.home_button.click_signal.connect(self.goHome)self.home_button.move(680, 10)self.startgame_button = PushButton(cfg.BUTTON_IMAGEPATHS.get('startgame'), self)self.startgame_button.click_signal.connect(self.startgame)self.startgame_button.move(640, 240)self.regret_button = PushButton(cfg.BUTTON_IMAGEPATHS.get('regret'), self)self.regret_button.click_signal.connect(self.regret)self.regret_button.move(640, 310)self.givein_button = PushButton(cfg.BUTTON_IMAGEPATHS.get('givein'), self)self.givein_button.click_signal.connect(self.givein)self.givein_button.move(640, 380)# 落子标志self.chessman_sign = QLabel(self)sign = QPixmap(cfg.CHESSMAN_IMAGEPATHS.get('sign'))self.chessman_sign.setPixmap(sign)self.chessman_sign.setFixedSize(sign.size())self.chessman_sign.show()self.chessman_sign.hide()# 棋盘(19*19矩阵)self.chessboard = [[None for i in range(19)] for _ in range(19)]# 历史记录(悔棋用)self.history_record = []# 是否在游戏中self.is_gaming = True# 胜利方self.winner = Noneself.winner_info_label = None# 颜色分配and目前轮到谁落子self.player_color = 'white'self.ai_color = 'black'self.whoseround = self.player_color# 实例化aiself.ai_player = aiGobang(self.ai_color, self.player_color)# 落子声音加载pygame.mixer.init()self.drop_sound = pygame.mixer.Sound(cfg.SOUNDS_PATHS.get('drop'))'''鼠标左键点击事件-玩家回合'''def mousePressEvent(self, event):if (event.buttons() != QtCore.Qt.LeftButton) or (self.winner is not None) or (self.whoseround != self.player_color) or (not self.is_gaming):return# 保证只在棋盘范围内响应if event.x() >= 50 and event.x() <= 50 + 30 * 18 + 14 and event.y() >= 50 and event.y() <= 50 + 30 * 18 + 14:pos = Pixel2Chesspos(event)# 保证落子的地方本来没有人落子if self.chessboard[pos[0]][pos[1]]:return# 实例化一个棋子并显示c = Chessman(self.cfg.CHESSMAN_IMAGEPATHS.get(self.whoseround), self)c.move(event.pos())c.show()self.chessboard[pos[0]][pos[1]] = c# 落子声音响起self.drop_sound.play()# 最后落子位置标志对落子位置进行跟随self.chessman_sign.show()self.chessman_sign.move(c.pos())self.chessman_sign.raise_()# 记录这次落子self.history_record.append([*pos, self.whoseround])# 是否胜利了self.winner = checkWin(self.chessboard)if self.winner:self.showGameEndInfo()return# 切换回合方(其实就是改颜色)self.nextRound()'''鼠标左键释放操作-调用电脑回合'''def mouseReleaseEvent(self, event):if (self.winner is not None) or (self.whoseround != self.ai_color) or (not self.is_gaming):returnself.aiAct()'''电脑自动下-AI回合'''def aiAct(self):if (self.winner is not None) or (self.whoseround == self.player_color) or (not self.is_gaming):returnnext_pos = self.ai_player.act(self.history_record)# 实例化一个棋子并显示c = Chessman(self.cfg.CHESSMAN_IMAGEPATHS.get(self.whoseround), self)c.move(QPoint(*Chesspos2Pixel(next_pos)))c.show()self.chessboard[next_pos[0]][next_pos[1]] = c# 落子声音响起self.drop_sound.play()# 最后落子位置标志对落子位置进行跟随self.chessman_sign.show()self.chessman_sign.move(c.pos())self.chessman_sign.raise_()# 记录这次落子self.history_record.append([*next_pos, self.whoseround])# 是否胜利了self.winner = checkWin(self.chessboard)if self.winner:self.showGameEndInfo()return# 切换回合方(其实就是改颜色)self.nextRound()'''改变落子方'''def nextRound(self):self.whoseround = self.player_color if self.whoseround == self.ai_color else self.ai_color'''显示游戏结束结果'''def showGameEndInfo(self):self.is_gaming = Falseinfo_img = QPixmap(self.cfg.WIN_IMAGEPATHS.get(self.winner))self.winner_info_label = QLabel(self)self.winner_info_label.setPixmap(info_img)self.winner_info_label.resize(info_img.size())self.winner_info_label.move(50, 50)self.winner_info_label.show()'''认输'''def givein(self):if self.is_gaming and (self.winner is None) and (self.whoseround == self.player_color):self.winner = self.ai_colorself.showGameEndInfo()'''悔棋-只有我方回合的时候可以悔棋'''def regret(self):if (self.winner is not None) or (len(self.history_record) == 0) or (not self.is_gaming) and (self.whoseround != self.player_color):returnfor _ in range(2):pre_round = self.history_record.pop(-1)self.chessboard[pre_round[0]][pre_round[1]].close()self.chessboard[pre_round[0]][pre_round[1]] = Noneself.chessman_sign.hide()'''开始游戏-之前的对弈必须已经结束才行'''def startgame(self):if self.is_gaming:returnself.is_gaming = Trueself.whoseround = self.player_colorfor i, j in product(range(19), range(19)):if self.chessboard[i][j]:self.chessboard[i][j].close()self.chessboard[i][j] = Noneself.winner = Noneself.winner_info_label.close()self.winner_info_label = Noneself.history_record.clear()self.chessman_sign.hide()'''关闭窗口事件'''def closeEvent(self, event):if not self.send_back_signal:self.exit_signal.emit()'''返回游戏主页面'''def goHome(self):self.send_back_signal = Trueself.close()self.back_signal.emit()

整个逻辑是这样的:

设计并实现游戏的基本界面之后,先默认永远是玩家先手(白子),电脑后手(黑子)。然后,当监听到玩家鼠标左键点击到棋盘网格所在的范围内的时候,捕获该位置,若该位置之前没有人落子过,则玩家成功落子,否则重新等待玩家鼠标左键点击事件。玩家成功落子后,判断是否因为玩家落子而导致游戏结束(即棋盘上有5颗同色子相连了),若游戏结束,则显示游戏结束界面,否则轮到AI落子。AI落子和玩家落子的逻辑类似,然后又轮到玩家落子,以此类推。

需要注意的是:为保证响应的实时性,AI落子算法应当写到鼠标左键点击后释放事件的响应中(感兴趣的小伙伴可以试试写到鼠标点击事件的响应中,这样会导致必须在AI计算结束并落子后,才能显示玩家上一次的落子和AI此次的落子结果)。

开始按钮就是重置游戏,没啥可说的,这里为了避免有些人喜欢耍赖,我实现的时候代码写的是必须完成当前对弈才能重置游戏(毕竟小伙子小姑娘们要学会有耐心地下完一盘棋呀)。

因为是和AI下,所以悔棋按钮直接悔两步,从历史记录列表里pop最后两次落子然后从棋盘对应位置取下这两次落子就OK了,并且保证只有我方回合可以悔棋以避免出现意料之外的逻辑出错。

认输按钮也没啥可说的,就是认输然后提前结束游戏。

接下来我们来实现一下联机对战,这里我们选择使用TCP/IP协议进行联机通信从而实现联机对战。先启动游戏的一方作为服务器端:

通过新开一个线程来实现监听:

threading.Thread(target=self.startListen).start()
'''开始监听客户端的连接'''
def startListen(self):while True:self.setWindowTitle('五子棋——彳余大胆 ——> 服务器端启动成功, 等待客户端连接中')self.tcp_socket, self.client_ipport = self.tcp_server.accept()self.setWindowTitle('五子棋——彳余大胆 ——> 客户端已连接, 点击开始按钮进行游戏')

后启动方作为客户端连接服务器端并发送客户端玩家的基本信息:

self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_socket.connect(self.server_ipport)
data = {'type': 'nickname', 'data': self.nickname}
self.tcp_socket.sendall(packSocketData(data))
self.setWindowTitle('五子棋——彳余大胆 ——> 已经成功连接服务器, 点击开始按钮进行游戏')

当客户端连接到服务器端时,服务器端也发送服务器端的玩家基本信息给客户端:

data = {'type': 'nickname', 'data': self.nickname}
self.tcp_socket.sendall(packSocketData(data))

然后客户端和服务器端都利用新开的线程来实现网络数据监听接收:

'''接收客户端数据'''
def receiveClientData(self):while True:data = receiveAndReadSocketData(self.tcp_socket)self.receive_signal.emit(data)
'''接收服务器端数据'''
def receiveServerData(self):while True:data = receiveAndReadSocketData(self.tcp_socket)self.receive_signal.emit(data)

并根据接收到的不同数据在主进程中做成对应的响应:

'''响应接收到的数据'''
def responseForReceiveData(self, data):if data['type'] == 'action' and data['detail'] == 'exit':QMessageBox.information(self, '提示', '您的对手已退出游戏, 游戏将自动返回主界面')self.goHome()elif data['type'] == 'action' and data['detail'] == 'startgame':self.opponent_player_color, self.player_color = data['data']self.whoseround = 'white'self.whoseround2nickname_dict = {self.player_color: self.nickname, self.opponent_player_color: self.opponent_nickname}res = QMessageBox.information(self, '提示', '对方请求(重新)开始游戏, 您为%s, 您是否同意?' % {'white': '白子', 'black': '黑子'}.get(self.player_color), QMessageBox.Yes | QMessageBox.No)if res == QMessageBox.Yes:data = {'type': 'reply', 'detail': 'startgame', 'data': True}self.tcp_socket.sendall(packSocketData(data))self.is_gaming = Trueself.setWindowTitle('五子棋——彳余大胆 ——> %s走棋' % self.whoseround2nickname_dict.get(self.whoseround))for i, j in product(range(19), range(19)):if self.chessboard[i][j]:self.chessboard[i][j].close()self.chessboard[i][j] = Noneself.history_record.clear()self.winner = Noneif self.winner_info_label:self.winner_info_label.close()self.winner_info_label = Noneself.chessman_sign.hide()else:data = {'type': 'reply', 'detail': 'startgame', 'data': False}self.tcp_socket.sendall(packSocketData(data))elif data['type'] == 'action' and data['detail'] == 'drop':pos = data['data']# 实例化一个棋子并显示c = Chessman(self.cfg.CHESSMAN_IMAGEPATHS.get(self.whoseround), self)c.move(QPoint(*Chesspos2Pixel(pos)))c.show()self.chessboard[pos[0]][pos[1]] = c# 落子声音响起self.drop_sound.play()# 最后落子位置标志对落子位置进行跟随self.chessman_sign.show()self.chessman_sign.move(c.pos())self.chessman_sign.raise_()# 记录这次落子self.history_record.append([*pos, self.whoseround])# 是否胜利了self.winner = checkWin(self.chessboard)if self.winner:self.showGameEndInfo()return# 切换回合方(其实就是改颜色)self.nextRound()elif data['type'] == 'action' and data['detail'] == 'givein':self.winner = self.player_colorself.showGameEndInfo()elif data['type'] == 'action' and data['detail'] == 'urge':self.urge_sound.play()elif data['type'] == 'action' and data['detail'] == 'regret':res = QMessageBox.information(self, '提示', '对方请求悔棋, 您是否同意?', QMessageBox.Yes | QMessageBox.No)if res == QMessageBox.Yes:pre_round = self.history_record.pop(-1)self.chessboard[pre_round[0]][pre_round[1]].close()self.chessboard[pre_round[0]][pre_round[1]] = Noneself.chessman_sign.hide()self.nextRound()data = {'type': 'reply', 'detail': 'regret', 'data': True}self.tcp_socket.sendall(packSocketData(data))else:data = {'type': 'reply', 'detail': 'regret', 'data': False}self.tcp_socket.sendall(packSocketData(data))elif data['type'] == 'reply' and data['detail'] == 'startgame':if data['data']:self.is_gaming = Trueself.setWindowTitle('五子棋——彳余大胆 ——> %s走棋' % self.whoseround2nickname_dict.get(self.whoseround))for i, j in product(range(19), range(19)):if self.chessboard[i][j]:self.chessboard[i][j].close()self.chessboard[i][j] = Noneself.history_record.clear()self.winner = Noneif self.winner_info_label:self.winner_info_label.close()self.winner_info_label = Noneself.chessman_sign.hide()QMessageBox.information(self, '提示', '对方同意开始游戏请求, 您为%s, 执白者先行.' % {'white': '白子', 'black': '黑子'}.get(self.player_color))else:QMessageBox.information(self, '提示', '对方拒绝了您开始游戏的请求.')elif data['type'] == 'reply' and data['detail'] == 'regret':if data['data']:pre_round = self.history_record.pop(-1)self.chessboard[pre_round[0]][pre_round[1]].close()self.chessboard[pre_round[0]][pre_round[1]] = Noneself.nextRound()QMessageBox.information(self, '提示', '对方同意了您的悔棋请求.')else:QMessageBox.information(self, '提示', '对方拒绝了您的悔棋请求.')elif data['type'] == 'nickname':self.opponent_nickname = data['data']

对战过程实现的基本逻辑和人机对战是一致的,只不过要考虑数据同步问题,所以看起来代码略多了一些。当然对于联机对战,我也做了一些小修改,比如必须点击开始按钮,并经过对方同意之后,才能正式开始对弈,悔棋按钮只有在对方回合才能按,对方同意悔棋后需要记得把落子方切换回自己。然后加了一个催促按钮,同样必须在对方回合才能按。其他好像也没什么特别的改动了。

好了,就这么简单的就完成了,大家有啥不懂的可以直接在下方评论就好了~
害,又是一天摸鱼的好时光呀~~

可以跟小伙伴联机对战的五子棋,你会吗?今天用Python教大家搞定!!快上车!相关推荐

  1. 五子棋联机对战(JAVA实现)含源码

    本次面向对象编程课程设计,是制作一款五子棋联机对战游戏.在上学期做过一款五子棋单机版,其耦合度低,便将其拿来直接制作联机版本. 主要应用的技术:C/S架构 JAVA swing,JAVA socket ...

  2. J2ME的五子棋游戏蓝牙联机对战研究

    taobao搜基于J2ME的五子棋游戏 蓝牙联机对战研究: 实现人人对战,人机对战,蓝牙联机对战功能,AI智能 蓝牙对战时,选择客户端或者是主机,如果是客户端,需要连接到另外的主机进行对战,如果是主机 ...

  3. winkawaks1.45如何联机?winkawaks1.45怎样联机对战(其他版本类似)

    <winkawaks1.45如何联机?winkawaks1.45怎样联机对战>是街机中国小编为大家汇集整理的winkawaks攻略,很多小伙伴还有winkawaks1.45如何联机?win ...

  4. 利用C++打造双人对战的五子棋游戏,界面新颖「附源码」

    五子棋游戏的历史可谓源远流长,是一款老少皆宜的两人对弈纯策略游戏,讲究的是有攻有守的五子棋技巧,玩法简单易上手,五个棋子连成一线就可获胜.单机五子棋,双人五子棋,好友联机对战模式-你想要的都能在这里找 ...

  5. 全套源码丨超实用的双人联机对战游戏开发分享,拒绝踩坑!

    在手游市场高度同质化的趋势下,随着各家手机厂商纷纷布局智慧大屏.平板.PC 等不同形态的设备,强调系统与生态侧的场景协同就成为了发展刚需,多终端协同游戏针对游戏体验本身,带来玩法上的更多可能性. Co ...

  6. 单机版五子棋java功能_java实现单机版和网络对战版五子棋程序

    [实例简介] java实现单机版和网络对战版五子棋,功能包括计时.悔棋.聊天.显示双方状态等等.详细见https://blog.csdn.net/qq_37913997/article/details ...

  7. 腾讯云为小游戏开发者升级工具箱 小游戏联机对战引擎免费用

    由微信小游戏举办的"微信小游戏创意大赛"正在火热进行中.12月23日,腾讯云宣布,除了给参赛者提供基础云资源,还将提供更多工具支持.开发者在通过初赛后,可免费使用腾讯云" ...

  8. c语言五子棋人机对弈算法,使用canvas基于AI算法实现人机对战之五子棋

    这是我使用canvas基于AI算法实现的人机对战之五子棋 黑棋是我 下了几局,真心下不过啊!!! 不说了,源码奉上: 人机大战之五子棋 canvas{ display: block; margin:5 ...

  9. 怎么开发联机小游戏_微信小游戏创意大赛火热进行中,小游戏联机对战引擎免费用...

    腾讯云为小游戏开发者升级工具箱 小游戏联机对战引擎免费用 由微信小游戏举办的"微信小游戏创意大赛"正在火热进行中.12月23日,腾讯云宣布,除了给创意大赛的参赛者提供基础云资源,还 ...

最新文章

  1. GPUImage实现水印
  2. Hadoop详解(二)——HDFS的命令,执行过程,Java接口,原理详解。RPC机制
  3. win7录屏_谁说Windows7没有自带录屏功能?教你一招轻松录制,不会用可惜了
  4. eda技术试卷_EDA技术试题库-试题库
  5. python之class
  6. 3dmax简单的bip制作
  7. c语言电子通讯录程序设计实验报告,定稿毕业设计通讯录c语言程序设计喜欢就下吧(电子版)...
  8. aircrack安装并破解wifi
  9. 蒙特卡洛积分 matlab,菜鸟之路——数学建模之蒙特卡罗积分(投点法,平均值法)+牛顿法解方程组MATLAB实现...
  10. 1.3寸OLED SH1106 IIC驱动显示错误解决方法
  11. mysql数据库储存过程
  12. VUE3 子传父 父传子 双向传递
  13. 计算机中的PS颜色填充快捷键,ps颜色填充快捷键【解决技巧】
  14. 看不见的竞争 带宽优化
  15. Fedora下载地址
  16. 这套ai的思维让我感到了一个细思极恐的开源项目
  17. STM32 F7的MAC层过滤使用+实例代码
  18. shell制作简易计算器和可能出现的“too many argument”问题的解决
  19. DS3231的使用(一)
  20. 阿里如何实现高性能分布式强一致的独立 Paxos 基础库?

热门文章

  1. Window Server 2012许可证过期解决方法
  2. Oracle KEEP的用法
  3. SQLServer从mdf和ldb还原数据库
  4. html5游戏ztype源码,Ztype打字游戏!
  5. 微信二维码线下推广是否有效
  6. C# Web页面打印网页
  7. win10安装红警运行出现FATALString Manager failed to initilaized properly
  8. 英语听力采用计算机化考试,一图读懂北京高考英语听力机考流程,附特点及应对建议...
  9. 2019秋招|从春招到秋招,Java岗经验总结(收获AT)
  10. 乒乓球比赛赛程_2018国际乒联乒乓球重大赛事详细赛程表,看点爆点早知道