目录
“
三子棋是对C语言学习前期学习的一个很好的总结,其中包含了数组,循环,函数等等C语言知识点,将实现三子棋当作一个C语言的学业测试,能够独立的完成这个简单的小游戏,就说明你已经及格了🤔
”
一、预览
废话不多说,先上代码⬇️⬇️⬇️
(详细内容看代码之后的思路解析🐤)
1. 函数声明(game.h)
#include<stdio.h>
#include<stdlib.h> //生成随机数需要的两个头文件
#include<time.h>
#define ROW 3 //这里用预处理指令,便于以后对行列的更改
#define COL 3
void InitBoard(char Board[ROW][COL],int row,int col); //初始化棋盘
void PrintBoard(char Board[ROW][COL], int row, int col); //打印棋盘
void player_move(char Board[ROW][COL], int row, int col); //玩家输入
void computer_move(char Board[ROW][COL], int row, int col); //电脑随机下棋
char Judeg_win(char Board[ROW][COL], int row, int col); //判断输赢以及平局
2.测试文件(text.c)
#include"game.h" //包含函数声明的头文件
void menu() //打印菜单
{
printf("******************\n");
printf("*******1.play*****\n");
printf("*******0.exit*****\n");
printf("******************\n");
}
void game() //游戏的总体思路
{
char Board[ROW][COL]; //创建棋盘(二维字符数组),并将其初始化
InitBoard(Board,ROW,COL);
PrintBoard(Board, ROW, COL);
char ret;
while (1) //让玩家与电脑不断循环下棋,直到分出输赢break跳出循环
{
player_move(Board, ROW, COL); //玩家下棋 -> 打印棋盘 -> 判断输赢
PrintBoard(Board, ROW, COL);
ret = Judeg_win(Board, ROW, COL);
if (ret != 'C')
break;
computer_move(Board, ROW, COL); //电脑下棋 -> 打印棋盘 -> 判断输赢
PrintBoard(Board, ROW, COL);
ret = Judeg_win(Board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == '*') //用判断输赢的函数返回的值判断下列情况
printf("玩家赢!\n");
if (ret == '#')
printf("电脑赢!\n");
if (ret == 'D') //Deaw——平局
printf("平局\n");
}
void test() //进入游戏页面
{
srand((unsigned int)time(NULL)); //时间戳,具体问题课翻阅我之前的文章
int input = 0;
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1:
game();
case 0:
break;
default:
printf("选择错误\n");
}
} while (input);
}
int main()
{
test();
return 0;
}
3. 函数的实现(game.c)
#include"game.h"
void InitBoard(char Board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
Board[i][j] = ' '; //将二维数组的每一个位置初始化为‘空格’
}
}
}
void PrintBoard(char Board[ROW][COL], int row, int col) //打印棋盘,包括间隔线
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf(" %c ", Board[i][j]); //每打印一个棋盘格用 ‘|’ 间隔开
if (j != col - 1) //每一行的最后一列之后不用打印
printf("|");
}
printf("\n");
if (i != row - 1) //最后一行不用打印分隔线
{
for (int 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");
while (1)
{
printf("请输入你的坐标:\n");
int x = 0;
int y = 0;
scanf("%d %d", &x, &y); //以玩家的视角来看,棋盘坐标是数组下标+1
//输入的时候是从1开始,-1之后才为数组下标
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (Board[x-1][y-1] == ' ')
{
Board[x-1][y-1] = '*';
break;
}
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; //随机数模3,及为0-2的数
int y = rand() % col;
if (Board[x][y] == ' ')
{
Board[x][y] = '#';
break;
}
}
}
static int is_full(char Board[ROW][COL], int row, int col) //该函数判断棋盘是否已满
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (Board[i][j] == ' ')
return 0;
}
}
return 1;
}
char Judeg_win(char Board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++) //一行三个棋子相同
{
if (Board[i][0] == Board[i][1]
&& Board[i][0] == Board[i][2]
&& Board[i][0] != ' ')
return Board[i][0]; //返回连成三个的棋子类型
}
for (int i = 0; i < col; i++) //一列三个棋子相同
{
if (Board[0][i] == Board[1][1]
&& Board[0][i] == Board[2][i]
&& Board[0][i] != ' ')
return Board[0][i];
}
if(Board[0][0] == Board[1][1] //主对角线三个棋子相同
&& Board[0][0] == Board[2][2]
&& Board[0][0] != ' ')
return Board[0][0];
if (Board[0][2] == Board[1][1] //副对角线三个棋子相同
&& Board[0][2] == Board[2][0]
&& Board[0][2] != ' ')
return Board[0][2];
if (is_full == 1) //前面四种情况都不满足,判断棋盘是否已满
return 'D'; //已满is_full函数返回1,在返回 ‘D’ ,平局
return 'C'; //以上全不满足,返回 ‘C’ 继续下棋
}
二、思路分析
这儿我把我自己分析的草稿分享给大家,希望对大家理解有帮助⬇️
1. 打印菜单以及初始化棋盘
① 首先打印菜单,定义变量input,输入1表示进入游戏,输入0表示退出,输入其他则重新输入
② 用do-while循环,第一次不用判断直接进入循环,再用switch分支语句,根据input的值判断是开始游戏还是跳出循环,结束游戏
2.打印棋盘
① 首先我们需要在脑海中想象打印后棋盘的模样🤔⬇️
② 有了大致模样之后,我们就用for循环来将其挨个打印,第一步打印一行数组元素,用 ‘ | ’ 间隔,用if语句判断,当位于最后一列时,不打印间隔线;
③ 打印完一行棋盘数组中的元素之后紧接的就是打印每行的间隔线,但是注意最后一行打印完之后不需要间隔线,所以首先要用 if 判断是否为最后一行,完了之后用for循环打印 ‘ --- ’,同样时最后一列不打印 ‘ | ’
3.玩家以及电脑下棋
玩家下棋:
※ 因为有所下位置已有棋子和所下位置在棋盘之外两种情况,所以我们直接用while(1)进入死循环,直到下棋的位置合法,用break跳出循环。
※ 还需要注意的是玩家认为的棋盘坐标与实际二维数组坐标的区别⬇️
所以当玩家下棋的时候,要对输入的坐标-1
电脑下棋:
※ 电脑下棋任有所下位置已有棋子的情况,所以同样进入while(1)死循环,当所下位置合法时break跳出循环;
※ 生成随机数再模 3 ,得到的就是 0 - 2 的数字了,及二维数组坐标
之前详细的解析过如何生成随机数:https://blog.csdn.net/Dusong_/article/details/127577970?spm=1001.2014.3001.5501
4.输赢与平局
① 行、列、主对角线、副对角线有三个相同棋子
• 直接用for循环和if判断是否有三个字符相等,注意三个相等且不为‘空格’
• 返回值为三个相同的棋子之中的一个(字符),所以在 text.c 中直接通过返回的字符,判断时那方赢
② 棋盘已满
• 抽出一个函数(is_full),用于判断棋盘是否已满:用两个for循环遍历数组,若有空余空间则返回0,遍历完了也没有返回值则说明棋盘已满,此时返回1;
• 回到game.c , 若is_full函数返回1,则返回 ‘ D(draw) ’ ,表示平局。
③ 继续下棋
• 以上所有多没有返回值,则最后返回 ‘ C(continue) ’ ,表示继续游戏,所以每次玩家和电脑下完棋之后都要调用该函数判断游戏是否出了结果;
• 当返回值不为 ‘ C ’ 时,表示游戏结束,game函数里break跳出循环,在对其返回值判断结局;
三、如何将其转化成.exe文件
1. 将Debug转换成Release
2. 重新生成解决方案
生成 -> 重新生成解决方案
3. 在Release文件中找到.exe
将他复制粘贴给你的室友和老师吧!⬇️
Summery💐
总的来说,完成一个程序需要一个完整的代码思路,将情况考虑全面,是一个精细活✔️