三子棋是黑白棋的一种。三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、井字棋等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将一方自己的三个棋子走成一条线,就能获胜。
以上介绍来自百度百科
下面来以我自己的理解讲解如何用C语言来实现三子棋
(可拓展棋盘,改变棋子数为n字棋)
对于一款游戏,首先我们必须要有一个可视化的菜单,供玩家选择进入游戏或者退出游戏。
void Menu_Game()
{
printf("*****************************************\n");
printf("********* 1.开始游戏 *********\n");
printf("********* 0.结束游戏 *********\n");
printf("*****************************************\n");
}
同时我们需要接收玩家的输入,进行相应的操作,我们可以运用switch语句来完成这个菜单。同时,当玩家输入错误时我们应该让他重新输入,而当他游戏结束时,也应当让他选择继续游戏或者进行下一次游戏
do
{
Menu_Game();
printf("请输入您的选择:\n");
scanf("%d", &n);
switch (n)
{
case 1:
Game(Checkerboard, ROW, COL);
break;
case 0:
printf("结束游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (n);
如果想用C语言来实现三子棋,我们可以想到用一个二维数组来作为这个三子棋的棋盘,为了方便棋盘的拓展,我们可以用宏来定义棋盘的行数和列数以及棋子数
#define ROW 3
#define COL 3
#define CHESS_NUMBER 3
下面就可以开始具体来写游戏的实现方法,首先我们需要给数组初始化,让棋盘置空,可以遍历一次数组来让所有元素都为空格。
void Init_Checkerboard(char Checkerboard[ROW][COL], int row, int col)
{
int i, j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
Checkerboard[i][j] = ' ';
}
}
}
然后我们需要一个函数,能够显示我们的棋盘,让我们能够看清游戏的局势,当然这个棋盘也要有一定的美观。
![在这里插入图片描述](https://img-blog.csdnimg.cn/2019101020453066.png
void Show_Checkerboard(char Checkerboard[ROW][COL], int row, int col)
{
int i, j;
for (i = 0; i < row; i++)
{
printf("---");
printf("|");
}
printf("\n");
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf(" %c ", Checkerboard[i][j]);
printf("|");
}
printf("\n");
for (j = 0; j < col; j++)
{
printf("---");
printf("|");
}
printf("\n");
}
}
棋盘做好了,下一步就应该让玩家和电脑在棋盘中下棋,我们先从玩家的下棋开始实现。
我们需要让玩家输入他要下的位置,同时我们还要判断他输入的位置是否合法,当他输入的位置不存在的时候我们可以输出一个输入错误,提示他重新输入,同时还需要判断他输入的位置是否被占用,如果被占用同样提示一遍并让其重新输入。而当他输入成功时,我们可以用符号 ‘*’来代替他的棋子,将星号保存在对应的位置,代码如下:
void Player_Move(char Checkerboard[ROW][COL], int row, int col)
{
int x, y;
while (1)
{
printf("请输入您要下的位置:\n");
scanf("%d%d", &x, &y);
if (x > 0 && x <= ROW && y > 0 && y <= COL)
{
if (Checkerboard[x - 1][y - 1] == ' ')
{
Checkerboard[x - 1][y - 1] = '*';
break;
}
else
{
printf("该坐标已被输入\n");
}
}
else
{
printf("输入错误,请重新输入\n");
}
}
}
接下来就应该写电脑的落子,对于电脑的落子,我们需要保证其具有一定的随机性,所以我们需要用到几个函数,rand(),time(),srand(),。
rand 提供随机数
time 为随机数设置范围
srand 作为随机数的设置器,保证数字每一次都不同
同时我们还需要引入对应头文件
#include
#include
我们用 rand()分别与行数列数取余数,得到随机的位置,并对其判断是否被占用,如果被占用则重新取随机数。当成功时,将一个#符号作为电脑的棋子保存入棋盘。
代码如下:
void Computer_Move(char Checkerboard[ROW][COL], int row, int col)
{
int x, y;
while (1)
{
x = rand() % ROW;
y = rand() % COL;
if (Checkerboard[x][y] == ' ')
{
Checkerboard[x][y] = '#';
break;
}
}
}
下棋的步骤完成了,下面就应该判断游戏的结束条件。游戏的结束条件有三种,胜利,失败,平局。而平局的标准就是所有棋子下满但没有人取得胜利,所以我们可以先从这方面入手,我们只需要遍历棋盘,查看棋盘是否存在空位置,若不存在则为下满,我们可以返回一个N 既NOT,若下满则返回F 既FULL。
char Full_Checkerboard(char Checkerboard[ROW][COL], int row, int col)
{
int i, j;
for (i = 0; i < row; i++)
for (j = 0; j < col; j++)
if (Checkerboard[i][j] == ' ')
return 'N';
return 'F';
}
然后我们接下来就可以判断究竟是电脑获胜还是玩家获胜,首先,要连成三子,我们可以行,可以列,可以左右对角线四种情况,当连成三子且不为空位置时,返回其中一个棋子,根据返回的是代表玩家的*还是代表电脑的#来判断胜负。
首先来写行的判断
为了能拓展至n子,我们要考虑其的泛用性,所以我们需要一个flag变量作为计数器,当达成
Checkerboard[i][j] == Checkerboard[i + 1][j]) && (Checkerboard[i][j] != ' '
这个条件时,计数器加一,而不达成时,计数器清空,这样就可以保证为连续的子.因为我们每一次与下一项进行对比,则需要判断棋子数-1次达成条件,所以当计数器与我们的棋子数-1相同时,则说明达成胜利条件,返回其中一个子。
同理对于列我们需要判断
Checkerboard[i][j] == Checkerboard[i + 1][j]) && (Checkerboard[i][j] != ' '
对于左对角线
Checkerboard[i][j] == Checkerboard[i + 1][j + 1]) && (Checkerboard[i][j] != ' '
右对角线
Checkerboard[i][j] == Checkerboard[i + 1][j - 1]) && (Checkerboard[i][j] != ' '
当以上四个条件没有达成时,我们还需要判断是否下满,既平局,如果所有条件没有达成,则说明游戏没有结束,返回一个N
代码如下:
char Win_Game(char Checkerboard[ROW][COL], int row, int col)
{
int i, j, flag = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col - 1; j++)
{
if ((Checkerboard[i][j] == Checkerboard[i][j + 1]) && (Checkerboard[i][j] != ' '))
{
flag++;
}
else if (flag == CHESS_NUMBER - 1)
return Checkerboard[i][j];
else
{
flag = 0;
}
}
}
//判断行是否三子
for (i = 0; i < row - 1; i++)
{
for (j = 0; j < col; j++)
{
if ((Checkerboard[i][j] == Checkerboard[i + 1][j]) && (Checkerboard[i][j] != ' '))
{
flag++;
}
else
{
flag = 0;
}
if (flag == CHESS_NUMBER - 1)
return Checkerboard[i][j];
}
}
//判断列是否三子
flag = 0;
for (i = 0, j = 0; i < row - 1, j < col - 1; i++, j++)
{
if ((Checkerboard[i][j] == Checkerboard[i + 1][j + 1]) && (Checkerboard[i][j] != ' '))
{
flag++;
}
else
{
flag = 0;
}
if (flag == CHESS_NUMBER - 1)
return Checkerboard[i][j];
}
//判断左对角线是否三子
flag = 0;
for (i = 0, j = row - 1; i < row - 1, j>0; i++, j--)
{
if ((Checkerboard[i][j] == Checkerboard[i + 1][j - 1]) && (Checkerboard[i][j] != ' '))
{
flag++;
}
else
{
flag = 0;
}
if (flag == CHESS_NUMBER - 1)
return Checkerboard[i][j];
}
//判断右对角线是否三子
if (Full_Checkerboard(Checkerboard, ROW, COL) == 'F')
return 'F';
//判断棋盘是否下满
return 'N';
}
//判断游戏胜负情况
这样,所有的游戏函数完成了,我们就要开始游戏控制函数的编写。
我们需要一个循环,让玩家和电脑下棋,当游戏没有结束的时候循环就不会跳出。而每一次我们需要让玩家和电脑分别下棋,并且显示棋盘的情况,以及判断是否胜利,因为前面我们完成了所有的游戏函数的编写,在这里直接调用就可以完成了
void Game(char Checkerboard[ROW][COL], int row, int col)
{
char flag = 'N';
srand((unsigned int)time(NULL));
//设置随机数
Init_Checkerboard(Checkerboard, ROW, COL);
Show_Checkerboard(Checkerboard, ROW, COL);
while (1)
{
printf("现在是你的回合\n");
Player_Move(Checkerboard, ROW, COL);
Show_Checkerboard(Checkerboard, ROW, COL);
if ((flag = Win_Game(Checkerboard, ROW, COL)) != 'N')
break;
printf("现在是对手的回合\n");
Computer_Move(Checkerboard, ROW, COL);
Show_Checkerboard(Checkerboard, ROW, COL);
if ((flag = Win_Game(Checkerboard, ROW, COL)) != 'N')
break;
}
if (flag == '*')
{
printf("玩家获胜\n");
}
else if (flag == '#')
{
printf("电脑获胜\n");
}
else if (flag == 'F')
{
printf("棋盘满,平局\n");
}
}
到这里游戏就彻底完成了
如果需要改成n子棋,我们只需要在头文件中修改棋子数量和棋盘大小即可
如8x8的五子棋
下面展示完整的代码
头文件head.h
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include
#include
#include
#define ROW 3
#define COL 3
#define CHESS_NUMBER 3
void Menu_Game();
void Init_Checkerboard(char Checkerboard[ROW][COL], int row, int col);
void Show_Checkerboard(char Checkerboard[ROW][COL], int row, int col);
void Player_Move(char Checkerboard[ROW][COL], int row, int col);
void Computer_Move(char Checkerboard[ROW][COL], int row, int col);
char Full_Checkerboard(char Checkerboard[ROW][COL], int row, int col);
char Win_Game(char Checkerboard[ROW][COL], int row, int col);
void Game(char Checkerboard[ROW][COL], int row, int col);
游戏文件 game.c
#include "head.h"
void Menu_Game()
{
printf("*****************************************\n");
printf("********* 1.开始游戏 *********\n");
printf("********* 0.结束游戏 *********\n");
printf("*****************************************\n");
}
void Init_Checkerboard(char Checkerboard[ROW][COL], int row, int col)
{
int i, j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
Checkerboard[i][j] = ' ';
}
}
}
//初始化棋盘
void Show_Checkerboard(char Checkerboard[ROW][COL], int row, int col)
{
int i, j;
for (i = 0; i < row; i++)
{
printf("---");
printf("|");
}
printf("\n");
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf(" %c ", Checkerboard[i][j]);
printf("|");
}
printf("\n");
for (j = 0; j < col; j++)
{
printf("---");
printf("|");
}
printf("\n");
}
}
//显示棋盘
void Player_Move(char Checkerboard[ROW][COL], int row, int col)
{
int x, y;
while (1)
{
printf("请输入您要下的位置:\n");
scanf("%d%d", &x, &y);
if (x > 0 && x <= ROW && x > 0 && x <= ROW)
{
if (Checkerboard[x - 1][y - 1] == ' ')
{
Checkerboard[x - 1][y - 1] = '*';
break;
}
else
{
printf("该坐标已被输入\n");
}
}
else
{
printf("输入错误,请重新输入\n");
}
}
}
//玩家移动
void Computer_Move(char Checkerboard[ROW][COL], int row, int col)
{
int x, y;
while (1)
{
x = rand() % ROW;
y = rand() % COL;
if (Checkerboard[x][y] == ' ')
{
Checkerboard[x][y] = '#';
break;
}
}
}
//电脑移动
char Full_Checkerboard(char Checkerboard[ROW][COL], int row, int col)
{
int i, j;
for (i = 0; i < row; i++)
for (j = 0; j < col; j++)
if (Checkerboard[i][j] == ' ')
return 'N';
return 'F';
}
//判断棋盘是否下满
char Win_Game(char Checkerboard[ROW][COL], int row, int col)
{
int i, j, flag = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col - 1; j++)
{
if ((Checkerboard[i][j] == Checkerboard[i][j + 1]) && (Checkerboard[i][j] != ' '))
{
flag++;
}
else if (flag == CHESS_NUMBER - 1)
return Checkerboard[i][j];
else
{
flag = 0;
}
}
}
//判断行是否三子
for (i = 0; i < row - 1; i++)
{
for (j = 0; j < col; j++)
{
if ((Checkerboard[i][j] == Checkerboard[i + 1][j]) && (Checkerboard[i][j] != ' '))
{
flag++;
}
else
{
flag = 0;
}
if (flag == CHESS_NUMBER - 1)
return Checkerboard[i][j];
}
}
//判断列是否三子
flag = 0;
for (i = 0, j = 0; i < row - 1, j < col - 1; i++, j++)
{
if ((Checkerboard[i][j] == Checkerboard[i + 1][j + 1]) && (Checkerboard[i][j] != ' '))
{
flag++;
}
else
{
flag = 0;
}
if (flag == CHESS_NUMBER - 1)
return Checkerboard[i][j];
}
//判断左对角线是否三子
flag = 0;
for (i = 0, j = row - 1; i < row - 1, j>0; i++, j--)
{
if ((Checkerboard[i][j] == Checkerboard[i + 1][j - 1]) && (Checkerboard[i][j] != ' '))
{
flag++;
}
else
{
flag = 0;
}
if (flag == CHESS_NUMBER - 1)
return Checkerboard[i][j];
}
//判断右对角线是否三子
if (Full_Checkerboard(Checkerboard, ROW, COL) == 'F')
return 'F';
//判断棋盘是否下满
return 'N';
}
//判断游戏胜负情况
void Game(char Checkerboard[ROW][COL], int row, int col)
{
char flag = 'N';
srand((unsigned int)time(NULL));
//设置随机数
Init_Checkerboard(Checkerboard, ROW, COL);
Show_Checkerboard(Checkerboard, ROW, COL);
while (1)
{
printf("现在是你的回合\n");
Player_Move(Checkerboard, ROW, COL);
Show_Checkerboard(Checkerboard, ROW, COL);
if ((flag = Win_Game(Checkerboard, ROW, COL)) != 'N')
break;
printf("现在是对手的回合\n");
Computer_Move(Checkerboard, ROW, COL);
Show_Checkerboard(Checkerboard, ROW, COL);
if ((flag = Win_Game(Checkerboard, ROW, COL)) != 'N')
break;
}
if (flag == '*')
{
printf("玩家获胜\n");
}
else if (flag == '#')
{
printf("电脑获胜\n");
}
else if (flag == 'F')
{
printf("棋盘满,平局\n");
}
}
测试文件 test.c
#include "head.h"
int main()
{
int n;
char Checkerboard[ROW][COL] = { 0 };
srand((int)time(NULL));
do
{
Menu_Game();
printf("请输入您的选择:\n");
scanf("%d", &n);
switch (n)
{
case 1:
Game(Checkerboard, ROW, COL);
break;
case 0:
printf("结束游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (n);
return 0;
}
这是个很简单的小游戏,总代码数在270行左右,很容易就可以实现。
代码链接:https://github.com/HONGYU-LEE/test/tree/master/project/tic-tac-toe