- 魔王的介绍:😶🌫️一名双非本科大一小白。
- 魔王的目标:🤯努力赶上周围卷王的脚步。
- 魔王的主页:🔥🔥🔥大魔王.🔥🔥🔥
❤️🔥大魔王与你分享:罗曼罗兰说过:这个世上只有一种真正的英雄主义,那就是认清生活的真相并且任然热爱他。
文章目录
- 一、前言
- 二、文件介绍
- 三、代码实现
- 1.完成test.c文件模板
- 2.创建棋盘并进行初始化
- 3.打印棋盘
- 4.玩家下棋
- 5.电脑下棋
- 6.判断输赢
- 7.判断是否满了
- 8.电脑智能下棋
- 四、对于初学者较陌生的知识
- 1.休眠函数
- 2.清屏函数
- 3.随机数函数
- 五、总代码
- game.h
- 2.game.c
- 3.test.c
- 4.效果展示图
- 六、总结
一、前言
相信大家都玩过三子棋或者五子棋游戏,但你会通过编程完美实现吗,本篇将带领你真正学会三子棋,并与你自己编译出来的程序分出胜负。
二、文件介绍
test.c:测试文件
game.c:源文件,进行函数的定义
game.h:头文件(只进行声明)
我们用测试文件调用game.h声明、game.c文件中实现的函数,这就是三个文件的关系。
为了使三者联系起来,我们需要让两个源文件(.c)文件都包含头文件,这样他们中的函数就可以一起使用了。
三、代码实现
1.完成test.c文件模板
模板介绍:我们在玩游戏时,首先肯定需要看到菜单,然后菜单中有选线供玩家选择,等玩家选择完成后,就要根据玩家的选择展开不同的路线来执行程序。为了使游戏可以重复完,我们还需要让程序能够玩完一次继续玩,直到玩家不想玩选择退出程序,程序再结束。
#include "game.h"
void menu()
{
printf("********************\n");
printf("****** 1.play ******\n");
printf("****** 0.exit ******\n");
printf("********************\n");
}
void test()
{
int input = 0;
do
{
menu();
printf("请选择:> ");
scanf("%d", &input);
switch (input)
{
case 1:
printf("开始游戏\n");
game();
break;
default:
printf("输入错误,请重新输入\n");
break;
case 0:
printf("退出游戏\n");
}
} while (input);
}
int main()
{
test();
return 0;
}
2.创建棋盘并进行初始化
因为我们是要把符号输入到二维数组中,结合我们构造出来的图案形成三子棋的模样,所以我们需要创建一个二维数组并进行初始化(因为棋盘最开始为空白的,所以二维数组初始化的内容为空格)。
我们先在test.c文件中创建一个棋盘然后再调用game.c文件中初始化棋盘的代码。
- 初始化棋盘代码如下:
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] = ' ';
}
}
}
3.打印棋盘
我们创建并初始化棋盘后,需要打印出三子棋图供玩家进行观察和下棋,如果玩家想玩不只3*3格的三子棋,或者想把三子棋改为五子棋,也就是说当规模较大时,为了方便玩家,我们给每行每列开头都给定标号,便利玩家。
//打印棋盘
void PrintBoard(char board[ROW][COL], int row, int col)
{
int k = 0;
printf(" ");
printf(" ");
for (k = 0; k < col; k++)
{
printf(" ");
printf("%d", k + 1);
printf(" ");
printf(" ");
}
printf("\n");
int i = 0;
for (i = 0; i < row; i++)//打印row行
{
int j = 0;
printf("%-2d", i + 1);//打印每行开头的行数
for (j = 0; j < col; j++)//第一种行中的那一小段(空格、数组、空格)
{
printf(" ");
printf("%c", board[i][j]);
printf(" ");
if (j < col - 1)
printf("|");//打印一个|
}
printf("\n");
if (i < row - 1)//第二种行中的那一小段(-、-、-)
{
printf(" ");
printf(" ");//对应补齐每行开头的行数(不然棋盘会错一位)
for (j = 0; j < col; j++)
{
printf("-");
printf("-");
printf("-");
if (j < col - 1)
printf("|");//打印一个|
}
printf("\n");
}
}
}
4.玩家下棋
游戏是由程序员编译的,但是玩家可能是个小白,玩家并不知道数组下标是从0开始的,所以我们需要让玩家输入的两个值都减去1才是玩家真实想填入的地方。但是我们在填入时,首先需要判断输入的坐标是否合法,也就是说是否为坐标范围之外的值,如果是,需要重新输入,判断完这个,我们还需要判断这个坐标是否已经被使用,只有这两个条件都满足,我们才可以将棋子落入玩家想下的地方。
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
while (1)
{
printf("玩家下棋,请输入坐标:> ");
scanf("%d %d", &i, &j);
if ((i > 0 && i <= row) && (j > 0 && j <= col))
{
if (board[i - 1][j - 1] == ' ')
{
board[i - 1][j - 1] = '*';
break;
}
else
printf("输入坐标已经使用,请重新输入\n");
}
else
printf("输入坐标不在范围内,请重新输入\n");
}
}
5.电脑下棋
电脑下棋需要用到随机数rand(),最后会统一讲对初学者比较陌生的库函数。和玩家下棋很相似,不过不用减一了,因为这是电脑操作的。但是还需要判断是否已被占用,若已被占用,就重新给定一组值,直到电脑成功下一个棋。
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
while (1)
{
int i = rand() % row;
int j = rand() % col;
if (board[i][j] == ' ')
{
board[i][j] = '#';
break;
}
}
}
6.判断输赢
三子棋的胜利分为四种形式,一种是横向三个,一种是纵向三个,一种是右下倾斜(\),一种是左下倾斜(/),这四种方式我们都会采用遍历法实现。
//判断输赢
char JudgeGame(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 - 2; j++)
{
if (board[i][j] == board[i][j + 1] && board[i][j] == board[i][j + 2] && board[i][j] != ' ')
return board[i][j];
}
}
//竖
for (j = 0; j < col; j++)
{
for (i = 0; i < row - 2; i++)
{
if (board[i][j] == board[i+1][j] && board[i][j] == board[i+2][j] && board[i][j] != ' ')
return board[i][j];
}
}
//右下方向(\)
for (i = 0; i < row - 2; i++)
{
for (j = 0; j < col - 2; j++)
{
if (board[i][j] == board[i+1][j + 1] && board[i][j] == board[i+2][j + 2] && board[i][j] != ' ')
{
return board[i][j];
}
}
}
//左下方向(/)
for (i = 0; i <row; i++)
{
for (j = col - 1; j > 1; j--)
{
if (board[i][j] == board[i+1][j - 1] && board[i][j] == board[i+2][j - 2] && board[i][j] != ' ')
return board[i][j];
}
}
return 0;
}
7.判断是否满了
每次判断完是否胜利后,如果没有胜利,我们不能直接让另一个下棋,我们还要判断是否已经满了,如果满了还没分出胜负(因为是先判断输赢,再判断是否满的,所以这一点不用用管就是这样),那就是平局。
//判断是否棋盘已经下满
int is_full(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;
}
8.电脑智能下棋
我们如果不给电脑赋一点智商,他真的好笨,让他赢都赢不了,难受啊,但又没办法给他说话,那么就让我们的思想赋给电脑,这样就是两个你在对比输赢了,哈哈哈哈。
说实话让电脑智能一点其实自己想的时候没有思路,于是看了安 度 因(点击安度因可以跳转到他的文章)大佬的智能三子棋,但是真的,就瞄一眼思路瞬间就出来了,我们只要想到从获胜那里提取思路,真的一下就出来了,无非就是:那四种情况下有两个棋子,所谓的两个棋子又分为三种情况:比如说###获胜,那么情况就有三个,##空、#空#、空##,都是这三种情况,代码如下:
//电脑智能下棋
int intelligent_move(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 - 2; j++)
{
if (board[i][j] == board[i][j + 1] && board[i][j] != ' ' && board[i][j + 2] == ' ')
{
board[i][j+2] = '#';
return 1;
}
if (board[i][j] == board[i][j + 2] && board[i][j] != ' ' && board[i][j + 1] == ' ')
{
board[i][j+1] = '#';
return 1;
}
if (board[i][j + 1] == board[i][j + 2] && board[i][j + 1] != ' ' && board[i][j] == ' ')
{
board[i][j] = '#';
return 1;
}
}
}
//纵行拦截/下棋
for (j = 0; j < col; j++)
{
for (i = 0; i < row - 2; i++)
{
if (board[i][j] == board[i+1][j] && board[i][j] != ' ' && board[i+2][j] == ' ')
{
board[i+2][j] = '#';
return 1;
}
if (board[i][j] == board[i+2][j] && board[i][j] != ' ' && board[i+1][j] == ' ')
{
board[i+1][j] = '#';
return 1;
}
if (board[i+1][j] == board[i+2][j] && board[i+1][j] != ' ' && board[i][j] == ' ')
{
board[i][j] = '#';
return 1;
}
}
}
//右下方向拦截/下棋
for (i = 0; i < row - 2; i++)
{
for (j = 0; j < col - 2; j++)
{
if (board[i][j] == board[i + 1][j + 1] && board[i][j] != ' ' && board[i + 2][j + 2] == ' ')
{
board[i + 2][j + 2] = '#';
return 1;
}
if (board[i][j] == board[i + 2][j + 2] && board[i][j] != ' ' && board[i + 1][j + 1] == ' ')
{
board[i + 1][j + 1] = '#';
return 1;
}
if (board[i+2][j+2] == board[i + 1][j + 1] && board[i+2][j+2] != ' ' && board[i][j] == ' ')
{
board[i][j] = '#';
return 1;
}
}
}
//左下方向拦截/下棋
for (i = 0; i < row - 2; i++)
{
for (j = col-1; j > 1; j--)
{
if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' ' && board[i + 2][j - 2] == ' ')
{
board[i + 2][j - 2] = '#';
return 1;
}
if (board[i][j] == board[i + 2][j - 2] && board[i][j] != ' ' && board[i + 1][j - 1] == ' ')
{
board[i + 1][j - 1] = '#';
return 1;
}
if (board[i+2][j-2] == board[i + 1][j - 1] && board[i+2][j-2] != ' ' && board[i][j] == ' ')
{
board[i][j] = '#';
return 1;
}
}
}
return 0;
}
四、对于初学者较陌生的知识
1.休眠函数
睡眠函数:我们在玩时是不是觉得电脑反应好快,我们明明已经给他赋上智慧了,他却还那么快,这不就比我们都聪明了,为了让他笨一点,我们让他下的时候反应一秒,这就引入了睡眠函数,C语言库里是小写sleep,但是VS必须开头大写,也就是Sleep,我们按小写说,那就是sleep(unsigned int),里面的无符号整型单位是ms毫秒,所以我们写1000,实际就是每次走到这个程序停顿1秒。对应的头文件是<windows.h>
2.清屏函数
清屏函数:每次棋盘被修改,我们都要打印一次,但是这次打印出来上次的不就没用了吗,让他们一直留着好麻烦,所以我们引入一个清屏函数,system(“cls”);对应的头文件是<windows.h>
3.随机数函数
- 随机数函数rand:
-
rand函数原型:
一、#include <stdlib.h>;
二、int rand(void); -
rand函数调用:
一、rand()函数每次调用前会查询是否调用过srand(seed),是否给seed设定了一个值,如果有那么它会自动调用srand(seed)一次来初始化它的起始值。
二、若之前没有调用srand(seed),那么系统会自动给seed赋初始值,即srand(1)自动调用它一次。
- srand函数:
-
一、srand函数是随机数发生器的初始化函数,原型:
void srand(unsigned int seed);
二、这个函数需要提供一个种子,如srand(1),用1来初始化种子。
三、rand函数产生随机数时,如果srand(seed)播下种子之后,一旦种子相同,产生的随机数将是相同的。但是我们很多时候都是要让rand()产生一个随机数,那么这就意味着我们需要让种子值时刻发生变化,所以就可以这样赋值:srand(time(NULL)),那么我们就又引入了时间函数,详细如下
- time函数:
-
我们常常使用系统时间来初始化,使用time函数来获取系统时间,得到的值是一个时间戳,即从1970年1月1日到现在时间的秒数,然后将得到的time_t类型数据转化为(unsigned int)的数,然后再传给srand函数,用法如下:
一、srand(unsigned int)time(NULL);对应的头文件:<time.h>,我们在使用rand和srand时,主要使用的就是这一种初始化方法。
二、如果感觉时间间隔太小,可以让这个整体乘上一个数:srand((unsigned)time(NULL)*10)
三、time的参数传NULL表示不需要经过参数获得的time_t数据。
四、time函数原型如下:
time_t time(time_t * tloc); time_t类型被定义为一个长整型
五、详细的rand讲解可以点击这里进入该文章
五、总代码
game.h
#pragma once
#include <stdio.h>
#include <windows.h>
#include <time.h>
#include <stdlib.h>
#define ROW 3
#define COL 3
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);
//打印棋盘
void PrintBoard(char board[ROW][COL], int row, int col);
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);
//判断输赢
char JudgeGame(char board[ROW][COL], int row, int col);
//判断是否棋盘已经下满
int is_full(char board[ROW][COL], int row, int col);
//电脑智能下棋
int intelligent_move(char board[ROW][COL], int row, int col);
2.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 PrintBoard(char board[ROW][COL], int row, int col)
{
int k = 0;
printf(" ");
printf(" ");
for (k = 0; k < col; k++)
{
printf(" ");
printf("%d", k + 1);
printf(" ");
printf(" ");
}
printf("\n");
int i = 0;
for (i = 0; i < row; i++)//打印row行
{
int j = 0;
printf("%-2d", i + 1);//打印每行开头的行数
for (j = 0; j < col; j++)//第一种行中的那一小段(空格、数组、空格)
{
printf(" ");
printf("%c", board[i][j]);
printf(" ");
if (j < col - 1)
printf("|");//打印一个|
}
printf("\n");
if (i < row - 1)//第二种行中的那一小段(-、-、-)
{
printf(" ");
printf(" ");//对应补齐每行开头的行数(不然棋盘会错一位)
for (j = 0; j < col; j++)
{
printf("-");
printf("-");
printf("-");
if (j < col - 1)
printf("|");//打印一个|
}
printf("\n");
}
}
}
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
while (1)
{
printf("玩家下棋,请输入坐标:> ");
scanf("%d %d", &i, &j);
if ((i > 0 && i <= row) && (j > 0 && j <= col))
{
if (board[i - 1][j - 1] == ' ')
{
board[i - 1][j - 1] = '*';
break;
}
else
printf("输入坐标已经使用,请重新输入\n");
}
else
printf("输入坐标不在范围内,请重新输入\n");
}
}
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
while (1)
{
int i = rand() % row;
int j = rand() % col;
if (board[i][j] == ' ')
{
board[i][j] = '#';
break;
}
}
}
//判断输赢
char JudgeGame(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 - 2; j++)
{
if (board[i][j] == board[i][j + 1] && board[i][j] == board[i][j + 2] && board[i][j] != ' ')
return board[i][j];
}
}
//竖
for (j = 0; j < col; j++)
{
for (i = 0; i < row - 2; i++)
{
if (board[i][j] == board[i+1][j] && board[i][j] == board[i+2][j] && board[i][j] != ' ')
return board[i][j];
}
}
//右下方向(\)
for (i = 0; i < row - 2; i++)
{
for (j = 0; j < col - 2; j++)
{
if (board[i][j] == board[i+1][j + 1] && board[i][j] == board[i+2][j + 2] && board[i][j] != ' ')
{
return board[i][j];
}
}
}
//左下方向(/)
for (i = 0; i <row; i++)
{
for (j = col - 1; j > 1; j--)
{
if (board[i][j] == board[i+1][j - 1] && board[i][j] == board[i+2][j - 2] && board[i][j] != ' ')
return board[i][j];
}
}
return 0;
}
//判断是否棋盘已经下满
int is_full(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;
}
//电脑智能下棋
int intelligent_move(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 - 2; j++)
{
if (board[i][j] == board[i][j + 1] && board[i][j] != ' ' && board[i][j + 2] == ' ')
{
board[i][j+2] = '#';
return 1;
}
if (board[i][j] == board[i][j + 2] && board[i][j] != ' ' && board[i][j + 1] == ' ')
{
board[i][j+1] = '#';
return 1;
}
if (board[i][j + 1] == board[i][j + 2] && board[i][j + 1] != ' ' && board[i][j] == ' ')
{
board[i][j] = '#';
return 1;
}
}
}
//纵行拦截/下棋
for (j = 0; j < col; j++)
{
for (i = 0; i < row - 2; i++)
{
if (board[i][j] == board[i+1][j] && board[i][j] != ' ' && board[i+2][j] == ' ')
{
board[i+2][j] = '#';
return 1;
}
if (board[i][j] == board[i+2][j] && board[i][j] != ' ' && board[i+1][j] == ' ')
{
board[i+1][j] = '#';
return 1;
}
if (board[i+1][j] == board[i+2][j] && board[i+1][j] != ' ' && board[i][j] == ' ')
{
board[i][j] = '#';
return 1;
}
}
}
//右下方向拦截\下棋
for (i = 0; i < row - 2; i++)
{
for (j = 0; j < col - 2; j++)
{
if (board[i][j] == board[i + 1][j + 1] && board[i][j] != ' ' && board[i + 2][j + 2] == ' ')
{
board[i + 2][j + 2] = '#';
return 1;
}
if (board[i][j] == board[i + 2][j + 2] && board[i][j] != ' ' && board[i + 1][j + 1] == ' ')
{
board[i + 1][j + 1] = '#';
return 1;
}
if (board[i+2][j+2] == board[i + 1][j + 1] && board[i+2][j+2] != ' ' && board[i][j] == ' ')
{
board[i][j] = '#';
return 1;
}
}
}
//左下方向拦截/下棋
for (i = 0; i < row - 2; i++)
{
for (j = col-1; j > 1; j--)
{
if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' ' && board[i + 2][j - 2] == ' ')
{
board[i + 2][j - 2] = '#';
return 1;
}
if (board[i][j] == board[i + 2][j - 2] && board[i][j] != ' ' && board[i + 1][j - 1] == ' ')
{
board[i + 1][j - 1] = '#';
return 1;
}
if (board[i+2][j-2] == board[i + 1][j - 1] && board[i+2][j-2] != ' ' && board[i][j] == ' ')
{
board[i][j] = '#';
return 1;
}
}
}
return 0;
}
3.test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
printf("********************\n");
printf("****** 1.play ******\n");
printf("****** 0.exit ******\n");
printf("********************\n");
}
void game()
{
char board[ROW][COL];//创建棋盘
InitBoard(board, ROW, COL);//初始化棋盘
PrintBoard(board, ROW, COL);//打印棋盘
char ret = 0;
while (1)
{
PlayerMove(board, ROW, COL);//玩家下棋,玩家为*
system("cls");//清屏
PrintBoard(board, ROW, COL);//打印棋盘
ret = JudgeGame(board, ROW, COL);//判断输赢
if (ret != 0)
break;
if (ret = is_full(board, ROW, COL) == 1)
break;
int flag = intelligent_move(board, ROW, COL);//如果这一步电脑智能下棋确实下了,那么flag为1,否则为0
if(flag==0)//flag为0,说明没有智能下棋,那么就进行随机下棋
ComputerMove(board, ROW, COL);//电脑下棋,电脑为#
Sleep(1000);//休眠一秒(让电脑思考一会,哈哈哈哈哈)
system("cls");//清屏
PrintBoard(board, ROW, COL);//打印棋盘
ret = JudgeGame(board, ROW, COL);//判断输赢
if (ret != 0)
break;
if (ret = is_full(board, ROW, COL) == 1)
break;
}
if (ret == '*')
printf("恭喜你赢啦!!!\n");
else if (ret == '#')
printf("好遗憾,你连自己设置的电脑都玩不赢!!!\n");
else
printf("棋盘下满了,未分出胜负,电脑表示不服!!!\n");
}
void test()
{
int input = 0;
do
{
menu();
printf("请选择:> ");
scanf("%d", &input);
switch (input)
{
case 1:
printf("开始游戏\n");
game();
break;
default:
printf("输入错误,请重新输入\n");
break;
case 0:
printf("退出游戏\n");
}
} while (input);
}
int main()
{
srand((unsigned int)time(NULL));
test();
return 0;
}
4.效果展示图
六、总结
💘原创不易,还希望各位佬佬支持一下 \textcolor{colourful}{💘原创不易,还希望各位佬佬支持一下} 💘原创不易,还希望各位佬佬支持一下
💓点赞,你的认可是我创作的动力! \textcolor{red}{💓点赞,你的认可是我创作的动力!} 💓点赞,你的认可是我创作的动力!
💕收藏,你的青睐是我努力的方向! \textcolor{orange}{💕收藏,你的青睐是我努力的方向!} 💕收藏,你的青睐是我努力的方向!
💞评论,你的意见是我进步的财富! \textcolor{aqua}{💞评论,你的意见是我进步的财富!} 💞评论,你的意见是我进步的财富!
✨请点击下面进入主页关注大魔王
如果感觉对你有用的话,就点我进入主页关注我吧!