Python程序设计:五子棋
tbghg

作 者 姓 名: 田冰航、吴华骅、钟希鸣
指 导 教 师: 张颖杰
项 目 整 理 日 期:2021 年6 月14 日
项 目 完 成 日 期:2021 年3 月7 日
所 属 学 校:大连理工大学

引 言

笔者只实现了五子棋的部分功能,编写的目的主要还是以巩固自己python的理解为主。

设计方法是以参考网上案例和个人理解相结合,在吸收他人的成果基础上进行类比和再创作。在进行程序设计时,笔者由于能力有限,算法和程序结构较为低级,代码冗长。

该程序的设计依据首先立足于用户的需求,为开发出能满足用户基本需求的程序,笔者参考了网上已有的程序框架进行编写。

这其中主要参考了CSDN上许多博主的代码,在此向笔者学习过的博主表示感谢。

该项目的设计重点,在功能模块上分为四个大模块,第一块是人人对战,第二块是人机对战,第三块是图形界面,第四块是琐碎功能模块。这些模块笔者将在后面进行详细介绍。

在此项目的编写中,笔者与钟希鸣、吴华骅三人一起组成了一个小组,在这一个月中,我们相互交流,相互讨论,最终完成了这个程序。

这其中比较有难度的地方是需要与队友实时沟通,如何合理地进行安排分工,如何进行更加高效正确地实现程序的对接,最终实现小组的完美配合。

该项目的设计重点,在于具有清晰的逻辑思维,避免因逻辑混乱而导致的大面积系统崩溃。

然后文件的读写也是难点。文件读写是程序必不可少的部分。文件读写将直接决定系统储存的数据能否再次使用,如果数据无法正常保存或输出,那么这个游戏的娱乐性将大打折扣

为了解决以上难点,笔者不断地学习相关知识,并观察分析实例代码,而为了真正理解代码,笔者也花费了大量时间用于编程操作,在实践中感悟总结。

程序设计思路

系统设计

根据功能要求,可将学生成绩管理系统的系统分为四个模块:人人对战、人机对战、图形界面、琐碎功能模块。各个模块相互关联、相互协调,共同进行信息管理。

功能设计

1.3.1 菜单页面

菜单页面分为两个层次,第一个开始界面,第二个为游戏进行中的菜单界面。

五子棋的规则约束主要由笔者进行编写,其中包括记录整个棋盘,检查是否胜利,检查此处是否可以落子等功能。

1.3.3 图形界面渲染

该部分主要保存于render.py文件中,包括张贴背景图片,张贴棋盘图片,张贴棋子,播放背景音乐等功能,主要是对该程序的图形界面进行渲染。

1.3.4 存盘复盘

存盘中先显示出6个存档位置,点击后就会将棋局现状图片和棋局状态存入savedata文件夹中进行保存。复盘时则会弹出六个存档的现状(有存档的位置粘贴棋局图片,无存档的位置粘贴背景图片),在点击后,即会进入相应的存档,并将棋局状态读取出来,继续下棋。

1.3.5 悔棋及显示上一步落子

悔棋和显示上一步落子则是开创了一个二维的列表保存最近走过的四步,并不断更新,并将上两步标记出来。当进行悔棋时,则将棋盘中最近下的三四步的状态设置为空,将三四步移至列表位置的一二步。

1.3.6 人机对战

人机对战主要采用对加分制,对各个棋局状态进行加减分,最后得到整个棋局的分数,选择最佳的下棋点。

程序流程模块组成

2.1 程序流程图

五子棋的程序流程如图2.1:

image

图2.1 程序流程图

程序开始运行后,赋值n0 =True进入选择的循环,当选择相应模块后将n0设置为False,退出循环进而实现界面选择的功能。

进入棋局界面后,接收用户点击的位置并进行判断:若为悔棋,则将棋盘中最近下的三四步的状态设置为空,将三四步移至列表位置的一二步;若为菜单,则弹出菜单界面,并等待界面;若为下棋,则将相应的位置设置为指定的棋子状态;若为无效按键,则直接进行下一操作;若为键盘按键ESC,则弹出菜单。

3 文件IO模式

3.1 文件写入代码

在笔者的存档、复档中,笔者通过主要通过file.write()、file.read()函数完成文件的读写。笔者的一段代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def Save_Data(self, num, map, chess_state, enable_ai, isAI):
if self.Data_Have[num-1]:
os.remove(SAVE_PATH + str(num) + '.jpg')
fl = open(SAVE_PATH+ str(num)+ ".txt", "w+")

for i in map:
for j in i:
fl.write(str(j))
fl.write(' ')
fl.write('\n')

if chess_state == ChessboardState.WHITE :
state = 2
else :
state = 1
fl.write(str(state) + '\n') # 下一步为黑棋还是白棋
if enable_ai :
fl.write(str(1) + '\n') # 本局是否为人机对战
else:
fl.write(str(0) + '\n')
if isAI :
fl.write(str(1) + '\n') # 若为人机对战下一步是否为人
else:
fl.write(str(0) + '\n')
fl.close()

其中map为棋局状态,enable_ai为是否使用AI,state是当前下棋方,1代表黑棋方,2代表白棋方。

读档则是与之相对应进行读取、判断,最后获得曾将存档的状态,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def Read_Data(self, num):
fl = open(SAVE_PATH + str(num) + ".txt", "r")
i=0
map = []
lines = fl.readlines()
for msg in lines:
i+=1
if i <= 15 :
msg = msg.strip('\n')
adm = msg.split(' ')
map.append(adm)
if i == 16 :
chess_state = int(msg)
if i == 17 :
msg = int(msg)
self.enable_ai = bool(msg)
if i == 18:
msg = int(msg)
self.isAI = bool(msg)
for j in range(15):
for i in range (15):
if int(map[j][i]) == 0:
self.board[j][i] = ChessboardState.EMPTY
if int(map[j][i]) == 1:
self.board[j][i] = ChessboardState.BLACK
if int(map[j][i]) == 2:
self.board[j][i] = ChessboardState.WHITE

fl.close()
if chess_state == 1:
self.chess_state = ChessboardState.BLACK
elif chess_state == 2 :
self.chess_state = ChessboardState.WHITE

4 关键代码&算法分析

4.1 人机对战时AI的分析

人机对战的部分代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# M:代表我的棋子,P:代表对方的棋子或超出范围,X:表示此处为空
if m_range == 5:
count[FIVE] += 1

# 活四:XMMMMX
# 冲四:XMMMMP, PMMMMX
if m_range == 4:
left_empty = right_empty = False
if line[left_idx - 1] == empty:
left_empty = True
if line[right_idx + 1] == empty:
right_empty = True
if left_empty and right_empty:
count[FOUR] += 1
elif left_empty or right_empty:
count[SFOUR] += 1

# 冲四: MXMMM, MMMXM 两种可同时存在
# 活三: XMMMXX, XXMMMX
# 眠三: PMMMX, XMMMP, PXMMMXP
if m_range == 3:
left_empty = right_empty = False
left_four = right_four = False
if line[left_idx - 1] == empty:
if line[left_idx - 2] == mine: # MXMMM
setRecord(self, x, y, left_idx - 2, left_idx - 1, dir_index, dir)
count[SFOUR] += 1
left_four = True
left_empty = True

计算分数的方式是考虑棋盘上所有的情况,例如所有的死四、活三、跳三等,并对不同场面进行赋分,敌方场面进行减分,最后得出最优状况并进行下棋。

5 曾经遇到的困难和解决方法

5.1 AI导入的问题

笔者最开始尝试将AI导入时,曾想投机取巧,直接从网络中进行搬运,而后面在进行棋局匹配时发现根本无法对应上,最终又重新填写造成较多不必要的麻烦。

后面笔者在参考了网上的多个案例后,独立编写AI的基本框架,并借鉴了部分网上的计分代码。

在经历了对python的系统学习后,笔者成功地实现了五子棋的基本功能,虽然有着较多的困难,但都在网络和队友的帮助下一一化解,最终完成这个项目。

笔者回顾了之前的错误,发现还是python基础知识掌握不够牢固,这是基本功问题,与高深的算法无关,因此很多编写程序中的问题最终都归于一点:革命尚未成功,笔者仍需努力。

5.2 逻辑错误

相较于语法错误,逻辑错误显得更为可怕,通常会遇见半小时写函数三小时调试的问题。在编写系统前必须拥有较为清晰的逻辑架构,在此架构的基础上再选择去敲代码,这样能避免较多的问题。不过幸运的是python是单句进行执行,所以遇到问题可以较为明确的看出来,提高自己的编写效率。

6 对程序设计的理解

程序设计需要清晰的思路,只有思路正确才不会走较多的弯路,并凝练出较为简洁的算法。

算法是一个程序的核心,算法的高级与否直接会影响到编写程序的难易。

而算法依赖数学知识,现在所学的高数和线型代数都是在为编程打基础,笔者刚接触矩阵时曾怀疑其实用价值,但当了解到人工智能依靠矩阵进行计算时,笔者更切实地感受到基础学科知识对编程的重要性。

现阶段笔者仅仅接触过C语言和python,但在使用过这两个语言后,笔者较为明确了面对过程编程和面对对象编程的区别。

不管算法也好,编程思想也罢,一切都建立在语法的基础上,若连语法都不懂又怎可能使系统运行呢?而此系统则只运用了一些低级算法和低级语法,笔者也将在日后的学习中对该系统不断进行完善。

效果演示

开始界面

image

开始界面

游戏界面

image

游戏界面

游戏复盘

image

游戏复盘

结 论

笔者曾经认为程序设计是及其风光的一件事,一个人,一台电脑,一顿乱打,一个惊喜,而在我真正设计时才真正了解到真实情况:一个人,一台电脑,一个月,一个系统,一具尸体。

其实写代码也并非那么煎熬,每当你调试成功时,总会有满满的成就感用处,也正是在这一步一步的喜悦下笔者将这个系统坚持做了下来。

虽然在学校没有学习python课程,但还是要进行自学的,而这次自学经历也让笔者充分的探索了自学的方法,受益匪浅

在科中的日子算不上多苦多累,这中间也有着较多的欢乐,突如其来的寒假作业虽然算不上多累,但不得不说也占用了我较多的娱乐时间,不过感觉做出来后成就感还是满满的。

最后祝愿电气创新实践基地能够越办越好。

参考文献

[1] 菜鸟教程

[2] 一枚新手程序媛:《Python写五子棋游戏》

[3] xerwin:《[深度学习]实现一个博弈型的AI,从五子棋开始》

[4] marble_xu:《python 五子棋AI实现(2):棋型评估函数实现》

[5] 在校学渣一枚:《python写五子棋小游戏(pygame模块)》

致 谢

能完成我的五子棋,我需要对以下的人进行感谢。

首先感谢电气创新实践基地能够给我提供这样的机会让我能够比较系统地学习python。

然后我要感谢软件组的学长学姐的辛苦付出和指导教诲,你们让我体验到了科中精神:积极进取、求真务实、传承奉献。

我要特别感谢我的队友,假期中真是与他们的不断交流才让我有了不断学习的动力,在我遇到问题无法解决之际,正是他们的指导让我走出迷茫,继续前行。

最后也必须要感谢下我自己,坚持了下来,没有让自己后悔。

PS

项目已发布至GitHub

 评论
评论插件加载失败
正在加载评论插件