司机镇楼!!!!!!
口号:好好学习,努力养家
备注:看了正式版看不懂可以转接娱乐版
注意:这是个对于初学者来说的大型工程,分成了三部分来写:test.c,game.c,game.h
正式版:
兄台,也不知道你同不同意,我就放个链接!谢谢!
(18条消息) 三子棋代码实现思路解析与易错点纠正_xbhinsterest11的博客-CSDN博客
这位兄台的就很不错,正式版就用这位大神的吧
娱乐版:
test.c(游戏的测试逻辑)
#define _CRT_SECURE_NO_WARNINGS 1
//游戏的测试逻辑,game.h和game.c才是游戏的主题,这边你可以认为就是个“主函数”
//game为“副函数”船长离不开大副
//船长只是一个主心骨负责统领,大副才是真正干实事的大佬
//#include证明我存在过,只是后来觉得重复,你不要了,哎!像极了备胎
#include"game.h"//这是自己写的头文件
void menu()
{
printf("**************\n");
printf("****1.play****\n");
printf("****0.exit****\n");
printf("**************\n");
}
void game()
{
//三子棋,要先有棋盘
//要有电脑和人相互下棋的操作
//任何下棋后下棋的位置要保存起来
//输入的棋子是输入的字符,而棋盘输入是3*3的,我们输入字符输入时是几行几列
//所以相当于是一个字符类型的二维数组
//******************************************
//以上为思考,下面是定义
//数据存储到一个字符的二维数组中,玩家下棋是“*”电脑下棋是“#”
//这里先构建棋盘,再下棋
char board[ROW][COL] = { 0 };//数组的内容应该是全部空格
InitBoard(board, ROW, COL);//初始化棋盘
//打印棋盘确定是不是初始化正确,不过说实话,你一步步来咋可能错吗?
DisplayBoard(board, ROW, COL);
//注意哦,不要一看到打印就行到printf,这也打印不出来呀!
char ret = 0;
while (1)
{
player_move(board,ROW,COL);//玩家下棋一样是在几行几列下棋
//是在这个二维数组里面下棋
DisplayBoard(board, ROW, COL);//打印棋盘
ret = is_win(board, ROW, COL);//马上来一次判断是平局还是继续
if (ret != 'C')//-C是继续,-Q是平局
{
break;//意思就是如果返回值不等于继续-'C'就跳出循环
//就是说只要不是继续下棋肯定就是出结果了,不要呆在这个while循环里了
}
computer_move(board, ROW, COL);//电脑下棋
DisplayBoard(board, ROW, COL);//打印棋盘
ret = is_win(board, ROW, COL);//注意一定要有,不然你会很困惑,建议尝试去点一下
//补充
if (ret != 'C')
{
break;
}
//到了这里,框架结束,要开始判断输赢了
}
if(ret == '*')
{
printf("玩家胜利!\n");
}
else if(ret == '#')
{
printf("电脑赢!\n");
}
else
{
printf("平局!\n");
}
}
void test()
{
int input = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("三子棋\n");
game();//真正的三子棋游戏
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误\n");
}
} while (input);//非零为真,运行选择程序,为零则跳出结束游戏
//这就是用do while 的原因,while是零也已经进行了一次do循环
}
int main()
{
srand((unsigned)time(NULL));
test();
return 0;
}
game.h(游戏的实现函数声明)
//建议最开始来这么一句,就好像做ppt呀开题要告诉下面的老师,你要讲啥
//游戏的实现函数的声明
#define _CRT_SECURE_NO_WARNINGS 1
#include//这个东东写在这纯属因为啊printf动不动来个未定义
//反正两个c文件都要用,干脆写在都需要调用的game.h里面
#include
#include
//在头文件里宏定义行和列
//减少不必要的操作且直观清楚
//即如果是10*10的十字棋,只需要在宏定义修改即可
//说实话,你要是觉得也麻烦不想写英文也不用这样
#define ROW 3
#define COL 3
void InitBoard(char board[ROW][COL], int row, int col);//注意区分大小写
//注意:这里的row和ROW不是一个东西
//InitBoard(board,ROW,COL);//初始化棋盘操作
//这句话括号内的参数ROW和COL是int整形,用row表示,这样清楚且不冲突
//这里只是函数声明,函数实现在game.c里进行
void DisplayBoard(char board[ROW][COL], int row, int col);
//啰嗦一句,数组传参,用char a[][]用来接收a[][],或者用指针
//详细说明一下
void player_move(char board[ROW][COL],int row,int col);
//电脑下棋,在ROW*COL的棋盘的(row,col)下棋
void computer_move(char board[ROW][COL], int row, int col);
//判断输赢的代码
//玩家赢-'*'
//电脑赢-'#'
//平局-'Q'
//继续-'C'
char is_win(char board[ROW][COL], int row, int col);
game.c(游戏的实现逻辑)
#define _CRT_SECURE_NO_WARNINGS 1
//游戏的实现逻辑
#include"game.h"
//留一行给未来的老婆,(原谅我复习的时候也不记得这个啥意思了)
void InitBoard(char board[ROW][COL], int row, int col)
{//在board(3*3的棋盘)中下棋,row和col为落子位置的行和列
//即将数组全部初始化为空格,构建棋盘
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ' ';//注意啊,这里有个空格
}
}
}
//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
// //此处并不是直接打印空格就行了
// int i = 0;
// int j = 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");
// }
// //注意,可以试着打印一下
// //发现第三行打印的这个———没有必要
// //哈哈哈,你终于打完了棋盘!congratuation!!!
// //结果你发现这种方叫做傻瓜打印法,只适用于三子棋
// //注释掉
// }
//
//}
void DisplayBoard(char board[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 ",board[i][j]);//逗号后面别忘记,说给自己听的
if (j < col - 1)
{
printf("|");
}//打个比方,3行3列,i=0,j=0,打印第一列,0<3-1,打印|
//j=1,打印第二列,1<3-1,打印|
//j=2,打印第三列,2=3-1,不打印
}
printf("\n");//把他们分开,换行,打印完第一行打印第二行
//下面打印---|---|---
//分割行
//把你放在身边怕忘记
//井
// | | (数组第一行)
//---|---|--- (这就是分割线)
// | | (数组第二行)
//---|---|--- (分割线)
// | | (数组第三行)
//所以下一步看怎么打印分割线
if (i < row - 1)//同理第三行不要了
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
}
printf("\n");
}//别忘记!
}
void player_move(char board[ROW][COL], int row, int col)
{
printf("玩家下棋:\n");//井字棋,玩家只能下在三行三列这个范围内
int x = 0;
int y = 0;
while (1)
{
scanf("%d %d", &x, &y);//想办法加个条件让他只能3*3
if (x >= 1 && x <= row && y >= 1 && y <= row)
{
//下过棋的地方不能再下棋
//我们看到的第一行第一列,电脑看到的是第零行零列
//所以x-1,y-1
if (board[x - 1][y - 1] == ' ')//注意此处是单引号
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("该坐标被占用,请重新输入!\n");
}
//留心细节单双引号,单引号在字符常量时使用,表示单个字符。
//char c = 'aA'; // 编译出错,单引号只能是一个字符
//char c = ''; // 单引号中间没有任何字符时,编译出错
//双引号在表示字符串常量时使用,可以表示0到多个字符组成的字符串。
//char s1[] = "a";
//char s2[] = "a1A";
//char s3[] = ""; // 双引号中间可以没有任何字符,表示空字符串
}
else
{
printf("坐标非法!请重新输入!\n");
}
}
}
//这样电脑下棋太蠢,待优化
void computer_move(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋:\n");
//电脑随机下,更难的不会,初学者望谅解
while(1)
{
x = rand() % ROW;//这样余数就只可能是0,1,2三种
y = rand() % COL;
if (board[x][y] == ' ')//这里没有减一是因为前面xy已经限制好了
{
board[x][y] = '#';
break;//一定要,不然电脑就会一直下棋
}//不需要else了,不解释
}
}
//判断结果
int is_full(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
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 < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
{
return board[i][1];
}
}
//三列的判断
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
{
return board[1][i];
}
}
//对角线的情况判断
if (board[0][0] == board[1][1] && board[1][1] == board[2][2]&&board[1][1]!=' ')
{
return board[1][1];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
{
return board[1][1];
}
//判断平局
//即有空格说明没有平局
if (1 == is_full(board, row, col))
//注意这是在函数char is_win(char board[ROW][COL], int row, int col)里
{
return 'Q';
}
//继续
return 'C';
}