如何利用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 相关文章推荐
MySQL中表的复制以及大型数据表的备份教程
Nov 25 Python
解决python3.5 正常安装 却不能直接使用Tkinter包的问题
Feb 22 Python
python对常见数据类型的遍历解析
Aug 27 Python
python 公共方法汇总解析
Sep 16 Python
Python实现报警信息实时发送至邮箱功能(实例代码)
Nov 11 Python
python通过matplotlib生成复合饼图
Feb 06 Python
PyTorch在Windows环境搭建的方法步骤
May 12 Python
Python 字典中的所有方法及用法
Jun 10 Python
pycharm激活码免费分享适用最新pycharm2020.2.3永久激活
Nov 25 Python
使用gunicorn部署django项目的问题
Dec 30 Python
Python基础之数据类型知识汇总
May 18 Python
PyTorch 如何自动计算梯度
May 23 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
phpmyadmin 访问被拒绝的真实原因
2009/06/15 PHP
PHP中使用CURL模拟登录并获取数据实例
2014/07/01 PHP
codeigniter上传图片不能正确识别图片类型问题解决方法
2014/07/25 PHP
谈谈PHP中substr和substring的正确用法及相关参数的介绍
2015/12/16 PHP
js截取小数点后几位的写法
2013/11/14 Javascript
JavaScript和JQuery的鼠标mouse事件冒泡处理
2015/06/19 Javascript
jquery实现实时改变网页字体大小、字体背景色和颜色的方法
2015/08/05 Javascript
好好了解一下Cookie(强烈推荐)
2016/06/14 Javascript
vue2.0使用Sortable.js实现的拖拽功能示例
2017/02/21 Javascript
JavaScript无阻塞加载和defer、async详解
2017/02/26 Javascript
js实现添加删除表格(两种方法)
2017/04/27 Javascript
探索Vue高阶组件的使用
2018/01/08 Javascript
vue将单页面改造成多页面应用的方法
2018/11/25 Javascript
vue项目中实现的微信分享功能示例
2019/01/21 Javascript
JavaScript 判断iPhone X Series机型的方法
2019/01/28 Javascript
JavaScript实现文件下载并重命名代码实例
2019/12/12 Javascript
VUE : vue-cli中去掉路由中的井号#操作
2020/09/04 Javascript
Pyramid将models.py文件的内容分布到多个文件的方法
2013/11/27 Python
天翼开放平台免费短信验证码接口使用实例
2013/12/18 Python
实例讲解python函数式编程
2014/06/09 Python
Python缩进和冒号详解
2016/06/01 Python
Python正则表达式完全指南
2017/05/25 Python
python中map()函数的使用方法示例
2017/09/29 Python
Python实现的多进程和多线程功能示例
2018/05/29 Python
Python实用技巧之利用元组代替字典并为元组元素命名
2018/07/11 Python
使用Python的datetime库处理时间(RPA流程)
2019/11/24 Python
给Python学习者的文件读写指南(含基础与进阶)
2020/01/29 Python
Anaconda使用IDLE的实现示例
2020/09/23 Python
使用CSS3中的calc()属性来以算式表达尺寸数值
2016/06/06 HTML / CSS
美国波西米亚风格服装品牌:Show Me Your Mumu
2018/01/05 全球购物
FLOS美国官网:意大利高级照明工艺的传奇
2018/08/07 全球购物
Europcar西班牙:全球汽车租赁领域的领导者
2018/09/17 全球购物
致铅球运动员加油稿
2014/02/13 职场文书
中国入世承诺
2014/04/01 职场文书
学习十八大的心得体会
2014/09/01 职场文书
2015年科室工作总结
2015/04/10 职场文书