目录
前言
一、问题阐述
二、基本流程
三、步骤
1.主体程序
2.game()函数的设计?
1).棋盘的打印
?2).电脑和玩家的落子
?3).胜负的判定
?4).game函数主体?
四、结果演示
?五、代码呈现
前言
在对C语言的学习达到一定的深度时,可以通过对数组,循环语句的灵活运用来实现较为简答的一些小游戏,三子棋就是比较适合初学着来实现是一个游戏,其中不涉及指针和结构体等较为复杂的知识。
一、问题阐述
通过C语言中的数组以及循环来实现三子棋。
二、基本流程
在代码的编写之前,首先需要理清游戏进行的思路。
1.让玩家选择,进行游戏或者退出程序
2.在屏幕上打印出棋盘
3.玩家用‘*’进行落子,电脑随后用‘#’进行落子
4.电脑判断玩家胜利,或是电脑胜利
5通过判断信息,在屏幕上打印出对胜放的断定。
三、步骤
1.主体程序
首先完成主体程序的编写,通过menu()函数实现菜单,选择1进入程序,选择0退出程序。
void menu()
{
printf("**************************\n");
printf("********* 1.play *********\n");
printf("********* 0.exit *********\n");
printf("**************************\n");
}
int main()
{
int input = 0;
srand((unsigned int) time(NULL));
do //不管有任何指令,都要先执行一次菜单,所以用do while语句
{
menu();
printf("请选择是否游玩:>");
scanf("%d", &input);
switch (input)//用switch case语句来实现进入或退出游戏的选择
{
case 1:
game();
break;
case 0:
printf("退出游戏:");
break;
default:
printf("输入错误,请重新输入");
break;
}
} while (input);
//菜单界面设置为“0.exit”,让输入的值在计算机中为假,放入while循环中即可立刻退出程序,避免了多余的不必要的操作
return 0;
}
2.game()函数的设计?
在game()函数的主体中,我们要实现棋盘的打印,电脑和玩家的落子,以及胜负的判定。
1).棋盘的打印
首先,运用宏定义便于之后对于棋盘大小的改变,方便后期实现行列的改变,避免了把代码写死。
#define ROW 3 #define COL 3
然后对存放元素的数组初始化为‘ ’(空格),否则在数组中初始化为零的形况下win系统不会打印,会造成棋盘的格式错误。(如下图,对比)
void init_board(char board[ROW][COL], int row, int col)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
初始化结束后,通过循环的嵌套来实现数组的打印。?
代码1
可以通过如下代码进行打印,过程较为简单。虽然,这样的代码也可以实现三行三列棋盘的打印,但是无法做到随着宏定义行列的改变而一同改变,行可以变换,列会被限制死,不满足高内聚低耦合。不能算是较好的代码。
void show_board(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
//数据
printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
//---|---|---
if(i<row-1)
printf("---|---|---\n");
}
}
改变宏定义数值之后效果如下:
代码2
?如下为更加好的写法。
void show_board(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j != COL - 1)
printf("|");
}
printf("\n");
if (i != row - 1)
{
for (j = 0; j < row; j++)
{
printf("---");
if (j != COL - 1)
printf("|");
}
printf("\n");
}
}
}
改变宏定义后可以很好的实现列的改变,效果如下:
?2).电脑和玩家的落子
玩家输入:
通过比对行列,电脑判断落子是否在棋盘内,格子是否已经落过子,之后实现玩家的下棋。但是要注意非程序员玩家不知道数字的下标从0开始,所以要在比对时把输入的行列减一,防止越界。
void my_move(char board[ROW][COL], int row, int col)
{
int x = 0, y = 0;
while (1)
{
scanf("%d %d", &x, &y);
//非程序员的玩家会按照习惯输入行列,不做改动会引发数组的越界,要减一
if (x <= row && x >= 1 && y <= col && y >= 1)//此二者的逻辑不能相反,
//否则无论如何选择都会显示“格子被占用”
{
if (board[x - 1][y - 1] == ' ') //同上
{
board[x - 1][y - 1] = '*';
break;
}
else
printf("此格已被占用,请重新落子。");
}
else
printf("非法输入,请重新落子。");
}
}
电脑输入:
通过rand()实现随机数的生成,但是多次实验之后发现生成的随机数是固定的,这时就需要在主函数里调用srand()函数,在srand()的函数调用符号中只要加入一个随时随地在变化的数字就可以实现rand()随机数的生成,因此就需要用到时间戳,因为srand的类型是unsigned int 因而需要给时间戳time(NULL)进行强制内容转换?。
所以在上文中的主函数出现了如下图的srand((unsigned int) time(NULL));
void computer_move(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
x = rand() % 3;//通过rand函数实现随机数
y = rand() % 3;//%3就会产生小于3的整数0,1,2
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
?3).胜负的判定
不管哪方胜利,都返回练成三个的符号,不用重新定义其他变量,让代码更加的简洁。平局返回Q,没有填满盘则返回C实现代码继续运行。
//判断二维数组是否已经被填满
static int is_full(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
if (' ' == board[i][j])
{
return 0;
}
}
}
return 1;
}
char is_win(char board[ROW][COL], int row, int col)
{
int i = 0 ;
//判断列是否相等
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')//列是否相同
return board[0][i];
}
//判断行是否相等
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')//行是否相同
return board[i][0];
}
//判断对角线是否相等
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')//对角线是否相同
return board[1][1];
if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
return board[1][1];
//是否平局,数组被填满则为平局
if (is_full(board, row, col) == 1)
{
return 'Q';
}
return 'C';
}
?4).game函数主体?
void game()
{
char ret = 0;//定义为char使程序更加简洁,具体见判断处
char board[ROW][COL] = { 0 };//运用二维数字来实现棋盘中元素的存放
init_board(board, ROW, COL);
show_board(board, ROW, COL);
while (1)
{
printf("请输入你想要落子的行列:>");
my_move(board, ROW, COL);
printf("\n");
show_board(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')
break;
computer_move(board, ROW, COL);
printf("电脑的落子:");
printf("\n");
show_board(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == 'Q')
printf("平局。\n");
//通过‘*’和‘#’来判断,让函数可以直接传回棋盘中的落子,无需设置更多的变量,让代码更加简洁
else if (ret == '*')
printf("恭喜你,获胜啦!\n");
else
printf("电脑都下不赢,你怕不是头猪?!\n");
printf("\n");
}
四、结果演示
玩家胜利: ?
电脑胜利:
?平局:
五、代码呈现
编写是代码分了三个文件:test.c(main函数和game函数两大代码主体),game.c(各类函数的编写),game.h(函数的头文件和声明)
test.c:
#include"game.h"
void menu()
{
printf("**************************\n");
printf("********* 1.play *********\n");
printf("********* 0.exit *********\n");
printf("**************************\n");
}
void game()
{
char ret = 0;//定义为char使程序更加简洁,具体见判断处
char board[ROW][COL] = { 0 };//运用二维数字来实现棋盘中元素的存放
init_board(board, ROW, COL);
show_board(board, ROW, COL);
while (1)
{
printf("请输入你想要落子的行列:>");
my_move(board, ROW, COL);
printf("\n");
show_board(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')
break;
computer_move(board, ROW, COL);
printf("电脑的落子:");
printf("\n");
show_board(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == 'Q')
printf("平局。\n");
//通过‘*’和‘#’来判断,让函数可以直接传回棋盘中的落子,无需设置更多的变量,让代码更加简洁
else if (ret == '*')
printf("恭喜你,获胜啦!\n");
else
printf("电脑都下不赢,你怕不是头猪?!\n");
printf("\n");
}
int main()
{
int input = 0;
srand((unsigned int) time(NULL));
do //不管有任何指令,都要先执行一次菜单,所以用do while语句
{
menu();
printf("请选择是否游玩:>");
scanf("%d", &input);
switch (input)//用switch case语句来实现进入或退出游戏的选择
{
case 1:
game();
break;
case 0:
printf("退出游戏:");
break;
default:
printf("输入错误,请重新输入");
break;
}
} while (input);
//菜单界面设置为“0.exit”,让输入的值在计算机中为假,放入while循环中即可立刻退出程序,避免了多余的不必要的操作
return 0;
}
game.c:
#include"game.h"
void init_board(char board[ROW][COL], int row, int col)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
//把数字初始化为空格,否则在数组中初始化为零的形况下win系统不会打印,会造成棋盘的格式错误
}
}
}
//这样的代码也可以实现三行三列棋盘的打印,但是无法做到随着宏定义行列的改变而一同改变,会被限制死为三列,不满足高内聚低耦合
//void show_board(char board[ROW][COL], int row, int col)
//{
// int i = 0;
// for (i = 0; i < row; i++)
// {
// //数据
// printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
// //---|---|---
// if(i<row-1)
// printf("---|---|---\n");
// }
//}
//如下的写法为更加好的写法
void show_board(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j != COL - 1)
printf("|");
}
printf("\n");
if (i != row - 1)
{
for (j = 0; j < row; j++)
{
printf("---");
if (j != COL - 1)
printf("|");
}
printf("\n");
}
}
}
void my_move(char board[ROW][COL], int row, int col)
{
int x = 0, y = 0;
while (1)
{
scanf("%d %d", &x, &y);
//非程序员的玩家会按照习惯输入行列,不做改动会引发数组的越界,要减一
if (x <= row && x >= 1 && y <= col && y >= 1)//此二者的逻辑不能相反,
//否则无论如何选择都会显示“格子被占用”
{
if (board[x - 1][y - 1] == ' ') //同上
{
board[x - 1][y - 1] = '*';
break;
}
else
printf("此格已被占用,请重新落子。");
}
else
printf("非法输入,请重新落子。");
}
}
void computer_move(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
x = rand() % 3;//通过rand函数实现随机数
y = rand() % 3;//%3就会产生小于3的整数0,1,2
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
//判断二维数组是否已经被填满
static int is_full(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
if (' ' == board[i][j])
{
return 0;
}
}
}
return 1;
}
char is_win(char board[ROW][COL], int row, int col)
{
int i = 0 ;
//判断列是否相等
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')//列是否相同
return board[0][i];
}
//判断行是否相等
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')//行是否相同
return board[i][0];
}
//判断对角线是否相等
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')//对角线是否相同
return board[1][1];
if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
return board[1][1];
//是否平局,数组被填满则为平局
if (is_full(board, row, col) == 1)
{
return 'Q';
}
return 'C';
}
?game.h:
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 3//运用宏定义便于之后对于棋盘大小的改变,避免了把代码写死
#define COL 3
//棋盘的初始化
void init_board(char board[ROW][COL], int row, int col);
//展示棋盘上的布局
void show_board(char board[ROW][COL], int row, int col);
//玩家在棋盘上落子
void my_move(char board[ROW][COL], int row, int col);
//电脑在棋盘上落子
void computer_move(char board[ROW][COL], int row, int col);
//判断输赢
char is_win(char board[ROW][COL], int row, int col);
|