三子棋以及N子棋的实现
- 初始化棋盘
- 打印棋盘
- 玩家下棋
- 电脑下棋
- 判断输赢
- 主函数的实现(test.c)
- game.c的实现
- game.h的实现
铁汁们~今天给大家分享一篇三子棋以及N子棋的实现,来吧,开造⛳️
实现流程:
1.游戏不退出,继续玩下一把(循环);
2.应用多文件的形式写代码(玩游戏实现过程),包含初始化棋盘、打印棋盘、玩家下棋、电脑下棋、判断输赢操作。
初始化棋盘
思路:初始化棋盘为空格
需要遍历棋盘(二维数组),采用双重for循环来分别控制行、列。
void InitBoard(char board[Row][Col], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
打印棋盘
版本1:
缺点:因为初始化棋盘为空格,直接遍历棋盘,打印棋盘,全为空格(空格在屏幕上不显示)。
void print(char board[Row][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", board[i][j]);
}
printf("\n");
}
}
版本2:
缺点:若要实现多子棋,该版本不能完成,只适用于三子棋的打印(受限制)。
void print(char board[Row][Col], int row, int col)
{
int i = 0;
for(i = 0; i < row; i++)
{
printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);//打印数据
if (i < row - 1)
printf("---|---|---\n");//打印分割线
}
}
正确的版本(版本三):
思路:用双层for循环来遍历二维数组(共使用三个for循环),第一层for循环来控制’打印数据行加分割行’总次数,第二个for循环来控制数据行的打印(每次都打印‘空格数据空格|’),第三个for循环来控制打印分割行的打印(每次打印’—|')。
void print(char board[Row][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 ", board[i][j]);
if (j < col - 1) //前提条件
{
printf("|");
}
}
printf("\n"); //注意
if (i < col - 1) //前提条件
{
j = 0; //打印分割线
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
玩家下棋
思路:输入坐标,判断下标是否合法,不合法,重新输入,合法时还需判断该坐标处是否已经被落子。
void PlayMove(char board[Row][Col], int row, int col)
{
printf("玩家输入\n");
int x = 0;
int y = 0;
while (1) //玩家要进行多次下棋(循环)
{
printf("请输入下棋的坐标,中间以空格隔开>:");
scanf("%d %d", &x, &y);
if (x <= row && y <= col && x >= 1 && y >= 1) //判断下标是否合法
{ //合法
if (board[x - 1][y - 1] ==' ') //判断是否能落子
{ //能落子
board[x - 1][y - 1] = '*';
break;
}
else //不能落子
{
printf("坐标被占用,不能落子,请重新输入\n");
}
}
else //下标不合法
{
printf("坐标非法,请重新输入\n");
}
}
}
注意:0<x<=row,0<y<=col,x,y不可以等于0,因为大部分玩家并不是程序员,只有程序员才知道下标是从0开始的,为了符合大众化。
电脑下棋
思路:电脑产生随机坐标值,在判断坐标值是否合法,不合法无需做任何操作。
void ComputerMove(char board[Row][Col], int row, int col)
{
printf("电脑输入\n");
int x = 0;
int y = 0;
while (1)
{
x = rand() % 3; //rand函数产生随机数,rand()%3产生的数值范围为0~2,需要调用srand函数
y = rand() % 3;
if (board[x][y] == ' ') //判断下标是否合法
{ //合法
board[x][y] = '#';
break;
}
}
}
判断输赢
情况种类:四种情况:玩家赢,返回’*‘、 电脑赢,返回’#‘、 平局(电脑玩家均没赢,但棋盘已满),返回‘q’、 继续(电脑玩家均没赢,但棋盘未满),返回’c’.
三子棋判断输赢思路:分别进行行、列、正对角线以及负对角线的遍历,从而判断输赢。
正对角线i和j相同、负对角线i+j=row-1。
char Winner(char board[Row][Col], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++) //行
{
if (board[i][0] == board[i][1] && board[i][2] == board[i][1] && board[i][0] != ' ')
return board[i][0];
}
i = 0;
for (i = 0; i < col; i++) //列
{
if (board[0][i] == board[1][i] && board[2][i] == board[1][i] && board[0][i] != ' ')
return board[0][i];
}
if (board[0][0] == board[1][1] && board[0][0] == board[2][2]&& board[0][0] != ' ') //正对角线
return board[0][0];
if (board[0][2] == board[1][1] && board[0][2] == board[2][0]&& board[0][2] != ' ') //负对角线
return board[0][2];
if (isfull(board, row, col)) //平局
{
return 'q';
}
else //继续
return 'c';
}
多子棋判断输赢思路:用双层循环遍历二维数组:处于对角线上所有元素相等时,相邻两元素相等的个数等于行数-1。
1.行的判断:
第一层for循环来控制行数,第二层for循环来控制列数(循环变量<列数-1),每一行前一个元素与后一个元素进行比较,若在一行中有一对元素不相等,就跳出内层循环,出内层for循环的两种情况(已经‘有一行中各元素都相等且都不等于空格’或‘一行中各元素不相等’),而对于‘有一行中各元素都相等且都不等于空格’此情况,此时循环变量等于列数-1,直接返回该行中的最后一个元素。
2.列的判断:
第一层for循环来控制列数,第二层for循环来控制行数(循环变量<行数-1) ,每一列前一个元素与后一个元素进行比较,若在一列中有一对元素不相等,就跳出内层循环,出内层for循环的两种情况(已经‘有一列中各元素都相等且都不等于空格’或‘一列中各元素不相等’),而对于‘有一列中各元素都相等且都不等于空格’此情况,此时循环变量等于行数-1,直接返回该列中的最后一个元素。
3.正对角线的判断:
由观察可知,在正方体中处于正对角线的元素行数和列数相等,此时只需要行数和列数分别都加一就可以找到下一个元素,从而来遍历正对角线,前一个元素与后一个元素进行比较,创建一个计数器来记录相邻两元素相等的个数,每次两元素相等个数就加一,由于当正对角线上所有元素相等且不等于空格时,相邻两元素相等的个数等于行数-1,当出了循环只需判断计数器的个数是否等于行数-1且不等于空格,相等就直接返回正对角线上任意元素。
4.负对角线的判断:
由观察可知,在正方体中处于负对角线的元素行数加列数相等行数-1,此时只需要行数+1、列数-1就可以找到下一个元素,从而来遍历负对角线,前一个元素与后一个元素进行比较,创建一个计数器来记录相邻两元素相等的个数,每次两元素相等个数就加一,由于当负对角线上所有元素相等且不等于空格时,相邻两元素相等的个数等于行数-1,当出了循环只需判断计数器的个数是否等于行数-1且不等于空格,相等就直接返回正对角线上任意元素。
char Winner(char board[Row][Col], int row, int col)
{
int i = 0;//行
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col - 1; j++)
{
if (board[i][j] != board[i][j + 1])//若一行中有一个不相等就跳出一行的循环
break;
}
if (j == col - 1 && board[i][j] != ' ')//相等但不能为空格
return board[i][j];
}
i = 0;//列
for (i = 0; i < col; i++)
{
int j = 0;
for (j = 0; j < row - 1; j++)
{
if (board[j][i] != board[j + 1][i])//若一列中有一个不相等就跳出一行的循环
break;
}
if (j == row - 1 && board[j][i] != ' ')
return board[j][i];
}
int count = 0;//用来记录对角线上相邻元素相等的个数
i = 0;
for (i = 0; i < row-1; i++)//对角线(\)
{
if (board[i][i] == board[i + 1][i + 1]&&board[i][i]!=' ')
{
count++;
}
if (count == row - 1)
return board[i][i];
}
i = 0;//对角线(/)
for (i = 0; i < row-1; i++)
{
int j = 0;
for (j =col-1; j>=1;j--)
{
if (i + j ==row - 1)
{
if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' ')
{
count++;
}
if (count == row - 1)
return board[i][j];
}
}
}
主函数的实现(test.c)
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menue() //菜单栏
{
printf("****************\n");
printf("**** 1.play ****\n");
printf("**** 0.exit ****\n");
printf("****************\n");
}
void game()
{
char board[Row][Col] = { 0 };//因在玩游戏过程中要进行数据的存储,采用Row*Col的二维数组进行存储有效数据
InitBoard(board, Row, Col);
char ret = 0;
while (1)
{
PlayMove(board, Row, Col); //玩家下棋
print(board, Row, Col); //打印棋盘
ret = Winner(board, Row, Col); //判断输赢
if (ret != 'c')
break;
ComputerMove(board, Row, Col); //电脑下棋
print(board, Row, Col); //打印棋盘
ret = Winner(board, Row, Col); //判断输赢
if (ret != 'c')
break;
}
if (ret == '*')
printf("玩家赢\n");
else if (ret == '#')
printf("电脑赢\n");
else
printf("平局\n");
}
int main()
{
srand((unsigned int)time(NULL)); //产生随机数时函数的调用(rand->srand->time),srand函数调用#include<stdlib.h>头文件,time函数调用#include<time.h>头文件
int input = 0;
do //先进行菜单栏的选择,若玩完一把还想在玩->循环(do..while循环体执行次数比条件判断次数多一)
{
menue();
printf("请选择<:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
game.c的实现
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void InitBoard(char board[Row][Col], int row, int col)//初始化棋盘为空格
{
int i = 0;
for (i = 0; i < row; i++)//遍历二维数组
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
void print(char board[Row][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 ", board[i][j]);
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
if (i < col - 1) //打印分割线
{
j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
void PlayMove(char board[Row][Col], int row, int col) //玩家下棋
{
printf("玩家输入\n");
int x = 0;
int y = 0;
while (1)
{
printf("请输入下棋的坐标,中间以空格隔开>:");
scanf("%d %d", &x, &y);
if (x <= row && y <= col && x >= 1 && y >= 1) //判断下标是否合法
{
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 = 0;
int y = 0;
while (1)
{
x = rand() % 3; //产生随机坐标(合法)
y = rand() % 3;
if (board[x][y] == ' ') //坐标为被占用
{
board[x][y] = '#';
break;
}
}
}
int isfull(char board[Row][Col], int row, int col) //判断棋盘是否满了
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ') //没满
return 0;
}
}
return 1; //满了
}
char Winner(char board[Row][Col], int row, int col) //判断输赢
{ //多子棋的判断
int i = 0;//行
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col - 1; j++)
{
if (board[i][j] != board[i][j + 1])//若一行中有一个不相等就跳出一行的循环
break;
}
if (j == col - 1 && board[i][j] != ' ')//相等但不能为空格
return board[i][j];
}
i = 0;//列
for (i = 0; i < col; i++)
{
int j = 0;
for (j = 0; j < row - 1; j++)
{
if (board[j][i] != board[j + 1][i])//若一列中有一个不相等就跳出一行的循环
break;
}
if (j == row - 1 && board[j][i] != ' ')
return board[j][i];
}
int count = 0;//用来记录对角线上相邻元素相等的个数
i = 0;
for (i = 0; i < row-1; i++)//对角线(\)
{
if (board[i][i] == board[i + 1][i + 1]&&board[i][i]!=' ')
{
count++;
}
if (count == row - 1)
return board[i][i];
}
i = 0;//对角线(/)
for (i = 0; i < row-1; i++)
{
int j = 0;
for (j =col-1; j>=1;j--)
{
if (i + j ==row - 1)
{
if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' ')
{
count++;
}
if (count == row - 1)
return board[i][j];
}
}
}
/*for (i = 0; i < row; i++) //三子棋的判断
{
if (board[i][0] == board[i][1] && board[i][2] == board[i][1] && board[i][0] != ' ')
return board[i][0];
}
i = 0;
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[2][i] == board[1][i] && board[0][i] != ' ')
return board[0][i];
}
if (board[0][0] == board[1][1] && board[0][0] == board[2][2]&& board[0][0] != ' ')
return board[0][0];
if (board[0][2] == board[1][1] && board[0][2] == board[2][0]&& board[0][2] != ' ')
return board[0][2];
*/
if (isfull(board, row, col)) //平局的判断
{
return 'q';
}
return 'c';
}
game.h的实现
作用:用于存放声明自定义函数
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#define Row 3 //便于实现多子棋时,行、列的更改
#define Col 3
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void InitBoard(char board[Row][Col], int row, int col);
void print(char board[Row][Col], int row, int col);
void PlayMove(char board[Row][Col], int row, int col);
void ComputerMove(char board[Row][Col], int row, int col);
char Winner(char board[Row][Col], int row, int col);
int isfull(char board[Row][Col], int row, int col);
运行结果
铁铁们,分支语句和循环语句就到此结束啦,请动动你们的手给作者点个👍鼓励吧,你们的鼓励就是我的动力✨✨✨