当前位置:网站首页>C语言入门小游戏—三子棋
C语言入门小游戏—三子棋
2022-08-02 03:22:00 【凉世弥音九月秋】
三子棋
一、何为三子棋
三子棋又称为井字棋,相信大家都玩过这个小游戏,其规则就是哪一方先将相邻的的三枚棋子连在一起便获得胜利,那么今天就由我为大家讲解如何使用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++)
{
//数据
//printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
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)
{
//printf("---|---|---\n");
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);
//1.坐标的合法性
//2.坐标是否被占用
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");
//%3利用取模运算控制取值范围
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 };
//这里可以进行数组的初始化,防止'\n'等因素无法打印造成棋盘的错误
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);
}
//玩家赢 - '*'
//电脑赢 - '#'
//平局了 - 'P'
//游戏继续 - 'R'
- 该代码是属于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.判断平局
平局的关键在于棋盘的空间状态,即判断棋盘是否满了。
//如果棋盘满了,返回1
//不满,返回0
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.运行结果


总结:这是一个简单的三子棋游戏,还有部分功能可以实现,以及你自己的想法也可以添加进去;
关键在于基础的函数应用和对二维数组的熟练使用。
边栏推荐
- 磷脂-聚乙二醇-叠氮,DSPE-PEG-Azide,DSPE-PEG-N3,MW:5000
- AttributeError: ‘Upsample‘ object has no attribute ‘recompute_scale_factor‘
- Redis简单学习笔记
- Deveco studio Hongmeng app access network detailed process (js)
- 云服务器安装部署Nacos2.0.4版本
- Amazon sellers how to improve the conversion
- A senior test engineer asked me these questions as soon as the interview came
- 【装机】老毛桃的安装及使用
- MySQL8.0与MySQL5.7差异分析
- ModuleNotFoundError No module named ‘xxx‘可能的解决方案大全
猜你喜欢
知识工程作业2:知识工程相关领域介绍
Chapter 10 Clustering
HCIP-第十一天-MPLS+BGP
「PHP基础知识」PHP中对象的使用
UserWarning:火炬。meshgrid:在以后的版本中,它将被要求通过索引ing argu
API 低代码开发:接口大师,一套开发、管理和提供接口的产品框架
DSPE-PEG-DBCO Phospholipid-Polyethylene Glycol-Dibenzocyclooctyne A Linear Heterobifunctional Pegylation Reagent
APK的安装过程分析 PMS包管理器
第十一天&shell脚本
新工程加载YOLOV6的预训练权重问题
随机推荐
AttributeError: Can‘t get attribute ‘SPPF‘ on <module ‘models.common‘ from ‘/yolov5-5.0/models/commo
3 minutes to take you to understand WeChat applet development
周日数据库作业
MySQL分页查询的5种方法
getattr() function analysis
ssm various configuration templates
MySQL分组后取最大一条数据【最优解】
ssm各类配置模板
动态代理工具类
MySQL中JOIN的用法
RHCSA第二天
Debian 10 NTP Service Configuration
oracle内连接和外连接
Usage of JOIN in MySQL
3分钟带你了解微信小程序开发
The usage of json type field in mysql
debian 10 nat 与路由转发
第十一天&shell脚本
yolov5调用ip摄像头时出现的问题
Monaco Editor 的基本用法