用c语言写一个简单的三子棋(井字棋)
emmmm…大致就是这个效果
目录
1.所需的知识
c语言基本语法,二维数组。
2.总体思路
将三子棋看作3x3的一个二维数组,由玩家和电脑分别填充这个数组。当行、列、叉能够达到三连时,就判断输赢。
默认玩家先行且棋子为 ‘ X ’,电脑后手棋子为 ‘ 0 ’。
下面是用到的一些函数。
//game.h
#ifndef __GAME_H__
#define __GAME_H__
#define ROW 3
#define CLO 3
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <memory.h>
void menu(); //打印菜单
void game(char arr[ROW][CLO], int row, int clo); //执行菜单
void play(char arr[ROW][CLO], int row, int clo); //游戏主体
void InitBoard(char arr[ROW][CLO], int row, int clo); //打印棋盘
void Pmove(char arr[ROW][CLO], int row, int clo); //玩家下棋
void Cmove(char arr[ROW][CLO], int row, int clo); //电脑下棋
int Isfull(char arr[ROW][CLO], int row, int clo); //检测盘满
int Iswin(char arr[ROW][CLO], int row, int clo); //判断输赢
#endif
下面是主函数
//test.c
#include"game.h"
int main()
{
srand((unsigned int)time(NULL)); //产生随机数种子
char arr[ROW][CLO];
game(arr,ROW,CLO);
system("pause");
return 0;
}
3.详细实现
打印菜单
void menu()
{
printf("*******************\n");
printf("*******1.play *****\n");
printf("*******2.exit *****\n");
printf("*******************\n");
}
这个函数打印菜单选项。
执行菜单
void game(char arr[ROW][CLO], int row, int clo)
{
int input = 0;
do
{
menu();
scanf_s("%d",&input);
memset(arr, ' ', ROW*CLO * sizeof(arr[0][0]));
switch (input)
{
case 1:
play(arr, row, clo); break;
case 2:
exit(0); break;
default:printf("你按错键了?\n");
}
} while (input);
}
这个函数是菜单执行部分。其中 memset 用来初始化二位字符数组为全‘ 空格 ’。
接着,用switch语句来判断玩家选项。
游戏主体
void play(char arr[ROW][CLO], int row, int clo)
{
int flag = 0;
InitBoard(arr, row, clo);
while (1)
{
printf("人类走\n");
Pmove(arr, row, clo);
InitBoard(arr, row, clo);
flag = Iswin(arr, row, clo);
if (flag == 1)
{
printf("玩家赢\n");
break;
}
flag = Isfull(arr, row, clo);
if (flag == 4)
{
printf("棋盘满,游戏结束\n");
break;
}
printf("电脑走\n");
Cmove(arr, row, clo);
InitBoard(arr, row, clo);
flag = Iswin(arr, row, clo);
if (flag == 1)
{
printf("电脑赢\n");
break;
}
}
}
这个函数里面拥有一个 while 死循环,它的跳出条件是 玩家赢 、 电脑赢 、棋盘满。
flag == 1 表示电脑或者玩家获胜,flag == 4 表示棋盘满了。
这里将棋盘满的检测放在玩家行动后,因为只有先手才能使棋盘满。
棋盘打印
void InitBoard(char arr[ROW][CLO], int row, int clo)
{
int i = 0;
int j = 0;
for (j = 0; j < row; j++)
{
for (i = 0; i < clo; i++)
{
printf(" %c ", arr[j][i]);
if (i < (clo - 1))
{
printf("|");
}
}
printf("\n");
if (j < row - 1)
{
for (i = 0; i < clo; i++)
{
printf("---");
if (i < (clo - 1))
{
printf("|");
}
}
}
printf("\n");
}
}
这个棋盘打印函数可以扩展。只要改变ROW 和 CLO 的值就可以改变棋盘大小。
例如: 7x7的棋盘
玩家下棋
void Pmove(char arr[ROW][CLO], int row, int clo)
{
printf("请输入坐标(空格隔开)\n");
int m = 0, n = 0;
while (1)
{
scanf_s("%d %d", &m, &n);
if (((m >= 1) && (m <= 3)) && ((n >= 1) && (n <= 3)))
{
if (arr[m - 1][n - 1] == ' ')
{
arr[m - 1][n - 1] = 'X';
break;
}
else
{
printf("这里可能已经有棋子了\n");
}
}
else
{
printf("可能你输错了\n");
}
}
}
这个是玩家输入函数。这里注意判断输入是否合法,输入范围1~3,并查看该点是否有棋子。考虑玩家因素,将获取的数字-1,使之成为数组变量。
判断输赢
int Iswin(char arr[ROW][CLO], int row, int clo)
{
//行
int i = 0;
for (i = 0; i < row; i++)
{
if (((arr[i][0] == arr[i][1]) && (arr[i][1] == arr[i][2])) && ((arr[i][0] != ' ')))
{
return 1;
}
}
//列
for (i = 0; i < clo; i++)
{
if ((arr[0][i] == arr[1][i]) && (arr[1][i] == arr[2][i]) && ((arr[0][i] != ' ') ))
{
return 1;
}
}
//叉
if ((arr[0][0] == arr[1][1]) && (arr[1][1] == arr[2][2]) && ((arr[0][0] != ' ') ))
{
return 1;
}
if ((arr[0][2] == arr[1][1]) && (arr[1][1] == arr[2][0]) && ((arr[0][2] != ' ')))
{
return 1;
}
return 0;
}
这个函数判断输赢。由于3行3列,所以用for循环扫描3次。当连续三字相同且不为‘ 空格 ’,那么取得胜利。
电脑下棋(核心)
void Cmove(char arr[ROW][CLO], int row, int clo)
{
int flag = 0;
int m = 0, n = 0;
for (m = 0; m < row; m++)
{
for (n = 0; n < row; n++)
{
if (arr[m][n] == ' ')
{
arr[m][n] = '0';
flag = Iswin(arr, row, clo);
if (flag == 0)
{
arr[m][n] = ' ';
}
if (flag == 1)
{
break;
}
}
}
if (flag == 1)
{
break;
}
}
if (flag == 0)
{
for (m = 0; m < row; m++)
{
for (n = 0; n < row; n++)
{
if (arr[m][n] == ' ')
{
arr[m][n] = 'X';
flag = Iswin(arr, row, clo);
if (flag == 0)
{
arr[m][n] = ' ';
}
if (flag == 1)
{
arr[m][n] = '0';
break;
}
}
}
if (flag == 1)
{
break;
}
}
if ((m == 3) && (n == 3))
{
while (1)
{
m = rand() % 3;
n = rand() % 3;
if (arr[m][n] == ' ')
{
arr[m][n] = '0';
break;
}
}
}
}
}
这是个电脑下棋函数。这里动了点歪脑筋。
第一个双层for循环的作用是:依次检测空位。如果是空位,那么先让该空位被’ 0 ‘填充,然后判断能不能获胜。能获胜就跳出 且 flag == 1,不能获胜就把改位再次置成空位。
接上述不能获胜。如果扫描完后发现并不能获胜,那么采取防守策略。
第二个双层for循环也是检测空位。如果是空位,那么让该位被 ’ X ‘填充,(相当电脑伪装成玩家先走一步)。填充后判断输赢,如果能够获胜,那么电脑再将这个位置改为‘ 0 ’,达到阻止玩家取胜的目的。
最后如果既不能取得胜利,也不用阻止玩家获胜。那么,就让电脑随机找位置下棋。
判断盘满
int Isfull(char arr[ROW][CLO], int row, int clo)
{
int i = 0, j = 0;
int count = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < clo; j++)
{
if (arr[i][j] != ' ')
{
count ++;
}
}
}
if (count == 9)
{
return 4;
}
return 3;
}
这个函数是判断棋盘是否满。使用count变量计数,扫描棋盘,如果不为‘ 空格 ’的位置有九个,那么就表示棋盘满了。
3.效果演示
会堵人的电脑
会优先自己胜利的电脑
棋盘满效果
PS:先手优势很大,不出意外的话是先手赢,或是平局。
依此类推,五子棋在15x15及其以上的棋盘,先手必胜。
但是电脑获胜算法非常复杂,反正我是写不出来。