文章目录
- 一、游戏的实现思路
- 二、游戏的实现步骤
- 1. 菜单函数
- 2. 设置棋盘
- 3. 初始化棋盘
- 4. 打印棋盘
- 5. 玩家下棋
- 6. 电脑下棋
- 7. 多子棋判断输赢
- 8. 判断棋盘是否已满
- 三、模块化代码实现
- 1. test.c
- 2. game.h
- 3. game.c
- 四、结果演示
由于模块化编程的需要,我们需要把整个游戏的实现分成三个部分。
- test.c:测试游戏的逻辑。
- game.c:游戏代码的实现。
- game.h:游戏代码的声明。
自由判断输赢
得到第一个位置的棋盘内容,然后直接循环判断其他位置的内容是否等于第一个位置的内容,且不等于空格,一旦等于提前结束循环
一、游戏的实现思路
- 创建菜单函数供玩家选择进入游戏或者退出游戏。
- 创建一个储存棋盘的二维数组,并使用初始化函数将其全部初始化为空格。
- 利用打印函数将创建好的棋盘打印出来。
- 玩家下棋,并打印出棋盘(玩家以行列的坐标形式来下棋,并且玩家的棋子为 ‘ o ’)。
- 电脑下棋,并打印出棋盘(让电脑在随机的位置进行落子,并且电脑的棋子为 ’ x ’ )。
- 判断游戏输赢,判断游戏是否继续进行(字符 ’ c ',为继续游戏)。
- 继续回到步骤 1,继续进行下一把游戏,直到玩家退出游戏为止。
二、游戏的实现步骤
1. 菜单函数
- 菜单界面函数实际上就是我们的一个界面,用来选择玩或不玩这个游戏。示例代码如下:
void menu()
{
printf("|--------------------------------|\n");
printf("|-----------三子棋游戏-----------|\n");
printf("|********************************|\n");
printf("|****** 1.play 0.exit ******|\n");
printf("|********************************|\n");
printf("|玩家为 o -------------- 电脑为 x|\n");
printf("|--------------------------------|\n");
}
2. 设置棋盘
#define ROW 3 //行
#define COL 3 //列
- 为了后续方便维护,以及更改棋盘的大小(n子棋),这里利用 define 来定义棋盘的行列
3. 初始化棋盘
- 三子棋的期盼格式和俺们的二维数组一样,所以创建一个二维数组用来存放玩家/电脑下的棋子。
- 将二维数组内存放的内容全部初始化为空格,以达到在棋盘上看起来什么都没有的效果。
//初始化棋盘
void InitBoard(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++)
{
board[i][j] = ' ';
}
}
}
4. 打印棋盘
- 当我们进入游戏的时候,首先要把被初始化好的棋盘展示出来。
//打印棋盘
void DisplayBoard(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++)
{
//打印的数组内容一开始都是空格,后面为落的棋子
printf(" %c ", board[i][j]);
if (j < col - 1)
{
printf("|");
}
}
putchar('\n');
//打印 row -1 行分隔符
if (i < row - 1)//最后一行的 --- 不用打印
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");//棋盘的竖杠只打印两列
}
}
putchar('\n');
}
}
}
- 打印的棋盘效果如下图所示:
5. 玩家下棋
- 玩家落子为 ‘ o ’。
- 站在玩家的角度上来说,行&列都是从 1 开始的,而我们数组下标访问是从0开始的。所以,我们要从1行1列为第一个进行打印。在程序实现就把输入坐标的值 - 1。
- 玩家落子需要在棋盘范围内,玩家要在棋盘上空的地方落子,如果输入的坐标不满足要重新输入。
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("请输入坐标:");
scanf("%d %d", &x, &y);
printf("\n玩家下棋\n");
//判断玩家落子的范围是否合法
if (x > 0 && x <= row && y > 0 && y <= col)
{
//将玩家输入的坐标 -1 才是实际的坐标
if (' ' == board[x - 1][y - 1])
{
board[x - 1][y - 1] = 'o';
break;
}
else
{
printf("该坐标已被占用\n");
}
}
else
{
printf("坐标非法,请重新输入坐标\n");
}
}
}
6. 电脑下棋
- 电脑落子为 ‘ x ’。
- 利用 rand 函数生成两个随机的坐标,再判断生成的坐标上是否是空格,如果是则此位置可以下棋。
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋\n");
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (' ' == board[x][y])
{
board[x][y] = 'x';
break;
}
}
}
7. 多子棋判断输赢
- 玩家赢了 —— ‘ o ’
- 电脑赢了 —— ‘ x ’
- 平局的话 —— ‘ q ’
- 游戏继续 —— ‘ c ’
- 注意:返回结果的值是字符,所以我们这里要用到字符串 char 来进行返回
实现思路
设置四个计数器,分别记录 行、列、左对角、右对角 中相同字符的个数
- 判断行相等:取出行的第一个字符,依次将该行后面的内容和第一个字符进行判断,如果字符相等且不为空格时,行计数器+1。等到计数器记录的数和行数相同时,返回该行的第一个字符,从而判断输赢。
- 判断列相等:与行同理,区别就是拿出列的第一个字符和后面的进行比较。
- 判断左对角线:取出左上角的字符,然后依次拿对角线后面的字符进行比较。
- 判断右对角线:与左对角线同理,不过右上角那个元素的下标为:board[0][row-1]。
- 判断平局:棋盘满了时还没分出胜负就是平局了。
代码实现
//判断输赢
char IsWin(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
//判断行列
for (i = 0; i < row; i++)
{
int recordrow = 0;//记录行中相同字符的个数
int recordcol = 0;//记录列中相同字符的个数
for (j = 0; j < col; j++)
{
//判断行相等
if (board[i][0] == board[i][j] && board[i][j] != ' ')
{
recordrow++;
}
//判断列相等
if (board[0][i] == board[j][i] && board[j][i] != ' ')
{
recordcol++;
}
}
if (row == recordrow)
{
return board[i][0];
}
if (col == recordcol)
{
return board[0][i];
}
}
int recordleft = 0;//记录左对角线中相同字符的个数
int recordright = 0;//记录右对角线中相同字符的个数
//判断左对角线
for (i = 0; i < row; i++)
{
j = i;
if (board[0][0] == board[i][j] && board[i][j] != ' ')
{
recordleft++;
}
}
if (recordleft == row)
{
return board[0][0];
}
//判断右对角线
for (i = 0; i < row; i++)
{
j = row - 1 - i;
if (board[0][row - 1] == board[i][j] && board[i][j] != ' ')
{
recordright++;
}
}
if(recordright == row)
{
return board[0][row - 1];
}
//判断平局
if (IsFull(board, row, col))
{
return 'q';//游戏平局
}
else
{
return 'c';//未分胜负,游戏继续
}
}
8. 判断棋盘是否已满
- 遍历二维数组,当出现一个空格时,则说明棋盘没满,返回 0,反之说明已经下满,返回 1。
//判断棋盘是否满了,满了则平局
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;//棋盘已满
}
三、模块化代码实现
1. test.c
#include "game.h"
//将库函数的头文件以及定义的常量放在 game.h 里,
//在其他文件里只引用 game.h 就可以使用了
void menu()
{
printf("|--------------------------------|\n");
printf("|-----------三子棋游戏-----------|\n");
printf("|********************************|\n");
printf("|****** 1.play 0.exit ******|\n");
printf("|********************************|\n");
printf("|玩家为 o -------------- 电脑为 x|\n");
printf("|--------------------------------|\n");
}
void game()//整个玩游戏的过程
{
char board[ROW][COL] = { 0 };
char ret = 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 ('c' != ret)
{
break;
}
//电脑下棋,然后打印棋盘,最后判断输赢
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
ret = IsWin(board, ROW, COL);
if ('c' != ret)
{
break;
}
}
if ('o' == ret)
{
printf("玩家胜利\n");
}
else if ('x' == ret)
{
printf("电脑胜利\n");
}
else
{
printf("游戏平局\n");
}
DisplayBoard(board, ROW, COL);
}
int main()
{
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);
return 0;
}
2. game.h
- 关于游戏包含的函数声明,符号声明头文件的包含以及宏定义。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 3 //行
#define COL 3 //列
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); //判断棋盘是否填满
3. game.c
- 游戏代码的实现
#include "game.h"
//初始化棋盘
void InitBoard(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++)
{
board[i][j] = ' ';
}
}
}
//打印棋盘
void DisplayBoard(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++)
{
printf(" %c ", board[i][j]);
if (j < col - 1)
{
printf("|");
}
}
putchar('\n');
//打印 row -1 行分隔符
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");//棋盘的竖杠只打印两列
}
}
putchar('\n');
}
}
}
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("请输入坐标:");
scanf("%d %d", &x, &y);
printf("\n玩家下棋\n");
//判断玩家落子的范围是否合法
if (x > 0 && x <= row && y > 0 && y <= col)
{
if (' ' == board[x - 1][y - 1])
{
board[x - 1][y - 1] = 'o';
break;
}
else
{
printf("该坐标已被占用\n");
}
}
else
{
printf("坐标非法,请重新输入坐标\n");
}
}
}
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋\n");
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (' ' == board[x][y])
{
board[x][y] = 'x';
break;
}
}
}
//判断棋盘是否满了,满了则平局
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;//棋盘已满
}
//判断输赢
char IsWin(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
//判断行列
for (i = 0; i < row; i++)
{
int recordrow = 0;//记录行中相同字符的个数
int recordcol = 0;//记录列中相同字符的个数
for (j = 0; j < col; j++)
{
//判断行相等
if (board[i][0] == board[i][j] && board[i][j] != ' ')
{
recordrow++;
}
//判断列相等
if (board[0][i] == board[j][i] && board[j][i] != ' ')
{
recordcol++;
}
}
if (row == recordrow)
{
return board[i][0];
}
if (col == recordcol)
{
return board[0][i];
}
}
int recordleft = 0;//记录左对角线中相同字符的个数
int recordright = 0;//记录右对角线中相同字符的个数
//判断左对角线
for (i = 0; i < row; i++)
{
j = i;
if (board[0][0] == board[i][j] && board[i][j] != ' ')
{
recordleft++;
}
}
if (recordleft == row)
{
return board[0][0];
}
//判断右对角线
for (i = 0; i < row; i++)
{
j = row - 1 - i;
if (board[0][row - 1] == board[i][j] && board[i][j] != ' ')
{
recordright++;
}
}
if(recordright == row)
{
return board[0][row - 1];
}
//判断平局
if (IsFull(board, row, col))
{
return 'q';//游戏平局
}
else
{
return 'c';//未分胜负,游戏继续
}
}
四、结果演示
1. 玩家胜利
2. 电脑胜利
3. 游戏平局