今天介绍的内容是关于从三子棋到N子棋的代码实现与优化。
话不多说直入正题
首先,我们要将游戏的外部框架设计好。即为游戏的测试程序和游戏菜单的设计。由于夜已较深我们直接上代码。
void menu() //菜单
{
printf(" #####################\n");
printf("###### 1.Play 0.Exit ######\n"); //这里将 1 设为进入游戏,0 设为退出游戏在下面的条件语句中有妙用
printf(" #####################\n");
}
void test() //这里是游戏的测试程序
{
srand((unsigned int)time(NULL));
//在用srand()生成随机数种子时不需要进行循环,所以直接放这。并且srand()要用time()函数来获取时间戳来生成随机数种子。time函数声明头文件<time.h>
int input = 0;
do //以do while 循环作为外部框架先执行一次程序再进行循环。
{
menu(); //这里是游戏菜单的函数
printf("请输入:> ");
scanf("%d", &input);
printf("\n");
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出成功\n");
break;
default:
printf("请重新输入:> \n");
break;
}
} while (input); //上面菜单的设定使得这里巧妙的用input的值来做判断条件
}
int main()
{
test();
return 0;
}
第二布就是将game()函数里的内部逻辑(三子棋的内部逻辑)进行相关的编写了。还是直接上代码
void game() //游戏的主要框架
{
char arr[Row][Col] = { 0 }; //创建数组存储下棋信息
InitBoard(arr, Row, Col); //初始化棋盘,即将数组内容全部初始化为空格
DisplayBoard(arr, Row, Col); //打印棋盘
char ret = 0; //声明变量待会用来接受判断输赢函数的返回值
while (1)
{
PlayerMove(arr, Row, Col); //玩家下棋
DisplayBoard(arr, Row, Col); //打印棋盘
ret = Is_Win(arr, Row, Col); //对游戏过程可能出现的四种情况进行判断,玩家赢返回 '*',电脑赢返回'#',平局返回'p',继续返回'c'(注意这里将玩家、电脑赢的返回值和对应落子形状设定相同将有妙用)
if (ret != 'c') //每一次玩家下完棋都要判断一次对局情况
{
break;
}
ComputerMove(arr, Row, Col); //电脑下棋
DisplayBoard(arr, Row, Col); //打印棋盘
ret = Is_Win(arr, Row, Col);
if (ret != 'c') //同理每次电脑落完子也要判断一次对局情况
{
break;
}
}
if (ret == '*')
{
printf("玩家赢\n");
}
else if (ret == '#')
{
printf("电脑赢\n");
}
else if (ret == 'p')
{
printf("平局\n");
}
}
第三步就是对游戏内部需要调用的所有函数进行实现
void InitBoard(char arr[Row][Col], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
arr[i][j] = ' ';
}
}
}
void DisplayBoard(char arr[Row][Col], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf(" %c ", arr[i][j]); //将棋盘的每一行每一列都分开来打印,使得棋盘的大小可以随便改变。
if (j < col - 1) //这里要判断是因为当打印到最后一列时就不需要打印'|'
{
printf("|");
}
}
printf("\n");
if (i < row - 1) //这里的判断作用同上,当打印到最后一行时就不需要打印'---'了。
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
}
printf("\n");
}
}
void PlayerMove(char arr[Row][Col], int row, int col)
{
int a, b;
while (1)
{
printf("请输入:> ");
scanf("%d%d", &a, &b);
if (a <= row && a >= 1 && b <= col && b >= 1) //注意此处玩家输入的坐标值可能超出了棋盘大小,或者是这个位置已经有棋子了。所以这里要对这两种情况进行判断。
{
if (arr[a - 1][b - 1] == ' ')
{
arr[a - 1][b - 1] = '*';
break;
}
else
printf("此坐标已被占用,请重新输入!\n");
}
else
printf("请重新输入!\n");
}
}
void ComputerMove(char arr[Row][Col], int row, int col)
{
printf("电脑走:> \n");
while (1)
{ //在用rand()之前要先调用srand()函数,生成随机数的种子。rand函数要声明头文件<stdlib.h>
int a = rand() % row; //此处用生成随机值的方式使得电脑随机落子,并且巧妙的用随机值模上棋盘行列信息使得这个落子范围在棋盘内。
int b = rand() % col;
if (arr[a][b] == ' ')
{
arr[a][b] = '#';
break;
}
}
}
char Is_Win(char arr[Row][Col], int row, int col)
{
int count = 0;
int i = 0;
int j = 0;
for (i = 0; i < row; i++) //控制行 //横排是否相等的判断
{
for (j = 0; j < col - 2; j++) //控制列
if (arr[i][j] == arr[i][j + 1] && arr[i][j + 1] == arr[i][j + 2] && arr[i][j] != ' ')
{
return arr[i][0]; //这里就是刚刚提到的妙用了,因为玩家下的棋与玩家赢的返回值一致,所以当三个数相等的时候直接返回棋盘信息即可,不需要再对棋盘信息进行判断来判断谁赢
}
}
for (j = 0; j < col; j++) //控制列 //数列是否相等的判断
{
for (i = 0; i < row - 2; i++) //控制行
{
if (arr[i][j] == arr[i+1][j] && arr[i+1][j ] == arr[i+2][j] && arr[i][j] != ' ')
return arr[i][j];
}
}
for (i = 0; i < col - 2; i++) //控制列 //从左上至右下的斜列的判断
{
for (j = 0; j < row - 2; j++) //控制行
{
if (arr[j][i] == arr[j + 1][i + 1] && arr[j + 1][i + 1] == arr[j + 2][i + 2] && arr[j][i] != ' ')
{
return arr[j][i]; // | |
} //---|---|---
} //---|---|---
} // | |
for (i = 0; i < row - 2; i++) //控制行 //从左下至右上的斜列的判断
{
for (j = 2; j <= col; j++) //控制列
{
if (arr[i][j] == arr[i+1][j-1] && arr[i+1][j-1] == arr[i+2][j-2] && arr[i][j] != ' ')
{
return arr[i][j];
}
}
}
for (i = 0; i < row; i++) //这里判断平局和继续游戏的情况
{
for (j = 0; j < col; j++)
{
if (arr[i][j] == ' ') //这里引入一个初始化值为0的外界变量count,只要有一个元素为空格,就将他赋值为 1
count = 1;
}
}
if (count == 1) //如果count为1,则证明该棋盘还有空格可以落子,则游戏继续
return 'c';
else
return 'p'; //当没人赢且棋盘满了,就代表双方平局,所以这里如果count为0,即代表上面的判断语句不成立,棋盘中没有空格,此时平局。
}
emmm因为实在是比较晚了今天就弄得粗糙一点,都是一大坨一大坨的代码与注释。噢噢噢差点忘了还有个自定义函数的点。这里就直接在这做解释了。
#define Row 5 //这里采用宏定义的方式来设置行数与列数,可以通过改变宏定义设置的常量值直接控制棋盘大小。这样的话就能更方便使得棋盘信息可以得到扩充,而不是直接设为常量固定了行数与列数
#define Col 5
xdm慢慢看哈,阿西吧俺真的要溜去睡觉觉了。如果代码方面有相关问题欢迎评论区进行指正。