一、何为三子棋
??三子棋又称为井字棋,相信大家都玩过这个小游戏,其规则就是哪一方先将相邻的的三枚棋子连在一起便获得胜利,那么今天就由我为大家讲解如何使用C语言实现简单的三子棋游戏吧。
二、解题思路
1.首先确定编写形式
??多文件形式实现无疑是最清晰、最直观的编写形式。
主函数(调用):test.c 测试三子棋游戏(实现游戏的大体逻辑和功能) 模块(被调用):game.c 游戏函数及其功能的实现 头文件(声明):game.h 三子棋游戏的函数声明
我先将头文件展示出来,为大家下来的解题提供明确的思路:
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 3
#define COL 3
void init_board(char board[ROW][COL], int row, int col);
void display_board(char board[ROW][COL], int row, int col);
void player_move(char board[ROW][COL], int row, int col);
void computer_move(char board[ROW][COL], int row, int col);
char is_win(char board[ROW][COL], int row, int col);
- 由图可见,一共有三个库函数所需要的头文件以及五个为实现游戏功能而创建的函数;
- 我认为打印棋盘尤为重要,无论是在何种情况下,我们都需要在成功实现某些功能时打印棋盘检验功能的完整性。
2.设计游戏菜单页面
??游戏菜单必须出现在我们最终生成的结果当中,也就是需要“printf”将其打印出来,而main函数作为一个程序的入口,那么下面这段代码便是在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");
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择!\n");
break;
}
} while (input);
return 0;
}
- 使用do…while循环实现游戏的重复可玩性;
- switch语句实现游戏的菜单操作,为玩家提供选项;
- 这里预处理操作中已经引入自定义函数(头文件内已经包含所需要的一切库以及函数声明);
3.打印棋盘
??根据棋盘的样式,我们可以联想到三子棋棋盘应该有九个待输入数据(可存储),且形式为3*3,所以在这里二维数组便是解题关键。 实现打印棋盘功能代码可以放在game.c文件中。
void init_board(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 display_board(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 display_board(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 < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}
}
- 初始化棋盘应为空格,方便玩家数据的输入。(注意:'\n’不会打印在结果里,故而此处打印空格);
- 美化棋盘:待输入数据两端各一个空格;
- 此时棋盘可以分为n组打印(每组打印一行数据和一行分割线),n为二维数组的列。(注意:此处不使用行数为打印条件是因为在二维数组中,列数可以确定行数,而行数不能确定列数);
- 简陋棋盘无法完成棋盘的形态转换,所以此处不建议使用
4.游戏环节
??玩家下棋:输入坐标(需要考虑坐标合法性,坐标是否被占用)
void player_move(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");
}
}
}
- 作为玩家下棋环节应该做到正常坐标输入,即第一行第一列坐标输入应为(1,1),所以我们在判断坐标时应该对其减一达到我们所要的效果。
??简单电脑下棋:随机下棋(利用时间戳)
void computer_move(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] = '#';
break;
}
}
}
- 这里电脑下棋我利用的是时间戳作为随机数,并且利用取模运算控制其数值范围;
- 设置随机数需要用到srand(unsigned seed)以及time(NULL),所以我们还需要<stdlib.h>和<time.h>两个头文件,为了方便管理,我已经将所需头文件以及其他预处理操作放入game.h文件中。
5.判断输赢(游戏状态)
??游戏过程的功能实现
void game()
{
char ret = 0;
char board[ROW][COL] = { 0 };
init_board(board, ROW, COL);
display_board(board, ROW, COL);
while (1)
{
player_move(board, ROW, COL);
display_board(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'R')
break;
computer_move(board, ROW, COL);
display_board(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'R')
break;
}
if (ret == '*')
{
printf("恭喜你获胜!\n");
}
else if (ret == '#')
{
printf("弱爆了,败给了人机!\n");
}
else if (ret == 'P')
{
printf("打成平手,继续训练吧!\n");
}
display_board(board, ROW, COL);
}
- 该代码是属于test.c文件中的函数实现,目的是为了将整个游戏的过程以及游戏最终结果打印出来,实现游戏的实时互动及可见性。
??判断获胜方的函数代码实现
char is_win(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];
}
}
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
{
return board[0][i];
}
}
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];
}
- 逻辑判断的方式是分为三种状态,第一种为横行三个相连,第二种为纵行三个相连,第三种为两条对角线的判断;
- 这是一段针对三子棋判断输赢的简陋方法,此处无法实现动态的判断。等博主空闲好好研究之后便会修改上传。
6.判断平局
??平局的关键在于棋盘的空间状态,即判断棋盘是否满了。
static 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;
}
if (is_full(board, row, col) == 1)
{
return 'P';
}
return 'R';
}
- 首先我们可以创建is_full函数对棋盘这个二维数组的是否含有空格判断得出,棋盘此时的状态;
- 然后我们就可以使用is_full函数的返回值进行游戏结果平局的判断。
7.运行结果
总结:这是一个简单的三子棋游戏,还有部分功能可以实现,以及你自己的想法也可以添加进去; 关键在于基础的函数应用和对二维数组的熟练使用。
|