文章目录
一、 需求分析1.1 设计意义1.2 设计要求 二、 总体设计2.1系统流程模块2.2 数据结构设计2.3 函数功能设计2.4 磁盘文件 三、详细设计3.1 主函数3.2 主界面人机对战模式:双人对站模块:排行榜模块: 3.3 开发日志 四、调试与测试4.1 游戏开始界面4.2游戏进行界面游戏进行时界面游戏结束界面 4.3 排行榜模块排行榜显示界面 五、用户手册六、设计体会一、 需求分析
1.1 设计意义
五子棋是全国智力运动会竞技项目之一,起源于中国古代的传统黑白棋种之一,日文亦有连五子、五子连、串珠、五目、五目碰、五格、五石、五法、五联、京棋等多种称谓;英文则称之为FIR(Five In A Row的缩写)、Gomoku(日语“五目”的罗马拼音)、Gobang、connect 5、mo-rphion等;捷克文piskvorky,韩文omok等等。五子棋是一种两人对弈的纯策略型棋类游戏。双方分别使用黑白两色的棋子,下在棋盘直线与横线的交叉点上,先形成五子连珠者获胜。五子棋容易上手,老少皆宜,而且趣味横生,引人入胜。它不仅能增强思维能力,提高智力,而且富含哲理,有助于修身养性。是一种训练人逻辑思维严密性的好游戏,本程序将用c语言实现一个简单的五子棋,旨在讲述五子棋开发的原理。
1.2 设计要求
1、问题描述
请用所学的C语言知识实现一个命令行下的五子棋游戏。要求有棋盘界面,并实现人与人、人与计算机两种对弈模式。
2、功能要求
(1)要求有棋盘界面,并实现人与人、人与计算机两种对弈模式。
(2)游戏初始选择是否可以悔棋。
(3)查看游戏排行榜前五名,排行榜以文件形式保存。
(4)游戏进行过程中可以投降。
1.3 开发工具及应用平台
Visual Studio,EasyX 和 Windows11
二、 总体设计
2.1系统流程模块
2.2 数据结构设计
定义一个结构体类型inf,其中包括姓名和步数两个变量。
typedef struct inf{char name[20];int score;}info;
2.3 函数功能设计
函数功能设计的表格,如表1所示:
表1 函数功能设计表
2.4 磁盘文件
本系统使用一个磁盘文件:
存放玩家排行榜的文件(paihang.txt)
三、详细设计
3.1 主函数
主函数主要是 播放音乐 以及 显示菜单的一个过程。
此模块主函数main()代码如下:
int main(){hello();//播放游戏背景音乐+等待游戏打开start();//显示游戏主菜单 让玩家选择return 0;}
3.2 主界面
主要由三大模块组成:人机对战模式;双人对站模式;排行榜显示;主界面显示函数如下:
void start(){int cont = 0;initgraph(600, 800);cleardevice();settextcolor(RED);//设置文字颜色setbkmode(TRANSPARENT);//设置窗口透明settextstyle(128, 0, "微软雅黑"); //设置字体大小、宽度、字体outtextxy(100, 100, "五子棋"); setlinecolor(RED); rectangle(150, 300, 450, 650);//绘制矩形rectangle(160, 310, 440, 640);line(160, 420, 440, 420);//画一条线line(160, 530, 440, 530);settextstyle(65, 0, "微软雅黑");outtextxy(170, 340, "人机大战");outtextxy(170, 450, "双人对战");outtextxy(170, 560, "玩家排行");setlinecolor(WHITE);settextcolor(WHITE);settextstyle(23, 0, "微软雅黑");outtextxy(527, 37, "音乐");circle(550, 50, 25);IMAGE gc;//定义鼠标信息变量loadimage(&gc, "image//jjkk.png");putimage(220, 660, &gc);while (true){MOUSEMSG m;m = GetMouseMsg();//鼠标信息if (m.uMsg == WM_LBUTTONDOWN)//鼠标左键{if (m.x <= 440 && m.x >= 160 && m.y < 640 && m.y > 530)ret();//排名界面if (m.x <= 440 && m.x >= 160 && m.y < 530 && m.y > 420)behind();//玩家对战if (m.x <= 440 && m.x >= 160 && m.y < 420 && m.y > 310)machine();//人机对战if (m.x <= 575 && m.x >= 528 && m.y < 75 && m.y > 25){if (cont % 2 == 0){mciSendString(TEXT("pause mysong"), NULL, 0, NULL);cont++;}else{mciSendString(TEXT("resume mysong"), NULL, 0, NULL);cont++;}}}}}
各个模块显示信息如下:
人机对战模式:
玩家可以与电脑进行PK,同时如果成绩高超可以将分数记入排行榜之中,来和别人横向比较一下与他人的实力差距。
此模块查找函数search_record()
代码如下:
void machine(){board();setlinecolor(WHITE);MOUSEMSG m; // 定义鼠标消息int i = 0, x, y, q = 0, p = 0;int red[15][15], white[15][15], stick[15][15], whtie1[15][15];while (true){m = GetMouseMsg();x = m.x % 50;y = m.y % 50;setrop2(R2_COPYPEN);//二元光栅操作码:R2_COPYPEN当前颜色if (m.uMsg == WM_LBUTTONDOWN)// 使用鼠标左键进行操作{if (m.x <= 1130 && m.y < 680 && m.x >= 860 && m.y > 590) //投降win(1);if (m.x <= 1130 && m.y < 590 && m.x >= 860 && m.y > 500)//重开machine();if (m.x <= 1130 && m.y < 500 && m.x >= 860 && m.y > 410){for (int x1 = 0; x1 < 15; x1++) {for (int y1 = 0; y1 < 15; y1++) {if (whtie1[x1][y1] != white[x1][y1]) {white[x1][y1] = 0;stick[x1][y1] = 0;}}}red[p][q] = 0;stick[p][q] = 0;regret(red, whtie1);continue;}if (x >= 25)m.x = m.x - x + 50;elsem.x = m.x - x;if (y >= 25)m.y = m.y - y + 50;elsem.y = m.y - y;p = m.x / 50 - 1;q = m.y / 50 - 1;if (p < 15 && q < 15){if (stick[p][q] == 1)break;if (m.x <= 750 && m.y <= 750 && m.x >= 50 && m.y >= 50){setfillcolor(RED);fillcircle(m.x, m.y, 20);red[p][q] = 1;if (success(red) == 1)win(2);stick[p][q] = 1;}for (int x1 = 0; x1 < 15; x1++) {for (int y1 = 0; y1 < 15; y1++) {whtie1[x1][y1] = white[x1][y1];}}robot(red, white, stick);}}}}int robot(int x[15][15], int y[15][15], int z[15][15]){srand((unsigned)time(NULL));int m, n;/*-------------------------------------------------------判断四-----------------------------------------------------------*/for (m = 0; m < 15; m++){for (n = 0; n < 15; n++){if (x[m][n] == 1 && x[m][n + 1] == 1 && x[m][n + 2] == 1 && x[m][n + 3] == 1 && n < 11 && z[m][n + 4] != 1){y[m][n + 4] = 1;z[m][n + 4] = 1;setfillcolor(WHITE);fillcircle((m + 1) * 50, (n + 5) * 50, 20);return 0;}if (x[m][n] == 1 && x[m][n + 1] == 1 && x[m][n + 2] == 1 && x[m][n + 3] == 1 && n > 0 && z[m][n - 1] != 1){y[m][n - 1] = 1;z[m][n - 1] = 1;setfillcolor(WHITE);fillcircle((m + 1) * 50, n * 50, 20);return 0;}if (x[m][n] == 1 && x[m + 1][n] == 1 && x[m + 2][n] == 1 && x[m + 3][n] == 1 && m < 11 && z[m + 4][n] != 1){y[m + 4][n] = 1;z[m + 4][n] = 1;setfillcolor(WHITE);fillcircle((m + 5) * 50, (n + 1) * 50, 20);return 0;}if (x[m][n] == 1 && x[m + 1][n] == 1 && x[m + 2][n] == 1 && x[m + 3][n] == 1 && m > 0 && z[m - 1][n] != 1){y[m - 1][n] = 1;z[m - 1][n] = 1;setfillcolor(WHITE);fillcircle(m * 50, (n + 1) * 50, 20);return 0;}if (x[m][n] == 1 && x[m + 1][n + 1] == 1 && x[m + 2][n + 2] == 1 && x[m + 3][n + 3] == 1 && n < 11 && m < 11 && z[m + 4][n + 4] != 1){y[m + 4][n + 4] = 1;z[m + 4][n + 4] = 1;setfillcolor(WHITE);fillcircle((m + 5) * 50, (n + 5) * 50, 20);return 0;}if (x[m][n] == 1 && x[m + 1][n + 1] == 1 && x[m + 2][n + 2] == 1 && x[m + 3][n + 3] == 1 && n > 0 && m > 0 && z[m - 1][n - 1] != 1){y[m - 1][n - 1] = 1;z[m - 1][n - 1] = 1;setfillcolor(WHITE);fillcircle(m * 50, n * 50, 20);return 0;}if (n > 3 && m < 11 && x[m][n] == 1 && x[m + 1][n - 1] == 1 && x[m + 2][n - 2] == 1 && x[m + 3][n - 3] == 1 && z[m + 4][n - 4] != 1){y[m + 4][n - 4] = 1;z[m + 4][n - 4] = 1;setfillcolor(WHITE);fillcircle((m + 5) * 50, (n - 3) * 50, 20);return 0;}if (n < 14 && m > 0 && x[m][n] == 1 && x[m + 1][n - 1] == 1 && x[m + 2][n - 2] == 1 && x[m + 3][n - 3] == 1 && z[m - 1][n + 1] != 1){y[m - 1][n + 1] = 1;z[m - 1][n + 1] = 1;setfillcolor(WHITE);fillcircle(m * 50, (n + 2) * 50, 20);return 0;}}}/*-------------------------------------------------------判断三-----------------------------------------------------------*/for (m = 0; m < 15; m++){for (n = 0; n < 15; n++){if (x[m][n] == 1 && x[m][n + 1] == 1 && x[m][n + 2] == 1 && n < 12 && z[m][n + 3] != 1){y[m][n + 3] = 1;z[m][n + 3] = 1;setfillcolor(WHITE);fillcircle((m + 1) * 50, (n + 4) * 50, 20);return 0;}if (x[m][n] == 1 && x[m][n + 1] == 1 && x[m][n + 2] == 1 && n > 0 && z[m][n - 1] != 1){y[m][n - 1] = 1;z[m][n - 1] = 1;setfillcolor(WHITE);fillcircle((m + 1) * 50, n * 50, 20);return 0;}if (x[m][n] == 1 && x[m + 1][n] == 1 && x[m + 2][n] == 1 && m < 12 && z[m + 3][n] != 1){y[m + 3][n] = 1;z[m + 3][n] = 1;setfillcolor(WHITE);fillcircle((m + 4) * 50, (n + 1) * 50, 20);return 0;}if (x[m][n] == 1 && x[m + 1][n] == 1 && x[m + 2][n] == 1 && m > 0 && z[m - 1][n] != 1){y[m - 1][n] = 1;z[m - 1][n] = 1;setfillcolor(WHITE);fillcircle(m * 50, (n + 1) * 50, 20);return 0;}if (x[m][n] == 1 && x[m + 1][n + 1] == 1 && x[m + 2][n + 2] == 1 && n < 12 && m < 12 && z[m + 3][n + 3] != 1){y[m + 3][n + 3] = 1;z[m + 3][n + 3] = 1;setfillcolor(WHITE);fillcircle((m + 4) * 50, (n + 4) * 50, 20);return 0;}if (x[m][n] == 1 && x[m + 1][n + 1] == 1 && x[m + 2][n + 2] == 1 && n > 0 && m > 0 && z[m - 1][n - 1] != 1){y[m - 1][n - 1] = 1;z[m - 1][n - 1] = 1;setfillcolor(WHITE);fillcircle(m * 50, n * 50, 20);return 0;}if (n > 2 && m < 12 && x[m][n] == 1 && x[m + 1][n - 1] == 1 && x[m + 2][n - 2] == 1 && z[m + 3][n - 3] != 1){y[m + 3][n - 3] = 1;z[m + 3][n - 3] = 1;setfillcolor(WHITE);fillcircle((m + 4) * 50, (n - 2) * 50, 20);return 0;}if (n < 14 && m > 0 && x[m][n] == 1 && x[m + 1][n - 1] == 1 && x[m + 2][n - 2] == 1 && z[m - 1][n + 1] != 1){y[m - 1][n + 1] = 1;z[m - 1][n + 1] = 1;setfillcolor(WHITE);fillcircle(m * 50, (n + 2) * 50, 20);return 0;}}}/*-------------------------------------------------------判断二-----------------------------------------------------------*/for (m = 0; m < 15; m++){for (n = 0; n < 15; n++) {if (x[m][n] == 1 && x[m][n + 1] == 1 && n < 13 && z[m][n + 2] != 1){y[m][n + 2] = 1;z[m][n + 2] = 1;setfillcolor(WHITE);fillcircle((m + 1) * 50, (n + 3) * 50, 20);return 0;}if (x[m][n] == 1 && x[m][n + 1] == 1 && n > 0 && z[m][n - 1] != 1){y[m][n - 1] = 1;z[m][n - 1] = 1;setfillcolor(WHITE);fillcircle((m + 1) * 50, n * 50, 20);return 0;}if (x[m][n] == 1 && x[m + 1][n] == 1 && m < 13 && z[m + 2][n] != 1){y[m + 2][n] = 1;z[m + 2][n] = 1;setfillcolor(WHITE);fillcircle((m + 3) * 50, (n + 1) * 50, 20);return 0;}if (x[m][n] == 1 && x[m + 1][n] == 1 && m > 0 && z[m - 1][n] != 1){y[m - 1][n] = 1;z[m - 1][n] = 1;setfillcolor(WHITE);fillcircle(m * 50, (n + 1) * 50, 20);return 0;}if (x[m][n] == 1 && x[m + 1][n + 1] == 1 && n < 13 && m < 13 && z[m + 2][n + 2] != 1){y[m + 2][n + 2] = 1;z[m + 2][n + 2] = 1;setfillcolor(WHITE);fillcircle((m + 3) * 50, (n + 3) * 50, 20);return 0;}if (x[m][n] == 1 && x[m + 1][n + 1] == 1 && n > 0 && m > 0 && z[m - 1][n - 1] != 1){y[m - 1][n - 1] = 1;z[m - 1][n - 1] = 1;setfillcolor(WHITE);fillcircle(m * 50, n * 50, 20);return 0;}if (n > 1 && m < 13 && x[m][n] == 1 && x[m + 1][n - 1] == 1 && z[m + 2][n - 2] != 1){y[m + 2][n - 2] = 1;z[m + 2][n - 2] = 1;setfillcolor(WHITE);fillcircle((m + 3) * 50, (n - 1) * 50, 20);return 0;}if (n < 14 && m > 0 && x[m][n] == 1 && x[m + 1][n - 1] == 1 && z[m - 1][n - 1] != 1){y[m - 1][n - 1] = 1;z[m - 1][n - 1] = 1;setfillcolor(WHITE);fillcircle(m * 50, (n + 2) * 50, 20);return 0;}}}while (1){for (m = 0; m < 15; m++){for (n = 0; n < 15; n++){if (x[m][n] == 1){setfillcolor(WHITE);int p = rand() % 8;switch (p){case 0:if (m == 0)break;y[m - 1][n] = 1;z[m - 1][n] = 1;fillcircle(m * 50, (n + 1) * 50, 20);return 0;case 1:if (m == 0 || n == 14)break;y[m - 1][n + 1] = 1;z[m - 1][n + 1] = 1;fillcircle(m * 50, (n + 2) * 50, 20);return 0;case 2:if (n == 14)break;y[m][n + 1] = 1;z[m][n + 1] = 1;fillcircle((m + 1) * 50, (n + 2) * 50, 20);return 0;case 3:if (m == 14 || n == 14)break;y[m + 1][n + 1] = 1;z[m + 1][n + 1] = 1;fillcircle((m + 2) * 50, (n + 2) * 50, 20);return 0;case 4:if (m == 14)break;y[m + 1][n] = 1;z[m + 1][n] = 1;fillcircle((m + 2) * 50, (n + 1) * 50, 20);return 0;case 5:if (m == 14 || n == 0)break;y[m + 1][n - 1] = 1;z[m + 1][n - 1] = 1;fillcircle((m + 2) * 50, n * 50, 20);return 0;case 6:if (n == 0)break;y[m][n - 1] = 1;z[m][n - 1] = 1;fillcircle((m + 1) * 50, n * 50, 20);return 0;case 7:if (m == 0 || n == 0)break;y[m - 1][n - 1] = 1;z[m - 1][n - 1] = 1;fillcircle(m * 50, n * 50, 20);return 0;}}}}}}
双人对站模块:
玩家可通过现实的商讨来决定谁先下棋,从而利用鼠标进而进行PK。
此模块排序函数search_record()
代码如下:
void behind(){board();setlinecolor(WHITE);MOUSEMSG m; // 定义鼠标消息int i = 0, x, y, q = 0, p = 0;int red[15][15], white[15][15], stick[15][15];while (true){m = GetMouseMsg();x = m.x % 50;y = m.y % 50;setrop2(R2_COPYPEN);//二元光栅操作码:R2_COPYPEN当前颜色if (m.uMsg == WM_LBUTTONDOWN)// 使用鼠标左键进行操作{if (i % 2 == 0 && m.x <= 1130 && m.y < 680 && m.x >= 860 && m.y > 590)//投降win(1);if (i % 2 == 1 && m.x <= 1130 && m.y < 680 && m.x >= 860 && m.y > 590)win(2);if (m.x <= 1130 && m.y < 590 && m.x >= 860 && m.y > 500)//重开behind();if (m.x <= 1130 && m.y < 500 && m.x >= 860 && m.y > 410){if (i % 2 == 1){red[p][q] = 0;stick[p][q] = 0;i++;}else {white[p][q] = 0;stick[p][q] = 0;i++;}regret(red, white);}if (x >= 25)m.x = m.x - x + 50;elsem.x = m.x - x;if (y >= 25)m.y = m.y - y + 50;elsem.y = m.y - y;p = m.x / 50 - 1;q = m.y / 50 - 1;if (p < 15 && q < 15){if (stick[p][q] == 1)continue;if (i % 2 == 0 && m.x <= 750 && m.y <= 750 && m.x >= 50 && m.y >= 50){setfillcolor(RED);fillcircle(m.x, m.y, 20);red[p][q] = 1;if (success(red) == 1)win(2);stick[p][q] = 1;i++;}else if (i % 2 == 1 && m.x <= 750 && m.y <= 750 && m.x >= 50 && m.y >= 50){setfillcolor(WHITE);fillcircle(m.x, m.y, 20);white[p][q] = 1;if (success(white) == 1)win(1);stick[p][q] = 1;i++;}}}}}
排行榜模块:
打印出迄今为止所有玩家的游戏前五名排行榜,其中计入排行榜姓名时均为两个汉字的名字排行榜,其中步数均设置有域宽,所以无论步数为两位数或者是一位数均可对齐。
void ret(){initgraph(600, 600);setbkcolor(WHITE);cleardevice();IMAGE img;loadimage(&img, "R-C.jpg");putimage(200, 200, &img);settextcolor(RED);settextstyle(40, 0, "微软雅黑");outtextxy(200, 50, "点击下面空白处返回");outtextxy(50, 500, "请在小黑框内查看排名");//打印榜前五名 FILE *fp;int n = 1;Linklist *t;fp = fopen("zy.dat", "wb");t = head -> next;while(t){puts("用户名---用时--");if(n <= 5)printf("%5s %11.6lf %d\n", t -> name, t -> score, n);fprintf(fp, "%s %lf\n", t -> name, t -> score);t = t -> next;n ++;}fclose(fp);while (true){MOUSEMSG m;m = GetMouseMsg();if (m.uMsg == WM_LBUTTONDOWN)if (m.x <= 350 && m.x >= 250 && m.y <= 350 && m.y >= 250)start();}}
3.3 开发日志
程序开发从构思到完成共历时5天,基本过程如下:
5.22:认真观看老师布置的作业的要求以及评判机制
5.23:三人建立微信群聊认真商讨如何去做好课程设计,合理分工。
5.24白天:去bilibili
与csdn
搜素相关资源进行学习并且跟着视频或者博客尝试进行开发
5.24夜晚: 下载EasyX图形库,配合使用Visual Studio进行开发,基本完成了菜单,音乐,人机对战的内容。
5.25白天:在人机对战的基础上进行举一反三,开发出来人人对战的功能,并且完善了悔棋,投降,重来的基本功能。
5.25晚上: 按照作业要求,完善排行功能,听取老师建议,再次对界面进行优化升级。
5.26: 以MarkDown作为起草文档后,再认真书写word文档,以做到内容精确无误。
四、调试与测试
4.1 游戏开始界面
游戏一开始时,会出现通过EasyX绘制出来的新的窗口,主界面将会包括五子棋游戏,音乐,人机对战,双人对战,排行榜的可以使用鼠标进行点击的按钮,点击人机对战,玩家将进入人机对战的模式,点击双人对战,玩家将会进入双人对战模式,点击玩家排行,玩家就可以去查看该游戏人机模式中历来的游戏玩家所走的最短步数的排行榜,步数越少排名就越靠前,为了使得玩家多玩游戏,我们把结束游戏,设置成为当你玩完一把游戏后才会出现。玩家可以根据自己的需求按照表上的内容去输入不同的数字来进行开始的操作。游戏开始时的主界面,如图2所示:
图2 游戏进入的主界面
1、选择进行的对战模式
进入主菜单界面后,玩家如果想要直接开始战斗,那么可以点击人机对战,此时玩家将进入人机对战界面,或者玩家也可以点击双人对战,此时玩家将进入双人对战界面,如果选择人机对战的话,根据鼠标点击人机对战来选择人机对战。人机对战的界面,如图3所示:
图3 人机对战界面
2、人机对战模式时的先手下棋界面
在人机对战模式下,玩家使用红棋先手下棋,在下棋的过程中玩家可以悔棋,重来,投降这三个选项,使得游戏更加有趣味性和丰富多彩性。下面是玩家与人机下棋的图片 如图四:
图4 玩家与人机对战进行界面
4.2游戏进行界面
游戏进行时界面
游戏进行时的界面如图5所示,在玩家经过了模式的选择以及先手的选择之后,无论用户选择进行人机对战模式亦或是双人对战模式,主界面都将由棋盘,双色棋子,系统的提示(包括如需悔棋请按T键进行操作;如需投降请按Esc键进行操作;请玩家下棋等)组成。然后玩家可以通过操作鼠标上的左键,来对对界面上的光标进行下棋。游戏进行界面,如图5所示:
图5 玩家与人机对战进行界面
游戏结束界面
当游戏进行时有一方投降,或者是有一方获胜时游戏结束,此时,界面上将弹出获胜以及继续进行游戏的选项:留下大名后继续游戏和退出游戏。有一方投降时的结束页面,如图6所示:
图6 胜利显示的界面
当我们输入完名字和步数的时候就可以看到的界面如图7所示:
图7 输入名字后显示的界面
4.3 排行榜模块
1、游戏获胜后进行留名界面
当玩家进行人机对战时最终所用的步数低于排行榜中最后一名的步数时,此时玩家需要留下自己的姓名以供后续玩家查看,当留下姓名之后屏幕将会显示提醒玩家接下来可以进行的操作游戏留名界面,如图8所示:
图8 游戏留名界面
留名结束后的显示界面,如图9所示:
图9 留名结束后的显示界面
排行榜显示界面
排行榜显示界面
在主菜单亦或是游戏结束之后均可以进行查看排行榜的操作,排行榜显示界面,如图10所示:
图10 排行榜显示界面
磁盘文件界面
排行榜中的信息会以二进制的形式储存在磁盘文件中paiming文档中,排行榜中信息的更新等也是通过文件指针在该文档中进行操作,后台也可以通过更改文档中的信息来更改排行榜中的信息,磁盘文件界面,如图13所示:
五、用户手册
使用说明:
本程序所有的指令均是点击鼠标进行的。
输入姓名的时候最好使用英文来进行留名。
游戏过程存在悔棋,重来,投降这样的便捷的按钮。
六、设计体会
通过近一周的不断学习与改进代码的过程,学到了很多新的知识,同时也对之前学过的知识再次进行了巩固,得到了新的理解,特别是文件这一部分,通过对程序游戏排行榜的加入,用了近9个小时的时间去不断地进行研究和实验,才终于将其完成,最让我觉得很舒服的一点是,做自己开心的事情的时候,计算熬夜,待在实验室一天,真的不觉得难受,反而与大家商讨对策,认真负责做一些事,晚上做好程序然后发一个csdn动态 很美好哈哈哈。
如果觉得《C语言---五子棋游戏之我的大一课程设计》对你有帮助,请点赞、收藏,并留下你的观点哦!