五子棋-电气班C语言程序设计报告
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
五子棋-电气班C语言程序设计报告
C语言程序设计报告
题目: 五子棋
班级: 电气Qxxx班
人数: 3人
小组成员: xx、xx、xx
指导老师:xx
时间: 2015.11.30
目录
第一章课程设计的目的和要求 (4)
1.1 课程设计的目的 (4)
1.2 课程设计的要求 (4)
1.3 课程设计的实验环境 (4)
第二章功能描述 (5)
第三章总体设计 (6)
3.1 功能模块设计 (6)
3.1.1 任务执行流程图 (6)
3.1.2 下棋函
数流程图 (8)
3.2 数据结构设计 (10)
3.2.1 定义结构体 (10)
3.2.2 定义数组 (10)
3.2.3 全局变量 (10)
3.3 函数功能描述 (10)
第四章程序实现 (11)
4.1源码分析 (11)
4.2运行结果及界面介绍 (43)
第五章后记 (48)
第一章课程设计的目的和要求
1.1 课程设计的目的
1.加深对C语言数据类型,运算,语句结构及其程序设计的基本方法理解和掌握;
2.熟练掌握流程图的绘制、程序设计文档的书写;
3.通过编写一个完整的程序,一方面可以检查我们这学期的学习情况,为以后的学习打下坚实的基础;
4.熟悉C语言游戏编程,掌握五子棋游戏开发的基本原理,从而为以后的程序开发奠定基础。
1.2 课程设计的要求
1、编写程序代码,调试所写程序使其能够正确运行;
2、能进行基本的五子棋操作,有图形界面,能够用键盘操作;
3、能够实现悔棋、存档和读档等附加功能
1.3 课程设计的实验环境
该课程设计在设计与实验过程中需要在windows XP系统/windows 2000以上系统中进行,程序设计要求在visual C++6.0平台中进行,完成代码的编写、编译、调试、测试等工作。
本游戏对计算机硬件和操作系统要求极低,所以在这里只是把自己的电脑硬件参数和系统参数列下:
硬件:Cpu:2.1GHZ,内存,2GB,硬盘:320GB,操作系统:windows xp 软件环境:安装VC++6.0
第二章功能描述
本程序用C语言实现了五子棋游戏,能进行基本的五子棋操作。
程序能实现界面的初始化功能、下棋功能、人机智能对战功能、胜负判断功能、悔棋功能、读档及存档功能,通过键盘操作控制下棋。
(1)显示欢迎界面。
在游戏开始时出现一个欢迎的界面同时介绍了游戏的规则;(2)初始化功能。
程序初始化屏幕和棋盘,默认玩家先行。
(3)下棋操作。
利用W、S、A、D及空格键实现下棋操作,在下棋过程中能随时按ESC键退出。
(4)人机智能对战功能。
电脑根据玩家的下棋对棋盘进行智能分析,然后下棋,实现人机对弈。
(5)悔棋功能。
玩家可以有三次悔棋机会。
(6)胜负判断功能。
程序能对下棋的结果进行判断,分出胜负。
并显示获胜方。
(7)读档、存档功能。
游戏中途退出会提示是否存档,如果存档,则下次开始的时候会提示是否读档继续上次的游戏。
第三章总体设计3.1 功能模块设计
3.1.1 任务执行流程图
是
3.1.2 下棋函数流程图
否是
否
否
是
3.2 数据结构设计
3.2.1 定义结构体
将棋盘上每个点的左边定义为一个结构体;
typedef struct
{
int x, y;
}point;
3.2.2 定义数组
定义数组board[15][15]表示棋盘,用来记录棋盘上每个棋子的状态;
3.2.3 全局变量
定义整形数组back[4] 用来记录前两步双方下棋的状态,便于后面进行悔棋操作
定义整形n = 3; 用来记录悔棋次数
3.3 函数功能描述
1、显示欢迎信息bool welcome();
2、初始化棋盘void InitBoard();
3、输出棋盘void chessboard();
4、判断胜负int Win(char c);
5、下棋void play(point &r);
6、显示获胜void showsusscced(char c);
7、悔棋bool BackStep(int back[]);
8、人机对战智能算法void ComAlgo(point &r);
9、存盘函数bool SaveLoad();
10、读盘函数bool DownLoad();
第四章程序实现
4.1源码分析
1、显示欢迎信息
bool welcome()
{
char ch;
printf("\n\n\n\n");
printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
printf("┃Welcome you to gobang World! ┃\n");
printf("┃1、You can use the A,D,W and S key to move the chessman; ┃\n");
printf("┃2、You can press Space key to enter after you move it; ┃\n"); printf("┃3、You can use Esc key to exit the game; ┃\n"); printf("┃4、Don't move the pieces out of the chessboard. ┃\n"); printf("┃Do you want to continue?(Y/N) ┃\n");
printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n"); while (! strchr("YN", ch = toupper(getch()))) {
putchar('\a');
}
if(ch == 'N') return false;
else return true;
}
2、初始化棋盘
void InitBoard()
{
char ch; int i, j;
printf("\n\n是否读档?(Y/N)\n");
if((ch = toupper(getch())) == 'Y' && DownLoad())
printf("读档成功!\n");
else
{
for(i = 0; i < 15; i ++)
for(j = 0; j < 15; j ++)
board[i][j] = ' ';
}
chessboard();
}
3、输出棋盘
void chessboard()
{
//清屏
system("cls");
//输出棋盘的上边缘
printf(" 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 \n"); printf(" ┏━┳━┳━┳━┳━┳━┳━┳━┳━┳━┳━┳━┳━┳━┳━┳━┓\n"); printf(" ┃│││││││││││││││┃\n");
for(int i = 1; i <= 15; i ++)
{
//输出列序号及相应的列元素
printf("%02d┣─", i);
for(int j = 1; j <= 15; j ++)
{
switch (board[i - 1][j - 1])
{
//(由于在命令行模式下显示,所以,颜色是颠倒的)
case ' ': printf("┼─"); break; //如果当前位置无子,则输出棋盘
case 'h': printf("○─"); break; //如果是黑子,则输出黑子的符号
case 'b': printf("●─"); break; //如果是白子,则输出白子的符号
case 'g': printf("⊙─"); break; //显示光标
}
}
//输出每列的最后一个制表符
printf("┫%02d\n", i);
printf(" ┃│││││││││││││││┃\n");
}
printf(" ┗━┷━┷━┷━┷━┷━┷━┷━┷━┷━┷━┷━┷━┷━┷━┷━┛\n");
printf(" 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 \n");
}
4、下棋
void play(point &r)
{
char key, c;
do
{
while (! strchr("ADWSZ", key = toupper(getch())))
{
if(key == 27 || key == 32) break;
putchar('\a');
}
switch(key)
{
case 'A': //向左
if(r.y <= 1) break;
else r.y --;
c = board[r.x - 1][r.y - 1]; //记录光标
board[r.x - 1][r.y - 1] = 'g';
chessboard();
board[r.x - 1][r.y - 1] = c;
break;
case 'D': //向右
if(r.y >= 15) break;
else r.y ++;
c = board[r.x - 1][r.y - 1]; board[r.x - 1][r.y - 1] = 'g'; chessboard();
board[r.x - 1][r.y - 1] = c; break;
case 'W': //向上
if(r.x <= 1) break;
else r.x --;
c = board[r.x - 1][r.y - 1]; board[r.x - 1][r.y - 1] = 'g'; chessboard();
board[r.x - 1][r.y - 1] = c; break;
case 'S': //向下
if(r.x >= 15) break;
else r.x ++;
c = board[r.x - 1][r.y - 1]; board[r.x - 1][r.y - 1] = 'g'; chessboard();
board[r.x - 1][r.y - 1] = c;
break;
case 32: //SPACE空格
if(board[r.x - 1][r.y - 1] != ' ') key = 0; //key的值修改为非32的数值
else
{
board[r.x - 1][r.y - 1] = 'b';
back[0] = r.x - 1; back[1] = r.y - 1; //记录当前位置,便于悔棋
chessboard();
}
break;
case 'Z': //悔棋
BackStep(back);
break;
case 27: //ESC退出
printf("Game Over!\n");
printf("是否存档?(Y/N)\n");
if((c = toupper(getch())) == 'Y' &&
SaveLoad())
printf("存档成功!\n");
exit(1);
default:
fflush(stdin);
}
}while(key != 32);
}
注:
1、W、S、A、D分别表示上下左右键。
如果超出了棋盘则不进行操作。
否则向键值指示的方向移动一步。
2、c = board[r.x - 1][r.y - 1];
board[r.x - 1][r.y - 1] = 'g';
chessboard();
board[r.x - 1][r.y - 1] = c;
这四句表示记录当前移动位置,然后显示光标,显示完以后还原棋盘。
3、按空格键表示下棋,如果当前位置有棋子则不进行操作。
back[0] = r.x - 1; back[1] = r.y - 1; 用于记录当前位置,便于悔棋。
4、按Z悔棋
5、按ESC键退出,并且提示是否存档。
5、判断胜负
int Win(char c)
{
int i, j, ok = 1;
//判断横着的5个是否都相等
for(i = 0; i < 15; i ++)
{
for(j = 0; j < 11; j ++)
if(board[i][j]==c && board[i][j+1]==c && board[i][j+2]==c && board[i][j+3]==c && board[i][j+4]==c)
return ok;
}
//判断竖着的5个是否都相等
for(j = 0; j < 15; j ++)
{
for(i = 0; i < 11; i ++)
if(board[i][j]==c && board[i+1][j]==c && board[i+2][j]==c && board[i+3][j]==c && board[i+4][j]==c)
return 1;
}
//判断左斜5个是否都相等
for(i = 0; i < 11; i ++)
{
for(j = 0; j < 11; j ++)
if(board[i][j]==c && board[i+1][j+1]==c && board[i+2][j+2]==c && board[i+3][j+3]==c && board[i+4][j+4]==c)
return ok;
}
//判断右斜5个是否都相等
for(i = 0; i < 11; i ++)
{
for(j = 14; j > 3; j --)
if(board[i][j]==c && board[i+1][j-1]==c && board[i+2][j-2]==c && board[i+3][j-3]==c && board[i+4][j-4]==c)
return ok;
}
return ok = 0;
}
注:全篇扫面棋盘,从四个方向判断是否存在连着5个棋子
6、显示获胜
void showsusscced(char c)
{
if(c == 'b')
{
printf("游戏结束白棋获胜!\n"); }
else if(c == 'h')
{
printf("游戏结束黑棋获胜!\n"); }
else
{
printf("游戏结束和棋!\n");
}
}
7、悔棋
bool BackStep(int back[])
{
if(n > 0)
{
board[back[0]][back[1]] = board[back[2]][back[3]] = ' ';
chessboard();
n --;
printf("悔棋成功,您还有%d次悔棋机会\n", n);
return true;
}
else
{
printf("悔棋超过三次,您不能悔棋了!\n");
return false;
}
}
8、存盘函数
bool SaveLoad()
{
FILE *f;
f = fopen("Load.TXT", "w");
if(f == NULL)
{
printf("存档失败!\n");
return false;
}
else
{
fwrite(board, sizeof(char), 225, f);
fclose(f);
return true;
}
}
9、读盘函数
bool DownLoad()
{
FILE *f;
f = fopen("Load.TXT", "r");
if(f == NULL)
{
printf("读档失败!\n");
return false;
}
else
{
fread(board, sizeof(char), 225, f);
fclose(f);
return true;
}
}
注:存盘存在当前文件夹内的Load.txt文件中,用fwrite()函数和fread()函数读取和存储数组元素。
10、人机对战智能算法
void ComAlgo(point &r)
{
//棋型数组
int qiju[2][15][15][8][2] = {0};
/*其中第一个下标为0时表示白棋,为1时表示黑棋,第二和第三个下标表示(x,y),
第四个下标表示8个方向,最后一个下标为0时表示棋子数,为1时表示空格数*/
char d; //表示黑棋或白棋
int k, i, j, q, a = 1; //(k, i, j, q, 0/1)表示棋型数组; a, b用来计数
int b = 0, y1 = 0, y2 = 0, x1 = 0, x2 = 0; // b表示得分; (x1, y1)和(x2, y2)表示坐标
int a1[15][15] = {0}, a2[15][15] = {0}; //用来记录白棋和黑棋各个棋子位置的得分
/****************为双方填写棋型表************/
for(k = 0; k < 2; k ++)
for(i = 0; i < 15; i ++)
for(j = 0; j < 15; j ++)
{
if(board[i][j] == ' ')
{
for(q = 0; q < 8; q ++)
{
if(k == 0) d = 'b';
else d = 'h';
//左←
if(q == 0 && j >= 0)
{
for(;j - a >= 0;)
{
if(board[i][j-a] == d) {b++; a++; continue;}
else break;
}
qiju[k][i][j][q][0] = b; b = 0;
if(board[i][j-a] == ' ' && j - a >= 0) {qiju[k][i][j][q][1] = 1; a = 1;}
else { qiju[k][i][j][q][1] = 0; a = 1;}
}
//左上↖
if(q == 1 && i >= 0 && j >= 0)
{
for(;i - a >= 0 && j - a >= 0;)
{
if(board[i-a][j-a] == d) {b++; a++; continue;}
else break;
}
qiju[k][i][j][q][0] = b; b = 0;
if(board[i-a][j-a] == ' ' && j - a >= 0 && i - a >= 0) {qiju[k][i][j][q][1] = 1; a = 1;}
else {qiju[k][i][j][q][1] = 0; a = 1;}
}
//上↑
if(q == 2 && i >= 0)
{
for(;i - a >= 0;)
{
if(board[i-a][j] == d) {b++; a++; continue;}
else break;
}
qiju[k][i][j][q][0] = b; b = 0;
if(board[i-a][j] == ' ' && i - a >= 0) {qiju[k][i][j][q][1] = 1; a = 1;}
else {qiju[k][i][j][q][1] = 0; a = 1;}
}
//右上↗
if(q == 3 && i >= 0 && j < 15)
{
for(;i - a >= 0 && j + a < 15;)
{
if(board[i-a][j+a] == d) {b++; a++; continue;}
else break;
}
qiju[k][i][j][q][0] = b; b = 0;
if(board[i-a][j+a] == ' ' && i - a >= 0 && j + a < 15) {qiju[k][i][j][q][1] = 1; a =
1;}
else {qiju[k][i][j][q][1] = 0; a = 1;}
}
//右→
if(q == 4 && j < 15)
{
for(;j + a < 15;)
{
if(board[i][j+a] == d) {b++; a++; continue;}
else break;
}
qiju[k][i][j][q][0] = b; b = 0;
if(board[i][j+a] == ' ' && j + a < 15) {qiju[k][i][j][q][1] = 1; a = 1;}
else {qiju[k][i][j][q][1] = 0; a = 1;}
}
//右下↘
if(q == 5 && i < 15 && j < 15)
{
for(;i + a < 15 && j + a < 15;)
{
if(board[i+a][j+a] == d) {b++; a++; continue;}
else break;
}
qiju[k][i][j][q][0] = b; b = 0;
if(board[i+a][j+a] == ' ' && i + a < 15 && j + a < 15) {qiju[k][i][j][q][1] = 1; a = 1;}
else {qiju[k][i][j][q][1] = 0; a = 1;}
}
//下↓
if(q == 6 && i < 15)
{
for(;i + a < 15;)
{
if(board[i+a][j] == d) {b++; a++; continue;}
else break;
}
qiju[k][i][j][q][0] = b; b = 0;
if(board[i+a][j] == ' ' && i + a < 15) {qiju[k][i][j][q][1] = 1; a = 1;}
else {qiju[k][i][j][q][1] = 0; a = 1;}
}
//左下↙
if(q == 7 && j >= 0 && i < 15)
{
for(;i + a < 15 && j - a >= 0;)
{
if(board[i+a][j-a] == d) {b++; a++; continue;}
else break;
}
qiju[k][i][j][q][0] = b; b = 0;
if(board[i+a][j-a] == ' ' && i + a < 15 && j - a >= 0) {qiju[k][i][j][q][1] = 1; a = 1;}
else {qiju[k][i][j][q][1] = 0; a = 1;}
}
}
}
}
/******************根据评分规则对每一个空格评分***************/
for(k = 0; k < 2; k ++)
for(i = 0; i < 15; i ++)
for(j = 0; j < 15; j ++)
{
if(k == 0) //为白棋评分
{
for(q = 0; q < 4; q ++)
{
//活四
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 4
&& qiju[k][i][j][q][1] == 1 && qiju[k][i][j][q+4][1] == 1 )
b += 7000;
//活三
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 3
&& qiju[k][i][j][q][1] == 1 && qiju[k][i][j][q+4][1] == 1 )
b += 301;
//活二
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 2
&& qiju[k][i][j][q][1] == 1 && qiju[k][i][j][q+4][1] == 1 )
b += 43;
//活一
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 1
&& qiju[k][i][j][q][1] == 1 && qiju[k][i][j][q+4][1] == 1 )
b += 11;
//死四
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 4
&& ( (qiju[k][i][j][q+4][1] == 0) || (qiju[k][i][j][q][1] == 0) ) )
b += 7000;
//死三
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 3
&& ( (qiju[k][i][j][q][1] == 1 && qiju[k][i][j][q+4][1] == 0)
|| (qiju[k][i][j][q][1] == 0 && qiju[k][i][j][q+4][1] == 1) ) )
b += 63;
//死二
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 2
&& ( (qiju[k][i][j][q][1] == 1 && qiju[k][i][j][q+4][1] == 0)
|| (qiju[k][i][j][q][1] == 0 && qiju[k][i][j][q+4][1] == 1) ) )
b += 6;
//死一
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 1
&& ( (qiju[k][i][j][q][1] == 1 && qiju[k][i][j][q+4][1] == 0)
|| (qiju[k][i][j][q][1] == 0 && qiju[k][i][j][q+4][1] == 1) ) )
b += 1;
}
if(b == 126 || b == 189 || b == 252) b = 1500;
if(b == 106) b = 1000;
a1[i][j] = b; b = 0;
}
if(k == 1) //为黑棋评分
{
for(q =0 ; q < 4; q ++)
{
//活四
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 4
&& qiju[k][i][j][q][1] == 1 && qiju[k][i][j][q+4][1] == 1 )
b += 30000;
//活三
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 3
&& qiju[k][i][j][q][1] == 1 && qiju[k][i][j][q+4][1] == 1 )
b += 1500;
//活二
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 2
&& qiju[k][i][j][q][1] == 1 && qiju[k][i][j][q+4][1] == 1 )
b += 51;
//活一
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 1
&& qiju[k][i][j][q][1] == 1 && qiju[k][i][j][q+4][1] == 1 )
b += 16;
//死四
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 4
&& ( (qiju[k][i][j][q+4][1] == 0) || (qiju[k][i][j][q][1] == 0) ) )
b += 30000;
//死三
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 3
&& ( (qiju[k][i][j][q][1] == 1 && qiju[k][i][j][q+4][1] == 0)
|| (qiju[k][i][j][q][1] == 0 && qiju[k][i][j][q+4][1] == 1) ) )
b += 71;
//死二
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 2
&& ( (qiju[k][i][j][q][1] == 1 && qiju[k][i][j][q+4][1] == 0)
|| (qiju[k][i][j][q][1] == 0 && qiju[k][i][j][q+4][1] == 1) ) )
b += 7;
//死一
if( (qiju[k][i][j][q][0] + qiju[k][i][j][q+4][0]) == 1
&& ( (qiju[k][i][j][q][1] == 1 && qiju[k][i][j][q+4][1] == 0)
|| (qiju[k][i][j][q][1] == 0 && qiju[k][i][j][q+4][1] == 1) ) )
b += 2;
}
if(b == 142 || b == 213 || b == 284) b = 1500;
if(b == 122) b = 1300;
a2[i][j] = b; b = 0;
}
}
/******************算出分数最高的空位,填写坐标*********************/
for(i = 0; i < 15; i ++)
for(j = 0; j < 15; j ++)
{
if(a1[x1][y1] < a1[i][j])
{ x1 = i; y1 = j; }
}
for(i = 0; i < 15; i ++)
for(j = 0; j < 15; j ++)
{
if(a2[x2][y2] < a2[i][j])
{ x2 = i; y2 = j; }
}
if(a2[x2][y2] >= a1[x1][y1]) //进攻
{
board[x2][y2] = 'h';
r.x = x2 + 1; r.y = y2 + 1;
back[2] = x2; back[3] = y2; //记录当前位置,便于悔棋
}
else //防守
{
board[x1][y1] = 'h';
r.x = x1 + 1; r.y = y1 + 1;
back[2] = x1; back[3] = y1; //记录当前位置,便于悔棋
}
chessboard();
}
11、主函数
int main()
{
char c;
point r;
//设定棋盘大小、颜色
system ("color 8f");
system ("mode con cols=72 lines=43"); system ("title 五子棋游戏!(制作人:xx)");
if(!welcome()) exit(1); //输出欢迎界面InitBoard(); //初始化棋盘
r.x = 8; r.y = 8; //初始化光标
while(1)
{
printf("该您下了!(按ESC退出,按z悔棋)\n");
c = 'b';
play(r);
if(Win(c)) break;
c = 'h';
ComAlgo(r);
if(Win(c)) break;
}
showsusscced(c); //显示获胜system("pause>nul"); return 0;
}
4.2运行结果及界面介绍
欢迎界面
开始
运行中
悔棋
结束
第五章后记
该游戏代码实现了五子棋的基本操作,由于受编译器限制故在图形界面上有所欠缺,比如不能将当前下的棋子用不同的颜色表示出来,最后赢了之后将成了的5个棋子用不同颜色表示,显示获胜的几个字不能调大字体等。
关于人机对战代码:
1、得到最大值也许不止一个点,但在我的程序中只选择第一个最大点,这个可以用于个随机数来决定,选择那一个最大值点,也可以对这些最大值点再作进一步的分析。
2、在这个算法中我只考虑了周围有棋子的点,而其它点我没有考虑。
3、可以再更进一步,用这个算法来预测以后的几步棋,再选择预测值最好的一步,这样电脑的AI就更高了。
4、这个算法没有考虑禁手规则(ps:实在没看明白禁手规则啥意思)。