三子棋 N*N(棋盘) 实现
思路
1.棋盘的初始化,二维数组的初始化使每个位置都为空格
2.棋盘的打印,这是一个比较难以理解的,最快的理解方法就是调试一遍
3.下棋,这分为玩家落子和电脑落子,玩家落子需考虑数组的下标是从0开始的,电脑落子都是随机数需考虑随机数的范围。
4.判断输赢,这是实现N*N棋盘的三子棋当中是最难的。你必须找到每条对角线与二维数组的规则进行判断(可以先是4 * 4试验一下)
目录
- 1.分文件编写
- 2.各个函数的实现
- 3.总结
1.分文件编写
创建一个项目
一个头文件写的是函数的声明,宏定义,库函数的包含等
一个源文件,源文件需要引用自定义的头文件自定义的引用方式为#include“game.h”
一个主体源文件文件test.c或其他名字也可以
1.1头文件game.h界面
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<time.h> //时间随机数
#include<windows.h> //黑框界面操作
#define ROW 3 //行数 宏定义
#define COL 3 //列数 宏定义
//初始化棋盘
void InitBorad(char borad[][COL],int row,int col);
//打印棋盘
void DisplayBorad(char borad[][COL], int row, int col);
//玩家回合
void Player_play(char borad[][COL], int row, int col);
//电脑回合
void Computer_play(char borad[][COL], int row, int col);
//判断棋盘是否满
int IsFull(char borad[][COL], int row, int col);
//判断输赢
char IsWin(char borad[][COL], int row, int col);
1.2game.c界面
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
//初始化棋盘
void InitBorad(char borad[][COL],int row,int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
borad[i][j] = ' ';//将棋盘的每个位置都设置为空格。方便落子和打印
}
}
}
//打印棋盘
void DisplayBorad(char borad[][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf(" %c ",borad[i][j]);//打印数据
if(j<col-1)
printf("|");
}
printf("\n");
if (i < row - 1)//打印分割线
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}
}
//玩家回合
void Player_play(char borad[][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("请输入对应棋盘位置的坐标\n");
printf("玩家下棋:>");
scanf("%d%d",&x,&y);
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
if (borad[x - 1][y - 1] == ' ')
{
borad[x - 1][y - 1] = '*';
break;
}
else
{
printf("该位置已被占用,重新输入\n");
}
}
else
{
printf("超出棋盘范围,重新输入\n");
}
}
}
//电脑回合
void Computer_play(char borad[][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋:>\n");
while (1)
{
x = rand() % row;
y = rand() % col;
if (borad[x][y] == ' ')
{
borad[x][y] = '#';
break;
}
/*else
printf("该位置已被占用");*/
}
}
//判断棋盘是否满
int IsFull(char borad[][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
if (borad[i][j] == ' ')//棋盘中还有位置是空的则表示 没有满
return 0;
}
}
return 1;//没有空的则表示满了 返回 1
}
/* * 表示 玩家赢了
# 表示 电脑赢了
Q 表示 平局
C 表示 继续
*/
//判断输赢
char IsWin(char borad[][COL], int row, int col)
{
int i = 0;
//行
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 2; j < col; j++)
{
if (borad[i][j - 2] == borad[i][j-1] && borad[i][j-1] == borad[i][j] && borad[i][j]!=' ')
{
return borad[i][j];
}
}
}
//列
for (i = 0; i < col; i++)
{
int j = 0;
for (j = 2; j < row; j++)
{
if (borad[j][i] == borad[j - 1][i] && borad[j - 1][i] == borad[j - 2][i]&&borad[j][i] != ' ')
{
return borad[j][i];
}
}
}
//副对角线
for (i = 2; i < row; i++)// 这样的/ 斜向右上
{
int j = 0;
for (j = 2; j < col; j++)
{
if (borad[i - 2][j] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j - 2] && borad[i][j-2] != ' ')
{
//巨大问题: borad[][]!=''要与前面的三个有关系
return borad[i][j-2]; //返回值应该是相等中的一个
}
}
}
//主对角线
for (i = 2; i < row; i++)// \这样的斜向右下
{
int j = 0;
for (j = 2; j < col; j++)
{
if (borad[i - 2][j - 2] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j] && borad[i][j] != ' ')
{
return borad[i][j];
}
}
}
if (IsFull(borad, row, col) == 1)
{
return 'Q';
}
return 'C';
}
1.3test.c界面
#include"game.h"
//菜单
void Show_Menu()
{
printf("********************\n");
printf("******1.play********\n");
printf("******0.退出********\n");
printf("********************\n");
}
//游戏整个过程
void Game()
{
printf("三子棋游戏\n");
char borad[ROW][COL] = { 0 };
srand((unsigned)time(NULL));//随系统时间变化的随机数
InitBorad(borad, ROW, COL);
DisplayBorad(borad, ROW, COL);
char ret;//获取IsWin()函数的返回值
while (1)//循环下棋操作 直到有一方赢或平局
{
Player_play(borad, ROW, COL);
DisplayBorad(borad, ROW, COL);
ret = IsWin(borad, ROW, COL);
if (ret != 'C')
{
break;
}
Computer_play(borad, ROW, COL);
DisplayBorad(borad, ROW, COL);
ret = IsWin(borad, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == '*')
printf("玩家获胜\n");
else if (ret == '#')
printf("电脑获胜\n");
else
printf("平局\n");
printf("3s之后返回主界面\n");
Sleep(3000);//界面停留3秒
system("cls");//清屏
}
int main()
{
int chioce = 0;
do
{
Show_Menu();
printf("请输入您的选择:>");
scanf("%d",&chioce);
if (chioce == 1)
{
system("cls");
Game();
}
} while (chioce);
return 0;
}
2.各个函数的实现
初始化棋盘void InitBorad(char borad[][COL],int row,int col)
void InitBorad(char borad[][COL],int row,int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
borad[i][j] = ' ';//将棋盘的每个位置都设置为空格。方便落子和打印
}
}
}
整个棋盘就相当与一个二维数组 只是数组遍历是从下标为零开始的
打印棋盘void DisplayBorad(char borad[][COL], int row, int col)
该函数的实现是将整个棋盘的大概模样显示出来
void DisplayBorad(char borad[][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf(" %c ",borad[i][j]);//打印二维数组“ %c ”数据两边留有空格
if(j<col-1)
printf("|");
}
printf("\n");
if (i < row - 1)//打印分割线
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");//与上面的 | 和打印一起出来的 | 容易搞混 建议调试查看打印过程便于理解
}
printf("\n");
}
}
}
打印棋盘的整个过程很多地方难以理解建议F10调试看起打印过程
玩家回合void Player_play(char borad[][COL], int row, int col)
实现的注意事项
1. 常规的棋盘起始坐标都是从1开始,但数组的下标是从0开始的所以实现时需 为x-1,y-1 的形式才符合。列如坐标为1,1,但在二维数组中为borad[0][0];所以要将输入的x和y坐标都-1才符合数组的规则。
2.输入的x,y两个坐标必须在所定义的棋盘范围之内。
3.下的位置为占用,需重新输入。
整个过程为循环过程,玩家落子了才结束循环,到电脑的回合
void Player_play(char borad[][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("请输入对应棋盘位置的坐标\n");
printf("玩家下棋:>");
scanf("%d%d",&x,&y);
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
if (borad[x - 1][y - 1] == ' ')
{
borad[x - 1][y - 1] = '*';
break;
}
else
{
printf("该位置已被占用,重新输入\n");
}
}
else
{
printf("超出棋盘范围,重新输入\n");//超过最大棋盘范围
}
}
}
电脑回合void Computer_play(char borad[][COL], int row, int col)
电脑落子的实现
1.随机数的获取
直接使用rand()获取的是伪随机数,需有test.c文件中的srand((unsigned)time(NULL)); 所获得的随机数便是随系统时间变化的随机数
需包含头文件#include<time.h>
2.循环
循环获取符合没有被占用的随机数的x,y坐标
注 因为随机数 范围为0~row-1 和 0~col-1 所以赋给数组时不用-1
void Computer_play(char borad[][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋:>\n");
while (1)
{
x = rand() % row;//随机数 范围为0~row-1
y = rand() % col;//随机数 范围为0~col-1
if (borad[x][y] == ' ')
{
borad[x][y] = '#';
break;
}
/*else
printf("该位置已被占用");*/
}
}
判断赢方char IsWin(char borad[][COL], int row, int col)
N*N 棋盘 赢方判断
1.行、列成三子连线的实现
只有3 * 3以上的棋盘才能实现三子连线
遍历行时 行不变,列 变
行实现三子连线条件 举列
0行
第0列有 *或 # 第1列有 *或 # 第2列有 *或 #
数组表示:
borad[0][0] == borad[0][1] && borad[0][1] == borad[0][2] && borad[0][2] !=’ ';
第1列有 *或 # 第2列有 *或 # 第3列有 *或 #
数组表示:
borad[0][1] == borad[0][2] && borad[0][2] == borad[0][3] && borad[0][3] !=’ ';
类推
第2列有 *或 # 第2列有 *或 # 第3列有 *或 #
数组表示:
borad[0][2] == borad[0][3] && borad[0][3] == borad[0][4] && borad[0][4] !=’ ';
…
…
列与行类似就是 列 不变 行变 此处就不举例了
int i = 0;
//行
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 2; j < col; j++)
{
if (borad[i][j - 2] == borad[i][j-1] && borad[i][j-1] == borad[i][j] && borad[i][j]!=' ')
{
return borad[i][j];
}
}
}
//列
for (i = 0; i < col; i++)
{
int j = 0;
for (j = 2; j < row; j++)
{
if (borad[j][i] == borad[j - 1][i] && borad[j - 1][i] == borad[j - 2][i]&&borad[j][i] != ' ')
{
return borad[j][i];
}
}
}
2.主对角线与副对角线
主队角线遍历规则
//主对角线
for (i = 2; i < row; i++)// \这样的斜向右下
{
int j = 0;
for (j = 2; j < col; j++)
{
if (borad[i - 2][j - 2] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j] && borad[i][j] != ' ')
{
return borad[i][j];
}
}
}
4*4棋盘举例
5 * 5或是6 * 6都符合如下规则
borad[i - 2][j - 2] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j] && borad[i][j] != ’ ’
副对角线遍历规则
//副对角线
for (i = 2; i < row; i++)// 这样的/ 斜向右上
{
int j = 0;
for (j = 2; j < col; j++)
{
if (borad[i - 2][j] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j - 2] && borad[i][j-2] != ' ')
{
//巨大问题: borad[][]!=''要与前面的三个有关系
return borad[i][j-2]; //返回值应该是相等中的一个
}
}
}
4*4棋盘举例
规则
borad[i - 2][j] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j - 2] && borad[i][j-2] != ’ ’
*表示 玩家赢了
#表示 电脑赢了
Q 表示 平局 棋盘被下满了就平局了 添加一个判断棋盘是否满的函数int Is Full() 返回值为int
C 表示 继续
*和#两个符号作为返回值的好处
因为 *表示玩家落子 # 表示电脑落子 直接返回所落子成为三子连线的某个位置的值即可 并且不需要繁琐的判断等
char IsWin(char borad[][COL], int row, int col)
{
int i = 0;
//行
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 2; j < col; j++)
{
if (borad[i][j - 2] == borad[i][j-1] && borad[i][j-1] == borad[i][j] && borad[i][j]!=' ')
{
return borad[i][j];
}
}
}
//列
for (i = 0; i < col; i++)
{
int j = 0;
for (j = 2; j < row; j++)
{
if (borad[j][i] == borad[j - 1][i] && borad[j - 1][i] == borad[j - 2][i]&&borad[j][i] != ' ')
{
return borad[j][i];
}
}
}
//副对角线
for (i = 2; i < row; i++)// 这样的/ 斜向右上
{
int j = 0;
for (j = 2; j < col; j++)
{
if (borad[i - 2][j] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j - 2] && borad[i][j-2] != ' ')
{
//巨大问题: borad[][]!=''要与前面的三个有关系
return borad[i][j-2]; //返回值应该是相等中的一个
}
}
}
//主对角线
for (i = 2; i < row; i++)// \这样的斜向右下
{
int j = 0;
for (j = 2; j < col; j++)
{
if (borad[i - 2][j - 2] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j] && borad[i][j] != ' ')
{
return borad[i][j];
}
}
}
if (IsFull(borad, row, col) == 1)
{
return 'Q';
}
return 'C';
}
3.总结
1.判断输赢中的主副对角线若是棋盘太大,时间复杂度会很大,应该还有其他更好解决方法
2.可以增加实现玩家与玩家对战的功能