本人0基础开始学编程,我能学会的,你也一定可以,学会多少写多少。
下载安装请从官网入手,社区版本即可,这里主要使用的软件是VS2019,图标如下。
上一篇:从0开始学c语言-16-数组以及数组传参应用:冒泡排序_阿秋的阿秋不是阿秋的博客-优快云博客
放一个链接吧
链接:https://pan.baidu.com/s/1rLHiBu8U58xowDRmrswddA?pwd=vo19
提取码:vo19
目录
三字棋
不进行喂饭式的引导,讲一点思路就直接抛代码了昂。注释里写得很详细,这玩意能看懂就行,看不懂就直接问我。
三字棋是怎么玩的呀?
你看,就像这样,
在选择游戏后,提示游戏开始并出现棋盘,随后玩家和电脑下棋,直到分出胜负。
然后回到菜单,选择游戏或者退出。
思路
三个文件分别写需要的代码,主要想清楚棋盘如何打印,以及棋盘和二维数组的关系。理清楚玩家输入数字和数组下标关系,思考电脑怎么下棋(提示随机数设置)。
注意在test.c和game.c中引用game.h。
#include "game.h"
看好后续程序里的注释里是什么。
创建文件
我们在头文件中进行函数声明以及变量的定义,在test.c中书写主要程序,在game.c中书写函数的具体实现过程。
主函数test.c
int main()
{
menu();
printf("请选择:>");
int input = 0;
scanf("%d", &input); //总是忘记&符号
switch (input)
{
case 1:
printf("游戏开始");
begin();
break;
case 2:
printf("下次再见~");
break;
default:
printf("输入错误,请重新输入");
break;
}
return 0;
}
这样的主函数代码没办法实现我们玩完游戏后选择是否继续游戏,所以我们加上循环就可以了。
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
//设置电脑下棋的随机数起点
do {
menu();
printf("请选择:>");
scanf("%d", &input); //总是忘记&符号
switch (input)
{
case 1:
printf("游戏开始\n");
begin(); //游戏函数
break;
case 2:
printf("下次再见~\n");
return 0; //break只能跳出离他最近的switch,换成这个来结束程序
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
菜单test.c
void menu()
{
printf(" 1.玩游戏\n");
printf(" 2.退出\n");
printf("………………\n");
}
游戏头文件game.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//定义常量
#define HANG 3
#define LIE 3
//声明函数
//void zero(char aq[HANG][LIE], int HANG, int LIE)
//这样写就不是变量而是常量了,所以我们要做个区分
//棋盘需要的函数
void zero(char aq[HANG][LIE], int hang, int lie); //初始化数据
void xian(char aq[HANG][LIE], int hang, int lie); //打印棋盘
//开始游戏
//玩家下棋
void player(char aq[HANG][LIE], int hang, int lie);
//电脑下棋
void computer(char aq[HANG][LIE], int hang, int lie);
//输赢判断
char end(char aq[HANG][LIE], int hang, int lie);
主函数中的游戏函数test.c
void begin()
{
//第一步——创建棋盘
//
//1·有二维数组来存放数据
//2·数组初始化应该放什么
//3·打印棋盘以及分割线
//1·有二维数组来存放数据
char aqiu[HANG][LIE]; //大小写的区别
//不用数字来代表是因为我们需要这样一个变量来帮助我们完成游戏
//2·数组初始化应该放什么-放空格
zero(aqiu,HANG,LIE);
//3·打印棋盘以及分割线__本质是打印数组的内容
xian(aqiu, HANG, LIE);
//第二步——开始玩耍并判断输赢
//1·玩家怎么下
//2·电脑怎么下
//3·判断输赢——结束
1·玩家怎么下
//player(aqiu, HANG, LIE);
2·电脑怎么下
//computer(aqiu, HANG, LIE);
现在已经实现了电脑和玩家分别下一步棋
那么我们需要做的是,让它们可以继续下棋
继续下棋就意味着需要循环这个下棋过程
循环需要一个判断是否继续游戏的函数
而是否继续游戏也和输赢判断有关
因为输赢后,游戏也就结束了
所以我们把游戏继续和输赢判断写在一起
//
//3·判断输赢
// 我们需要判断输赢的一个条件
// 那么便需要一个返回值
// 如下
//3·1 玩家赢——*
//3·2 电脑赢——#
//3·3 平局 ——E
//3·4 游戏继续——C
char sign = 0; //因为返回值都是字符,所以用char返回类型
//不能写在循环里,否则跳出循环后无法判断输赢的情况
//循环下棋直到分出胜负为止
while (1)
{
player(aqiu, HANG, LIE);
//end(aqiu, HANG, LIE);//写成这样没办法接收这个函数的返回值,需要一个变量来储存
sign = end(aqiu, HANG, LIE);
//那么这里的sign会有四种情况
//只有C字符才会允许电脑继续下棋
//其他字符都会结束游戏
if (sign != 'C')//意味着结束
{
break;
}
computer(aqiu, HANG, LIE);
sign = end(aqiu, HANG, LIE);
if (sign != 'C')//意味着结束
{
break;
}
}
//跳出循环意味着输赢已经分出
if (sign == '*')
{
printf("恭喜玩家取得胜利~\n");
}
else if (sign == '#')
{
printf("恭喜电脑取得胜利~\n");
}
else
{
printf("高手过招,点到为止,恭喜二位平局\n");
}
}
游戏头文件对应函数过程game.c
//定义函数_数组初始化
void zero(char aq[HANG][LIE], int hang, int lie)
{
int i = 0; //变换行需要的变量
int j = 0; //变换列需要的变量
for (i = 0; i < HANG; i++)
{
for (j = 0; j < LIE; j++)
{
aq[i][j] = ' '; //放空格
}
}
}
//打印棋盘
//按行来划分,分为有数组的和没数组的
void xian(char aq[HANG][LIE], int hang, int lie)
{
int i = 0; //行
int j = 0; //列
for (i = 0; i < HANG ; i++) //每一行
{
printf(" %c | %c | %c \n",aq[i][j],aq[i][j + 1],aq[i][j + 2]);
if(i < HANG - 1)
printf("---|---|---\n"); //要比数组少一行
}
}
//这样写只能打印3*3的棋盘
//我们还需要改进
//这次我们把行和列一起看
void xian(char aq[HANG][LIE], int hang, int lie)
{
int i = 0; //行
int j = 0; //列
for (i = 0; i < HANG; i++) //每一行
{
for (j = 0; j < LIE-1; j++) //每一列
{
printf(" %c |", aq[i][j]);
}
printf("\n"); //加(换)行
for (j = 0; j < LIE; j++) //每一列
{
printf("---|");
}
printf("\n");
}
}
//但是不对,没考虑到边缘区域
现在这就是我们打印棋盘的代码。
void xian(char aq[HANG][LIE], int hang, int lie)
{
int i = 0; //行
int j = 0; //列
for (i = 0; i < HANG; i++) //每一行
{
//打印棋子线
for (j = 0; j < LIE; j++) //每一列
{
printf(" %c ", aq[i][j]);
if (j < LIE - 1) //边缘不打
{
printf("|");
}
}
printf("\n"); //换行
//打印分割线
if(i < HANG - 1) //要找到这个条件,这分割线是比数组行少1的
{
for (j = 0; j < LIE; j++) //每一列
{
printf("---");
if (j < LIE - 1) //边缘不打
{
printf("|");
}
}
}
printf("\n");//换行
}
}
//玩家下棋
void player(char aq[HANG][LIE], int hang, int lie)
{
int x = 0; //玩家输入横坐标变量
int y = 0; //玩家输入纵坐标变量
//考虑输入坐标和下标之间的关系
//坐标范围1-3
//下标范围0-2
while (1) //因为后续重新输入的关系,需要循环
{
printf("玩家下棋\n");
printf("请输入坐标:>");
scanf("%d %d", &x, &y); //取地址啊!又忘了
//判断输入坐标是否在范围内
if (x >= 1 && x <= hang && y >= 1 && y <= hang )
{
//aq[x - 1][y - 1]='*; //换空格为棋子
//不能直接下棋,应该判断如果输入棋子已经被占用
if (aq[x - 1][y - 1] == ' ') //未被占用
{
aq[x - 1][y - 1]='*'; //换空格为棋子
xian(aq, hang, lie); //打印棋盘与棋子
break; //下棋成功跳出循环
}
else
{
printf("位置已有棋子,请重新输入\n");
}
}
else
printf("超出范围,请重新输入\n"); //重新输入意味有循环
}
//整段玩家下棋的逻辑就是,给定两个坐标让玩家输入
//1·判断输入坐标是否在范围内——范围需要和下标比较,因为数组数据是用下标访问的
//2·输入坐标是否被其他棋子占用——也就是判断是不是空格
//如果输入坐标满足棋盘范围且未被占用则下棋成功,
//跳出循环并结束玩家下棋
//其他情况则继续让玩家输入坐标,直到下棋成功为止
}
//电脑下棋
void computer(char aq[HANG][LIE], int hang, int lie)
{
printf("电脑下棋:>\n"); //不加上换行的话,棋盘会有一行打印在这里
//电脑下棋如果智能的话很复杂,这里使用之前学过的随机数
while (1)
{
int x = rand() % hang;
int y = rand() % LIE; //在game.h中进行了头文件的引用
//上面两行代表电脑的xy坐标给定了0-2范围内的随机数
//和玩家手动输入的坐标不同,电脑的范围正好对应数组下标范围内
//那么我们现在应该考虑的是:棋位是否被占用
if (aq[x][y] == ' ')
{
aq[x][y] = '#'; //换空格为棋子
xian(aq, hang, lie); //打印棋盘
break; //电脑下棋成功,跳出循环
}
//如果被占用,那我们需要重新生成随机数,于是便有了循环
}
//和玩家下棋不同的是,电脑的坐标和数组下标范围相同
//不需要范围判断,只需要判断棋子是否被占用
//其次,如果被占用,那便重新随机生成坐标
//不需要提示被占用
//一直随机生成到电脑下棋成功,电脑的下棋过程也就结束了
}
//判断输赢
char end(char aq[HANG][LIE], int hang, int lie)
{
//我们设定end有四种返回符号
//那就意味着是有分支情况的
//赢的情况:行、列、对角线相同三种情况
//行、列都有三行
int x = 0; //棋盘hang下标
int y = 0; //棋盘lie下标
char re[2] = {'*','#'}; //判断字符需要
int i = 0; //访问字符数组下标
int flag = 0; //判断连续
for (i = 0; i < 2; i++)
{
//一·判断同一行的每列是否相等,共三行
for (x=0,y = 0; x < hang && y < lie; y++) //循环每一列 y范围0-2
{
if (aq[x][y] == re[i]) //第x行的每y列都相等
continue;
x++; //不等于就算下一行,算到x=hang时意味着所有行都不相等
y = -1; //下一行y要置零,因为语句结束会去到调整部分,所以等于-1
}
//跳到这里有两种情况
//1·有同一行的每列都相等
//2·所有行的每列都不相等——这种情况的x=hang
if (x != hang)
{
return re[i]; //行相等且赢
}
//二·判断同一列的每行是否相等,共三列
for (x = 0, y = 0; x < hang && y < lie; x++) //循环每一行 x范围0-2
{
if (aq[x][y] == re[i]) //第y列的每x行都相等
continue;
y++; //不等于就算下一列,算到y=lie时意味着所有行都不相等
x = -1; //下一行x要置零,因为语句结束会去到调整部分,所以等于-1
}
//跳到这里有两种情况
//1·有同一行的每列都相等
//2·所有行的每列都不相等——这种情况的y=lie
if (y != lie)
{
return re[i]; //列相等且赢
}
//三·左对角线是否相等——这里默认棋盘是正方形
for (x = 0, y = 0; x < hang && y < lie; x++, y++)
{
if (aq[x][y] == re[i]) //第y列的每x行都相等
continue;
break;
}
//跳到这里有两种情况
//1·左对角线都相等且赢——此时x与y都等于hang于列
//2·不相等——此时x小于hang,y小于lie
if (x == hang && y == lie)
{
return re[i]; //左对角线相等且赢
}
//四·右对角线是否相等
for (x = 0, y = lie - 1; x < hang && y >= 0; x++, y--)
{
if (aq[x][y] == re[i])
continue;
break;
}
//跳到这里有两种情况
//1·右对角线都相等且赢——此时x=hang,y=-1
//2·不相等——此时x<hang且y>=0
if (x == hang && y == -1)
{
return re[i]; //右对角线相等且赢
}
}
//以上四种情况
// 1·行相等且结束
// 2·列相等且结束
// 3·左对角线相等且结束
// 4·右对角线相等且结束
//如若都不满足的话
//数据会来到这里
//而来到这里的数据有两种情况
//1·满棋子且输赢未分
//2·未满棋子
//一共有四大类情况
//3·1 玩家赢——*
//3·2 电脑赢——#
//3·3 平局 ——A
//3·4 游戏继续——C
//现在3·1和3·2已经判断出来
//所以我们可以把3·3和3·4写在一起
//一个判断是否满棋子的函数
//如果满了,我们返回1
//没满我们返回0
//用变量j来储存函数返回值
int j = full(aq,hang,lie);
if (j == 1)
{
return 'A';
}
return 'C';
}
//判断满否?
int full(char aq[HANG][LIE], int hang, int lie)
{
//一个判断是否满棋子的函数
//如果满了,我们返回1
//没满我们返回0
int x = 0; //hang下标
int y = 0; //lie下标
for (x = 0; x < hang; x++)
{
for (y = 0; y < lie; y++)
{
if (aq[x][y] == ' ') //棋盘没满
{
return 0;
}
}
}
return 1;
}
难搞哦,小白还是先模仿吧,自己凭空写出来是需要点天赋的。
加油!
下一篇: