当前位置: 首页 > news >正文

C语言编写三子棋游戏:从概念到思路到实现

目录

一.文章概述

二.游戏规则概述

三.理解思路

1. 定义游戏数据结构

2. 游戏搭建思路及其步骤

·菜单选择列表:

·初始化棋盘:所有位置均为空格

·创建棋盘样式

·设置玩家下棋

·设置电脑下棋

·检查游戏状态:

四.代码示例

一.game.c部分

二.game.h部分

三.test.c部分

结论


一.文章概述

本文将深入探讨用C语言实现一个经典的双人策略游戏——三子棋(Tic Tac Toe)。我们不仅会详细解释游戏规则、数据结构选择,还会阐述如何设计和编码游戏的逻辑流程。通过本篇文章,读者能够理解如何从概念分析到具体代码实现,一步步构建出完整的三子棋游戏。

二.游戏规则概述

三子棋是一种经典的两人策略游戏,通常在一个3x3(本次代码可以随意更改棋盘大小)的网格上进行。玩家轮流放置他们的标记(这里代码玩家下棋用“*”,电脑下棋用“#”来示例),目的是在横行、竖列或斜对角线上获得三个连续的标记及为获胜。

1.一个玩家形成一条直线上的三个标记。

2.所有的格子都被占用(平局)。

三.理解思路

1. 定义游戏数据结构

创建一个二维数组来表示棋盘。使用字符型数组 

//存放数据需要一个3*3的二维数组char board[ROW][COL] = {0};

 来存储每个位置的棋子,其中 ‘’, ‘*’, 和 ‘#’分别代表空位、玩家和电脑。

2. 游戏搭建思路及其步骤

·菜单选择列表

//声明菜单是menu定义格式
void menu()
{printf("*******************************\n");printf("********    1. play      ******\n");printf("********    0. exit      ******\n");printf("*******************************\n");
}

·初始化棋盘:所有位置均为空格

//初始化棋盘InitBoard(board, ROW, COL);
//方法一
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 InitBoard(char board[ROW][COL], int row, int col)
{memset(&board[0][0], ' ', row*col*sizeof(board[0][0]));
}

·创建棋盘样式

void DisplayBoard(char board[ROW][COL], int row, int col)
{int i = 0;int j = 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");}
}

·设置玩家下棋

使用循环结构(如 while 循环)来处理玩家和电脑输入,询问玩家和电脑选择的位置并验证。通常需要一个函数来接受用户输入的行和列,并转换为有效的数组索引。

//判断玩家下棋输入坐标是否符合要求
//若符合要求,则判断该位置是否是没有超出棋盘范围的
//若没有超出且该位置是空的,玩家就下棋
//为了能达成赢,输,平局三种其一的局面
//需要while不停的循环
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 <= row && y >= 1 && y <= col){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){//假如row=9//rand()%row=0-8//那么+1就是1-9x = rand() % row;y = rand() % col;if (board[x][y] == ' '){board[x][y] = '#';break;}}
}

·检查游戏状态

需要有函数来检查游戏是否结束(是否有胜者或平局)和验证下棋操作的有效性(确保不是重复落子、非空位等)。

//判断输赢
//玩家赢 - '*'
//电脑赢 - '#'
//平局   - 'Q'
//继续   - 'C'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];}}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];}//判断是否平局if (IsFull(board, row, col)){return 'Q';}//游戏继续return 'C';
}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] == ' '){//没有空格子了就直接返回0终止程序return 0;}}}//继续下棋return 1;
}

四.代码示例

一.game.c部分

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"void InitBoard(char board[ROW][COL], int row, int col)
{int i,j;//方法一//for (i = 0; i < row; i++)//{//	for (j = 0; j < col; j++)//	{//		//刚开始棋盘上是什么都没有的//		board[i][j]=' ';//	}//}//方法二  包含头文件#include "string.h"//函数规定将&/*部分指向的地方前多大空间转化为空格(' ')memset(&board[0][0], ' ', row * col * sizeof(board[0][0]));}//void DispalyBoard(char board[ROW][COL], int row, int col)
//{
//	int i, j;
//
//	for (i = 0; i < row; i++)
//	{
//		//写死了只能打印3列
//		printf(" %c | %c | %c \n",board[i][0], board[i][1], board[i][2]);
//		if (i < row - 1)
//		{
//			printf("---|---|---\n");
//		}
//	}
//
//}void DispalyBoard(char board[ROW][COL], int row, int col)
{int i, j;//灵活变换的棋盘for (i = 0; i < row; i++){//把一个字符和一个竖杠看作一组//---|---|---//那么最后一组没有‘|’//先打印数据和|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");}}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 <= row && y >= 1 && y <= col){//正确了之后是否为空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 Is_Full(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;
}//判断输赢
//玩家赢 - '*'
//电脑赢 - '#'
//平局   - 'Q'
//继续   - 'C'
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];}}//判断列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];}}///判断主副对角线//为什么要是board[1][1] != ' ',board[0][0] != ' '不可以吗,前面不都是判断了board[1][1] ==board[0][0]//因为他是中间相互公共部分,是00或者22的话//意思就是如果要改的话,主副对角线就要检查对角位置就可以了,用中心检查是为了提高代码效率而已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 (Is_Full(board, row, col)){return 'Q';}return 'C';
}

二.game.h部分

#pragma once//头文件的包含
#include <string.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);

三.test.c部分

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"void menu()
{printf("***** 请    选    择 *****\n");printf("***** 1.play  0.exit *****\n");printf("**************************\n");
}void game()
{//如果是这样的话,确实打印出了棋盘//但是直接打印的话那么如何落子输入?/*printf("  |  |  \n");printf("--|--|--\n");printf("__|__|__\n");printf("  |  |  \n");*///运用数组解决问题//存放一个3*3的数组char board[ROW][COL] = { 0 };char ret = 0;//初始化棋盘InitBoard(board, ROW, COL);//打印棋盘DispalyBoard(board, ROW, COL);while (1){//下棋//玩家下PlayerMove(board, ROW, COL);//打印DispalyBoard(board, ROW, COL);//判断输赢ret=IsWin(board, ROW, COL);if (ret != 'C'){//在这里不判断谁赢//当不平局直接跳出,再进行判断//节省了代码break;}//电脑下ComputerMove(board, ROW, COL);//打印DispalyBoard(board, ROW, COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != 'C'){break;}}if ('*' == ret){printf("玩家赢\n");}else if ('#' == ret ){printf("电脑赢\n");}else if ('C' == ret){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;
}

结论

这个简单的C语言程序实现了三子棋的基本功能:定义游戏数据结构、游戏搭建思路及其步骤、检查胜利条件和切换玩家等。使用标准输入输出函数进行人机交互,并通过数组来管理棋盘状态。


我期待这篇博客示例能为你提供宝贵的思路和指导,在使用C语言构建三子棋游戏的过程中遇到问题时找到有效的解决方案。在这段旅程中,请记住,正确地挑选并应用合适的函数是关键所在——这一点在实际编程过程中尤为重要。通过合理选择这些功能,你可以确保程序不仅高效而且易于维护。希望这个资源能成为你编程之旅中的得力助手,并激发你进一步探索和优化代码的热情。

相关文章:

C语言编写三子棋游戏:从概念到思路到实现

目录 一.文章概述 二.游戏规则概述 三.理解思路 1. 定义游戏数据结构 2. 游戏搭建思路及其步骤 菜单选择列表&#xff1a; 初始化棋盘&#xff1a;所有位置均为空格 创建棋盘样式 设置玩家下棋 设置电脑下棋 检查游戏状态&#xff1a; 四.代码示例 一.game.c部分 …...

React.js如何使用Bootstrap

在 React.js 项目中使用 Bootstrap 有多种方法&#xff0c;主要包括直接引入 Bootstrap CSS 文件和使用 React Bootstrap 库。下面将详细介绍这两种方法。 方法一&#xff1a;直接引入 Bootstrap CSS 文件 这是最简单的方式&#xff0c;只需在项目中引入 Bootstrap 的 CSS 文…...

深入解析:Redis与Nacos分布式锁在业务中的具体应用

时间&#xff1a;2024年08月22日 作者&#xff1a;小蒋聊技术 邮箱&#xff1a;wei_wei10163.com 微信&#xff1a;wei_wei10 音频地址&#xff1a;https://xima.tv/1_HBPYxC?_sonic0 希望大家帮个忙&#xff01;如果大家有工作机会&#xff0c;希望帮小蒋内推一下&#x…...

MySQL索引的性能优化

1.数据库服务器的优化步骤 在数据库调优中&#xff0c;我们的目标就是响应时间更快&#xff0c;吞吐量更大。利用宏观的监控工具和微观的日志分析可以帮我们快速找到调优的思路和方式 数据库服务器的优化步骤 当我们遇到数据库调优问题的时候&#xff0c;该如何思考呢&#xf…...

协方差详解及在日常生活中的应用实例——天气温度与冰淇淋销量的关系

协方差详解及在日常生活中的应用实例——天气温度与冰淇淋销量的关系 文章目录 协方差详解及在日常生活中的应用实例——天气温度与冰淇淋销量的关系引言协方差的概念与背景数学公式推导实例背景数据收集计算过程结果解释计算相关系数为什么使用协方差&#xff1f;结论商业启示…...

Spring Boot3.3.X整合Mybatis-Plus

前提说明&#xff1a; 项目的springboot版本为&#xff1a;<version>3.3.2</version> 需要整合的mybatis-plus版本&#xff1a;<version>3.5.7</version> 废话不多说&#xff0c;开始造吧 1.准备好数据库和表 2.配置全局文件application.properti…...

快速了解软件测试——测试用例的方法

测试用例的编写方法有八种&#xff0c;其中等价类、边界值、判定表、场景法、流程图重要且使用得多 ●等价类●边界值●判定表●因果图[了解]●正交法[了解]●场景法●流程图●错误推测法[了解] 1、等价类 为什么要用等价类划分法? ●从大量数据中划分范围(等价类),然后从每…...

多线程、多进程,还是异步?-- Python 并发 API 如何选择

如何选择正确的 Python 并发 API模块 &#xff1f; Python 标准库提供了三种并发 API &#xff0c; 如何知道你的项目应该使用哪个 API&#xff1f; 在本教程将带逐步了解各API的特性、区别以及各自应用场景&#xff0c;指导你选择最合适的并发 API。 多线程、多进程&#xff0…...

汽车服务管理系统 _od8kr

TOC springboot580汽车服务管理系统 _od8kr--论文 系统概述 该系统由个人管理员和员工管理&#xff0c;用户三部分组成。其中&#xff1a;用户进入系统首页可以实现首页&#xff0c;热销汽车&#xff0c;汽车配件&#xff0c;汽车资讯&#xff0c;后台管理&#xff0c;在线客…...

带你玩转小程序推广,实现短链接一键跳转

不知道各位有没有想过&#xff0c;短链接直接跳转到微信小程序到底该怎么操作呢&#xff1f;掌握这个小技能&#xff0c;能让你的推广效率大幅提升哦。今天就给大家分享一个全新方法&#xff0c;教你如何从短链接直接跳转到微信小程序&#xff0c;实现高效的一键式跨越。 一、…...

OpenDDS的Rtps_Udp传输协议可靠性QoS收发基本流程

OpenDDS中,实现了Rtps_Udp传输协议(非纯udp)的可靠性传输。传输的线程包括: 1)发送方线程主要线程和定时器 《1》应用线程 《2》网络异步发送线程 《3》Heartbeat定时器 《4》Nak_response定时器 2)接收方主要线程和定时器 《1》网络异步接收线程 《2》heartbeat_respons…...

体育数据API纳米奥运会数据API:高阶数据包接口文档API示例⑦

纳米体育数据的数据接口通过JSON拉流方式获取200多个国家的体育赛事实时数据或历史数据的编程接口&#xff0c;无请求次数限制&#xff0c;可按需购买&#xff0c;接口稳定高效&#xff1b;覆盖项目包括足球、篮球、网球、电子竞技、奥运等专题、数据内容。 纳米数据API2.0版本…...

【中项第三版】系统集成项目管理工程师 | 第 15 章 组织保障

前言 本章的知识点预计上午会考1-2分&#xff0c;下午可能会考&#xff0c;一般与其他管理领域进行结合考查。学习要以教材为主。 目录 15.1 信息和文档管理 15.1.1 信息和文档 15.1.2 信息&#xff08;文档&#xff09;管理规则和方法 15.2 配置管理 15.2.1 基本概念 …...

数据结构——顺序栈和链式栈

目录 引言 栈的定义 栈的分类 栈的功能 栈的声明 1.顺序栈 2.链式栈 栈的功能实现 1.栈的初始化 (1)顺序栈 (2)链式栈 (3)复杂度分析 2.判断栈是否为空 (1)顺序栈 (2)链式栈 (3)复杂度分析 3.返回栈顶元素 (1)顺序栈 (2)链式栈 (3)复杂度分析 4.返回栈的大…...

PHP轻创推客集淘客地推任务平台于一体的综合营销平台系统源码

&#x1f680;轻创推客&#xff0c;营销新纪元 —— 集淘客与地推任务于一体的全能平台&#x1f310; &#x1f308;【开篇&#xff1a;营销新潮流&#xff0c;轻创推客引领未来】 在瞬息万变的营销世界里&#xff0c;你还在为寻找高效、全面的营销渠道而烦恼吗&#xff1f;&…...

three.js实现 加载3dtiles ,瓦片 ,倾斜摄影,功能

预览&#xff1a;https://z2586300277.github.io/three-cesium-examples/#/codeMirror?navigationThreeJS&classifyexpand&idloadTiles 部署站点预览&#xff1a;http://threehub.cn/ 开源地址&#xff1a;https://z2586300277.github.io/three-cesium-examples/#/e…...

Qt QTextEdit调用append数据重复的问题

使用QTextEdit写了个串口工具&#xff0c; 当串口有数据时通过一个signal传给slot&#xff0c;在 slot中调用QTextEdit的append(text)来增量显示串口数据&#xff0c;当串口关闭时调用clear()来清空显示。 结果发现append调用后显示的数据会有重复。 分析 分析代码&#xff0…...

数学基础(二)

一、导数 导数计算&#xff1a; 偏导数&#xff1a; 方向导数&#xff1a; 梯度&#xff1a; 函数在某点的梯度是一个向量&#xff0c;它的方向余方向导数最大值取得的方向一致。其大小正好是最大的方向导数 二、微积分 面积由来&#xff1a; 切线&#xff1a; 定积分&#x…...

Java设计模式原则及中介者模式研究

在软件开发过程中&#xff0c;设计模式作为解决常见设计问题的有效工具&#xff0c;对于提升代码质量、促进团队协作具有重要意义。本文系统地阐述了Java设计模式的六大基本原则——单一职责原则、开放封闭原则、里氏替换原则、依赖倒置原则、接口隔离原则以及迪米特法则&#…...

logstash入门学习

1、入门示例 1.1、安装 Redhat 平台 rpm --import http://packages.elasticsearch.org/GPG-KEY-elasticsearch cat > /etc/yum.repos.d/logstash.repo <<EOF [logstash-5.0] namelogstash repository for 5.0.x packages baseurlhttp://packages.elasticsearch.org…...

浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)

✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义&#xff08;Task Definition&…...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...