如何利用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在命令行下使用google翻译(带语音)
Jan 16 Python
python基础教程之元组操作使用详解
Mar 25 Python
Python中集合类型(set)学习小结
Jan 28 Python
安装ElasticSearch搜索工具并配置Python驱动的方法
Dec 22 Python
Python 专题二 条件语句和循环语句的基础知识
Mar 19 Python
在Python中使用AOP实现Redis缓存示例
Jul 11 Python
详解Python中的Numpy、SciPy、MatPlotLib安装与配置
Nov 17 Python
Python数据结构与算法之完全树与最小堆实例
Dec 13 Python
使用python编写简单的小程序编译成exe跑在win10上
Jan 15 Python
python如何将两个txt文件内容合并
Oct 18 Python
TensorFlow tf.nn.conv2d实现卷积的方式
Jan 03 Python
Python基于mediainfo批量重命名图片文件
Dec 29 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
SONY ICF-SW55的电路分析
2021/03/02 无线电
PHP 编写的 25个游戏脚本
2009/05/11 PHP
PHP 查找字符串常用函数介绍
2012/06/07 PHP
基于empty函数的输出详解
2013/06/17 PHP
PHP实现事件机制实例分析
2015/06/26 PHP
php 算法之实现相对路径的实例
2017/10/17 PHP
php源码的使用方法讲解
2019/09/26 PHP
基于prototype的validation.js发布2.3.4新版本,让你彻底脱离表单验证的烦恼
2006/12/06 Javascript
jquery获取URL中参数解决中文乱码问题的两种方法
2013/12/18 Javascript
js判断登录与否并确定跳转页面的方法
2015/01/30 Javascript
jQuery获取上传文件的名称的正则表达式
2015/05/21 Javascript
jquery实现的动态回到顶部特效代码
2015/10/28 Javascript
浅析node连接数据库(express+mysql)
2015/11/30 Javascript
angular十大常见问题
2017/03/07 Javascript
JavaScript中的工厂函数(推荐)
2017/03/08 Javascript
express框架实现基于Websocket建立的简易聊天室
2017/08/10 Javascript
浅谈angularJS的$watch失效问题的解决方案
2017/08/11 Javascript
最全正则表达式总结:验证QQ号、手机号、Email、中文、邮编、身份证、IP地址等
2017/08/16 Javascript
vue中axios处理http发送请求的示例(Post和get)
2017/10/13 Javascript
轻松理解vue的双向数据绑定问题
2017/10/30 Javascript
vue-router路由懒加载和权限控制详解
2017/12/13 Javascript
小程序实现左右来回滚动字幕效果
2018/12/28 Javascript
jquery UI实现autocomplete在获取焦点时得到显示列表功能示例
2019/06/04 jQuery
[26:21]浴火之凤-TI4世界冠军Newbee战队纪录片
2014/08/07 DOTA
python操作数据库之sqlite3打开数据库、删除、修改示例
2014/03/13 Python
利用Python抓取行政区划码的方法
2016/11/28 Python
Python制作简易注册登录系统
2016/12/15 Python
python人民币小写转大写辅助工具
2018/06/20 Python
Python使用LDAP做用户认证的方法
2019/06/20 Python
pymysql的简单封装代码实例
2020/01/08 Python
美国排名第一的在线葡萄酒商店:Wine.com
2016/09/07 全球购物
美国高端牛仔品牌:Silver Jeans
2019/12/12 全球购物
linux面试题参考答案(7)
2012/10/29 面试题
防灾减灾日活动总结
2014/08/26 职场文书
护士求职自荐信
2015/03/25 职场文书
学生通报表扬范文
2015/05/04 职场文书