本博客可能有点略长,按照本人设计思路一步步来。(可能会比较繁琐,感谢阅读)
目录
一、项目文件创建
1.头文件:
2.源文件:
二、主函数设计
三、关键函数的设计
四、项目架构整理
1. main.c
2. game.c
3. game.h
五、函数详细
1.int main();
3. void game();
4.void init_chess(char chess[ROW][COL], uint row, uint col);
5.void display_chess(char chess[ROW][COL], uint row, uint col);
6.int Pl_move(char chess[ROW][COL], uint row, uint col);
7. int Ai_move(char chess[ROW][COL], uint row, uint col)
8.int judge_chess(char chess[ROW][COL], uint row, uint col);
9.void ending(char out);
六、程序验证
七、优化细节
八、结语
一、项目文件创建
文件分头文件和源文件看,其中函数稍微比较多需要用到三个文件。
项目取名为"三子棋",其中:
1.头文件:
game.h
2.源文件:
main.c
game.c
二、主函数设计
int main()
{
do //先打印循环一遍;
{
menu(); //打印菜单 (中间提示1.play与0.exit )
int val = 0;
scanf("%d",&val); //输入一个选择,放进变量 int val
//(1表示开始游戏,0表示退出游戏)
switch( val )
{
case 1: game(); // 进入游戏环节
case 0: printf("游戏已结束!n"); // 显示退出游戏
break; //退出switch ,此时val = 0;
default: printf("选择错误,请重新输入!n");
break; //退出switch , 此时val != 0;
}
} while(val) //val为0时退出,val不为0时继续;
return 0;
}
三、关键函数的设计
void menu()
{
printf("**************************************************n");
printf("******************** 1.Play ********************n");
printf("**************************************************n");
printf("******************** 0.exit ********************n");
printf("**************************************************n");
} //都是对齐的;
void game()
{
// /初始化多维数组 - init_chess();
//输出棋盘 - display_chess();
//循环
// {
//
// 玩家下棋 - Pl_move();
// 输出棋盘 - display_chess();
// 判断棋局 - judge_chess();
//
// 电脑下棋 - Ai_move();
// 输出棋盘 - display_chess();
// 判断棋局 - judge_chess();
//
// }
}
四、项目架构整理
//从上面二和三中得出以下函数
int main(); //主函数
void menu(); //打印菜单
void game(); //游戏环节
init_chess(); //初始化多维数组
Pl_move(); //play玩家下棋
Ai_move(); //电脑下棋
display_chess();//打印当前棋盘
judge_chess(); //判断棋盘
main.c game.c
二、主函数设计
int main()
{
do //先打印循环一遍;
{
menu(); //打印菜单 (中间提示1.play与0.exit )
int val = 0;
scanf("%d",&val); //输入一个选择,放进变量 int val
//(1表示开始游戏,0表示退出游戏)
switch( val )
{
case 1: game(); // 进入游戏环节
case 0: printf("游戏已结束!n"); // 显示退出游戏
break; //退出switch ,此时val = 0;
default: printf("选择错误,请重新输入!n");
break; //退出switch , 此时val != 0;
}
} while(val) //val为0时退出,val不为0时继续;
return 0;
}
三、关键函数的设计
void menu()
{
printf("**************************************************n");
printf("******************** 1.Play ********************n");
printf("**************************************************n");
printf("******************** 0.exit ********************n");
printf("**************************************************n");
} //都是对齐的;
void game()
{
// /初始化多维数组 - init_chess();
//输出棋盘 - display_chess();
//循环
// {
//
// 玩家下棋 - Pl_move();
// 输出棋盘 - display_chess();
// 判断棋局 - judge_chess();
//
// 电脑下棋 - Ai_move();
// 输出棋盘 - display_chess();
// 判断棋局 - judge_chess();
//
// }
}
四、项目架构整理
//从上面二和三中得出以下函数
int main(); //主函数
void menu(); //打印菜单
void game(); //游戏环节
init_chess(); //初始化多维数组
Pl_move(); //play玩家下棋
Ai_move(); //电脑下棋
display_chess();//打印当前棋盘
judge_chess(); //判断棋盘
int main()
{
do //先打印循环一遍;
{
menu(); //打印菜单 (中间提示1.play与0.exit )
int val = 0;
scanf("%d",&val); //输入一个选择,放进变量 int val
//(1表示开始游戏,0表示退出游戏)
switch( val )
{
case 1: game(); // 进入游戏环节
case 0: printf("游戏已结束!n"); // 显示退出游戏
break; //退出switch ,此时val = 0;
default: printf("选择错误,请重新输入!n");
break; //退出switch , 此时val != 0;
}
} while(val) //val为0时退出,val不为0时继续;
return 0;
}
void menu()
{
printf("**************************************************n");
printf("******************** 1.Play ********************n");
printf("**************************************************n");
printf("******************** 0.exit ********************n");
printf("**************************************************n");} //都是对齐的;
void game()
{
// /初始化多维数组 - init_chess();
//输出棋盘 - display_chess();
//循环
// {
//
// 玩家下棋 - Pl_move();
// 输出棋盘 - display_chess();
// 判断棋局 - judge_chess();
//
// 电脑下棋 - Ai_move();
// 输出棋盘 - display_chess();
// 判断棋局 - judge_chess();
//
// }}
四、项目架构整理
//从上面二和三中得出以下函数
int main(); //主函数
void menu(); //打印菜单
void game(); //游戏环节
init_chess(); //初始化多维数组
Pl_move(); //play玩家下棋
Ai_move(); //电脑下棋
display_chess();//打印当前棋盘
judge_chess(); //判断棋盘
下面为项目各文件的函数整理:
1. main.c
文件名main.c
//---------------------------------
#include "game.h"
int main()
{
};
//函数中{}中内容还没确定,所以没写,不代表没有;
2. game.c
文件名game.c
//-------------------------------
#include "game.h"
void menu()
{};
void game()
{};
void init_chess()
{};
void display_chess()
{};
int Pl_move()
{};
int Ai_move()
{};
int judge_chess()
{};
//函数()中形参还没确定,所以没写,不代表没有;
//函数中{}中内容还没确定,所以没写,不代表没有;
3. game.h
文件名game.h
//--------------------------------
#include
#include // 取用srand(),rand();
#include //取用time()函数
#define uint unsigned int //个人习惯。用unsigned int缩写uint,
//本项目中uint都可以代替为int,
//此项在本博客中第五函数详细后加入;
#define ROW 3 //行,棋盘的行数 //此项在本博客中第五函数详细后加入;
#define COL 3 //列,棋盘的列数 //此项在本博客中第五函数详细后加入;
void menu();
void game();
void init_chess();
void display_chess();
int Pl_move();
int Ai_move();
int judge_chess();
//函数()中形参还没确定,所以没写,不代表没有;
五、函数详细
文件名game.c
//-------------------------------
#include "game.h"
void menu()
{};
void game()
{};
void init_chess()
{};
void display_chess()
{};
int Pl_move()
{};
int Ai_move()
{};
int judge_chess()
{};
//函数()中形参还没确定,所以没写,不代表没有;
//函数中{}中内容还没确定,所以没写,不代表没有;
3. game.h
文件名game.h
//--------------------------------
#include
#include // 取用srand(),rand();
#include //取用time()函数
#define uint unsigned int //个人习惯。用unsigned int缩写uint,
//本项目中uint都可以代替为int,
//此项在本博客中第五函数详细后加入;
#define ROW 3 //行,棋盘的行数 //此项在本博客中第五函数详细后加入;
#define COL 3 //列,棋盘的列数 //此项在本博客中第五函数详细后加入;
void menu();
void game();
void init_chess();
void display_chess();
int Pl_move();
int Ai_move();
int judge_chess();
//函数()中形参还没确定,所以没写,不代表没有;
五、函数详细
各函数详细化,为了函数之间更好联通,可能会增加函数和格外变量;
1.int main();
int main ()
{
uint val_val = 2; //uint定义参考头文件game.h,实际为unsigned int
do
{
menu();
printf(" >> "); //为了醒目在哪输入
scanf("%u",&val_val); //玩家输入选择
switch (val_val) //通过上述选择判断进入哪个分支;
{
case 0: //退出
printf("游戏已结束!n");
break;
case 1: //进入游戏环节
game();
break;
default: //输出错误;需要重新输入
printf("输入错误,请重新输入!n");
break;
}
} while (val_val); //val_val为0时退出,val_val不为0时继续
return 0;
2. void menu();
void menu() //打印菜单
{
printf("**************************************************n");
printf("******************** 1.Play ********************n");
printf("**************************************************n");
printf("******************** 0.exit ********************n");
printf("**************************************************n");
}
3. void game();
void game()
{
char chess[ROW][COL] = { '0' }; //多维数组来 存储 棋盘中'棋子',目前是3*3的棋盘;
char outcome = ' '; //此变量为表示棋盘状态;
init_chess(chess, ROW, COL);
display_chess(chess, ROW, COL);
while (outcome == ' ') //只有棋盘状态还未结束时,继续循环;
{
Pl_move(chess, ROW, COL); //玩家下棋用'*'
display_chess(chess, ROW, COL); //打印棋盘
outcome = judge_chess(chess, ROW, COL); //返回判断结果,
//返回值为'*'玩家赢、'#'电脑赢、' '为继续
//想不到好的方法结束游戏并返回主函数,
//于是用一个字符变量代替结果;
if (outcome != ' ') break; //棋盘结果已出后,直接跳出循环去通报就结果;
Ai_move(chess, ROW, COL); //电脑下棋用'#'
display_chess(chess, ROW, COL); //打印棋盘
outcome = judge_chess(chess, ROW, COL);
if (outcome != ' ') break; //棋盘结果已出后,直接跳出循环去通报就结果;
}
ending(outcome); //通报结果
//为了游戏结束后可以继续在菜单选择继续游戏或退出游戏,
//单独增加一个通报结果的函数;
}
4.void init_chess(char chess[ROW][COL], uint row, uint col);
void init_chess(char chess[ROW][COL], uint row, uint col) //初始化多维数组函数
{ //上面形参为数组、数组的行、列
srand((int)time(NULL)); //为随机值赋予时间戳;不懂时间戳可自行百度;
uint i = 0;
uint j = 0;
for (i = 0; i < row; i++) //row行col列的多维数值赋值为‘ ’,在棋盘上表示无棋子;
{ //此项目此处为3行3列
for (j = 0; j < col; j++)
{
chess[i][j] = ' ';
}
}
}
5.void display_chess(char chess[ROW][COL], uint row, uint col);
void display_chess(char chess[ROW][COL], uint row, uint col) //打印棋盘
{
uint i = 0;
uint j = 0;
printf("当前棋局如下:n"); //表示当前棋局状态;
for (i = 0; i < row; i++) //打印每行;
{
//-----------------------------------------
for (j = 0; j < col; j++) //打印每行中的棋子和‘|’;
{
printf(" %c ", chess[i][j]);
if (j < col - 1) //最后一个棋子后面不需要‘|’
{
printf("|");
}
}
printf("n"); //打印完“棋子行”后换行
//------------------------------------------
for (j = 0; j < col; j++) //偶数行需要打印分隔符行‘—--|---|---’
{ //上面演示为此项目中的3行3列中分隔符行;
if (i < row - 1) //“分割行” 总比 “棋子行” 少一行;
{
printf("---");
if (j < col - 1) //分割行中'|' 总比 '---' 少一个;
{
printf("|");
}
}
}
//------------------------------------------
if (i < row - 1) //打印完一个“棋子行n分割行”后换行,
{ //其中最后一行的“棋子行”后面没有“分割行”,
printf("n"); //自然不需要“分割行”后面的"n";
}
}
6.int Pl_move(char chess[ROW][COL], uint row, uint col);
int Pl_move(char chess[ROW][COL], uint row, uint col) //玩家下棋
{
uint x = 0;
uint y = 0;
while (1)
{
printf("玩家下棋:n"); //提醒玩家下棋
printf(" >> "); //醒目输入位置
int c = scanf("%u%u", &x, &y); // int c用来防止scanf警告,个人习惯;
printf("n"); // 上面中变量c无任何作用;
if (x > 0 && x < row+1 && y < col+1 && y > 0 )//判断位置是多维函数合理范围内;
{
if (chess[x-1][y-1] == ' ') //判断所下位置原本为空才能下;
{
chess[x-1][y-1] = '*'; //写入玩家棋子'*'
return 0; //正确写入退出“玩家下棋”返回 0
}
else
{
printf("当前位置已有棋,请重新选择位置!nn"); //当前位置已有棋的提示
continue; //未成功下棋,则重新循环,重新选择下棋位置
}
}
else
{
printf("输入坐标有误,请重新输入!nn"); //未成功下棋提示,
} //并重新循环,重新选择下棋位置
}
return -1; // 非正确写入退出“玩家下棋”返回 -1
} // 虽然本项目中未用到此功能,而且从逻辑上来说不可能走到这步;
7. int Ai_move(char chess[ROW][COL], uint row, uint col)
int Ai_move(char chess[ROW][COL], uint row, uint col) //电脑下棋
{ //随机下棋,并不智能;
printf("n电脑下棋:n"); //提示当前是电脑下棋 //本人目前水平为初学者;
while (1) //如果棋子为写入成功,会循环到写入成功为止; //如果你们用更好的想法,
{ //可以给电脑
int x = rand() % row; //用随机数取3的余数, //编写更聪明的下棋方法;
int y = rand() % col; //固定范围为0~2;
if (chess[x][y] == ' ') //只有棋子为空时才能写入棋子'#';
{
chess[x][y] = '#';
return 0; //正确写入退出“电脑下棋”返回 0
}
}
//printf("%d %dn",x,y); //测设专用,此处测试电脑下棋的随机性和正确性
return -1; // 非正确写入退出“电脑下棋”返回 -1
} // 虽然本项目中未用到此功能,而且从逻辑上来说不可能走到这步;
8.int judge_chess(char chess[ROW][COL], uint row, uint col);
int judge_chess(char chess[ROW][COL], uint row, uint col) //判断棋局
{
uint i = 0;
uint j = 0;
for (i = 0; i < row; i++)
{ //例子:chess[i][0] == chess[i][1] && chess[i][1] == '*'
if (chess[i][0]==chess[i][1]&&chess[i][1]==chess[i][2]&&chess[i][0]=='*')
{return '*';} //玩家横排判定 (博客宽度有限,我把其中空格去掉,
// 上面例子中空格为平时正常代码)
if (chess[i][0]==chess[i][1]&&chess[i][1]==chess[i][2]&&chess[i][0]=='#')
{return '#';} //电脑横排判定
//-------------------------------------------------------(上面为横排判定)
if (chess[0][i]==chess[1][i] && chess[1][i]==chess[2][i]&&chess[1][i]=='*')
{return '*';} // 玩家竖排判定
if (chess[0][i]==chess[1][i]&&chess[1][i]==chess[2][i]&&chess[1][i]=='#')
{return '#';} // 电脑竖排判定
//-------------------------------------------------------(上面为竖排判定)
}
if (chess[0][0]==chess[1][1]&&chess[1][1]==chess[2][2]&&chess[1][1]=='*')
{return '*';} // 玩家从左到右对角判定
if (chess[0][0]==chess[1][1]&&chess[1][1]==chess[2][2]&&chess[1][1]=='#')
{return '#';} // 电脑从左到右对角判定
//-------------------------------------------------------(上面为从左到右对角判定)
if (chess[0][2]==chess[1][1]&&chess[1][1]==chess[2][0]&&chess[1][1]=='*')
{return '*';} // 玩家从右到左对角判定
if (chess[0][2]==chess[1][1]&&chess[1][1]==chess[2][0]&&chess[1][1]=='#')
{return '#';} // 电脑从右到左对角判定
//-------------------------------------------------------(上面为从右到左对角判定)
for (i = 0; i < row; i++) //当上面未成功时,还有空位就返回' ',
{ //表示棋局继续;
for (j = 0; j < col; j++)
{
if (chess[i][j] == ' ')
return ' ';
}
}
//-------------------------------------------------------(未成功且还有空位判定)
return '1'; //当棋局未成功且无空位时,判定为平局,返回'1',表示平局;
//-------------------------------------------------------(未成功且无空位,平局判定)
}
9.void ending(char out);
void ending(char out) //out值来自棋盘判定函数返回值
{
switch (out) //根据judge_chess();返回值判断进入哪个分支
{
case '*':
printf("您赢了!n恭喜玩家获胜!n"); //游戏玩家赢
break;
case '#':
printf("您输了!n恭喜电脑获胜!n"); //游戏电脑赢
break;
case '1':
printf("平局!n"); //游戏平局
break;
}
printf("这场游戏结束了。nn"); //多个'n'让后面的菜单和上一场游戏分割开来
}
六、程序验证
刚开始时:
void menu() //打印菜单
{
printf("**************************************************n");
printf("******************** 1.Play ********************n");
printf("**************************************************n");
printf("******************** 0.exit ********************n");
printf("**************************************************n");
}
3. void game();
void game()
{
char chess[ROW][COL] = { '0' }; //多维数组来 存储 棋盘中'棋子',目前是3*3的棋盘;
char outcome = ' '; //此变量为表示棋盘状态;
init_chess(chess, ROW, COL);
display_chess(chess, ROW, COL);
while (outcome == ' ') //只有棋盘状态还未结束时,继续循环;
{
Pl_move(chess, ROW, COL); //玩家下棋用'*'
display_chess(chess, ROW, COL); //打印棋盘
outcome = judge_chess(chess, ROW, COL); //返回判断结果,
//返回值为'*'玩家赢、'#'电脑赢、' '为继续
//想不到好的方法结束游戏并返回主函数,
//于是用一个字符变量代替结果;
if (outcome != ' ') break; //棋盘结果已出后,直接跳出循环去通报就结果;
Ai_move(chess, ROW, COL); //电脑下棋用'#'
display_chess(chess, ROW, COL); //打印棋盘
outcome = judge_chess(chess, ROW, COL);
if (outcome != ' ') break; //棋盘结果已出后,直接跳出循环去通报就结果;
}
ending(outcome); //通报结果
//为了游戏结束后可以继续在菜单选择继续游戏或退出游戏,
//单独增加一个通报结果的函数;
}
4.void init_chess(char chess[ROW][COL], uint row, uint col);
void init_chess(char chess[ROW][COL], uint row, uint col) //初始化多维数组函数
{ //上面形参为数组、数组的行、列
srand((int)time(NULL)); //为随机值赋予时间戳;不懂时间戳可自行百度;
uint i = 0;
uint j = 0;
for (i = 0; i < row; i++) //row行col列的多维数值赋值为‘ ’,在棋盘上表示无棋子;
{ //此项目此处为3行3列
for (j = 0; j < col; j++)
{
chess[i][j] = ' ';
}
}
}
5.void display_chess(char chess[ROW][COL], uint row, uint col);
void display_chess(char chess[ROW][COL], uint row, uint col) //打印棋盘
{
uint i = 0;
uint j = 0;
printf("当前棋局如下:n"); //表示当前棋局状态;
for (i = 0; i < row; i++) //打印每行;
{
//-----------------------------------------
for (j = 0; j < col; j++) //打印每行中的棋子和‘|’;
{
printf(" %c ", chess[i][j]);
if (j < col - 1) //最后一个棋子后面不需要‘|’
{
printf("|");
}
}
printf("n"); //打印完“棋子行”后换行
//------------------------------------------
for (j = 0; j < col; j++) //偶数行需要打印分隔符行‘—--|---|---’
{ //上面演示为此项目中的3行3列中分隔符行;
if (i < row - 1) //“分割行” 总比 “棋子行” 少一行;
{
printf("---");
if (j < col - 1) //分割行中'|' 总比 '---' 少一个;
{
printf("|");
}
}
}
//------------------------------------------
if (i < row - 1) //打印完一个“棋子行n分割行”后换行,
{ //其中最后一行的“棋子行”后面没有“分割行”,
printf("n"); //自然不需要“分割行”后面的"n";
}
}
6.int Pl_move(char chess[ROW][COL], uint row, uint col);
int Pl_move(char chess[ROW][COL], uint row, uint col) //玩家下棋
{
uint x = 0;
uint y = 0;
while (1)
{
printf("玩家下棋:n"); //提醒玩家下棋
printf(" >> "); //醒目输入位置
int c = scanf("%u%u", &x, &y); // int c用来防止scanf警告,个人习惯;
printf("n"); // 上面中变量c无任何作用;
if (x > 0 && x < row+1 && y < col+1 && y > 0 )//判断位置是多维函数合理范围内;
{
if (chess[x-1][y-1] == ' ') //判断所下位置原本为空才能下;
{
chess[x-1][y-1] = '*'; //写入玩家棋子'*'
return 0; //正确写入退出“玩家下棋”返回 0
}
else
{
printf("当前位置已有棋,请重新选择位置!nn"); //当前位置已有棋的提示
continue; //未成功下棋,则重新循环,重新选择下棋位置
}
}
else
{
printf("输入坐标有误,请重新输入!nn"); //未成功下棋提示,
} //并重新循环,重新选择下棋位置
}
return -1; // 非正确写入退出“玩家下棋”返回 -1
} // 虽然本项目中未用到此功能,而且从逻辑上来说不可能走到这步;
7. int Ai_move(char chess[ROW][COL], uint row, uint col)
int Ai_move(char chess[ROW][COL], uint row, uint col) //电脑下棋
{ //随机下棋,并不智能;
printf("n电脑下棋:n"); //提示当前是电脑下棋 //本人目前水平为初学者;
while (1) //如果棋子为写入成功,会循环到写入成功为止; //如果你们用更好的想法,
{ //可以给电脑
int x = rand() % row; //用随机数取3的余数, //编写更聪明的下棋方法;
int y = rand() % col; //固定范围为0~2;
if (chess[x][y] == ' ') //只有棋子为空时才能写入棋子'#';
{
chess[x][y] = '#';
return 0; //正确写入退出“电脑下棋”返回 0
}
}
//printf("%d %dn",x,y); //测设专用,此处测试电脑下棋的随机性和正确性
return -1; // 非正确写入退出“电脑下棋”返回 -1
} // 虽然本项目中未用到此功能,而且从逻辑上来说不可能走到这步;
8.int judge_chess(char chess[ROW][COL], uint row, uint col);
int judge_chess(char chess[ROW][COL], uint row, uint col) //判断棋局
{
uint i = 0;
uint j = 0;
for (i = 0; i < row; i++)
{ //例子:chess[i][0] == chess[i][1] && chess[i][1] == '*'
if (chess[i][0]==chess[i][1]&&chess[i][1]==chess[i][2]&&chess[i][0]=='*')
{return '*';} //玩家横排判定 (博客宽度有限,我把其中空格去掉,
// 上面例子中空格为平时正常代码)
if (chess[i][0]==chess[i][1]&&chess[i][1]==chess[i][2]&&chess[i][0]=='#')
{return '#';} //电脑横排判定
//-------------------------------------------------------(上面为横排判定)
if (chess[0][i]==chess[1][i] && chess[1][i]==chess[2][i]&&chess[1][i]=='*')
{return '*';} // 玩家竖排判定
if (chess[0][i]==chess[1][i]&&chess[1][i]==chess[2][i]&&chess[1][i]=='#')
{return '#';} // 电脑竖排判定
//-------------------------------------------------------(上面为竖排判定)
}
if (chess[0][0]==chess[1][1]&&chess[1][1]==chess[2][2]&&chess[1][1]=='*')
{return '*';} // 玩家从左到右对角判定
if (chess[0][0]==chess[1][1]&&chess[1][1]==chess[2][2]&&chess[1][1]=='#')
{return '#';} // 电脑从左到右对角判定
//-------------------------------------------------------(上面为从左到右对角判定)
if (chess[0][2]==chess[1][1]&&chess[1][1]==chess[2][0]&&chess[1][1]=='*')
{return '*';} // 玩家从右到左对角判定
if (chess[0][2]==chess[1][1]&&chess[1][1]==chess[2][0]&&chess[1][1]=='#')
{return '#';} // 电脑从右到左对角判定
//-------------------------------------------------------(上面为从右到左对角判定)
for (i = 0; i < row; i++) //当上面未成功时,还有空位就返回' ',
{ //表示棋局继续;
for (j = 0; j < col; j++)
{
if (chess[i][j] == ' ')
return ' ';
}
}
//-------------------------------------------------------(未成功且还有空位判定)
return '1'; //当棋局未成功且无空位时,判定为平局,返回'1',表示平局;
//-------------------------------------------------------(未成功且无空位,平局判定)
}
9.void ending(char out);
void ending(char out) //out值来自棋盘判定函数返回值
{
switch (out) //根据judge_chess();返回值判断进入哪个分支
{
case '*':
printf("您赢了!n恭喜玩家获胜!n"); //游戏玩家赢
break;
case '#':
printf("您输了!n恭喜电脑获胜!n"); //游戏电脑赢
break;
case '1':
printf("平局!n"); //游戏平局
break;
}
printf("这场游戏结束了。nn"); //多个'n'让后面的菜单和上一场游戏分割开来
}
六、程序验证
刚开始时:
void init_chess(char chess[ROW][COL], uint row, uint col) //初始化多维数组函数
{ //上面形参为数组、数组的行、列
srand((int)time(NULL)); //为随机值赋予时间戳;不懂时间戳可自行百度;
uint i = 0;
uint j = 0;
for (i = 0; i < row; i++) //row行col列的多维数值赋值为‘ ’,在棋盘上表示无棋子;
{ //此项目此处为3行3列
for (j = 0; j < col; j++)
{
chess[i][j] = ' ';
}
}
}
5.void display_chess(char chess[ROW][COL], uint row, uint col);
void display_chess(char chess[ROW][COL], uint row, uint col) //打印棋盘
{
uint i = 0;
uint j = 0;
printf("当前棋局如下:n"); //表示当前棋局状态;
for (i = 0; i < row; i++) //打印每行;
{
//-----------------------------------------
for (j = 0; j < col; j++) //打印每行中的棋子和‘|’;
{
printf(" %c ", chess[i][j]);
if (j < col - 1) //最后一个棋子后面不需要‘|’
{
printf("|");
}
}
printf("n"); //打印完“棋子行”后换行
//------------------------------------------
for (j = 0; j < col; j++) //偶数行需要打印分隔符行‘—--|---|---’
{ //上面演示为此项目中的3行3列中分隔符行;
if (i < row - 1) //“分割行” 总比 “棋子行” 少一行;
{
printf("---");
if (j < col - 1) //分割行中'|' 总比 '---' 少一个;
{
printf("|");
}
}
}
//------------------------------------------
if (i < row - 1) //打印完一个“棋子行n分割行”后换行,
{ //其中最后一行的“棋子行”后面没有“分割行”,
printf("n"); //自然不需要“分割行”后面的"n";
}
}
6.int Pl_move(char chess[ROW][COL], uint row, uint col);
int Pl_move(char chess[ROW][COL], uint row, uint col) //玩家下棋
{
uint x = 0;
uint y = 0;
while (1)
{
printf("玩家下棋:n"); //提醒玩家下棋
printf(" >> "); //醒目输入位置
int c = scanf("%u%u", &x, &y); // int c用来防止scanf警告,个人习惯;
printf("n"); // 上面中变量c无任何作用;
if (x > 0 && x < row+1 && y < col+1 && y > 0 )//判断位置是多维函数合理范围内;
{
if (chess[x-1][y-1] == ' ') //判断所下位置原本为空才能下;
{
chess[x-1][y-1] = '*'; //写入玩家棋子'*'
return 0; //正确写入退出“玩家下棋”返回 0
}
else
{
printf("当前位置已有棋,请重新选择位置!nn"); //当前位置已有棋的提示
continue; //未成功下棋,则重新循环,重新选择下棋位置
}
}
else
{
printf("输入坐标有误,请重新输入!nn"); //未成功下棋提示,
} //并重新循环,重新选择下棋位置
}
return -1; // 非正确写入退出“玩家下棋”返回 -1
} // 虽然本项目中未用到此功能,而且从逻辑上来说不可能走到这步;
7. int Ai_move(char chess[ROW][COL], uint row, uint col)
int Ai_move(char chess[ROW][COL], uint row, uint col) //电脑下棋
{ //随机下棋,并不智能;
printf("n电脑下棋:n"); //提示当前是电脑下棋 //本人目前水平为初学者;
while (1) //如果棋子为写入成功,会循环到写入成功为止; //如果你们用更好的想法,
{ //可以给电脑
int x = rand() % row; //用随机数取3的余数, //编写更聪明的下棋方法;
int y = rand() % col; //固定范围为0~2;
if (chess[x][y] == ' ') //只有棋子为空时才能写入棋子'#';
{
chess[x][y] = '#';
return 0; //正确写入退出“电脑下棋”返回 0
}
}
//printf("%d %dn",x,y); //测设专用,此处测试电脑下棋的随机性和正确性
return -1; // 非正确写入退出“电脑下棋”返回 -1
} // 虽然本项目中未用到此功能,而且从逻辑上来说不可能走到这步;
8.int judge_chess(char chess[ROW][COL], uint row, uint col);
int judge_chess(char chess[ROW][COL], uint row, uint col) //判断棋局
{
uint i = 0;
uint j = 0;
for (i = 0; i < row; i++)
{ //例子:chess[i][0] == chess[i][1] && chess[i][1] == '*'
if (chess[i][0]==chess[i][1]&&chess[i][1]==chess[i][2]&&chess[i][0]=='*')
{return '*';} //玩家横排判定 (博客宽度有限,我把其中空格去掉,
// 上面例子中空格为平时正常代码)
if (chess[i][0]==chess[i][1]&&chess[i][1]==chess[i][2]&&chess[i][0]=='#')
{return '#';} //电脑横排判定
//-------------------------------------------------------(上面为横排判定)
if (chess[0][i]==chess[1][i] && chess[1][i]==chess[2][i]&&chess[1][i]=='*')
{return '*';} // 玩家竖排判定
if (chess[0][i]==chess[1][i]&&chess[1][i]==chess[2][i]&&chess[1][i]=='#')
{return '#';} // 电脑竖排判定
//-------------------------------------------------------(上面为竖排判定)
}
if (chess[0][0]==chess[1][1]&&chess[1][1]==chess[2][2]&&chess[1][1]=='*')
{return '*';} // 玩家从左到右对角判定
if (chess[0][0]==chess[1][1]&&chess[1][1]==chess[2][2]&&chess[1][1]=='#')
{return '#';} // 电脑从左到右对角判定
//-------------------------------------------------------(上面为从左到右对角判定)
if (chess[0][2]==chess[1][1]&&chess[1][1]==chess[2][0]&&chess[1][1]=='*')
{return '*';} // 玩家从右到左对角判定
if (chess[0][2]==chess[1][1]&&chess[1][1]==chess[2][0]&&chess[1][1]=='#')
{return '#';} // 电脑从右到左对角判定
//-------------------------------------------------------(上面为从右到左对角判定)
for (i = 0; i < row; i++) //当上面未成功时,还有空位就返回' ',
{ //表示棋局继续;
for (j = 0; j < col; j++)
{
if (chess[i][j] == ' ')
return ' ';
}
}
//-------------------------------------------------------(未成功且还有空位判定)
return '1'; //当棋局未成功且无空位时,判定为平局,返回'1',表示平局;
//-------------------------------------------------------(未成功且无空位,平局判定)
}
9.void ending(char out);
void ending(char out) //out值来自棋盘判定函数返回值
{
switch (out) //根据judge_chess();返回值判断进入哪个分支
{
case '*':
printf("您赢了!n恭喜玩家获胜!n"); //游戏玩家赢
break;
case '#':
printf("您输了!n恭喜电脑获胜!n"); //游戏电脑赢
break;
case '1':
printf("平局!n"); //游戏平局
break;
}
printf("这场游戏结束了。nn"); //多个'n'让后面的菜单和上一场游戏分割开来
}
六、程序验证
刚开始时:
int Pl_move(char chess[ROW][COL], uint row, uint col) //玩家下棋
{
uint x = 0;
uint y = 0;
while (1)
{
printf("玩家下棋:n"); //提醒玩家下棋
printf(" >> "); //醒目输入位置
int c = scanf("%u%u", &x, &y); // int c用来防止scanf警告,个人习惯;
printf("n"); // 上面中变量c无任何作用;
if (x > 0 && x < row+1 && y < col+1 && y > 0 )//判断位置是多维函数合理范围内;
{
if (chess[x-1][y-1] == ' ') //判断所下位置原本为空才能下;
{
chess[x-1][y-1] = '*'; //写入玩家棋子'*'
return 0; //正确写入退出“玩家下棋”返回 0
}
else
{
printf("当前位置已有棋,请重新选择位置!nn"); //当前位置已有棋的提示
continue; //未成功下棋,则重新循环,重新选择下棋位置
}
}
else
{
printf("输入坐标有误,请重新输入!nn"); //未成功下棋提示,
} //并重新循环,重新选择下棋位置
}
return -1; // 非正确写入退出“玩家下棋”返回 -1
} // 虽然本项目中未用到此功能,而且从逻辑上来说不可能走到这步;
7. int Ai_move(char chess[ROW][COL], uint row, uint col)
int Ai_move(char chess[ROW][COL], uint row, uint col) //电脑下棋
{ //随机下棋,并不智能;
printf("n电脑下棋:n"); //提示当前是电脑下棋 //本人目前水平为初学者;
while (1) //如果棋子为写入成功,会循环到写入成功为止; //如果你们用更好的想法,
{ //可以给电脑
int x = rand() % row; //用随机数取3的余数, //编写更聪明的下棋方法;
int y = rand() % col; //固定范围为0~2;
if (chess[x][y] == ' ') //只有棋子为空时才能写入棋子'#';
{
chess[x][y] = '#';
return 0; //正确写入退出“电脑下棋”返回 0
}
}
//printf("%d %dn",x,y); //测设专用,此处测试电脑下棋的随机性和正确性
return -1; // 非正确写入退出“电脑下棋”返回 -1
} // 虽然本项目中未用到此功能,而且从逻辑上来说不可能走到这步;
8.int judge_chess(char chess[ROW][COL], uint row, uint col);
int judge_chess(char chess[ROW][COL], uint row, uint col) //判断棋局
{
uint i = 0;
uint j = 0;
for (i = 0; i < row; i++)
{ //例子:chess[i][0] == chess[i][1] && chess[i][1] == '*'
if (chess[i][0]==chess[i][1]&&chess[i][1]==chess[i][2]&&chess[i][0]=='*')
{return '*';} //玩家横排判定 (博客宽度有限,我把其中空格去掉,
// 上面例子中空格为平时正常代码)
if (chess[i][0]==chess[i][1]&&chess[i][1]==chess[i][2]&&chess[i][0]=='#')
{return '#';} //电脑横排判定
//-------------------------------------------------------(上面为横排判定)
if (chess[0][i]==chess[1][i] && chess[1][i]==chess[2][i]&&chess[1][i]=='*')
{return '*';} // 玩家竖排判定
if (chess[0][i]==chess[1][i]&&chess[1][i]==chess[2][i]&&chess[1][i]=='#')
{return '#';} // 电脑竖排判定
//-------------------------------------------------------(上面为竖排判定)
}
if (chess[0][0]==chess[1][1]&&chess[1][1]==chess[2][2]&&chess[1][1]=='*')
{return '*';} // 玩家从左到右对角判定
if (chess[0][0]==chess[1][1]&&chess[1][1]==chess[2][2]&&chess[1][1]=='#')
{return '#';} // 电脑从左到右对角判定
//-------------------------------------------------------(上面为从左到右对角判定)
if (chess[0][2]==chess[1][1]&&chess[1][1]==chess[2][0]&&chess[1][1]=='*')
{return '*';} // 玩家从右到左对角判定
if (chess[0][2]==chess[1][1]&&chess[1][1]==chess[2][0]&&chess[1][1]=='#')
{return '#';} // 电脑从右到左对角判定
//-------------------------------------------------------(上面为从右到左对角判定)
for (i = 0; i < row; i++) //当上面未成功时,还有空位就返回' ',
{ //表示棋局继续;
for (j = 0; j < col; j++)
{
if (chess[i][j] == ' ')
return ' ';
}
}
//-------------------------------------------------------(未成功且还有空位判定)
return '1'; //当棋局未成功且无空位时,判定为平局,返回'1',表示平局;
//-------------------------------------------------------(未成功且无空位,平局判定)
}
9.void ending(char out);
void ending(char out) //out值来自棋盘判定函数返回值
{
switch (out) //根据judge_chess();返回值判断进入哪个分支
{
case '*':
printf("您赢了!n恭喜玩家获胜!n"); //游戏玩家赢
break;
case '#':
printf("您输了!n恭喜电脑获胜!n"); //游戏电脑赢
break;
case '1':
printf("平局!n"); //游戏平局
break;
}
printf("这场游戏结束了。nn"); //多个'n'让后面的菜单和上一场游戏分割开来
}
六、程序验证
刚开始时:
int judge_chess(char chess[ROW][COL], uint row, uint col) //判断棋局
{
uint i = 0;
uint j = 0;
for (i = 0; i < row; i++)
{ //例子:chess[i][0] == chess[i][1] && chess[i][1] == '*'
if (chess[i][0]==chess[i][1]&&chess[i][1]==chess[i][2]&&chess[i][0]=='*')
{return '*';} //玩家横排判定 (博客宽度有限,我把其中空格去掉,
// 上面例子中空格为平时正常代码)
if (chess[i][0]==chess[i][1]&&chess[i][1]==chess[i][2]&&chess[i][0]=='#')
{return '#';} //电脑横排判定
//-------------------------------------------------------(上面为横排判定)
if (chess[0][i]==chess[1][i] && chess[1][i]==chess[2][i]&&chess[1][i]=='*')
{return '*';} // 玩家竖排判定
if (chess[0][i]==chess[1][i]&&chess[1][i]==chess[2][i]&&chess[1][i]=='#')
{return '#';} // 电脑竖排判定
//-------------------------------------------------------(上面为竖排判定)
}
if (chess[0][0]==chess[1][1]&&chess[1][1]==chess[2][2]&&chess[1][1]=='*')
{return '*';} // 玩家从左到右对角判定
if (chess[0][0]==chess[1][1]&&chess[1][1]==chess[2][2]&&chess[1][1]=='#')
{return '#';} // 电脑从左到右对角判定
//-------------------------------------------------------(上面为从左到右对角判定)
if (chess[0][2]==chess[1][1]&&chess[1][1]==chess[2][0]&&chess[1][1]=='*')
{return '*';} // 玩家从右到左对角判定
if (chess[0][2]==chess[1][1]&&chess[1][1]==chess[2][0]&&chess[1][1]=='#')
{return '#';} // 电脑从右到左对角判定
//-------------------------------------------------------(上面为从右到左对角判定)
for (i = 0; i < row; i++) //当上面未成功时,还有空位就返回' ',
{ //表示棋局继续;
for (j = 0; j < col; j++)
{
if (chess[i][j] == ' ')
return ' ';
}
}
//-------------------------------------------------------(未成功且还有空位判定)
return '1'; //当棋局未成功且无空位时,判定为平局,返回'1',表示平局;
//-------------------------------------------------------(未成功且无空位,平局判定)
}
9.void ending(char out);
void ending(char out) //out值来自棋盘判定函数返回值
{
switch (out) //根据judge_chess();返回值判断进入哪个分支
{
case '*':
printf("您赢了!n恭喜玩家获胜!n"); //游戏玩家赢
break;
case '#':
printf("您输了!n恭喜电脑获胜!n"); //游戏电脑赢
break;
case '1':
printf("平局!n"); //游戏平局
break;
}
printf("这场游戏结束了。nn"); //多个'n'让后面的菜单和上一场游戏分割开来
}
六、程序验证
刚开始时:
刚开始时:
选择退出:
选择进入游戏: (此处打印的棋盘,多次错误,多次调整才建立好打印棋盘函数)
玩家下棋正常时: (输入坐标,按下回车便会打印棋盘,并且开始电脑下棋并打印)
玩家输入坐标错误时:
玩家输入坐标有棋时:
当玩家赢时:
当电脑赢时: (电脑下棋函数很随机,所以电脑很难赢,需要优化)
平局时:
继续游戏时:
不继续游戏,退出时:
//----------------------------------------------------------------------------//
基本把能测试的功能都测试了一遍;
但是在个别的地方进行其他测试可能会发生有趣的现象;
比如:在菜单处选择时输入字母时,此时菜单会无限循环。
如下图:
而此处比如要对菜单的选择输入值进行优化;
不过这是下一步的步骤了;
//----------------------------------------------------------------------------//
七、优化细节
上面说到菜单处输入字母会循环输出菜单,导致卡死;
这是代码的bug的表现,需要对输入部分进行限制;
//----------------------------------------------------------------------------//以下是我自己优化失败经历;
这期间我把输入值(val_val)类型改为char,但是我还是不满意其表现;
char val_val 表现为输入字母时,还是会循环字母个数遍菜单;
//----------------------------------------------------------------------------//
而我又把char类型改为char []类型,而后面switch只判断char [0]这个首元素,
但对于字母串,还是会循环一遍菜单;而且这样输入1开头的多个数字串时,
都会被认为1,从而进入游戏环节;这又是我不满意的表现,最后我上网去百度;
//----------------------------------------------------------------------------//以上是我自己优化失败经历
网上找到一种scanf()的优化方法;
表现为:
while(scanf("%u",&val_val) != 1) //这个方法可以读到字母时忽略它(百度所知)
{
getchar();
continue;
}
这种方法可以让val_val读入字母时,不会循环,
他会进入while循环中用getchar();把键盘缓冲区中把非正整数的字符,
用读取的方式,循环去除这些非正整数的字符。然后一直等待你输入正整数为止;
表现:
上面说到菜单处输入字母会循环输出菜单,导致卡死;
这是代码的bug的表现,需要对输入部分进行限制;
//----------------------------------------------------------------------------//以下是我自己优化失败经历;
这期间我把输入值(val_val)类型改为char,但是我还是不满意其表现;
char val_val 表现为输入字母时,还是会循环字母个数遍菜单;
//----------------------------------------------------------------------------//
而我又把char类型改为char []类型,而后面switch只判断char [0]这个首元素,
但对于字母串,还是会循环一遍菜单;而且这样输入1开头的多个数字串时,
都会被认为1,从而进入游戏环节;这又是我不满意的表现,最后我上网去百度;
//----------------------------------------------------------------------------//以上是我自己优化失败经历
网上找到一种scanf()的优化方法;
表现为:
while(scanf("%u",&val_val) != 1) //这个方法可以读到字母时忽略它(百度所知)
{
getchar();
continue;
}
这种方法可以让val_val读入字母时,不会循环,
他会进入while循环中用getchar();把键盘缓冲区中把非正整数的字符,
用读取的方式,循环去除这些非正整数的字符。然后一直等待你输入正整数为止;
表现:
可以看出无论是字符或者汉字字符,还有字母,都不会出现菜单循环,
除非是出现数字类,才会提醒输入错误,对于此,我很满意了;
于是把这段加入到int main()中:
int main () { uint val_val = 2; do { menu(); printf(" >> "); while(scanf("%u",&val_val) != 1) //这个方法可以读到字母或字符时忽略它 { //(百度所知) char c = getchar(); //这个用char c防止getchar()警告; continue; //此处char c是无意义的,仅仅只是个人习惯; } switch (val_val) { case 0: //退出 printf("游戏已结束!n"); break; case 1: //进入游戏环节 game(); break; default://输出错误; printf("输入错误,请重新输入!n"); break; } } while (val_val); return 0; }
八、结语
最后这三子棋中,肯定还有其他的地方需要被优化,就需要大家自己去琢磨和思考了;
比如————让电脑更好的下赢棋;
比如————用中的system("cls); 清屏,达到每次下棋都会是全新的棋盘刷新,表现在输出窗口中。达到简洁的目的;
这都需要大家自己去探索了。
还可以修改game.h中的ROW和COL的代表的数。把棋盘变为5*5或10*10或更大......
不过这样的话,判断棋盘函数 可能就需要重新调整了;
//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
本标题的简简单单可能对初学者不太简单,
需要反复练习代码才能熟练;
最后的最后,希望大家能再进一步,坚持下去,献上我最美好的祝福;
最后这三子棋中,肯定还有其他的地方需要被优化,就需要大家自己去琢磨和思考了;
比如————让电脑更好的下赢棋;
比如————用
这都需要大家自己去探索了。
还可以修改game.h中的ROW和COL的代表的数。把棋盘变为5*5或10*10或更大......
不过这样的话,判断棋盘函数 可能就需要重新调整了;
//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
本标题的简简单单可能对初学者不太简单,
需要反复练习代码才能熟练;
最后的最后,希望大家能再进一步,坚持下去,献上我最美好的祝福;



