C语言实习报告一担挑游戏.docx
《C语言实习报告一担挑游戏.docx》由会员分享,可在线阅读,更多相关《C语言实习报告一担挑游戏.docx(14页珍藏版)》请在冰豆网上搜索。
![C语言实习报告一担挑游戏.docx](https://file1.bdocx.com/fileroot1/2022-12/14/4e8ea07a-f586-4974-8b51-6e21146f6ebe/4e8ea07a-f586-4974-8b51-6e21146f6ebe1.gif)
C语言实习报告一担挑游戏
§1.1题目描述
【一担挑游戏】
【要求】
简单计算机模拟棋盘对弈。
其中棋盘左上角坐标为(1,1),右下角为(3,3)。
游戏规则如下:
1.计算机移动时,使用相应函数扫描棋盘矩阵,寻找未占单元,发现空单元后,置为“O”,未发现时,报告平局后退出。
2.轮到对弈者时,使用相应函数要求对弈者回答想要把“X”放在哪里。
3.谁先三点连成一线了,则报告谁胜出。
4.计算机与对弈者每下一步棋都会使相应位置变为“O”和“X”,便于在屏幕上显示矩阵。
【提示】
棋盘位置用二维数组代表,初始状态每一位置均应为“”(空),其分隔线(即棋盘线)可用——或││来分隔
§1.2算法设计
一担挑小游戏,简单、休闲、益智。
全程序大体由四部分组成:
“玩家落子模块”、“计算机落子模块”、“判断胜负模块”及“打印棋盘模块”。
考量到棋类游戏计算上的复杂性,本程序尽可能简化算法,使计算机简单地按照棋盘顺序落子。
总流程图设计构想如下:
各功能模块流程图设计如下:
1.玩家落子模块
该位置置为“X”
【注】棋盘坐标定义为从1,1到3,3
该模块要求玩家输入想要落子的坐标row,column。
如果位置row-1,column-1处字符数组元素不存在或已经被占,系统给出错误提示,并要求玩家重新输入;否则该位置置为“X”。
2.计算机落子模块
该模块设计成3*3的循环,按次序检索该单元是否为空。
如果单元已被占则跳过;否则置为“O”。
3.判断胜负模块
用穷举法判断某行、某列、对角线是否连成一线。
判断顺序如上。
若某一判断为“是”,则将连成线的字符赋值给字符变量winner再交由主函数尾的程序辨析谁获胜;若一直判断为“否”,将空值赋给变量winner,再交由玩家落子模块或计算机落子模块运行。
4.打印棋盘模块
主函数流程(程序设计布局)
§1.3程序设计及代码分析
源程序如下:
#include"stdio.h"
#include"stdlib.h"
voidprintboard();//打印棋盘
voidcheck();//检查结果是否分出胜负
charboard[3][3]={{'\0','\0','\0'},{'\0','\0','\0'},{'\0','\0','\0'}};//棋盘初始化
charwinner='\0';//标记谁是胜者
intmain()
{introw=0,column=0;//定义棋盘的行列
intstep=0;//定义计算步数的变量
printf("\nwelcometothisgame,player!
\n");
printboard();//打印空棋盘
while(winner=='\0'&&step!
=9)//当未分胜负时,循环双方对弈
{//玩家落子
do//当棋子落在无效区时,报错
{printf("\npleaseenterthecoordinatewhereyouwanttoputyourX:
");
scanf("%d,%d",&row,&column);//输入欲落子的坐标
if(row*column>9||row*column<1||board[row-1][column-1]!
='\0')
printf("\nfaultposition!
pleasereput:
");
}while(row*column>9||row*column<1||board[row-1][column-1]!
='\0');
printf("\n\nplayermove\n");
board[row-1][column-1]='X';//落子有效
printboard();//即打印棋盘
step++;//每下一步步数加一
check();//检查是否分出胜负
if(winner!
='\0'||step==9)break;//如果是,结束游戏
//计算机落子
printf("\n\ncomputermove\n");
for(row=0;row<3;row++)//计算机按循环的顺序落子
{
for(column=0;column<3;column++)
{
if(board[row][column]=='\0')
{
board[row][column]='O';
gotoend;//如果计算机已落子,停止扫描棋盘
}
}
}end:
;
printboard();//打印棋盘
step++;//每下一步步数加一
check();//检查是否分出胜负
}//一回合完成,若未分胜负,返回继续执行大循环
//当游戏结束,辨析谁是胜者
if(winner=='O')
printf("*****youlose!
*****\n");//winner值为“O”表示计算机赢
elseif(winner=='X')
printf("*****youwin!
*****\n");//winner值为“X”表示玩家赢
elseif(winner=='\0')
printf("*****draw!
*****\n");//winner值为“\0”表示平局
return0;
}//main函数结束
voidprintboard()//定义打印棋盘函数
{printf("\n");
printf("%c|%c|%c\n",board[0][0],board[0][1],board[0][2]);
printf("-----------\n");
printf("%c|%c|%c\n",board[1][0],board[1][1],board[1][2]);
printf("-----------\n");
printf("%c|%c|%c\n",board[2][0],board[2][1],board[2][2]);
printf("\n\n");
}
voidcheck()//定义判断胜负函数
{if((board[0][0]==board[1][1]&&board[0][0]==board[2][2])||(board[0][2]==board[1][1]&&board[0][2]==board[2][0]))//★
winner=board[1][1];//若某条对角线连成线,返回正中心棋子的值
else
if((board[0][0]==board[0][1]&&board[0][0]==board[0][2])||(board[0][0]==board[1][0]&&board[0][0]==board[2][0]))//★
winner=board[0][0];//若第一行或第一列连成线,返回左上角棋子的值
else
if((board[0][2]==board[1][2]&&board[0][2]==board[2][2])||(board[2][0]==board[2][1]&&board[2][0]==board[2][2]))//★
winner=board[2][2];//若第三行或第三列连成线,返回右下角棋子的值
else
if((board[0][1]==board[1][1]&&board[0][1]==board[2][1])||(board[1][0]==board[1][1]&&board[1][0]==board[1][2]))//★
winner=board[1][1];//若第二行或第二列连成线,返回正中心棋子的值
elsewinner='\0';//未连成线,返回空值
}
【注】1.绿字部分为程序的注释2.打星号处在源程序中不分行
代码分析:
本程序不需要模块的设计,使得功能受限,只能实现最基本的人机对战。
如果要使得功能更加完备,需要增加模块使对战更加灵活(尤其是计算机落子模块需要扩充)。
以下就程序的几个主要部分进行代码分析:
1.主函数
(1)初始化棋盘。
棋盘设计成3*3的二维字符数组,初始每个位置置为'\0',表示空
charboard[3][3]={{'\0','\0','\0'},{'\0','\0','\0'},{'\0','\0','\0'}};
(2)“玩家落子模块”和“计算机落子模块”合在主函数的同一while循环体下,作为整体的一回合运行。
通过循环控制,并未将两个模块单独拉出来做成独立的函数体。
当然,拉出来做成独立的函数体也可以,这样几个模块的关系会更清楚一点。
while(winner=='\0'&&step!
=9)
{//玩家落子模块
do
{printf("\npleaseenterthecoordinatewhereyouwanttoputyourX:
");
scanf("%d,%d",&row,&column);
if(row*column>9||row*column<1||board[row-1][column-1]!
='\0')
printf("\nfaultposition!
pleasereput:
");
}while(row*column>9||row*column<1||board[row-1][column-1]!
='\0');
printf("\n\nplayermove\n");
board[row-1][column-1]='X';
printboard();
step++;
check();
if(winner!
='\0'||step==9)break;
//计算机落子模块
printf("\n\ncomputermove\n");
for(row=0;row<3;row++)
{
for(column=0;column<3;column++)
{
if(board[row][column]=='\0')
{
board[row][column]='O';
gotoend;
}
}
}end:
;
printboard();
step++;
check();
}
(3)执行对弈的充分条件while(winner=='\0'&&step!
=9)表示胜者未出现且步数未满九。
一旦人机共走了九步,棋盘已被占满,则不论是否出现胜者都必须结束游戏。
(4)计算机落子采取逐行逐列(两个for循环的嵌套)检索空格再落子的办法,简单机械,实际上并未对玩家的落子情况做出应对。
不够智能是这个游戏程序的弱点。
(5)判断谁是赢家,利用winner带回的字符。
'O'是计算机的落子,故winner='O'表示计算机获胜。
同理玩家获胜、平局亦是如此。
if(winner=='O')
printf("*****youlose!
*****\n");
elseif(winner=='X')
printf("*****youwin!
*****\n");
elseif(winner=='\0')
printf("*****draw!
*****\n");
return0;
2.打印棋盘函数
双方每走一步都要打印棋盘,故该模块做成一个函数体。
函数本身只要求实现打印棋盘即可,只需调用一个printf函数,并不复杂。
3.判断胜负函数
双方每走一步都要判断是否分出胜负,以告知程序对战是否继续执行。
分析游戏规则,某行或某列或某条对角线连成线则胜者出现。
三行三列两对角,共八种情况,循环语句难以实现判断,不如干脆穷举罗列。
(详见voidcheck()函数的定义部分)在此基础上略作简化:
第一行、第一列交于左上格,故合并判断左上格的值即可;第二行、第二列交于中心格,故合并判断中心格的值即可;第三行、第三列交于右下格,故合并判断右下格的值即可;两条对角线交于中心格,故合并判断中心格的值即可。
这样if语句可省去一半。
§1.4实验数据及运行效果截图
棋盘界面
对弈过程,玩家输
平局
输入错误,重新输入
玩家获胜
§1.5设计中出现的错误及解决方法
首次尝试大一点的程序设计,没想到直接去设计游戏,果然很有挑战。
这个程序主体不大,不需要设计多少模块,程序的难点在算法上。
1.在判断玩家落子是否正确时,开始我用if((row*column>9||row*column<1)||board[row-1][column-1]!
='\0')来判定落子错误,结果输入1,4时,在棋盘2,1处输出了’X’,虽然有输出,但显然不合要求;输入4,1时不报错,但计算机有输出,显然错误。
【解决方法】追加了两个判断条件row>3和column>3,将上述判断条件改为
if(((row*column>9||row*column<1)||(board[row-1][column-1]!
='\0'||column>3))||row>3)这样,只要其中一个条件成立,则返回重新输入坐标。
2.在执行游戏时,开始未设计计算步数的变量step,结果棋盘下满时,不论是否分出胜负,均报告平局draw!
【解决方法】设计变量step,在玩家或计算机每下一步后使其加一,将对弈程序的循环条件改为while(winner=='\0'&&step!
=9),即未分胜负且棋盘未满,再统一判断胜负平。
3.在设计计算机落子模块时,开始时玩家落子后,计算机连下好几步,因为光用两个for循环只能要求计算机找出空位落子,不能控制计算机落子次数。
几经考虑,设计了两种方案:
(1)追加一个标记变量k,使计算机未落子时,k置为0,否则置为1;再在for循环中检查k的值,一旦为1,退出循环。
设计如下:
k=0;
for(row=0;row<3;row++)
{if(k==1)break;
for(column=0;column<3;column++)
{
if(board[row][column]=='\0')
{k=1;
board[row][column]='O';
break;}
}
}
}
(2)调用"stdlib.h"头文件中的goto函数。
计算机一旦落子,则执行,程序直接跳到循环末尾。
设计如下:
for(row=0;row<3;row++)
{for(column=0;column<3;column++)
{
if(board[row][column]=='\0')
{
board[row][column]='O';
gotoend;
}
}
}end:
;
【解决方法】方案
(2)更简洁,更清楚,实际采用了方案
(2)
4.在设计判断胜负模块时,在判断胜负的方法上几经改进,题目要求“三点一线”则获胜,3*3的棋盘有三行三列两对角,共八种情况,开始时设计八个if语句的嵌套结构,虽然可以运行,但程序冗长可读性低,且判断结果相互交叉,造成运行的事倍功半。
【解决方法】巧妙设计了一个成对判断的算法,使程序节省了一半篇幅。
详见第三节—>代码分析—>判断胜负函数
5.在考虑计算机的落子模式时,我曾尝试过更加复杂的算法,使其不单纯按顺序扫描棋盘落子,力图使其优先追加判断胜负的能力,能赢时下赢着,快输时下守着,但算法实在太复杂,考虑的情况太多,连用八个if语句的嵌套,程序变得十分臃肿难看,而且还不够且运行总是异常,考虑到时间因素,很遗憾未能深入考虑,优化设计,就此搁浅。
结语:
此次游戏软件的设计,不但让我熟悉了软件制作的流程,熟悉了用C进行程序开发的基本方法,还让我了解了软件制作的来之不易,更加坚定了“支持正版,打击剽窃”的信念。
一款好的软件必定集结了各种优秀的要素:
良好的制作环境、熟练的软件设计师、精炼到位的算法、清晰明了的模块化程序设计、千百次的编译调试等等。
开始制作游戏时,我一门心思考虑算法,碰到种种障碍,后来通过海量地查阅资料,才深感知识浅薄、无可奈何。
有些问题只要调用一个函数就能解决,我却洋洋洒洒罗列了各种情况分类讨论。
差距是显然的。
知识匮乏加经验欠缺,这是我在这几天所深深领会的东西。
我尽己所能完成了题目的要求。
在整个课程设计中,我认为最重要的就是耐心和细心。
细心,就是在编写过程中注意拼写,仔细编写程序;耐心,就是程序编译中出现很多错误的时候能够耐心去修改,只有这样,才能很好的完成程序。
虽然在整个过程中出现了不少错误,但最终还是圆满完成了,
此次c程序设计的经历,我学到很多,同时也让我深刻明白知识及经验对程序开发员的必要性。
从今往后,我还得不断丰富自己的知识储备和提高自己的编程经验。