目录
设计思路
游戏运行效果
函数的声明
头文件game.h
游戏主体(源文件)
1.game.c
2.test.c
各文件的阐述
各部分的设计心得
1.打印菜单
2.初始化棋盘
3.打印棋盘
棋盘最终效果
1.打印数据
2.打印分割线
如何扩展为10 x 10 棋盘?
4.玩家下棋、电脑下棋
玩家下棋
电脑下棋
5.游戏对局判断
①玩家赢 / 电脑赢
②平局
③继续
大体可以分为五个部分
1.打印菜单
2.初始化棋盘
3.打印棋盘
4.玩家下棋、电脑下棋
5.游戏对局判断
①玩家赢 / 电脑赢
②平局
③继续
#pragma once
//三子棋游戏函数声明文件
#include
#define COL 3
#define ROW 3
//打印菜单
void menu();
//初始化棋盘
void InitBoard(char Board[ROW][COL], int row, int col);
//打印棋盘
void DisPlayBoard(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 IsWin(char Board[ROW][COL], int row, int col);
//判断平局
int IsFull(char Board[ROW][COL], int row, int col);
#define _CRT_SECURE_NO_WARNINGS 1
//三子棋游戏主体
#include"game.h"
#include
void InitBoard(char Board[ROW][COL], int row, int col)//初始化棋盘为空格
{
int i = 0;
int j = 0;
for (i = 0; i < col; i++)
{
for (j = 0; j < col; j++)
{
Board[i][j] = ' ';
}
}
}
//打印棋盘
//版本1
//void DisPlayBoard(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 DisPlayBoard(char Board[ROW][COL], int row, int col)
//{
// int i = 0;
// for (i = 0; i < row; i++)
// {
// //1. 打印数据
// printf(" %c | %c | %c \n",Board[i][0], Board[i][1], Board[i][2]);
// //2.打印分割线
// if(i < row - 1)
// printf("---|---|---\n");
// }
//}
//版本3
void DisPlayBoard(char Board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
//1. 打印数据
int j = 0;
for (j = 0; j < col; j++)
{
printf(" %c ", Board[i][j]);
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
//2. 打印分割线
if (i < row - 1)
{
int j = 0;
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 <= col && y >= 1 && y <= row)
{
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;
}
}
}
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 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][0] != ' ')
{
return Board[i][0];
}
}
int j = 0;
for ( j = 0; j < col; j++)
{
//判断列
if (Board[0][j] == Board[1][j] && Board[1][j] == Board[2][j] && Board[0][j] != ' ')
{
return Board[0][j];
}
}
//判断对角线
if (Board[0][0] == Board[1][1] && Board[1][1] == Board[2][2] && Board[1][1] != ' ')
{
return Board[1][1];
}
if (Board[0][2] == Board[1][1] && Board[1][1] == Board[2][0] && Board[1][1] != ' ')
{
return Board[1][1];
}
//平局
if (IsFull(Board,ROW,COL) == 1)
{
return 'Q';
}
//继续
return '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] = { 0 };
InitBoard(Board, ROW, COL); //初始化棋盘
DisPlayBoard(Board, ROW, COL); //打印棋盘
char flag = 0;
while (1)
{
//玩家下棋
PlayerMove(Board, ROW, COL);
DisPlayBoard(Board, ROW, COL);
//判断输赢
flag = IsWin(Board, ROW, COL);
if (flag != 'C')
{
break;
}
//电脑下棋
ComputerMove(Board, ROW, COL);
DisPlayBoard(Board, ROW, COL);
//判断输赢
flag = IsWin(Board, ROW, COL);
if (flag != 'C')
{
break;
}
}
//游戏结束
if (flag == '*')
{
printf("玩家赢\n");
}
else if (flag == '#')
{
printf("电脑赢\n");
}
else
{
printf("平局\n");
}
}
int main()
{
int pick = 1;
srand((unsigned int)time(NULL));
while (pick)
{
menu();
printf("请选择:>");
scanf("%d", &pick);
switch (pick)
{
case 1:
game();
break;
case 0:
pick = 0;
break;
default:
printf("请重新输入\n");
break;
}
}
printf("退出游戏\n");
return 0;
}
game.h头文件用来声明函数
game.c游戏功能的实现
test调用game.c的函数,用于运行测试
封装一个专门打印菜单的函数,后期想要美化菜单直接在函数修改即可,不用在海量的主体代码寻找菜单部分的代码,提高工作效率
三子棋需要一个3 x 3 的棋盘
故此我们需要一个三行三列的 二维数组
#define ROW 3
#define COL 3
char Board[ROW][COL] = {0};
这里的定义在打印棋盘时有妙用
void InitBoard(char Board[ROW][COL], int row, int col)//初始化棋盘为空格
{
int i = 0;
int j = 0;
for (i = 0; i < col; i++)
{
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++)
{
//1. 打印数据
int j = 0;
for (j = 0; j < col; j++)
{
printf(" %c ", Board[i][j]);
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
//2. 打印分割线
if (i < row - 1)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
由于分割线为三个字符,故为了美观打印数据可以用空格补齐三个字符
在打印 | 时 需注意 其要比数据少打印一次
同理 在打印 | 时 需注意 其要比分割线少打印一次
注:上述打印棋盘操作以打印一行为例 故要嵌套循环 用外层循环来控制打印的行数
这是就体现了定义的好处了
可以通过修改定义来扩展棋盘
#define ROW 10
#define COL 10
char Board[ROW][COL] = {0};
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 <= col && y >= 1 && y <= row)
{
if (Board[x - 1][y - 1] ==' ')
{
Board[x - 1][y - 1] = '*';
break;
}
else
{
printf("坐标被占用,请重新输入\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
以玩家的视角 棋盘坐标x,y的范围是1
而且数组的下标从0开始 故判断玩家是否可以落子时
将其输入的x,y坐标 横纵坐标均减 1 以此让计算机判断
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;
}
}
}
电脑下棋需要生成棋盘范围内的随机数
通过rand()可以生产随机数 需与srand()配合使用
srand()只需要调用一次 故直接放在主函数即可
srand()与时间戳配合使用即可生成一个随机的生成器起点
srand(unsigned int(time(NULL)))
要将随机生成的数范围固定在棋盘内
通过取余的方法可以完成
x = rand() % row 得 0
三种情况
1.行
2.列
行列均可通过循环改变一个坐标 遍历所有行或列来判断
3.两条对角线
找出对角线坐标进行判断即可
返回赢家的下的棋子
平局的判断在赢之后 即赢的判断无返回值才会进行平局判断
判断棋盘是否满了 满了即平局
return 'Q'
return 'C'
注:返回值是一个巧妙的用法 因为赢的情况三个棋子都一样
任意返回一个棋子的数据即可
在未来改变棋子的形状时也无影响
⠀⠰⢷⢿⠄
⠀⠀⠀⠀⠀⣼⣷⣄
⠀⠀⣤⣿⣇⣿⣿⣧⣿⡄
⢴⠾⠋⠀⠀⠻⣿⣷⣿⣿⡀
⠀⢀⣿⣿⡿⢿⠈⣿
⠀⠀⠀⢠⣿⡿⠁⠀⡊⠀⠙
⠀⠀⠀⢿⣿⠀⠀⠹⣿
⠀⠀⠀⠀⠹⣷⡀⠀⣿⡄
⠀⠀⠀⠀⣀⣼⣿⠀⢈⣧ 爱你们哟么么哒
⠀⠀