1.问题描述

设计一个玩家下棋的操作类,里面封装了棋盘、棋子类别和输赢次数等成员数据。
操作类的函数包括有初始化棋盘、下棋、判断输赢等。定义一个类的对象,在调用类
函数时直接使用对象名加函数名即可。
将两个玩家定义为两个数组用于区分他们的信息,分别用’O’和’X’代表两个玩家所用棋
子,然后分别对两个玩家通过已定义的类的对象调用类中的函数,完成下棋过程。
进一步改进程序:
(1)程序中用if else if格式的语句来判断玩家的输赢,当棋盘增大时,三子连珠的各种可
能性成倍增加,使得语句非常繁琐,所以将其改为for语句的循环结构进行判断,当棋
盘增大时,只需更改循环条件结束的参数即可,可灵活使用于各种尺寸的棋盘。
(2)为防止用户误操作,特别是在姓名的输入时的失误,可以采用cin.getline函数读入在判断确认。
(3)在完成第一步的基础上可将三子连珠扩充为标准的五子棋。

2.基本要求

(1)将程序改写成类。
(2)将棋盘扩充成4×4。
(3)统计输赢结果并打印输出。
(4)使游戏更生动(输赢条件相同)。

3.需求分析

(1)程序要使得两个玩家能进行下棋比赛,并且决出胜负,或者是出现平局情况。

(2)任意玩家输入时以坐标方式输入棋子的坐标,单个坐标范围不得超过4个单位值。

(3)输出棋盘的内容,其中包括已输入的棋子。

(4)两个玩家依次进行下棋的坐标输入,直到出现一方赢得比赛或者双方平局的情
现。

(5)测试数据

正确输入的情况,输入正确坐标后输出带有输入坐标棋子的整个棋盘

错误输入的情况,输入重复坐标则输出棋盘出已有棋子,请重新输入;输入超出边界的坐标,要求重新输入

4.概要设计

图1 四子连珠流程图

类的设计: 

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
class CGobang //棋子类
{
private:
char chSort; //棋子的类别
int nWin; //赢棋的次数
int nLose; //输棋的次数
static int nDraw; //平局次数
static char achBoard[4][4]; //棋盘
static int nSize; //棋盘的尺寸 nSize X nSize
public:
CGobang(char chsort) //构造函数,决定一方棋子的类别
{
chSort=chsort;
nWin=nLose=nDraw=0;
}

void PlayTurn(void); //走1步棋
int Judge(); //判断是否连成一线,是则返回1,否则返回0
void Win(void); //赢棋
void Lose(void); //输棋
static void Draw(void); //平局
void PrintInfo(void); //输出总体情况
static void PrintBoard(void); //输出棋盘
static int GetFull(void); //判断棋盘是否已布满棋子
static void InitialBoard(void); //初始化棋盘
};

5.详细设计

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
void CGobang::Draw()  //平局函数实现
{
cout << "\n\n\t\t平局!\n\n";
nDraw++;
}

void CGobang::InitialBoard() //初始化棋盘
{
for(int i=0;i<nSize;i++)
for(int j=0;j<nSize;j++)
achBoard[i][j]=' ';
}

void CGobang::PrintBoard() //输出棋盘
{
cout << endl;
cout << " 1 2 3 4" << endl;
cout << "1 " << achBoard[0][0] << " | " << achBoard[0][1] << " | " << achBoard[0][2] << " | " << achBoard[0][3] << endl;
cout << " ---|---|---|---" << endl;
cout << "2 " << achBoard[1][0] << " | " << achBoard[1][1] << " | " << achBoard[1][2] << " | " << achBoard[1][3] << endl;
cout << " ---|---|---|---" << endl;
cout << "3 " << achBoard[2][0] << " | " << achBoard[2][1] << " | " << achBoard[2][2] << " | " << achBoard[2][3] << endl;
cout << " ---|---|---|---" << endl;
cout << "4 " << achBoard[3][0] << " | " << achBoard[3][1] << " | " << achBoard[3][2] << " | " << achBoard[3][3] << endl;
cout << endl;
cout << endl;
}

int CGobang::GetFull() //判断棋盘是否布满棋子,若是返回1
{
int flag=1;
for(int i=0;i<nSize;i++)
{
for(int j=0;j<nSize;j++)
{
if(achBoard[i][j] == ' ' )
{
flag=0;
break;
}
}
if(flag == 0)
break;
}
return flag;
}

void CGobang::Win() //赢棋
{
cout << "\n\n\t\t"<<chSort<<"方获胜!\n\n";
nWin++;
}

void CGobang::Lose() //输棋
{
nLose++;
}
void CGobang::PlayTurn(void) //走1步棋
{
int nRow,nCol;
cout <<"现在该 "<<chSort<<" 方下棋,请输入棋盘坐标(x,y):";
do
{
cin >> nRow >> nCol; //输入坐标
if(nRow>nSize || nCol>nSize) //判断坐标越界
{
cout <<"输入的坐标越界,x与y的范围应小于等于"<<nSize<<",请重新输入\n";
}
else if(achBoard[nRow-1][nCol-1]!=' ') //判断坐标合理
{
cout <<"棋盘("<<nRow <<" ,"<<nCol <<")处已有棋子,请重新输入\n";
}
else
{
achBoard[nRow-1][nCol-1]=chSort; //在坐标处放上棋子
break; //退出循环
}
}while(1);
}

int CGobang::Judge() //判断是否棋子连成一线,若是返回1
{//以下是各种可能连成一线的情况
int flag=0;
if(achBoard[0][0]==chSort && achBoard[1][1]==chSort && achBoard[2][2]==chSort && achBoard[3][3]==chSort)
flag=1;
else if(achBoard[3][0]==chSort && achBoard[2][1]==chSort && achBoard[1][2]==chSort && achBoard[0][3]==chSort)
flag=1;
for(int i=0;i<nSize;i++)
{
if(achBoard[i][0]==chSort && achBoard[i][1]==chSort && achBoard[i][2]==chSort && achBoard[i][3]==chSort)
flag=1;
else if(achBoard[0][i]==chSort && achBoard[1][i]==chSort && achBoard[2][i]==chSort && achBoard[3][i]==chSort)
flag=1;
}
return flag; //没有连成一线则返回0
}
void CGobang::PrintInfo(void) //打印总体情况
{
cout <<chSort<<"方共计赢 "<<nWin<<" 局,输 "<<nLose<<" 局,平 "<<nDraw<<" 局。"<<endl;
}

void PrintRule(void) //打印规则
{
cout << "\t\t欢迎使用三子连珠游戏!" << endl << endl;
cout << "\t游戏规则:" << endl;
cout << "\t1.每1步输入要下棋的格子的x,y坐标,按Enter键\n";
cout << "\t2.有1方首先4个棋子连成一线即判获胜\n";
cout << "\t3.当棋子布满棋盘但仍无获胜方即为平局\n";
cout << "\t4.X方先行\n";
cout << "\n\n\t\t请回车键开始下棋!\n\n";
}

int JudgePlay(CGobang & SideX,CGobang & SideO) //每下1步要进行输赢的判断,有结果则返回1
{
if(SideX.Judge()) //X方获胜
{
SideX.Win();
SideO.Lose();
return 1;
}
else if(SideO.Judge()) //O方获胜
{
SideO.Win();
SideX.Lose();
return 1;
}
else
return 0;
}
void Play(CGobang & SideX,CGobang & SideO) //开始一局游戏
{
while(1)
{
CGobang::PrintBoard(); //输出棋盘

SideX.PlayTurn(); //X方下棋
if(JudgePlay(SideX,SideO)) //判断输赢
break;
if(CGobang::GetFull()) //判断是否平局
{
CGobang::Draw();
break;
}

CGobang::PrintBoard(); //输出棋盘

SideO.PlayTurn(); //O方下棋
if(JudgePlay(SideX,SideO)) //判断输赢
break;
if(CGobang::GetFull()) //判断是否平局
{
CGobang::Draw();
break;
}
}
}

6.调试分析

问题1:

判断玩家输入坐标是否有错,开始采用无论是否为出界或者输入重复坐标,均统一输出“输入坐标有错”,但这样不能有效的分辨到底是哪种输入错误
解决:
因此通过分开判断两种错误的方式解决

问题2:

判断是否连成一线时第一次采用的是if和else if来实现该功能,是否可通过for循环来实现这个功能,使得程序更加简洁
解决:
利用flag来判断,首先,令flag=0,先单独对对角线两种连成四字的情况判断,若连成一条线flag=1,再用for循环判断横竖8种情况是否连成四子,若连成一条线flag=1

问题3:

对于如何通过调用judgeplay来判断是否有输赢
解决:
通过返回1或0,用逻辑判断来实现



问题4:

UltraEdit软件无法直接编译Cpp文件
解决:
首先下载MinGW库并且配置环境,然后在UltraEdit软件软件高级选项的工具中创建C++编译和运行工具

设计与实现的回顾讨论和分析: 

任何一个程序的实现都是从大的框架来实现的,因此画出流程图分析是不可缺少的一个步骤,其次在流程图已画出的情况下,通过参考其他文献来细化每个程序的实现以及各个程序关系,最后,通过各类判断条件以及函数之间的调用来实现某一模块的功能

7.用户使用说明

(1)首先输出游戏欢迎语句和规则,玩家按任意键进行下棋

(2)玩家输入坐标,有错即会报错,要求重新输入

(3)玩家下棋直到有一方赢得比赛或者平局


(4)当玩家不想再进行游戏时,结束游戏,输出统计结果

8.测试结果











9.课程设计总结

任何一个程序的实现都是从大的框架来实现的,因此画出流程图分析是不可缺少的一个步骤。通过流程图,我们可以分析出玩家每下一个棋,就需要判断一次是否有四子连成一条线,若有则结束程序运行,并且询问是否再开一局,若没有,则另一玩家再继续下棋。通过画出具体流程图可以分析得出,我们需要两个关键函数,一个是判断是否有四子连成一条线的函数,另一函数是判断棋盘是否布满(即是否为平局情况出现)。由于手上资源有限,因此需要参考其他文献来细化每个程序的实现以及各个程序关系。在查阅资料和询问相关人士后,程序内的各种小问题都被很好的解决了。整个程序花费3天实现,第一天构建出整体流程和模型,第二天确定具体流程和函数的实现,最后一天调试代码,解决各种问题,并且书写课程设计报告。本次课程设计让我体验到了程序设计的过程,总是由易到难,并不是一蹴而就的事情。从此中,我也体会到了运行自己开发的程序的乐趣和自我思考解决问题的成就感。本次C++课程设计在四子连珠中圆满完成。

10.参考文献

[1]孙静波,郭建伟. C++课程设计任务书[EB/OL].https://wenku.baidu.com/view/858ea37c27284b73f24250ac.html,2011-03-05

[2]步士旋.C++课程设计题目要求[EB/OL].https://wenku.baidu.com/view/88c27f5adcccda38376baf1ffc4ffe473268fd4d.html,2017-04-11

[3]钱芸生.c++课程设计论文三子连珠[EB/OL].https://www.docin.com/p-1486882349.html,2014-04-05

[4]N_0_1.把三字连珠(三子棋)棋盘改成4x4的[EB/OL].https://zhidao.baidu.com/question/2205231972327907748.html,2016-03-27

[5]xingyunbuzui.C++三子连珠编程问题[EB/OL].https://zhidao.baidu.com/question/471564808.html,2012-08-31

[6]人潮尽头.三子连珠游戏[EB/OL].http://tieba.baidu.com/p/1057812021,2011-04-21

[7]巴喆.MinGW的安装以及如n配置C++[EB/OL].http://tieba.baidu.com/p/2826016538?pid=44774220947#,2014-01-21

[8]a19161.UltraEdit配置C++开发环境[EB/OL].https://blog.csdn.net/a19161/article/details/54343914,2017-01-11

[9]狼本善.使用UltraEdit打造C/C++编译环境![EB/OL].https://blog.51cto.com/johnjohn/1407845,2012-10-21