用C语言来实现一个三子棋,我们首先要有一个思维框架。
游戏开始前首先要有一个菜单供玩家选择,选项有进入和退出两种选项。当然,也要考虑玩家不小心按错的情况。退出游戏则直接退出,而进入游戏结束一局游戏后应该再次让玩家进行菜单选择。这样就又回到了开始的选项,形成了死循环,跳出的途径只有选择菜单上的退出选项。
以上的情况就是我们游戏的基本设定,以C语言来实现也十分简单:
//三字棋
#include "play.h"
int main()
{
int tmp = 0;//定义一个变量来表示玩家的选择
do
{
menu();//打印游戏菜单
printf("请输入您的选择");
scanf("%d", &tmp);//玩家进行选择
switch (tmp)//选择后出现的情况
{
case 1:
game();
break;
case 0:
return 0;
break;
default:
printf("输入错误,重新输入\n");
}
} while (1);
return 0;
}
这就实现了整个三字棋程序的基本框架,接下来我们对这个框架进行填充就可以了。
打印游戏菜单函数 void menu():
这个函数就是打印出菜单信息,我们直接用printf函数打印出要给玩家传递的信息就可以了:
//打印三子棋游戏菜单
void menu()
{
printf("****************************\n");
printf("***** 三子棋 *****\n");
printf("***** 1.play 0.exit *****\n");
printf("****************************\n");
}
接下来玩家选择,的三种情况都有所处理,只要在实现game()函数就可以了。
game()函数中,我们先要打印出棋盘,并且考虑先手问题。电脑或玩家每次下子后还要打印棋盘使玩家能够看到当前游戏的状态。每次落子后要判断是否已经达成胜利条件,棋盘是否已满。若无人胜利并且棋盘已满则这局为平局。其中电脑的落子也要额外进行设计,这样game()函数将会比较复杂,所以我们依然先对它进行一个基本架构,之后再慢慢填充。
游戏体函数 void game():
//游戏函数架构
void game()
{
char arr[ROW][COL]; //这个数组的内容就是表示棋盘上落子的情况
init(arr, ROW, COL); //对数组进行初始化,这就是未落子时的状态
print(arr); //打印棋盘
int ret3 = XS(); //玩家选择先手方
while (1)
{
if (ret3)
{//电脑走,走完打印棋盘并判断游戏是否结束。
compter(arr, ROW, COL);
print(arr);
int ret1 = iswin(arr, ROW, COL);
if (panduan(ret1))
return;
/*if ('O' == ret1) //这个判断在玩家和电脑操作后均要进行判断
{ //所以我把它封装为一个函数,需要时调用即可。
printf("电脑赢\n");
return;
}
else if ('X' == ret1)
{
printf("玩家赢\n");
return;
}
else if (0 == ret1)
{
printf("平局\n");
return;
}*/
}
player(arr, ROW, COL);
print(arr);
int ret2 = iswin(arr, ROW, COL);
if (panduan(ret2))
return;
/*if ('O' == ret2)
{
printf("电脑赢\n");
return;
}
else if ('X' == ret2)
{
printf("玩家赢\n");
return;
}
else if (0 == ret2)
{
printf("平局\n");
return;
}*/
if (0 == ret3)
{
ret3 = ret3 + 1;
}
}
}
以上就是对这个游戏体的函数的架构,我们下来对它进行填充:
数组初始化函数 void init():
我们将未落子时的状态初始化为字符空格:‘ ’。
//数组初始化(未落子时的状态)
void init(char arr[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
arr[i][j] = ' ';
}
}
}
打印棋盘函数 void print():
我们打印一个棋盘,这个棋盘要显示当前的落子状态——即arr数组。为了让玩家能够便捷的找到坐标,我们在给棋盘加上行列号。
//打印棋盘
void print(char arr[ROW ][COL])
{
printf(" 0 1 2 \n");
printf(" |---|---|---|\n");
printf(" 0| %c | %c | %c |\n",arr[0][0],arr[0][1],arr[0][2]);
printf(" |---|---|---|\n");
printf(" 1| %c | %c | %c |\n",arr[1][0],arr[1][1],arr[1][2]);
printf(" |---|---|---|\n");
printf(" 2| %c | %c | %c |\n",arr[2][0],arr[2][1],arr[2][2]);
printf(" |---|---|---|\n");
}
玩家选择先手方函数 int XS():
我们让这个函数返回一个int型的值,返回1则为电脑先手,返回0则为玩家先手。
//选择先手方
int XS()
{
int i = 0;
printf("请您决定谁先手:1.电脑先手 0.您先手\n");
scanf("%d", &i);
return i;
}
电脑落子函数 void compter():
电脑落子的坐标我们用rand()%3产生随机数来得到,得到后再判断一下这个坐标是否为空。为空则电脑落子,函数结束。若已经有子则再次进行前面的操作,直到电脑落子为止。
//电脑落子
void compter(char arr[ROW][COL],int row,int col)
{
while (1)
{
srand((unsigned int)time(NULL));
int i = rand() % 3;
int j = rand() % 3;
if (' ' == arr[i][j]) //此处为空则落子,否则继续循环。
{
arr[i][j] = 'O';
return;
}
}
}
判断是否达成胜利条件函数 int iswin():
判断胜利条件是否达成?哪一方达成,则返回代表那一方棋子的字符的ASCII值。若未达成胜利条件且棋盘未满,则返回数字1(游戏继续)。若未达成胜利条件但棋盘已满,则返回数字0(游戏平局)。
//判断是否有人达成胜利条件,或游戏平局
int iswin(char arr[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
if ((arr[i][0] == arr[i][1])&&(arr[i][1]==arr[i][2])&& arr[i][0]!= ' ')
return arr[i][0];
}
for (j = 0; j < col; j++)
{
if ((arr[0][j] == arr[1][j])&& (arr[1][j]== arr[2][j])&& (arr[0][j]!= ' '))
return arr[0][j];
}
if ((arr[0][0] == arr[1][1])&&(arr[1][1] == arr[2][2])&&( arr[0][0]!= ' '))
return arr[0][0];
if ((arr[0][2] == arr[1][1])&&(arr[1][1] == arr[2][0])&&( arr[0][2]!= ' '))
return arr[0][2];
for (i = 0; i < row; i++) //若没有人胜利并且棋盘未满,返回1游戏继续
{
for (j = 0; j < col; j++)
{
if (' ' == arr[i][j])
{
return 1;
}
}
}
return 0; //无人胜利并且棋盘已满,返回0平局
}
判断游戏状态函数 int panduan():
对游戏的状态进行判断,游戏未结束则返回0。游戏结束则输出游戏胜负并且返回1。
//判断游戏状态
int panduan(int ret)
{
if (1 == ret)
{
return 0;
}
if ('O' == ret)
{
printf("电脑赢\n");
}
else if ('X' == ret)
{
printf("玩家赢\n");
}
else if (0 == ret)
{
printf("平局\n");
}
return 1;
}
玩家落子函数 void player():
玩家输入坐标,若坐标输入错误则进行提示,重新输入。
//玩家落子
void player(char arr[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("请输入您要下棋的坐标:>");
while (1)
{
scanf("%d %d", &x, &y);
if (x >= row || y >= col || arr[x][y] != ' ')
{
printf("坐标错误,请重新输入:>");
}
else
{
arr[x][y] = 'X';
return;
}
}
}
由于我们是将游戏基本架构函数 int main()和其它函数放在两个不同的源文件中,所以我们要自己创建一个头文件。
//头文件
#ifndef __PLAY_H__
#define __PLAY_H__
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define ROW 3
#define COL 3
char arr[ROW][COL];
void menu();
void init(char arr[ROW][COL],int row,int col);
void print(char arr[ROW][COL]);
void game();
int XS();
void compter(char arr[ROW][COL],int row,int col);
int iswin(char arr[ROW][COL],int row,int col);
void player(char arr[ROW][COL],int row,int col);
int panduan(int ret);
#endif //__PLAY_H__
以上就是我们这个三子棋的所有代码,我们最后再将它们整合在一起运行验证一下。
//头文件
#ifndef __PLAY_H__
#define __PLAY_H__
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define ROW 3
#define COL 3
char arr[ROW][COL];
void menu();
void init(char arr[ROW][COL],int row,int col);
void print(char arr[ROW][COL]);
void game();
int XS();
void compter(char arr[ROW][COL],int row,int col);
int iswin(char arr[ROW][COL],int row,int col);
void player(char arr[ROW][COL],int row,int col);
int panduan(int ret);
#endif //__PLAY_H__
//游戏基本主架构
//三字棋
#include "play.h"
int main()
{
int tmp = 0;//定义一个变量来表示玩家的选择
do
{
menu();//打印游戏菜单
printf("请输入您的选择");
scanf("%d", &tmp);//玩家进行选择
switch (tmp)//选择后出现的情况
{
case 1:
game();
break;
case 0:
return 0;
break;
default:
printf("输入错误,重新输入\n");
}
} while (1);
return 0;
}
//函数实现
#include "play.h"
int panduan(int ret)
{
if (1 == ret)
{
return 0;
}
if ('O' == ret)
{
printf("电脑赢\n");
}
else if ('X' == ret)
{
printf("玩家赢\n");
}
else if (0 == ret)
{
printf("平局\n");
}
return 1;
}
void game()
{
char arr[ROW][COL]; //这个数组的内容就是表示棋盘上落子的情况
init(arr, ROW, COL); //对数组进行初始化,这就是未落子时的状态
print(arr); //打印棋盘
int ret3 = XS(); //玩家选择先手方
while (1)
{
if (ret3)
{//电脑走,走完打印棋盘并判断游戏是否结束。
compter(arr, ROW, COL);
print(arr);
int ret1 = iswin(arr, ROW, COL);
if (panduan(ret1))
return;
}
player(arr, ROW, COL);
print(arr);
int ret2 = iswin(arr, ROW, COL);
if (panduan(ret2))
return;
if (0 == ret3)
{
ret3 = ret3 + 1;
}
}
}
void menu()
{
printf("****************************\n");
printf("***** 三子棋 *****\n");
printf("***** 1.play 0.exit *****\n");
printf("****************************\n");
}
void init(char arr[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
arr[i][j] = ' ';
}
}
}
void print(char arr[ROW ][COL])
{
printf(" 0 1 2 \n");
printf(" |---|---|---|\n");
printf(" 0| %c | %c | %c |\n",arr[0][0],arr[0][1],arr[0][2]);
printf(" |---|---|---|\n");
printf(" 1| %c | %c | %c |\n",arr[1][0],arr[1][1],arr[1][2]);
printf(" |---|---|---|\n");
printf(" 2| %c | %c | %c |\n",arr[2][0],arr[2][1],arr[2][2]);
printf(" |---|---|---|\n");
}
int XS()
{
int i = 0;
printf("请您决定谁先手:1.电脑先手 0.您先手\n");
scanf("%d", &i);
return i;
}
void compter(char arr[ROW][COL],int row,int col)
{
while (1)
{
srand((unsigned int)time(NULL));
int i = rand() % 3;
int j = rand() % 3;
if (' ' == arr[i][j]) //此处为空则落子,否则继续循环。
{
arr[i][j] = 'O';
return;
}
}
}
int iswin(char arr[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
if ((arr[i][0] == arr[i][1])&&(arr[i][1]==arr[i][2])&& arr[i][0]!= ' ')
return arr[i][0];
}
for (j = 0; j < col; j++)
{
if ((arr[0][j] == arr[1][j])&& (arr[1][j]== arr[2][j])&& (arr[0][j]!= ' '))
return arr[0][j];
}
if ((arr[0][0] == arr[1][1])&&(arr[1][1] == arr[2][2])&&( arr[0][0]!= ' '))
return arr[0][0];
if ((arr[0][2] == arr[1][1])&&(arr[1][1] == arr[2][0])&&( arr[0][2]!= ' '))
return arr[0][2];
for (i = 0; i < row; i++) //若没有人胜利并且棋盘未满,返回1游戏继续
{
for (j = 0; j < col; j++)
{
if (' ' == arr[i][j])
{
return 1;
}
}
}
return 0; //无人胜利并且棋盘已满,返回0平局
}
void player(char arr[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("请输入您要下棋的坐标:>");
while (1)
{
scanf("%d %d", &x, &y);
if (x >= row || y >= col || arr[x][y] != ' ')
{
printf("坐标错误,请重新输入:>");
}
else
{
arr[x][y] = 'X';
return;
}
}
}
运行结果:
验证结果成功。
但是上面的电脑落子是随机的,并不会和玩家进行“博弈”,这样游戏的可玩性就很差。我们应该对电脑进行一定的优化,使这个游戏具有一定的挑战。
我的优化十分简单:
1.电脑会抢中间的位置:
当电脑落子时,只要中间的位置为空,电脑就会在中间落子。
2.电脑会“拦截”:
电脑会判断当前的棋盘上有是否有两个连着的情形。如果有这种情况并且他们的第三个位置为空,则电脑会落在第三个位置。(但是没有对捺三个相同的这个情况进行考虑)。
电脑优化函数 void new_compter():
//电脑落子优化
void new_compter(char arr[ROW][COL], int row, int col) //电脑优化
{
while (1)
{
if (' ' == arr[1][1])
{
arr[1][1] = '0';
return;
}
int i = 0;
int j = 0;
for (i = 0; i < ROW; i++)
{
j = 0;
if (((arr[i][j] == arr[i][j + 1])&& arr[i][j]!= ' ')
|| ((arr[i][j] == arr[i][j + 2])&& arr[i][j]!= ' ')
|| ((arr[i][j + 1] == arr[i][j + 2])&& arr[i][j+1]!= ' '))
//行有两个相同时进行拦截
{
srand((unsigned int)time(NULL));
j = rand() % 3;
if (' ' == arr[i][j])
{
arr[i][j] = '0';
return;
}
}
}
for (j = 0; j < COL; j++)
{
i = 0;
if (((arr[i][j] == arr[i + 1][j])&& arr[i][j]!= ' ')
|| ((arr[i][j] == arr[i + 2][j])&& arr[i][j]!= ' ')
|| ((arr[i + 1][j] == arr[i + 2][j])&& arr[i+1][j]!= ' '))
//列有两个时进行拦截
{
srand((unsigned int)time(NULL));
i = rand() % 3;
if (' ' == arr[i][j])
{
arr[i][j] = '0';
return;
}
}
}
if (((arr[1][1] == arr[2][2])&& arr[1][1]!= ' ')
|| ((arr[1][1] == arr[3][3])&& arr[1][1]!= ' ')
|| ((arr[2][2] == arr[3][3])&& arr[2][2]!= ' '))
//撇有两个时进行拦截(捺未考虑,留做胜利后门)
{
srand((unsigned int)time(NULL));
i = rand() % 3;
j = i;
if (' ' == arr[i][j])
{
arr[i][j] = '0';
return;
}
}
srand((unsigned int)time(NULL));
i = rand() % 3;
j = rand() % 3;
if (' ' == arr[i][j])
{
arr[i][j] = '0';
return;
}
}
}
再次进行验证:
电脑果然聪明了许多。