C语言基础之——三子棋(详解)
前言:文章最后将给出完整代码,前边的代码会因为便于讲解而不全面,仅供参考。
目录
一.三子棋游戏规则
朋友们小时候肯定都玩过三子棋,无非就是一个三乘三的棋盘,两个人轮流落子,谁的棋子率先连成一排,一列或一个对角线就会胜出,但是这个游戏也会出现无子可落,也就是九个格子被占满了,造成平局。
二.分析如何布局
我们知道,写代码讲究一个整洁有序,即使是设计三子棋这样一个极为简单的游戏,也不是短短几十行代码就能搞定的。
如下图,我们通过创建一个自己的头文件"game.h"来集中存放我们需要使用到的头文件,以及声明我们需要用到的函数,只需要在源文件中都要声明我们自己的头文件#include"game.h"即可。
此外,我们又创建一个新的源文件game.c来集中创建我们需要的函数。
最后,我们游戏的主体代码则写在源文件test.c中。
三.代码展示与讲解
1.游戏菜单
任何游戏都需要一个菜单来选择游戏如何进行。
void menu()//打印菜单函数
{
printf("******************************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("******************************\n");
}
char board[ROW][COL] = {'0'};//游戏数组
int main()
{
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 (input);
return 0;
}
我们创建一个menu(菜单)函数来专门输出我们的菜单,并通过do-while循环,switch-case语句来实现游戏的循环游玩。
这里有一个小技巧,我们知道while的判断条件是0为假,非零为真,所以我们用case 0作为退出游戏,同时也能终止循环,而输入1或其他数字,则会继续循环,重新选择。
2.展示棋盘
我们理想中的棋盘长这个样子,这很明显需要一个二维数组,而且这个二维数组的元素都为" ",(空格)。
void SetBoard(char board[ROW][COL], int row, int col)//创建棋盘函数
{
int i;
int j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
void ShowBoard(char board[ROW][COL],int row,int col)//展示棋盘函数
{
int i;
int j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if(j < col - 1)
printf("|");
}
printf("\n");
if(i < row - 1)
printf("---|---|---\n");
}
}
上边我们已经定义了数组char board[ROW][COL] = {'0'},将其定义为只有一个元素"0"的数组,之后我们通过上述第一个函数SetBoard来对棋盘进行创建,再通过ShowBoard函数对棋盘进行分格修饰并展示。
3.玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)//玩家下棋函数
{
while (1)
{
printf("玩家下棋(选择坐标):");
int x, y;
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] = '*';
break;
}
else
printf("该坐标已被占用,请重新输入:\n");
}
else
printf("输入的坐标错误,请重新输入:\n");
}
}
我们这里下棋是对数组元素进行修改,玩家输入一个坐标,判断这个位置是否为空,为空则落子'*',如果输入的坐标越界,则做出警示,重新输入。这里让玩家下棋需要注意一点,那就是玩家不一定都是程序员,他们选择坐标不会输入数组对应的下标,是二行三列他们就会2 3,我们只需让这两个数字都-1,便可达到效果。
4.电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)//电脑下棋函数
{
printf("电脑下棋:\n");
int x, y;
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
srand((unsigned int)time(NULL));//生成随机数,rand函数需要用,此处无用,供参考
电脑下棋的一个重点就是随机性,这里的坐标就要用到rand函数生成随机数了,注意电脑自己下棋就没有玩家那么多讲究了,生成的随机数在0-2之间即可,电脑下棋我们规定落子为'#'。
5.判断输赢/平局
int IsFull(char board[ROW][COL], int row, int col)//判断平局函数
{
int i, j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}
char IsWin(char board[ROW][COL], int row, int col) //判断输赢函数
{
int i;
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 (IsFull(board, row, col))//判断平局
{
return 'Q';
}
return 'C';
}
这里我们定义IsWin函数来判断输赢,并通过返回值来实现。
我们规定以下四种方式来判断游戏进程:
玩家赢-->返回 '*'
电脑赢-->返回 '#'
平局-->返回 'Q'
游戏继续-->返回 'C'
为什么这样规定呢,我们知道玩家的棋子是'*',如果玩家赢了,那我们的某一行,一列,或者对角线都会是'*',这样我们随便返回一个元素,便可实现下边代码的玩家赢。电脑赢同理。同时我们也会出现平局的结果,那么如何才是平局呢?很简单,如果没有人获胜,并且棋盘已经填满,那不就代表着平局吗?
于是我们定义IsFull函数来判断数组是否填满,填满则返回1,这样最后的if判断语句为真,最终返回'Q',没填满则返回0,最终返回'C'。
char ret = 0;//判断结果
case 1:
SetBoard(board, ROW, COL);//创建棋盘
ShowBoard(board, ROW, COL);//展示棋盘
while (1)
{
//玩家先开始下棋
PlayerMove(board, ROW, COL);
ShowBoard(board, ROW, COL);//下棋之后展示棋盘
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
//电脑开始下棋
ComputerMove(board, ROW, COL);
ShowBoard(board, ROW, COL);//下棋之后展示棋盘
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == '*')
printf("玩家赢\n");
else if (ret == '#')
printf("电脑赢\n");
else
printf("平局\n");
break;
此处为主函数判断,我们定义ret来接收IsWin函数的返回值,并通过if -else判断语句来实现判断游戏结果。
四.全代码展示
1.game.h
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 3//行数为三
#define COL 3//列数为三
void SetBoard(char board[ROW][COL], int row, int col);//创建棋盘
void ShowBoard(char board[ROW][COL], int row, int col);//展示棋盘
void PlayerMove(char board[ROW][COL], int row, int col);//玩家下棋
void ComputerMove(char board[ROW][COL], int row, int col);//电脑下棋
char IsWin(char board[ROW][COL], int row, int col);//判断输赢
2.game.c
#include"game.h"
void SetBoard(char board[ROW][COL], int row, int col)//创建棋盘函数
{
int i;
int j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
void ShowBoard(char board[ROW][COL],int row,int col)//展示棋盘函数
{
int i;
int j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if(j < col - 1)
printf("|");
}
printf("\n");
if(i < row - 1)
printf("---|---|---\n");
}
}
void PlayerMove(char board[ROW][COL], int row, int col)//玩家下棋函数
{
while (1)
{
printf("玩家下棋(选择坐标):");
int x, y;
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] = '*';
break;
}
else
printf("该坐标已被占用,请重新输入:\n");
}
else
printf("输入的坐标错误,请重新输入:\n");
}
}
void ComputerMove(char board[ROW][COL], int row, int col)//电脑下棋函数
{
printf("电脑下棋:\n");
int x, y;
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
//玩家赢-->'*'
//电脑赢-->'#'
//平局-->'Q'
//游戏继续-->'C'
int IsFull(char board[ROW][COL], int row, int col)//判断平局函数
{
int i, j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}
char IsWin(char board[ROW][COL], int row, int col) //判断输赢函数
{
int i;
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 (IsFull(board, row, col))//判断平局
{
return 'Q';
}
return 'C';
}
3.test.c
#include"game.h"
void menu()//打印菜单函数
{
printf("******************************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("******************************\n");
}
char board[ROW][COL] = {'0'};//游戏数组
int main()
{
srand((unsigned int)time(NULL));//生成随机数,供电脑下棋使用
int input = 0;
char ret = 0;//判断结果
do {
menu();
printf("请选择->:");
scanf("%d", &input);//选择
switch (input)
{
case 1:
SetBoard(board, ROW, COL);//创建棋盘
ShowBoard(board, ROW, COL);//展示棋盘
while (1)
{
//玩家先开始下棋
PlayerMove(board, ROW, COL);
ShowBoard(board, ROW, COL);//下棋之后展示棋盘
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
//电脑开始下棋
ComputerMove(board, ROW, COL);
ShowBoard(board, ROW, COL);//下棋之后展示棋盘
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == '*')
printf("玩家赢\n");
else if (ret == '#')
printf("电脑赢\n");
else
printf("平局\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
五.总结
三子棋的创作大概就是这样了,博主也是初学者,很多地方可能会有更多的改进方案,今后将会在不断学习中提升自己来改进代码,同时也感谢朋友们能够耐心的看完我的讲解,如果有任何问题或者改进意见,欢迎各位私信评论。
最后,希望我们喜欢编程的小伙伴们都能越走越远,都能拿到自己心仪的offer,喜欢博主文章的话不要忘记一键三连哦,我们下期再见!