《面向对象程序设计》

更新时间:2023-06-01 05:30:01 阅读量: 实用文档 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

《面向对象程序设计》课程设计

实验报告

惠州学院

HUIZHOU UNIVERSITY

课程名称: 实验名称:

面向对象程序设计 五子棋游戏

姓名: 学号: 专业:

余建行 叶远祥

1214660221224 1214660221223

任课教师: 黄震 班级:

实验时间:

实验成绩:

计算机教育

第一周至第二周

批阅教师签字:

一、综合实验目的

1、掌握面向对象程序设计的基本思路和方法;

2、了解系统开发的需求分析、类层次设计、模块分解、编码测试等过程;

3、为学生提供一个独立实践的机会,将课本的理论知识和实际应用有机的结合起来,锻炼学生的分析解决实际问题的能力,提高学生的实践编程能力。

4、掌握书写程序设计说明文档的能力。

二、综合实验任务

本综合实验要解决如下问题:

(1)建立系统的功能模块及程序流程图; (2)设计合理的数据结构和系统框架;

(3)确定类的层次及类的成员函数并完成各个成员函数的定义,完成系统的应用(主函数设计);

(4)功能调试,能够正确运行程序;

(5)完成综合实验设计的文档。(文档内容包括综合实验的目的和要求、任务内容、详细设计说明、软件使用说明、本实验的心得体会等)。

三、综合实验项目:通讯录管理系统

1、问题需求

编写一个简单的五子棋游戏程序,从键盘中读入控制信息,设计棋盘为15x15的正方形的组成棋盘,棋子有黑白两种颜色,只要任一方在任意方向上构成连续的五颗棋子则为赢,若下满225步棋还没分出输赢,则为平局,游戏还有悔棋和退出的功能。

2、总体设计:游戏运行要显示菜单用于选择功能,菜单设计如下:

3、详细设计设计说明:本程序运行代码如下:

#include <stdio.h> #include <conio.h> #include <windows.h> #include <time.h>

#define TEXTS 7 // 文本颜色 #define CURSOR 48 // 光标颜色 #define CHESSBOARD 352 // 棋盘颜色 #define WHITECHESS 103 // 白棋颜色 #define SELECTEDWHITE 55 // 白棋被选中时的颜色 #define BLACKCHESS 96 // 黑棋颜色 #define SELECTEDBLACK 48 // 黑棋被选中时的颜色 #define qx1_num 27 // 防御棋形的数量 #define qx2_num 26 // 攻击棋形的数量

typedef struct node{ // 棋盘信息 int step; // 步数,步数为0表示该位置为空 int color; // 棋子的颜色 } NODE;

typedef struct point{ //点

int x; int y; } _POINT;

typedef struct qixing{ // 棋形信息 char qx[8]; // 棋形 int value; // 相应的权值 }QIXING;

HANDLE hOutput=GetStdHandle(STD_OUTPUT_HANDLE); // 得到标准输出的句柄 _POINT cursor; // 游戏中,光标所在的当前位置

int direction[8][2]={{0,-1},{0,1},{-1,0},{1,0},{-1,-1},{1,1},{-1,1},{1,-1}}; // 向量数组,依次为左、右、上、下、左上、右下、右上、左下

QIXING qx1[qx1_num]={{"x1111",200000},{"1x111",200000}, {"11x11",200000}, // 连五型

{"0x1110",6000},{"01x110",6000},{"101x101",6000}, // 活四型 {"0x111",1100},{"x0111",1100},{"0x1011",1100},{"0x1101",1100}, {"01x11",1100}, // 冲四型

{"011x1",1100},{"1x011",1100},{"10x11",1100},{"11x01",1100}, {"1x101",1100}, // 冲四型

{"x011102",250},{"0x110",250},{"01x10",250},{"0x01102",240}, {"0x101",240}, // 活三型 {"0x112",20},{"01x12",10},{"011x2",20},{"1x12",10}, {"0x10",20},{"0x010",5}}; // 死三活二

//防御的基本棋形及权值,0为空,1为对手,2为自己,x为下棋位置

QIXING qx2[qx2_num]={{"x1111",2000000},{"1x111",2000000}, {"11x11",2000000}, // 连五型

{"0x1110",24000},{"01x110",24000}, {"101x101",24000}, //活四型 {"0x111",2000},{"x0111",1900},{"0x1011",1900},{"0x1101",2000}, {"01x11",2000}, // 冲四型

{"011x1",2000},{"1x011",1900},{"10x11",2000},{"1x101",2000}, {"x01112",2000}, // 冲四型

{"0x110",850},{"01x10",850},{"0x0110",840},{"0x101",840},//活三型 {"0x112",125},{"01x12",125},{"011x2",115},{"1x12",115}, {"0x10",125},{"0x010",110}};// 死三活二

// 攻击的基本棋形及权值,0为空,1为自己,2为对手,x为下棋位置

//----------------------------------界面部分---------------------------------

void textcolor(int color) {// 更改字体颜色 SetConsoleTextAttribute(hOutput,color); }

void gotoxy(int x, int y) {// 将光标移动到指定位置

COORD coordScreen = { 0, 0 }; coordScreen.X=x; coordScreen.Y=y;

SetConsoleCursorPosition( hOutput, coordScreen ); }

void printnode(NODE chessboard[][15], int x, int y) // 打印棋盘上的一个点 { textcolor(CHESSBOARD); if(chessboard[x][y].step==0) { if (x==cursor.x && y==cursor.y) textcolor(CURSOR); // 如果光标在这个点,改成光标颜色 switch(x){ case 0: if(y==0) printf("┏"); // 左上角 else if(y==14) printf("┓"); // 右上角 else printf("┯"); // 上边线 break; case 3: if(y==0) printf("┠"); // 左边线 else if(y==3 || y==11) printf("+"); // 星位 else if(y==14) printf("┨"); // 右边线 else printf("┼"); // 交叉点 break; case 7: if(y==0) printf("┠"); // 左边线 else if(y==7) printf("+"); // 星位 else if(y==14) printf("┨"); // 右边线 else printf("┼"); // 交叉点 break; case 11: if(y==0) printf("┠"); // 左边线 else if(y==3 || y==11) printf("+"); // 星位 else if(y==14) printf("┨"); // 右边线 else printf("┼"); // 交叉点 break; case 14: if(y==0) printf("┗"); // 左下角 else if(y==14) printf("┛"); // 右下角 else printf("┷"); // 下边线 break; default: if(y==0) printf("┠"); // 左边线 else if(y==14) printf("┨"); // 右边线

else printf("┼"); // 交叉点 } } else if(chessboard[x][y].color==0){ // 如果是白棋 if (x==cursor.x && y==cursor.y) textcolor(SELECTEDWHITE); // 被选中的白棋 else textcolor(WHITECHESS); // 未被选中的白棋 printf("●"); } // 打印棋子 else{ if (x==cursor.x && y==cursor.y) textcolor(SELECTEDBLACK); // 被选中的黑子 else textcolor(BLACKCHESS); // 未被选中的黑子 printf("●"); } // 打印棋子 }

void printchessboard(NODE chessboard[][15]) {// 输出整个棋盘 int i,j; char letter[]={"ABCDEFGHIJKLMNO\n"}; for(i=0;i<15;i++){ // 行 textcolor(TEXTS); // 改为文本颜色 printf("%2d",15-i); // 打印行坐标 for(j=0;j<15;j++) // 列 printnode(chessboard,i,j); // 打印棋盘的每一块 textcolor(TEXTS); printf("\n"); } textcolor(TEXTS); //改为文本颜色 printf(" %s",letter); //打印列坐标 printf("移动:方向键 下棋:ENTER 悔棋:U 退出:F12"); }

void renew(NODE chessboard[][15], int x, int y) {// 更新棋盘指定位置的图像 COORD coordScreen; // 系统提示符位置 CONSOLE_SCREEN_BUFFER_INFO csbi; // 屏幕信息 if(x<0 || x>14 || y<0 || y>14) return; // 如果不在棋盘上直接返回 if( !GetConsoleScreenBufferInfo( hOutput, &csbi )) // 获取屏幕信息 return; // 不成功则返回 coordScreen=csbi.dwCursorPosition; // 获取系统提示符位置 gotoxy((y-1)*2+4,x+1); // 将系统提示符移动到棋盘的(x,y)所在位置 printnode(chessboard,x,y); // 重新打印这一块 SetConsoleCursorPosition( hOutput, coordScreen );// 系统提示符回复到原来位置 }

void showmenu() {// 输出主菜单 textcolor(TEXTS); system("cls"); printf("1.人机对战\n"); printf("2.双人对战\n"); printf("3.退出\n"); printf("\n请选择[1-3]:"); }

void showsubmenu() {// 打印子菜单 textcolor(TEXTS); system("cls"); printf("1.你先手\n"); printf("2.电脑先手\n"); printf("3.返回上级菜单\n"); printf("\n请选择[1-3]:"); }

int getchoose(int min, int max) {// 获取选项 int choose; do{ choose=getch()-48; }while(choose<min || choose>max); //过滤不在min到max之间的字符 printf("%d",choose); //屏幕回显 return choose; }

//----------------------------------控制部分---------------------------------

bool quit; //是否按下了退出热键 bool regret; //是否按下了悔棋热键

bool getmove(NODE chessboard[][15]) {// 获取光标移动,并响应

// 当按下悔棋、下子、退出热键时,返回true char c; for(;;){ c=getch(); if(c==-32) switch(getch()){ case 72: // 上 cursor.x--; if(cursor.x<0) cursor.x=0; renew(chessboard,cursor.x+1,cursor.y);

renew(chessboard,cursor.x,cursor.y); break; case 80: // 下 cursor.x++; if(cursor.x>14) cursor.x=14; renew(chessboard,cursor.x-1,cursor.y); renew(chessboard,cursor.x,cursor.y); break; case 75: // 左 cursor.y--; if(cursor.y<0) cursor.y=0; renew(chessboard,cursor.x,cursor.y+1); renew(chessboard,cursor.x,cursor.y); break; case 77: // 右 cursor.y++; if(cursor.y>14) cursor.y=14; renew(chessboard,cursor.x,cursor.y-1); renew(chessboard,cursor.x,cursor.y); break; case 134: // 退出 quit=true; return true; } else if(c==13 && chessboard[cursor.x][cursor.y].step==0) return true; // 下子 else if(c=='U' || c=='u'){ // 悔棋 regret=true; return true; } } }

void beback(NODE chessboard[][15], int step) {//悔棋 int i,j,tempx,tempy; if(step==1) return; // 如果才开始,直接返回 if(step>2){ // 如果下了多于两步 for(i=0;i<15;i++) // 搜索前两步所下的位置 for(j=0;j<15;j++) { if(chessboard[i][j].step==step-1){ // 找到上一步 chessboard[i][j].step=0; // 清空棋子标志 renew(chessboard,i,j);} // 重绘棋盘 else if(chessboard[i][j].step==step-2){ // 找到上两步 chessboard[i][j].step=0; // 清空棋子标志 tempx=cursor.x; // 记录光标位置 tempy=cursor.y; cursor.x=i; // 将光标回复到

两步前的位置 cursor.y=j; renew(chessboard,i,j); // 重绘棋盘 renew(chessboard,tempx,tempy); // 重绘光标原本所在处 } } }else if(step==2){ //如果下了一步 for(i=0;i<15;i++) //搜索上一步所下的位置 for(j=0;j<15;j++) if(chessboard[i][j].step==step-1){ // 找到上一步 chessboard[i][j].step=0; // 清空棋子标志 renew(chessboard,i,j);} // 重绘棋盘 tempx=cursor.x; // 记录光标位置 tempy=cursor.y; cursor.x=7; // 将光标移回棋盘中央 cursor.y=7; renew(chessboard,i,j); // 重绘棋盘 renew(chessboard,tempx,tempy); // 重绘光标原本所在处 } }

//------------------------------判断部分-------------------------------

bool inside(int x, int y)

{// 如果不在棋盘内返回false,否则返回true if(x<0 || x>14 || y<0 || y>14) return false; return(true); }

int line(NODE chessboard[][15], int dirt, int x, int y, int color)

{// 判断颜色为color的点(x,y),在dirt方向上有多少相连的同色棋子 int i; for(i=0;chessboard[x+direction[dirt][0]][y+direction[dirt][1]].step>0 && chessboard[x+direction[dirt][0]][y+direction[dirt][1]].color==color;i++) { x=x+direction[dirt][0]; y=y+direction[dirt][1]; if(!inside(x,y)) return i; } return i; }

bool win(NODE chessboard[][15], int x, int y, int color) {// 判断是否有人赢棋 if(line(chessboard,0,x,y,color)+line(chessboard,1,x,y,color)>3) return true; if(line(chessboard,2,x,y,color)+line(chessboard,3,x,y,color)>3) return true;

if(line(chessboard,4,x,y,color)+line(chessboard,5,x,y,color)>3) return true; if(line(chessboard,6,x,y,color)+line(chessboard,7,x,y,color)>3) return true; return false; }

//--------------------------------AI部分----------------------------------

int attacktrend,defenttrend; // 攻击防御平衡权值

bool macth1(NODE chessboard[][15], int x, int y, int dirt, int kind, int color)

{/* 匹配在颜色为color的点(x,y),在dirt方向上的第kind种防守棋形,成功返回true,否则返回false */ int k; char c; char *p; p=strchr(qx1[kind].qx,'x'); for(k=0;k<=p-qx1[kind].qx;k++) { x-=direction[dirt][0]; y-=direction[dirt][1]; } for(k=0;(unsigned)k<strlen(qx1[kind].qx);k++) { x+=direction[dirt][0]; y+=direction[dirt][1]; if(!inside(x,y)) return(false); if(chessboard[x][y].step>0 && chessboard[x][y].color==color) c='2'; else if(chessboard[x][y].step>0) c='1'; else c='0'; if(c=='0' && qx1[kind].qx[k]=='x') continue; if(c!=qx1[kind].qx[k]) return(false); } return true; }

int value_qx1(NODE chessboard[][15], int x, int y, int dirt, int color) {// 计算颜色为color的点8个方向上的防守权值之和 int i; for(i=0;i<qx1_num;i++) if(macth1(chessboard,x,y,dirt,i,color)) return qx1[i].value; return 0; }

bool macth2(NODE chessboard[][15], int x, int y, int dirt, int kind, int color)

{/* 匹配在颜色为color的点(x,y),在dirt方向上的第kind种进攻棋形,成功返回true,否则返回false */ int k; char c;

char *p; p=strchr(qx2[kind].qx,'x'); for(k=0;k<=p-qx2[kind].qx;k++) { x-=direction[dirt][0]; y-=direction[dirt][1]; } for(k=0;(unsigned)k<strlen(qx2[kind].qx);k++) { x+=direction[dirt][0]; y+=direction[dirt][1]; if(!inside(x,y)) return false; if(chessboard[x][y].step>0 && chessboard[x][y].color==color) c='2'; else if(chessboard[x][y].step>0) c='1'; else c='0'; if(c=='0' && qx2[kind].qx[k]=='x') continue; if(c!=qx2[kind].qx[k]) return false; } return true; }

int value_qx2(NODE chessboard[][15], int x, int y, int dirt, int color) {// 计算颜色为color的点8个方向上的进攻权值之和 int i; for(i=0;i<qx2_num;i++) if(macth2(chessboard,x,y,dirt,i,color)) return qx2[i].value ; return 0; }

void AI(NODE chessboard[][15], int *x, int *y, int color) {// AI的主要函数,将思考后的结果返回给*x和*y int max=0; // 价值的最大值 int maxi,maxj; // 获得最大值时所对应的坐标 int i,j,k; // 循环控制变量 int probability=1; // 几率参数 int value[15][15]={0}; // 棋盘上各点的价值 int valueattack[15][15]={{0}}; // 棋盘上各点的进攻价值 int valuedefent[15][15]={{0}}; // 棋盘上各点的防守价值 for(i=0;i<15;i++) // 计算棋盘上各点的防守价值 for(j=0;j<15;j++) { if(chessboard[i][j].step>0) continue; for(k=0;k<8;k++) valuedefent[i][j]+=value_qx1(chessboard,i,j,k,color); if(maxi<valuedefent[i][j]) maxi=valuedefent[i][j]; // 记录防守价值的最大值 } for(i=0;i<15;i++) // 计算棋盘上各点的进攻价值 for(j=0;j<15;j++) { if(chessboard[i][j].step>0) continue; for(k=0;k<8;k++)

valueattack[i][j]+=value_qx2(chessboard,i,j,k,(color+1) % 2); if(maxj<valuedefent[i][j])

maxj=valuedefent[i][j]; // 记录进攻价值的最大值 } if(rand()%(maxi+maxj+1)>maxi){ // 如果防守价值的最大值比较低 attacktrend=1; // AI优先进攻 defenttrend=1; }else{ // 相反 attacktrend=1; // AI优先防守 defenttrend=2; } for(i=0;i<15;i++) // 根据各点的进攻和防守价值 for(j=0;j<15;j++){ // 计算最终价值 value[i][j]=valuedefent[i][j]*defenttrend+valueattack[i][j]*attacktrend; if(max<value[i][j]){ max=value[i][j]; // 记录价值的最大值 maxi=i; // 以及相应的坐标 maxj=j; probability=1;} else if(max==value[i][j]){ // 如果出现相同价值的最大点 if(rand()%(probability+1)<probability) // 随机决定选取哪一个 probability++; /* 由于前面的点容易被淘汰,所以相应提高前面的点的被选择的几率 */ else{ probability=1; // 选择后面的点,则几率权值回复 max=value[i][j]; // 记录 maxi=i; maxj=j;} } } *x=maxi; // 返回价值最大的点 *y=maxj; }

//-----------------------主要部分------------------------------------

bool vshuman; //对手是否是人

void Vs(bool human) {//对局主要函数 int i,j; int color=1; // 黑棋先走 int lastx,lasty; // 光标的上一个位置 int computer; // 电脑执黑还是执白 NODE chessboard[15][15]={{0,0}}; // 棋盘 if(!human){ // 对手是电脑 showsubmenu(); // 选择谁是先手

switch(getchoose(1,3)){ case 1: computer=0; attacktrend=1; // 电脑先手则优先进攻 defenttrend=1; break; case 2: computer=1; // 电脑后手则优先防御 attacktrend=1; defenttrend=2; break; case 3:return; // 返回上级菜单 } }

for(i=0;i<15;i++) // 清空棋盘 for(j=0;j<15;j++) chessboard[i][j].step=0; cursor.x=7; // 光标居中 cursor.y=7; quit=false; // 清空退出标志 system("cls"); // 清屏 printf("\n");

printchessboard(chessboard); // 打印棋盘 for(i=1;i<=225;){ // 行棋主循环 gotoxy(0,0); // 系统光标移到屏幕左上角 textcolor(TEXTS); printf(" 第%03d手,",i); if(color==1) printf("黑棋下"); else printf("白棋下"); regret=false; // 清空悔棋标志 if(i>1){ // 第一子必须下在棋盘中央 if(color!=computer || human) getmove(chessboard); // 该人走棋 else{ // 该电脑走棋 lastx=cursor.x; // 记录光标位置 lasty=cursor.y; AI(chessboard,&cursor.x,&cursor.y,color); // 电脑走棋 renew(chessboard,lastx,lasty); // 更新棋盘 } } if(quit) return; // 如果按了退出热键则返回 if(regret){ // 如果按了悔棋热键则调用悔棋函数 beback(chessboard,i); if(i>2) i-=2; else if(i==2){ i=1; color=(color+1) % 2 ; } }else{ // 如果没有按热键,则在光标位置下子

chessboard[cursor.x][cursor.y].step=i++; chessboard[cursor.x][cursor.y].color=color; renew(chessboard,cursor.x,cursor.y); color=(color+1) % 2 ; } if(win(chessboard,cursor.x,cursor.y,(color+1) % 2) && !regret){// 有人赢 textcolor(TEXTS); gotoxy(0,0); printf(" "); gotoxy(0,0); if(color==1) printf(" 白棋赢了!"); else printf(" 黑棋赢了!"); getch(); return; } } gotoxy(0,0); // 如果下了225步还没人赢棋则平局 printf(" "); gotoxy(0,0); printf(" 平局!"); }

void main(void) {// 主函数 srand((unsigned)time(NULL)); // 初始化随机种子 for(;;){ showmenu(); // 输出主菜单 switch(getchoose(1,3)){ case 1:Vs(false); break; case 2:Vs(true); break; case 3: printf("\n"); return; } } }

4、程序运行结果截图:

进入游戏选择:

选择哪方先下:

第 16 页 共 1

9 页

四、本实验的心得体会

程序设计实验心得

我感觉C++是我所学当中最难的. 刚刚开始学习的是时候,我总是不能记住书本上的知识,各个类别极其代表意义,过眼即忘;众多全局变量、全局函数、无参宏使我头晕眼花。在过去的一个学期里,我掌握了C++中最为基础的知识体系,但除了课后作业以外却少有编写程序的经验,而且课后练习的程序编码也是少得可怜。

俗话说的好,实践是学习知识的最终目的。在这个学期,我经过十二个星期的努力,终于完成了程序设计实验,在这个的过程中,我知道到了自己的许多不足之处。

在刚刚开始做这个项目的时候,我做到事情只有一样:参考书中的代码。即便如此,我还是觉得很吃力,往往有些代码一条条拿出来的时候我知道是什么意思,但是放在一起的话我就无法理解有什么作用,这些代码仿佛把我花费了一个学期学习的基本知识分割得支离破碎,很明显的,在这段时期,我无法将我所学的东西很好的应用出来。大概用三个星期的时间,我在书本上的编码过程中学会了面向对象程序设计的基本思路和方法。我知道了如何根据实际需求进行分析,从而设计出整个程序的总体架构,再根据总体架构设计该项目所需要用的类并为其分层,最后在将整个程序分割成若干个模块,对每个模块进行“加工”。

在完成了准备工作后,我又被函数编写难倒了。除了书本上本来就有的函数以外,我对其它需要加上功能感觉到一片迷茫,不知道从何下手。于是我开始在互联网上吸取我所需要的知识,依靠无数次的尝试去写出那些功能的代码。其中我个人认为最为困难的就是查询函数的实现,我尝试过用指针去完成这个功能,但是经过无数次失败以后我选择了更为熟悉的数组。在参考了书本上的显示函数以及删除函数以后,终于实现了这个功能。

在随后的编码测试的过程中,老师帮我解决一些我自己没有注意到的小细节,这可以说是一个程序设计员的经验,没有经验的人一般都会忽略这些细节性问题。

在整个实验过程中,我学到了很多关于如何完成一个项目程序设计的知识,同时也获得了一定量的经验,为我以后的程序设计之路打下了坚实的基础。但是,我知道这些是远远不够的,要成为一名出色的程序设计师,我知道自己的路还有很长很长,我还需要更多的知识来充实自己,我还需要更多的经验让我设计的程序变得更完美。

最后,我想说的是:这次的程序设计让我进行了一次蜕变! 余建行

在未接触c++的时候,我怀着好奇地感觉去学习,但是真正学起来的时候真的很难,特别是指针,类以及类的其他派生类的函数和众多全局变量、全局函数、无参宏,感觉像是一头雾水一样,该记得内容总是搞混乱,而这次的为期四周的课程设计使我学到很多的东西,利用设计这次比较复杂点的五子棋游戏,复习了自己以前的知识,加强了对C++的认识,自己的逻辑思考能力也提高了些许,虽然这个设计大部分是参考网上的设计思维,但是从中我学会了很多比如要求分析、程序模块设计、功能需求、程序代码设计与分析、运行结果等,通过课程设计,复习了以前的旧知识,又学到了一些新的知识;如类的定义,类的实现,对象的定义等等; 当然了这次的设计我和搭档也遇到不少的困难,在编程的时候总会遇到不懂得错误,通过查质料,慢慢发现错误不断修改,这点我个人觉的还是不错的,可以所学有所学,所有所用,不至于自己所学知识由于没有经历可用过程,这有助于增强了我们用所学知识去解决具体问题的能力,在整个过程中可以认识到设计一个程序所要具备的知

识还有对知识的运用以及要有独当一面的意识! 叶远祥

本文来源:https://www.bwwdw.com/article/1cc1.html

Top