如何利用pygame实现简单的五子棋游戏


Posted in Python onDecember 29, 2019

前言

写程序已经丢掉很长一段时间了,最近觉得完全把技术丢掉可能是个死路,还是应该捡起来,所以打算借CSDN来记录学习过程, 由于以前没事的时候断断续续学习过python和用flask框架写过点web,所以第一步想捡起python,但是,单纯学习python有点枯燥,正好看到pygame,感觉还挺简单,所以想先写个小游戏练练手。

准备

python基础相关准备:

  1. python基础知识准备,廖雪峰的python基础知识简单好学,熟悉python基本的语法, 链接地址
  2. pygame的基础知识,参考目光博客的“用Python和Pygame写游戏-从入门到精通”, 链接地址
  3. 安装python 3.8.0 在python官网下载,不多说。
  4. 安装pygame,命令:pip install pygame
  5. 如安装较慢,可以参考如下命令,更改pip源为国内镜像站点:
    pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

计划

准备完成五子棋单机人机游戏,目前已完成界面以及判定输赢等功能,还未加入电脑AI,以后有时间再加(不知是否会坑),目前实现主要功能如下:

  1. 五子棋界面的绘制,鼠标左键点击落子(黑子先下,黑白子交替顺序)。
  2. 判定黑子或白子五子连珠。
  3. 一方胜利后弹出提示,结束游戏。

游戏界面是下面这个样子:

如何利用pygame实现简单的五子棋游戏

开始

设计思路

整个游戏的核心是将棋盘分成两个层面,第一个层面是物理层面上的,代表在物理像素的位置,主要用于绘图等操作,另外一个层面是将棋盘抽象成15*15的一个矩阵,黑子和白子是落在这个矩阵上的某个位置,具体位置用坐标(i,j)(0<=i,j<15)来表示,主要用于判断输赢和落子等。

  1. 棋盘的绘制,网上有棋盘和黑白子的图片资源可以下载使用,我下载后由于棋盘图片格子线像素位置不太精确,所以自己用ps做了一张544544的木质背景图,然后用程序来绘制棋盘线(如果PS更熟悉点的话,建议棋盘格线之类就画在棋盘背景图上),棋盘格线上下左右空20像素,棋盘格子大小36像素,网上下载的棋子大小是3232像素的。
  2. 输赢的判断,由于未出输赢的时候肯定没有五子连成线的,所以只需要判断最后落子位置的横、竖、斜、反斜四个方向上有没有五子连成线即可。

主要代码

1、main函数,pygame的主要控制流程,缩写代码如下:

def main():
 pygame.init() #pygame初始化
 size = width,height = 544,544
 screen = pygame.display.set_mode(size, 0, 32)
 pygame.display.set_caption('五子棋')
 font = pygame.font.Font('simhei.ttf', 48)
 clock = pygame.time.Clock() #设置时钟
 game_over = False
 renju = Renju() # Renju是核心类,实现落子及输赢判断等
 renju.init() # 初始化

 while True:
 clock.tick(20) # 设置帧率
 for event in pygame.event.get():
 if event.type == pygame.QUIT:
 sys.exit()
 if event.type == pygame.MOUSEBUTTONDOWN and (not game_over):
 if event.button == 1: # 按下的是鼠标左键
  i,j = renju.get_coord(event.pos) # 将物理坐标转换成矩阵的逻辑坐标
  if renju.check_at(i, j): # 检查(i,j)位置能否被占用,如未被占用返回True
  renju.drop_at(i, j) # 在(i,j)位置落子,该函数将黑子或者白子画在棋盘上
  if renju.check_over(): # 检查是否存在五子连线,如存在则返回True
  text = ''
  if renju.black_turn: #check_at会切换落子的顺序,所以轮到黑方落子,意味着最后落子方是白方,所以白方顺利
  text = '白方获胜,游戏结束!'
  else:
  text = '黑方获胜,游戏结束!'
  gameover_text = font.render(text, True, (255,0,0))
  renju.chessboard().blit(gameover_text, (round(width/2-gameover_text.get_width()/2), round(height/2-gameover_text.get_height()/2)))
  game_over = True
  else:
  print('此位置已占用,不能在此落子')
 
 screen.blit(renju.chessboard(),(0,0))
 pygame.display.update()
 pygame.quit()

2、renju类,核心类,落子及判断输赢等操作,代码如下:

Position = namedtuple('Position', ['x', 'y'])

class Renju(object):
 
 background_filename = 'chessboard.png'
 white_chessball_filename = 'white_chessball.png'
 black_chessball_filename = 'black_chessball.png'
 top, left, space, lines = (20, 20, 36, 15) # 棋盘格子位置相关???
 color = (0, 0, 0) # 棋盘格子线颜色
 
 black_turn = True # 黑子先手
 ball_coord = [] # 记录黑子和白子逻辑位置
 
 def init(self):
 try:
 self._chessboard = pygame.image.load(self.background_filename)
 self._white_chessball = pygame.image.load(self.white_chessball_filename).convert_alpha()
 self._black_chessball = pygame.image.load(self.black_chessball_filename).convert_alpha()
 self.font = pygame.font.SysFont('arial', 16)
 self.ball_rect = self._white_chessball.get_rect()
 self.points = [[] for i in range(self.lines)]
 for i in range(self.lines):
 for j in range(self.lines):
  self.points[i].append(Position(self.left + i*self.space, self.top + j*self.space))
 self._draw_board()
 except pygame.error as e:
 print(e)
 sys.exit()
 
 def chessboard(self):
 return self._chessboard
 
 # 在(i,j)位置落子 
 def drop_at(self, i, j):
 pos_x = self.points[i][j].x - int(self.ball_rect.width/2)
 pos_y = self.points[i][j].y - int(self.ball_rect.height/2)

 ball_pos = {'type':0 if self.black_turn else 1, 'coord':Position(i,j)}
 if self.black_turn: # 轮到黑子下
 self._chessboard.blit(self._black_chessball, (pos_x, pos_y))
 else:
 self._chessboard.blit(self._white_chessball, (pos_x, pos_y)) 
 
 self.ball_coord.append(ball_pos) # 记录已落子信息
 self.black_turn = not self.black_turn # 切换黑白子顺序
 
 # 画棋盘上的格子线,如果棋盘背景图做的足够精确,可省略此步骤
 def _draw_board(self): 
 # 画坐标数字
 for i in range(1, self.lines):
 coord_text = self.font.render(str(i), True, self.color)
 self._chessboard.blit(coord_text, (self.points[i][0].x-round(coord_text.get_width()/2), self.points[i][0].y-coord_text.get_height()))
 self._chessboard.blit(coord_text, (self.points[0][i].x-coord_text.get_width(), self.points[0][i].y-round(coord_text.get_height()/2)))
 
 for x in range(self.lines):
 # 画横线
 pygame.draw.line(self._chessboard, self.color, self.points[0][x], self.points[self.lines-1][x])
 # 画竖线
 pygame.draw.line(self._chessboard, self.color, self.points[x][0], self.points[x][self.lines-1])
 
 # 判断是否已产生胜方
 def check_over(self):
 if len(self.ball_coord)>8: # 只有黑白子已下4枚以上才判断
 direct = [(1,0),(0,1),(1,1),(1,-1)] #横、竖、斜、反斜 四个方向检查
 for d in direct:
 if self._check_direct(d):
  return True
 return False
 
 # 判断最后一个棋子某个方向是否连成5子,direct:(1,0),(0,1),(1,1),(1,-1)
 def _check_direct(self, direct):
 dt_x, dt_y = direct 
 last = self.ball_coord[-1]
 line_ball = [] # 存放在一条线上的棋子
 for ball in self.ball_coord:
 if ball['type'] == last['type']:
 x = ball['coord'].x - last['coord'].x 
 y = ball['coord'].y - last['coord'].y
 if dt_x == 0:
  if x == 0:
  line_ball.append(ball['coord'])
  continue
 if dt_y == 0:
  if y == 0:
  line_ball.append(ball['coord'])
  continue
 if x*dt_y == y*dt_x:
  line_ball.append(ball['coord'])

 if len(line_ball) >= 5: # 只有5子及以上才继续判断
 sorted_line = sorted(line_ball)
 for i,item in enumerate(sorted_line): 
 index = i+4
 if index < len(sorted_line):
  if dt_x == 0:
  y1 = item.y
  y2 = sorted_line[index].y
  if abs(y1-y2) == 4: # 此点和第5个点比较y值,如相差为4则连成5子
  return True
  else:
  x1 = item.x
  x2 = sorted_line[index].x
  if abs(x1-x2) == 4: # 此点和第5个点比较x值,如相差为4则连成5子
  return True
 else:
  break
 return False
 
 # 检查(i,j)位置是否已占用 
 def check_at(self, i, j):
 for item in self.ball_coord:
 if (i,j) == item['coord']:
 return False
 return True
 
 # 通过物理坐标获取逻辑坐标 
 def get_coord(self, pos):
 x, y = pos
 i, j = (0, 0)
 oppo_x = x - self.left
 if oppo_x > 0:
 i = round(oppo_x / self.space) # 四舍五入取整
 oppo_y = y - self.top
 if oppo_y > 0:
 j = round(oppo_y / self.space)
 return (i, j)

Renju类有几个函数说明:

  • init()方法主要做了几件事:
    • 载入资源,建立了_chessboard这个棋盘的surface对象
    • 计算棋盘所有落子点的物理坐标,并存放如points属性中,points是个二维数组,这样points[i][j]就可以表示逻辑位置(i,j)所对应的物理坐标了。
    • 调用_draw_board()方法,在_chessboard上画格线及标注等。
  • drop_at(i,j)方法,在逻辑位置(i,j)落子,至于是落白子和黑子通过Renju类的控制开关black_turn来决定。画图,并将已落子信息存入ball_coord列表中。
  • check_at(i,j)方法,通过遍历ball_coord列表来查看(i,j)位置是否能落子。
  • check_over()方法判断是否存在五子连线的情况,主要通过调用_check_direct方法分别判断四个方向上的情况。
  • _check_direct(direct)方法是判断五子连线的主要逻辑,通过判断最后一颗落子的某个方向落子实现。

结束

主要功能大概是这些,源码及程序中用到的图片等可以在我的资源中下载,或者github下载, 下载地址

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
Python中Class类用法实例分析
Nov 12 Python
利用Python获取赶集网招聘信息前篇
Apr 18 Python
使用pyecharts无法import Bar的解决方案
Apr 23 Python
python+selenium实现京东自动登录及秒杀功能
Nov 18 Python
解决PyCharm控制台输出乱码的问题
Jan 16 Python
几个适合python初学者的简单小程序,看完受益匪浅!(推荐)
Apr 16 Python
python安装scipy的方法步骤
Jun 26 Python
wxPython实现列表增删改查功能
Nov 19 Python
Python random库使用方法及异常处理方案
Mar 02 Python
Pytorch生成随机数Tensor的方法汇总
Sep 09 Python
python3美化表格数据输出结果的实现代码
Apr 14 Python
在Python 中将类对象序列化为JSON
Apr 06 Python
Python使用正则实现计算字符串算式
Dec 29 #Python
Django框架教程之中间件MiddleWare浅析
Dec 29 #Python
三个python爬虫项目实例代码
Dec 28 #Python
python scrapy重复执行实现代码详解
Dec 28 #Python
Python统计时间内的并发数代码实例
Dec 28 #Python
如何基于python实现脚本加密
Dec 28 #Python
python使用配置文件过程详解
Dec 28 #Python
You might like
Win2000+Apache+MySql+PHP4+PERL安装使用小结
2006/10/09 PHP
apache rewrite_module模块使用教程
2008/01/10 PHP
PHP开发者常犯的10个MySQL错误更正剖析
2012/01/30 PHP
php批量删除数据库下指定前缀的表以prefix_为例
2014/08/24 PHP
PHP的swoole扩展安装方法详细教程
2016/05/18 PHP
关于js中window.location.href,location.href,parent.location.href,top.location.href的用法与区别
2010/10/18 Javascript
onkeyup,onkeydown和onkeypress的区别介绍
2013/10/21 Javascript
基于NodeJS的前后端分离的思考与实践(一)全栈式开发
2014/09/26 NodeJs
使用jQuery实现input数值增量和减量的方法
2015/01/24 Javascript
AngularJS 中的指令实践开发指南(一)
2016/03/20 Javascript
浏览器复制插件zeroclipboard使用指南
2016/03/26 Javascript
Bootstrap插件全集
2016/07/18 Javascript
利用C/C++编写node.js原生模块的方法教程
2017/07/07 Javascript
Vue.js2.0中的变化小结
2017/10/24 Javascript
深入理解Vue 单向数据流的原理
2017/11/09 Javascript
vue自定义全局组件(自定义插件)的用法
2018/01/30 Javascript
jQuery常见的遍历DOM操作详解
2018/09/05 jQuery
在vue中多次调用同一个定义全局变量的实例
2018/09/25 Javascript
为什么Vue3.0使用Proxy实现数据监听(defineProperty表示不背这个锅)
2019/10/14 Javascript
微信小程序自定义yPicker组件实现省市区三级联动功能
2020/10/29 Javascript
[00:35]2016完美“圣”典风云人物:冷冷宣传片
2016/12/08 DOTA
在Python中用keys()方法返回字典键的教程
2015/05/21 Python
详谈Python基础之内置函数和递归
2017/06/21 Python
Python 200行代码实现一个滑动验证码过程详解
2019/07/11 Python
Python爬虫谷歌Chrome F12抓包过程原理解析
2020/06/04 Python
Pycharm2020.1安装无法启动问题即设置中文插件的方法
2020/08/07 Python
详解Canvas实用库Fabric.js使用手册
2019/01/07 HTML / CSS
日本即尚网:JSHOPPERS.com(支持中文)
2019/12/03 全球购物
银行学习十八大感想
2014/01/11 职场文书
董事长秘书职责
2014/01/31 职场文书
cf收人广告词大全
2014/03/14 职场文书
党支部先进事迹材料
2014/12/24 职场文书
检讨书范文2000字
2015/01/28 职场文书
劳务派遣管理制度(样本)
2019/08/23 职场文书
Java Kafka 消费积压监控的示例代码
2021/07/01 Java/Android
动画电影《擅长捉弄人的高木同学》6月10日上映!
2022/03/20 日漫