学习了一些C语言基础知识后,我们可以利用这些来写个简单的三子棋游戏,下面来看看我的写法思路和代码
一、思路设计
这里我们把这个游戏分成3个文件来写,一个测试文件“ test.c ”;一个函数实现文件“ game.c ”;一个函数声明文件“ game.h ”,方便后期维护修改。
设计思路:
- 打印一个菜单,然玩家选择是“玩游戏”还是“退出”,这个代码在 test.c 文件中实现;
- 函数声明、头文件定义等等都在 game.h 中;
- 函数实现的代码都在 game.c 中实现;
- 首先创建一个 3x3 的棋盘,默认玩家先走,然后判断输赢,再电脑走(使用函数生成随机数),然后判断输赢;
二、 代码实现
- 在 game.h 文件中声明头文件,都是代码中函数要使用到的头文件
#include <stdio.h>
#include <time.h>//time函数获取当前时间为srand函数使用
#include <stdlib.h>//srand函数初始化随机数生成器、rand函数生成随机数
- 写一个简易菜单:
void menu()
{
printf("*********************\n");
printf("***** 1. play *****\n");
printf("***** 0. exit *****\n");
printf("*********************\n");
}
- 用户选择页面
由于后面要使用 rand 函数,rand函数可以随机生成一个数字,它的范围是 0~32767,我们在使用 rand 函数之前要使用 srand 函数,因为 srand 函数它只写一次就行,因此我们可以把它放在main函数中或者放在只调用一次的地方;
这里我把放在 test 函数中。
void test()//测试函数
{
int input = 0;//接收玩家选择状态
srand((unsigned int)time(NULL));//随机数生成
do
{
menu();//调用打印菜单
printf("请选择:");
scanf("%d", &input);
//这里也可以用switch语句
while (input)
{
if (input == 1)
{
game();//游戏实现函数
break;
}
else if (input == 0)
printf("退出游戏...\n");
else
{
printf("输入错误!\n");
break;
}
}
} while (input);
}
- 写出 game 函数,在 test.c 文件中
void game()
{
char board[ROW][COL];//创建棋盘
initboard(board, ROW, COL);//初始化棋盘
print(board, ROW, COL);//打印棋盘
char ret = '\0';
while (1)
{
playergo(board, ROW, COL);//玩家走
print(board, ROW, COL);//打印棋盘
ret = judge(board, ROW, COL);//判断输赢
if (ret != 'C')//'C'为继续游戏
break;
computergo(board, ROW, COL);//电脑走
print(board, ROW, COL);//打印棋盘
ret = judge(board, ROW, COL);//判断输赢
if (ret != 'C')//'C'为继续游戏
break;
}
if (ret == '*')
printf("玩家胜利!\n");
else if (ret == '#')
printf("电脑胜利!\n");
else if (ret == 'Q')
printf("平局!\n");
}
- 声明函数
//初始化棋盘
void initboard(char disc[ROW][COL], int row, int col);
//打印棋盘
void print(char disc[ROW][COL], int row, int col);
//玩家走
void playergo(char disc[ROW][COL], int row, int col);
//电脑走
void computergo(char disc[ROW][COL], int row, int col);
//判断输赢
char judge(char disc[ROW][COL], int row, int col);
- 定义 ROW 和 COL,就是行或者列,这样方便后需要修改代码,在 game.h 中定义
#define ROW 3
#define COL 3
三、各个游戏函数代码实现
- 初始化棋盘
void initboard(char disc[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
disc[i][j] = ' ';
}
}
- 打印棋盘
void print(char disc[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
printf(" %c ", disc[i][j]);
printf("\n");
}
}
我们先把棋盘初始化为“ * ”,方便观察,这里可以看到,这样非常不好看,那我们再来改改;
void print(char disc[ROW][COL], int row, int col)
{
for (int i = 0; i < col; i++)
{
printf(" %c | %c | %c \n", disc[i][0], disc[i][1], disc[i][2]);
if (i < col - 1)
printf("---|---|---\n");
}
}
打印出来就是这样,但还是有缺点,这里让我们把 game.h 文件中的ROW和COL改成10,再看看。
这里可以看到,只打印了10行,所以这个泛用性还是不高,还可以继续修改;
void print(char disc[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf(" %c ", disc[i][j]);
if (j < row - 1)
printf("|");
}
printf("\n");
for (int j = 0; j < col; j++)
{
if (i < col - 1)
printf("---");
if (j < col - 1 && i < col - 1)
printf("|");
}
printf("\n");
}
}
这里让我们再把 game.h 文件中的ROW和COL改成10,再看看。
这样就能完全打印出 10x10 的棋盘了;
- 玩家走
这里我们让玩家输入坐标,在进行判断坐标是否合法,再进行修改
void playergo(char disc[ROW][COL], int row, int col)
{
int x = 0, y = 0;//接收玩家输入的坐标
printf("玩家走:");
while (1)
{
printf("请输入坐标:");
scanf("%d %d", &x, &y);//这里然玩家输入的是比坐标大一的数
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))//只有玩家输入的在这个范围内才进入
{
if (disc[x - 1][y - 1] == ' ')//将坐标-1,再判断坐标是否被占用
{
disc[x - 1][y - 1] = '*';//没被占用就将空格改为‘*’
break;
}
else
printf("坐标被占用,请重新输入!\n");
}
else
printf("非法坐标!\n");
}
}
- 电脑走
这里用 rand 生成符合范围的随机数,然后再判断坐标是否合法,并修改
void computergo(char disc[ROW][COL], int row, int col)
{
printf("电脑走...\n");
while (1)
{
int x = rand() % row;//只生成1~row之间的数
int y = rand() % col;//只生成1~col之间的数
if (disc[x][y] == ' ')
{
disc[x][y] = '#';
break;
}
}
}
- 判断输赢
这里我们的方法就是判断行、列,然后判断 \ 和 / 的三个坐标,但这种方法只适用于3子琪,泛用性也不高,后续修改
char judge(char disc[ROW][COL], int row, int col)
{
//不足判断版本
for (int i = 0; i < row; i++)//行
{
if (disc[i][0] == disc[i][1] && disc[i][1] == disc[i][2] && disc[i][1] != ' ')
return disc[i][1];
}
for (int i = 0; i < row; i++)//列
{
if (disc[0][i] == disc[1][i] && disc[1][i] == disc[2][i] && disc[1][i] != ' ')
return disc[1][i];
}
//判断 "\"
if (disc[0][0] == disc[1][1] && disc[1][1] == disc[2][2] && disc[1][1] != ' ')
return disc[1][1];
//判断 "/"
if (disc[0][2] == disc[1][1] && disc[1][1] == disc[2][0] && disc[1][1] != ' ')
return disc[1][1];
}
这4种情况判断完了,都没有返回结果,这样就要来判断是平局还是继续游戏,直接在 judge 函数里写个判断平局的函数
//判断平局
char ret = draw(disc, row, col);
return ret;//直接返回ret
再直接在 game.c 文件里实现这个判断平局或者继续游戏的函数,因为这个函数只有 judge 函数会使用,所以就在本文件内实现即可,还可以在这个函数前加上 static 关键字修饰更安全;
//判断平局
char draw(char disc[ROW][COL], int row, int col)
{
//平局返回'Q'
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (disc[i][j] == ' ')
return 'C';
}
}
return 'Q';
}
四、源代码展示
- 函数声明文件 game.h
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define ROW 3
#define COL 3
//初始化棋盘
void initboard(char disc[ROW][COL], int row, int col);
//打印棋盘
void print(char disc[ROW][COL], int row, int col);
//玩家走
void playergo(char disc[ROW][COL], int row, int col);
//电脑走
void computergo(char disc[ROW][COL], int row, int col);
//判断输赢
char judge(char disc[ROW][COL], int row, int col);
- 测试文件 test.c
#include "game.h"//用户自定义的文件用双引号括起来
void menu()//菜单函数
{
printf("*********************\n");
printf("***** 1. play *****\n");
printf("***** 0. exit *****\n");
printf("*********************\n");
}
void game()//游戏实现函数
{
char board[ROW][COL];//创建棋盘(3x3大小的棋盘);ROW,COL在game.h文件定义
initboard(board, ROW, COL);//初始化棋盘(全部初始化为 “ ”空格)
print(board, ROW, COL);//打印棋盘
char ret = '\0';//用ret接收游戏状态
while (1)
{
playergo(board, ROW, COL);//玩家走
print(board, ROW, COL);//打印棋盘
ret = judge(board, ROW, COL);//judge函数判断输赢
if (ret != 'C')//'C'为继续游戏
break;
computergo(board, ROW, COL);//电脑走
print(board, ROW, COL);//打印棋盘
ret = judge(board, ROW, COL);//判断输赢
if (ret != 'C')//'C'为继续游戏
break;
}
if (ret == '*')
printf("玩家胜利!\n");
else if (ret == '#')
printf("电脑胜利!\n");
else if (ret == 'Q')
printf("平局!\n");
}
void test()//test测试函数
{
int input = 0;//玩家选择状态
srand((unsigned int)time(NULL));//随机数生成
do//do...while循环
{
menu();
printf("请选择:");
scanf("%d", &input);
while (input)
{
if (input == 1)
{
game();//游戏实现函数
break;
}
else if (input == 0)
printf("退出游戏...\n");
else
{
printf("输入错误!\n");
break;
}
}
} while (input);
}
int main(void)
{
test();//test测试函数
return 0;
}
- 函数实现文件 game.c
#include "game.h"//用户自定义的文件用双引号括起来
//初始化棋盘
void initboard(char disc[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
disc[i][j] = ' ';
}
}
//打印棋盘
void print(char disc[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf(" %c ", disc[i][j]);
if (j < row - 1)
printf("|");
}
printf("\n");
for (int j = 0; j < col; j++)
{
if (i < col - 1)
printf("---");
if (j < col - 1 && i < col - 1)
printf("|");
}
printf("\n");
}
}
//玩家走
void playergo(char disc[ROW][COL], int row, int col)
{
int x = 0, y = 0;;
printf("玩家走:");
while (1)
{
printf("请输入坐标:");
scanf("%d %d", &x, &y);
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
if (disc[x - 1][y - 1] == ' ')
{
disc[x - 1][y - 1] = '*';
break;
}
else
printf("坐标被占用,请重新输入!\n");
}
else
printf("非法坐标!\n");
}
}
//电脑走
void computergo(char disc[ROW][COL], int row, int col)
{
printf("电脑走...\n");
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (disc[x][y] == ' ')
{
disc[x][y] = '#';
break;
}
}
}
//判断平局
char draw(char disc[ROW][COL], int row, int col)//可加上static关键字,然这个函数只在这个文件可见
{
//平局返回'Q'
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (disc[i][j] == ' ')
return 'C';
}
}
return 'Q';
}
//判断输赢
char judge(char disc[ROW][COL], int row, int col)
{
//不足判断版本,还可继续优化
for (int i = 0; i < row; i++)//行
{
if (disc[i][0] == disc[i][1] && disc[i][1] == disc[i][2] && disc[i][1] != ' ')
return disc[i][1];
}
for (int i = 0; i < row; i++)//列
{
if (disc[0][i] == disc[1][i] && disc[1][i] == disc[2][i] && disc[1][i] != ' ')
return disc[1][i];
}
//判断 "\"
if (disc[0][0] == disc[1][1] && disc[1][1] == disc[2][2] && disc[1][1] != ' ')
return disc[1][1];
//判断 "/"
if (disc[0][2] == disc[1][1] && disc[1][1] == disc[2][0] && disc[1][1] != ' ')
return disc[1][1];
//判断平局
char ret = draw(disc, row, col);
return ret;
}
总结
现在就写的这样,待以后技术更进一步进行优化,要有错误的地方,各位佬直接指出ヾ(≧▽≦*)o