前沿
井字棋其实也叫三子棋,但是我们这边叫井字棋。废话不多少 ,直接开整
首先我们要创建三个文件:
test.c——是测试的逻辑,写出来之后能让你把整个游戏制作思路表现出来。
game.h
game.c ——是游戏的实现,具体写代码的地方。
简单的运行流程
首先我们先在test.c中写主函数:
int main()
{
test();
return 0;
}
然后我们可以在test()函数中写出主要运行流程
1.打印菜单
先在test()函数中增加menu()——用来打印菜
void test()
{
do
{
menu();
} while ();
}
做一个简单的菜单表示
void menu()
{
printf("***************************\n");
printf("******** 1. play ******\n");
printf("******** 0. eixt ******\n");
printf("***************************\n");
}
这样就把菜单表示出来了
2.对菜单选项进行表示
void test()
{
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("井字棋\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误\n");
break;
}
} while (1);
}
不多废话,直接运行起来看看效果
这个整个框架就搭建起来了,但是我们输入1:玩游戏,只出现井字棋。这是不够的哦。
这样才算是进入了游戏门槛了。
3. 对游戏棋盘进行表示
所以我们单独打印一个中文数字是不行的,那就再加一个函数game();
首先我们存储数据,是需要一个3*3的数组。
我们初始化一下棋盘为全空格,与上图相对应
void game()
{
char board[3][3];
init_board(board, 3, 3);
}
这样我们就需要在game.h——文件中引用
并且不局限于三子棋,可以五子棋,甚至n子棋了。
这是因为我们数组的行与列是可以无限延伸的
#pragma once
#define ROW 3
#define COL 3
这里大写ROW 代表行 COL 代表列 后面的数字你写多少就表示多少哦
所以我们在game()函数中就让ROW COL 替换 3 3
void game()
{
char board[ROW][COL];
init_board(board, ROW, COL);
}
同时我们也要在game.h头文件中表示
#pragma once
#define ROW 3
#define COL 3
初始化棋盘
我们首先在game.h中声明函数
//初始化棋盘
void init_board(char board[ROW][COL], int row, int col);
后面具体操作就要在game.c中操作了
void intit_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++)
{
board[i][j] = ' ';
}
}
}
小插曲:因为我们是三个文件同时写的,且都要引用头文件game.h
所以我们就把其他需要引用的东西写在头文件中,这样就简单明了了
打印棋盘
初始化之后,我们要表示出来,就要打印了,这些具体操作就放在game.c中写了
同时其他两个文件,也要表示表示,你懂的,哈哈
void print_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]);
}
printf("\n");
}
}
这里我们使用了两个循环,是为了行与列都打印出来。
我们这个打印似乎缺少点什么了,我们的边界呢?所以是要优化一下的
void print_board(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
printf(" %c | %c | \n", board[i][0], board[i][1], board[i][2]);
if (i < row - 1)
printf("---|---|---\n");
}
}
来运行一波
这个不就可以了呀, 但是如果要玩五子棋呢?
简单因为我们有变量 ROW COL 直接在头文件换数字不就可以了
看看这个是啥玩意,五子棋不像三子棋,三子棋不像井字棋
这就露出鸡脚了吧! 所以为了追求完美还是要继续优化
void print_board(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
//printf(" %c | %c | \n", board[i][0], board[i][1], board[i][2]);
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)
{
//printf("---|---|---\n");
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}
}
我们是依据这个个循环打印的哦
玩家&电脑操作
现在所有的外部环境,背景都安排好了。就真的该上手操作了
同样在game.h和test.c中记得申明哦 废话不多说 开整
void player_move(char board[ROW][COL], int row, int col)
{
printf("玩家下棋\n");
while (1)
{
printf("请输入要下棋的坐标:>");
int x = 0;
int y = 0;
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
}
else
{
printf("该坐标被占用,请重新输入\n");
}
}
else
{
printf("坐标非法\n");
}
}
}
在这里是不是脑瓜子嗡嗡的,所以这听我娓娓道来
首先我们要输入坐标,但是坐标要在范围内,否则就非法访问了
然后我们进入门槛后,再判断你输入的位置是不是已经有棋子在那里了
没有的话就直接坐标替换了
接下来就是电脑操作了
电脑下棋,随机生成坐标,只要坐标没有被占用,就下棋
void computer_move(char board[ROW][COL], int row, int col)
{
printf("电脑下棋:\n");
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
这就完成一个小任务
这里的rand()是随机函数,还有时间戳(NULL),所以我们就要添加一些东西了
更加精确
判断输赢
接下来总不能一直玩下去吧,我们还要判断输赢吧
判断输赢
判断输赢的代码要告诉我:电脑赢了?玩家赢了?平局?游戏继续?
电脑赢:#
玩家赢:*
平局:Q
游戏继续:C
我们先在test.c中把整体判断框架搞出来
//判断输赢
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
computer_move(board, ROW, COL);
print_board(board, ROW, COL);
//判断输赢
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == '*')
printf("电脑赢了\n");
else if (ret == '#')
printf("玩家赢了\n");
else if (ret == 'Q')
printf("平局\n");
}
然后再对is_win函数在game.c中进行具体运行
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][0] != ' ')
{
return board[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];
}
}
//对角线
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 (is_full(board, row, col) == 1)
{
return 'Q';
}
//继续
//没有玩家或者电脑赢,也没有平局,游戏继续
return 'C';
}
在这里玩家和电脑循环下棋,每下一步就需要is_win()函数进行判断
三行,三列,对角线是否有同棋
或者平局(棋盘填满)
这里我们就又又又要写一个is_full()函数了
static 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;
}
我希望is_full()这个函数只是为了支持is_win()函数的,只是在is_win()函数内部使用
就没有必要在头文件中申明了。
这就可以把它给隐藏起来了,那就需要 static 把那个函数放在静态库中了。
只要我不给你game.c文件 就你看着头文件是不可能想到这个的哦
结尾:我也是边学,边写,搞了半天。
希望后面的学习,更加熟练,更加通畅,效率更高。
这么长时间居然连如何使用编译器都不清楚。
连小叮当的任意门都做不到
仍需继续努力