代码链接:
https://github.com/zhouzg/FiveChess/tree/master
https://download.csdn.net/download/dreamlike_zzg/10948075

VS2017,控制台输出字符显示棋盘。共定义的6个class,即棋子类(Chess)、棋盘类(ChessBoard)、棋手类(Player)、裁判类(Judge)、显示类(Displayer)、游戏类(Game)。有人机对弈人人对弈两种模式,时间原因AI下棋落子是随机的。

序:

建国70年2月1日周五,寒假首flag告拔——以C++编作五子棋。上学期,求学于雁西湖畔,受教于杨LX大师,获益匪浅,相见恨晚。作五子棋乃杨之令,然选课未果,虽遵令而学分犹不可得。当是时,期末众考威逼咄咄,不得顾,遂妥,暂留坑。1月29日始填坑,醉坑三日有余,今日坑毕时,如梦初醒,方觉此乃吾独作纯软project之首例,悲喜交加,不可断绝。何喜之有?首例终竣,从无生有,质变也。何悲之有?羞才疏学浅,恨竣工时晚,实愧也。感慨良多,情溢于言表,遂逐此文,以自勉。诚求各路贤士不吝赐教,请洒潘江,各倾陆海。

正文

程序按面向对象的方式编写,共定义的6个class,即棋子类(Chess)、棋盘类(ChessBoard)、棋手类(Player)、裁判类(Judge)、显示类(Displayer)、游戏类(Game)。其中棋手类作为基类派生出真人棋手类(Human)和电脑棋手类(AI)。由于本人写五子棋的目的为体会C++的面向对象编程,再加上个人时间能力有限,所以没有给AI什么策略,只让其随机落子。毕竟策略设计与面向对象编程关系不大。

接下来主要分享下本人关于这5个类设计的一点思考。在课上获得了些体会:C++比C语言难,不难在语法,而是难在对面向对象的理解和类的设计。类的设计有些艺术的味道,设计没有对错之分,但好的设计有很强的复用性,可以为以后的工作带来便利,节省时间。杨大师说,省时间就是省钱!

下面仅是我的分类设计,大家有不同想法欢迎留言讨论。

Chess类

定义棋子,棋子包含颜色、位置。

ChessBoard类

定义棋盘,主要包括棋盘的大小、每个位置的状态(是否有棋子)、添棋子和删棋子操作,棋谱,显示棋盘所需的字符。

Player类

定义棋手,主要包括棋手姓名、执棋子的颜色、下棋操作。Human和AI作为其派生类。

Judge类

判断落子棋子是否合法、是否有人获胜、执行悔棋操作。

Displayer类

负责所有的显示任务,包括显示棋盘和提示信息。

Game类

负责整个游戏的流程。

接下来谈一下设计类时让我有些纠结的地方

“悔棋”由哪个类来负责?

这个是上课讨论的一大焦点,主要分为两派:1.由棋手类负责。因为悔棋请求是由棋手发起的,而且现实中把棋盘上的棋子拿掉也需要人来执行。2.由裁判类负责。因为棋盘的状态变动都需要裁判类判断是否合法,而且悔棋需所有棋手都同意才行,有时不能悔棋,需要第三方调解。

个人观点:悔棋请求由玩家发起,撤回棋子由棋盘类负责,而裁判类决定是否撤棋子。

首先本人不同意第一个观点,因为撤棋子不一定非要玩家执行。因为悔棋时撤回的棋子是固定的,即最新下出的那步棋,这是由当前棋局决定的,所以不需棋手来额外判断。如果把该程序复用到多人棋类游戏中,由棋手类负责悔棋的话,悔棋时需要每个棋手设法获取自己所有落子的顺序,并轮流执行撤棋操作,感觉好麻烦。

若想悔棋多次,则需要获取棋盘中每个棋子的先后顺序,也就是要维护一个棋谱,而棋谱和棋盘状态均是棋盘类的成员,所以很自然就让棋盘类来执行撤棋操作。

悔棋需要所有玩家都同意才可执行,所以需要第三方来协调,这是裁判的职能。

这样设计给人的感觉是我们的棋盘桌是“电动”的,有个按钮,按下则棋盘自动把最近的那几步棋撤走。而按钮的掌控权在裁判手里,他会根据局势判断是否按下这个按钮。

还有一点,判断悔棋是否可以执行是在Judge的成员函数内完成?还是在游戏进行的流程中完成(即Game类中)?我是按照后者实现的。考虑到程序的复用性,我希望除Game类外的其他类的功能尽可能的独立于游戏流程。这样将程序修改为其他棋类时,由于不同的棋局进行流程可能不同,所以Game类的修改无法避免,但其他类的改动会相对较小。

“棋手轮换”在哪里实现?

棋盘类游戏都是玩家轮流下出棋子。所以Player *current_player(指向当前要出棋的玩家)所指向的对象要不断改变。“棋手轮换”在哪里实现与“判断悔棋是否可以执行” 在哪里实现面临的问题相似。由Judge来决定当前轮到谁出棋似乎很合理,但是“轮到谁”是游戏进行的流程决定的,轮到谁了就是谁,是很客观的,无需裁决。五子棋流程简单,两个玩家一替一次交换就行了,两种方式实现起来都不困难,但对于较复杂的游戏来说,可能出现次序中途有变动的情况(比如“大富翁”里强制休息一轮,“飞行棋”先到终点的玩家不必参加下一轮)。这时Judge想要判断下一步轮到谁了,还要从游戏进行情况来判断,需要许多描述游戏当前状况的参数,直接在Game类的游戏流程里实现似乎更方便些。

为何需要Displayer类?

本程序是在控制台里输出,直接cout就行了,为何要搞个显示类,再在Displayer的成员函数中使用cout?岂不多此一举?这里是为了方便修改为其他显示方式,修改时只需将成员函数中的cout替换就行了。

Player类的设计

       棋手分为真人玩家(Human)和电脑玩家(AI),均从Player类中派生得到。Human与AI的区别有两处:1. Human可以悔棋,AI不用2.落子方式不同,即成员函数GiveChess()不同。区别1容易处理,主要谈谈区别2。Human出棋需要手动输入棋子坐标,需要传参,即Human.GiveChess(Position p),而AI出棋不需要传参,根据棋局便可决定落子位置,即AI.GiveChess( )或AI.GiveChess(ChessBoard board)。这就导致了两个派生类存在同名不同参的成员函数,给多态性的保证造成影响。

Player类里将GiveChess定义为纯虚函数virtual Chess GiveChess( ) = 0;在派生类中直接分别重写Chess Human::GiveChess(Position p)和Chess AI::GiveChess( )的话,无法对基类虚函数覆盖,只是将其隐藏。执行下列语句必然error,

Player *ptr;

Human human;

AI ai;

ptr=&ai;

ptr->GiveChess();

ptr=&human;

ptr->GiveChess(position);

这个问题卡了我好久,也让我回想起了杨大师的一句话,意思是C++的类继承机制使派生类能加能改不能删,即可以增加成员,修改基类成员函数,但是不能丢弃基类成员。就是有些基类成员对派生类无用,但派生类依然要将其保留下来。杨大师善于举例子、打比方,士兵的坐骑由战马演变成了坦克,可以把战马看做基类,派生出了坦克类,从外表看坦克与马差异巨大,但是把坦克的盖子掀开,里面还“藏”了匹马。我这里就是想把基类的GiveChess丢弃,增加不同参数的同名函数。好像扯远了,哈哈。

为了解决这个问题,我把基类的虚函数这样写:

virtual Chess GiveChess(Position p = { -1,-1 }) = 0;

由于含有默认参数,调用AI::GiveChess( )时不需要传入参数,当然随便传入一个Position参数也没事儿,反正函数内部用不到这个Position。这样就从形式上解决了问题,虽然连自己都感觉怪怪的。

我还征求了其他同学的看法,有人说有时基类的源码不能随意修改,不能基类成员函数加默认参数。给出的方案是把AI::GiveChess需要的参数(position p)中作为新的数据成员加入AI类,在加一个成员函数AI::SetPosition(Position p)负责修改position p的值。在调用AI::GiveChess( )前先调用一下下AI::SetPosition(Position p)就行了。但是这也存在问题,ptr->SetPosition( )会报错,因为基类里没有SetPosition成员函数。该方案还是不行。

同名不同参的函数如何实现多态?这个本人目前还没有其他好的办法。换个思路吧。也许我的类设计本事就有些问题,非要C++去做他不善于做的事。那就改吧!睡前想到一个方案,干脆基类Player中写两个虚函数:

virtual Chess GiveChess(Position p) = 0;

virtual Chess GiveChess() = 0;

也不区分Human和AI类了,只派生一个Human_AI类,在Human_AI类里把两个函数都重写,需要真人就只调用GiveChess(Position p),需要电脑人就只调用GiveChess()。这样设计也有其合理性:真人下棋时会需要电脑提示,相当于让电脑替自己下一步棋,所以真人棋手GiveChess()和GiveChess(Position p)都需要。

字数逼近3K了,想说的也差不多了,总结一下吧。这次写五子棋的初衷已经达到了,的确对面向对象编程有了初步的体会。面向对象设计分类可以抽象出相对独立的模块,有助于大型项目的维护,也带来了更强的复用性。就这个五子棋而言,用C语言按照面向过程方式写或许更容易,甚至一个main函数就搞定了,但是复用性就要大打折扣了。但用C++,以后想要写个其他什么棋类游戏,只需修改那6个类的部分成员就行了,整体框架基本不变。

另外,写纯软程序感觉就是爽,定位导致出错的问题所在比写单片机程序快多了。不过过去写的单片机程序也不是一点用都没,在写游戏流程时,跟写单片机时似曾相识,一些经验同样适用。

好了,就到这吧!文笔不好,赘述颇多,感谢您的耐心阅读,谢谢!目前本人对C++的体会只有这些,可能很low,萌新入坑,在所难免,让大佬们见笑了。恳请各路高手多多指点批评。

祝大家新年快乐!事事顺心!

五子棋(C++面向对象实现)相关推荐

  1. 01-JAVA基础—>赏金任务—>五子棋(面向对象)

    做个记录 方便复制粘贴 Checkerboard Chess_player Referee Main // 棋盘类 // 棋盘类 public class Checkerboard {/*** 显示棋 ...

  2. Office2016免费下载:Office 2016 Pro Plus 64位 (迅雷复制链接就能下)

    转:https://blog.csdn.net/weixin_39917347/article/details/81706958 版本:Office 2016 Pro Plus 64位 文件名:SW_ ...

  3. 面向对象程序设计(Java)课程设计——五子棋小游戏

    目录 一.团队成员及任务 二.项目简介 三.功能设计图 四.运行结果截图 五.核心源码 六.课程设计感想与展望 一.团队成员及任务 队长:管俊杰  计科(智能)22-1 202203200037 负责 ...

  4. 面向对象程序设计(Java) 课程设计——三少五子棋(Final)

    背景:设计五子棋游戏背景, 初步猜想: ① 设计一个五子棋小游戏,实现人机对战和双人对战两功能. ② 设计五子棋小游戏,使用Java Swing设计可视化操作界面,并使用GraPhic 2D技术设计图 ...

  5. C语言面向过程与C++面向对象

    C语言面向过程与C++面向对象 一.面向对象与面向过程的区别 面向过程是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了:面向对象是把构成问题事务分解成 ...

  6. 新手java五子棋完整代码判断落子落在线上_Java初学者,编写小游戏五子棋的问题?...

    首先你需要掌握GUI编程,事件处理,已经监听器,你就掌握Swing的知识就好了Swing框架,JFrame,JPanel,鼠标.键盘监听事件 Java基础,面向对象,异常处理,集合,IO流 网络编程, ...

  7. 面向过程与面向对象编程的区别和优缺点

    ■面向过程与面向对象编程的区别 转载至:https://www.cnblogs.com/strivers/p/6681876.html 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步 ...

  8. 面向对象和面向过程的区别_面向过程和面向对象的区别

    先来看生活中的一个例子. 想必每个人都下过五子棋吧?在下棋的整个过程中可以有两种不同的思路来实现: 1.按步骤来看: (1).开始游戏,(2).黑子先走,(3).绘制画面,(4).判断输赢,(5).轮 ...

  9. ege函数库_基于c++ ege图形库实现五子棋游戏

    本文分享的五子棋实例,制作基于ege图像库, 首先需要安装配置ege环境 就可以编写小游戏了. 用到的ege库函数不多 , 主要是基于c++的. 先看界面效果: 输入界面:(就是控制台) 游戏胜利界面 ...

最新文章

  1. c语言二重循环的盒图怎么画,全国计算机二级C语言程序设计讲义 循环嵌套.ppt...
  2. Bochs调试Linux内核5 - 启动过程调试 - 认识Bootsect.S
  3. [网站链接]Debbie博客上的链接: [求职网站][博客链接][信息资源]……
  4. linux环境下的连接器,Linux下连接器ld链接如何排序
  5. NAS——在VMware 15虚拟机中安装黑群晖DSM解决方案
  6. qduoj - 今晚一起打CF吧——Codeforces,十三亿人的ACM梦。(排序背包)
  7. 天池 在线编程 最频繁出现的子串(字符串哈希)
  8. [Android] SharedPreference的使用
  9. MegaCli查看RIAD相关信息
  10. java线程释放_Java多线程出现异常会自动释放锁
  11. zepto获取html内容,基于Zepto的内容滑动插件:zepto.hwSlider.js
  12. VAX插件、vs2012
  13. linux下lamealsa进行音频流操作(四)alsa+lame将音频流转为MP3
  14. 在OpenWRT路由器上自动更新github等网站的hosts
  15. EHCache简单使用
  16. 几种实现数据扁平化的方法
  17. 从基础出发,带你深入了解Synchronized ,Synchronized 相 关 问 题讲解
  18. java if作业_19201528- JAVA所有作业总结
  19. win 10 电脑与 H C-05蓝牙模块连接方法集合(含k60 CRC 校验代码软件下载地址)
  20. 高分操作系统 第一章 操作系统概论

热门文章

  1. Linux 设置多指触控手势,以 Manjaro 为例
  2. 离职后心生不满、医院前网管“炫技性报复”
  3. Android动画实战-仿简聊App动画菜单
  4. 计算机软考考试难度大吗?据说是行业唯一的证书,自学还是报班好?
  5. 黄宇清 java_二面笔记 2015.9.19
  6. 德国4-0葡萄牙 穆勒帽子戏法佩佩染红
  7. Ceph中查找BUCKET INDEX所在位置的方法
  8. Token Bucket 令牌桶算法
  9. 艰难时刻,共克时艰。
  10. git FreshMan指南,五分钟上手(图解)