c语言实现三子棋

时间:2023-01-11 12:07:57

前言:

在此之前我们学习了循环,函数,数组等相关知识,我们来写一个小游戏练练手

概述:

代码大致分为三部分程序主函数,函数,声明函数(这一点我们在通讯录项目是就介绍过了,将代码分为三部分可以是代码各司其职,不冗余,可读性高),游戏框架大概分为以下几部分,主函数main,菜单函数,初始化棋盘,打印棋盘,玩家走函数,电脑走函数,判断输赢函数,所以我们也会按照这几个部分来讲解,先给大家演示一下,我们是通过输入坐标的形式来落子

c语言实现三子棋

程序主函数(course-8.c)

main程序入口

我们先搭建好程序的主体部分,然后在慢慢添加相应功能,这里进入主函数然后调用test函数

int main()
{
test();
return 0;
}

test函数

在test函数中我们可以加入菜单和根据用户输入判断出对应的不同提示语句,

srand((unsigned int)time(NULL));这句代码我们在猜数字游戏是就介绍过,他是用来生成随机数的(等函数部分涉及到我们在讲解),先说结构,利用一个do...while循环嵌套switch....case语句,根据用户输入判断出相应的功能(这里说明一点将0作为退出结束,是因为0在c语言中可以作为假,在进行while(0)即可跳出do...while循环)

void test()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("游戏开始\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
}

menu函数

void menu()
{
printf("********************************\n");
printf("****** 1 .play 0.exit ******\n");
printf("********************************\n");
}

game函数

在game中涉及到初始化棋盘,打印棋盘,玩家走函数,电脑走函数,判断输赢函数的调用,首先进入函数设置棋盘信息,然后初始化棋盘(目的是将棋盘置空),然后是打印棋盘,我们来探讨一下重点,就是判断输赢一共有四种状态   

*代表玩家赢

#代表电脑赢

q代表平局

c代表继续

通过调用函数iswin得到的返回值来判断是谁赢还是继续或者平局,判断机制是玩家或者电脑每下一步棋就进行一次判断,while循环是为了让玩家和电脑循环下棋,如果得到的返回值不得c了,即游戏不在继续,利用break跳出循环,根据返回值判断出对弈结果

void game()
{
char ret = 0;
char board[ROW][COL] = { 0 };//数组存放走出的棋盘信息
initboard(board, ROW, COL);//初始化棋盘
displayboard(board,ROW,COL);//打印棋盘
while (1)
{
//玩家下棋
playermove(board, ROW, COL);
displayboard(board, ROW, COL);//打印棋盘
//判断玩家是否赢
ret=iswin(board,ROW,COL);
if (ret != 'c')
{
break;
}
//电脑下棋
computermove(board, ROW, COL);
displayboard(board, ROW, COL);//打印棋盘
//判断电脑是否赢
ret=iswin(board, ROW, COL);
if (ret != 'c')
{
break;
}
}
if (ret == '*')
printf("玩家赢\n");
else if (ret == '#')
printf("电脑赢\n");
else
printf("平局\n");
}

程序的头文件即函数声明(game.h)

函数声明

这里特别声明一点define宏定义,之前我们没有把棋盘的row和col写死,而是利用一个变量,这里的变量就利用了宏定义,(我们单独将函数的声明写在一个文件里是因为可以增强函数的可读性)这样只需要在主函数文件(course-8.c)的顶部引用#include "game.h"即可

#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 displayboard(char board[ROW][COL], int rw, int col);
void playermove(char board[ROW][COL], int row, int col);
void computermove(char board[ROW][COL], int row, int col);
char iswin(char board[ROW][COL], int row, int col);

函数部分(game.c)

我们按照顺序依次介绍

棋盘初始化initboard

利用两个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] = ' ';//给棋盘初始化成空格
}
}
}

打印棋盘displayboard

首先展示一下我们棋盘的样子,他是由sanbufenzucheng,数据,| ,___  ,

c语言实现三子棋

我们是按行打印,首先我们利用for循环的嵌套先打印第一行数据,因为 “ | ”一行只需要两个所以if(j < col - 1),打印一行数据后换行,接着打印第二行也就是分割线" ___ ",(这里提示row是控制行,而col则是控制列)

void displayboard(char board[ROW][COL], int row, int col)//打印棋盘优化函数
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;

////////////1.打印一行数据
for (j = 0; j < col; j++)
{
printf(" %c ",board[i][j]);
if(j < col - 1)
printf("|");
}
printf("\n");

/////////////2.打印分割行
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}
}

玩家落子playermove

玩家走起的逻辑也很简单,就一个循环,之所以使用循环是因为玩家可能下的坐标不合法,所以首先我们使用一个while循环,让玩家落子,直接判断其坐标是否合法if (x >= 1 && x <= row && y >= 1 && y <= col),因为棋子要落在棋盘内且为空,如果条件满足则合法可以落子,否则循环回去重新下,如下,由于我们的数组是从0开始计数的但是玩家不知道,所以在玩家下完棋后我们需要将坐标减1才行

c语言实现三子棋

void playermove(char board[ROW][COL], int row, int col)//玩家走函数
{
int x = 0;
int y = 0;
printf("玩家走\n");
while (1)
{
printf("请输入要下的坐标:");
scanf("%d%d", &x, &y);
//判断坐标合法性
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("该坐标已被占用\n");
}
}
else
{
printf("该坐标非法,请重新输入\n");
}
}
}

c语言实现三子棋

电脑落子computermove

电脑落子和玩家落子逻辑其实差不多,要坐标合法,重点就是生成随机数的问题,我们利用rand函数生成随机数(rand需要srand函数的调用,俄日srand又需要时间戳的来生成随机数列,即srand((unsigned int)time(NULL));),通过处理rand() % row;则可以是随机数达到我们想要的范围,rand()%n,范围肯定是0~n-1即0-2,这里可能就有小伙伴问了,不是需要1-3的数嘛,大家仔细想一想,我们的数组是从0开始的

void computermove(char board[ROW][COL], int row, int col)//电脑走函数
{
int x = 0;
int y = 0;
printf("电脑走\n");
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}

c语言实现三子棋

判断棋盘满isfull

如果棋盘满了还没分出胜负,那就是平局呗,返回值0代表没满,1代表满了,我们遍历棋盘但凡是碰到一个空就是没满返回0,否则返回1

//判断棋盘满没满
//返回1满了
//返回0没满
int isfull(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;//没满
}
}
return 1;//满了
}

判断输赢iswin

判断输赢是整个游戏的核心,也是代码最复杂的,大家要多动脑思考其中的逻辑,首先我们进入函数,先判断行存不存在赢棋,我们利用for循环一行一行的判断,根据画图总结一行只需要判断两次即可确定,定义一个判断返回标志为int count = 0;//用来记录两次判断的结果(因为一行要判断两次),因为一行判断两次所以col-1,例如第一行 第一次00=01&&00!=‘ ’第二次01=02&&01!= ‘ ’,一行判断两次,如果count值为2则证明此行数据相同,则返回本行的第一个数据在主函数中的game函数中根据相关逻辑给出相应提示并结束游戏,判断列,和判断行相同原理,只是行列颠倒

c语言实现三子棋

接下来就是判断斜行,左斜行,count一样用来记录两次判断的结果,即00=11&&00!='' 和 11=22&&11!=‘’,此时的for循环就有需要两个变量了,做到同增同减

c语言实现三子棋

右斜行判断 02=11&&02!='' 和 11=20&&11!=‘’

c语言实现三子棋

如果,行列斜行都判断过了均没有返回,就剩下两种可能,平局或者游戏继续

棋盘满了则平局,否则游戏继续

char iswin(char board[ROW][COL], int row, int col)//判断输赢    
{
int i = 0;

//判断行
for (i = 0; i < row; i++)
{
int count = 0;//用来记录两次判断的结果(因为一行要判断两次)
int j = 0;
for (j = 0; j < col-1; j++)//一行判断两次所以col-1,例如第一行 第一次00=01&&00!=‘’第二次01=02&&01!=‘’
{
if (board[i][j] == board[i][j+ 1] && board[i][j] != ' ')
{
count++;//符合条件count++
}
}
if (count == col - 1)//一行判断两次,如果count值为2则证明此行数据相同
{
return board[i][0];//返回本行的第一个数据
}
}
//判断列,和判断行相同原理,只是行列颠倒
for (i = 0; i < col; i++)
{
int count = 0;
int j = 0;
for (j = 0; j < row-1; j++)
{
if (board[j][i] == board[j+1][i] && board[j][i] != ' ')
{
count++;
}
}
if (count == row - 1)
{
return board[0][i];
}
}

//判断两斜行
//左斜行
int j = 0;
int count = 0;//用来记录两次判断的结果
for (i = 0,j=0; i < row-1; i++,j++)//00=11&&00!='' 和 11=22&&11!=‘’
{

if (board[i][j] == board[i + 1][j + 1] && board[i][j] != ' ')
{
count++;
}
if (count == row-1)
{
return board[i][j];
}
}

//右斜行
int counts = 0;
for (i = 0, j = row - 1; i < row - 1; i++, j--)//02=11&&02!='' 和 11=20&&11!=‘’
{

if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' ')
{
counts++;
}

if (counts == row - 1)
{
return board[i][j];
}
}

//平局
if (isfull(board, ROW, COL) == 1)
{
return 'q';//平局
}


return 'c'; //继续

}

全部代码

主函数文件course-8.c

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include "game.h"

//三子棋游戏
void game()
{
char ret = 0;
char board[ROW][COL] = { 0 };//数组存放走出的棋盘信息
initboard(board, ROW, COL);//初始化棋盘
displayboard(board,ROW,COL);//打印棋盘
while (1)
{
//玩家下棋
playermove(board, ROW, COL);
displayboard(board, ROW, COL);//打印棋盘
//判断玩家是否赢
ret=iswin(board,ROW,COL);
if (ret != 'c')
{
break;
}
//电脑下棋
computermove(board, ROW, COL);
displayboard(board, ROW, COL);//打印棋盘
//判断电脑是否赢
ret=iswin(board, ROW, COL);
if (ret != 'c')
{
break;
}
}
if (ret == '*')
printf("玩家赢\n");
else if (ret == '#')
printf("电脑赢\n");
else
printf("平局\n");
}
void menu()
{
printf("********************************\n");
printf("****** 1 .play 0.exit ******\n");
printf("********************************\n");
}
void test()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("游戏开始\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}

函数声明game.h

#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 displayboard(char board[ROW][COL], int rw, int col);
void playermove(char board[ROW][COL], int row, int col);
void computermove(char board[ROW][COL], int row, int col);
char iswin(char board[ROW][COL], int row, int col);

函数部分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 displayboard(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"); //打印分割行
// }
// }
//}


void displayboard(char board[ROW][COL], int row, int col)//打印棋盘优化函数
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;

////////////1.打印一行数据
for (j = 0; j < col; j++)
{
printf(" %c ",board[i][j]);
if(j < col - 1)
printf("|");
}
printf("\n");

/////////////2.打印分割行
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}
}
// | |
//---|---|---
// | |
//---|---|---
// | |

void playermove(char board[ROW][COL], int row, int col)//玩家走函数
{
int x = 0;
int y = 0;
printf("玩家走\n");
while (1)
{
printf("请输入要下的坐标:");
scanf("%d%d", &x, &y);
//判断坐标合法性
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
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)//电脑走函数
{
int x = 0;
int y = 0;
printf("电脑走\n");
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
//判断棋盘满没满
//返回1满了
//返回0没满
int isfull(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;//没满
}
}
return 1;//满了
}
//一共有四种状态
//*代表玩家赢
//#代表电脑赢
//q代表平局
//c代表继续
//char iswin(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][1] == board[i][2] && board[i][1] != ' ')
// {
// return board[i][1];
// }
// }
// //判断竖三列
// for (i = 0; i < col; i++)
// {
// if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
// {
// return board[1][i];
// }
// }
// //判断两斜行
// if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
// return board[1][1];
// if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
// return board[1][1];
// if (isfull(board, ROW, COL) == 1)
// {
// return 'q';//平局
// }
// return 'c'; //继续
//}


char iswin(char board[ROW][COL], int row, int col)//判断输赢 自己写的优化代码
{
int i = 0;

//判断行
for (i = 0; i < row; i++)
{
int count = 0;//用来记录两次判断的结果(因为一行要判断两次)
int j = 0;
for (j = 0; j < col-1; j++)//一行判断两次所以col-1,例如第一行 第一次00=01&&00!=‘’第二次01=02&&01!=‘’
{
if (board[i][j] == board[i][j+ 1] && board[i][j] != ' ')
{
count++;//符合条件count++
}
}
if (count == col - 1)//一行判断两次,如果count值为2则证明此行数据相同
{
return board[i][0];//返回本行的第一个数据
}
}
//判断列,和判断行相同原理,只是行列颠倒
for (i = 0; i < col; i++)
{
int count = 0;
int j = 0;
for (j = 0; j < row-1; j++)
{
if (board[j][i] == board[j+1][i] && board[j][i] != ' ')
{
count++;
}
}
if (count == row - 1)
{
return board[0][i];
}
}

//判断两斜行
//左斜行
int j = 0;
int count = 0;//用来记录两次判断的结果
for (i = 0,j=0; i < row-1; i++,j++)//00=11&&00!='' 和 11=22&&11!=‘’
{

if (board[i][j] == board[i + 1][j + 1] && board[i][j] != ' ')
{
count++;
}
if (count == row-1)
{
return board[i][j];
}
}

//右斜行
int counts = 0;
for (i = 0, j = row - 1; i < row - 1; i++, j--)//02=11&&02!='' 和 11=20&&11!=‘’
{

if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' ')
{
counts++;
}

if (counts == row - 1)
{
return board[i][j];
}
}

//平局
if (isfull(board, ROW, COL) == 1)
{
return 'q';//平局
}


return 'c'; //继续

}

留言:

欢迎评论区交流哦