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. 游戏搭建思路及其步骤 菜单选择列表: 初始化棋盘:所有位置均为空格 创建棋盘样式 设置玩家下棋 设置电脑下棋 检查游戏状态: 四.代码示例 一.game.c部分 …...

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

深入解析:Redis与Nacos分布式锁在业务中的具体应用
时间:2024年08月22日 作者:小蒋聊技术 邮箱:wei_wei10163.com 微信:wei_wei10 音频地址:https://xima.tv/1_HBPYxC?_sonic0 希望大家帮个忙!如果大家有工作机会,希望帮小蒋内推一下&#x…...

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

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

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

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

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

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

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

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多个国家的体育赛事实时数据或历史数据的编程接口,无请求次数限制,可按需购买,接口稳定高效;覆盖项目包括足球、篮球、网球、电子竞技、奥运等专题、数据内容。 纳米数据API2.0版本…...

【中项第三版】系统集成项目管理工程师 | 第 15 章 组织保障
前言 本章的知识点预计上午会考1-2分,下午可能会考,一般与其他管理领域进行结合考查。学习要以教材为主。 目录 15.1 信息和文档管理 15.1.1 信息和文档 15.1.2 信息(文档)管理规则和方法 15.2 配置管理 15.2.1 基本概念 …...

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

PHP轻创推客集淘客地推任务平台于一体的综合营销平台系统源码
🚀轻创推客,营销新纪元 —— 集淘客与地推任务于一体的全能平台🌐 🌈【开篇:营销新潮流,轻创推客引领未来】 在瞬息万变的营销世界里,你还在为寻找高效、全面的营销渠道而烦恼吗?&…...

three.js实现 加载3dtiles ,瓦片 ,倾斜摄影,功能
预览:https://z2586300277.github.io/three-cesium-examples/#/codeMirror?navigationThreeJS&classifyexpand&idloadTiles 部署站点预览:http://threehub.cn/ 开源地址:https://z2586300277.github.io/three-cesium-examples/#/e…...

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

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

Java设计模式原则及中介者模式研究
在软件开发过程中,设计模式作为解决常见设计问题的有效工具,对于提升代码质量、促进团队协作具有重要意义。本文系统地阐述了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…...

【代码】Swan-Transformer 代码详解(待完成)
1. 局部注意力 Window Attention (W-MSA Module) class WindowAttention(nn.Module):r""" Window based multi-head self attention (W-MSA) module with relative position bias.It supports both of shifted and non-shifted window.Args:dim (int): Number…...

iframe.contentDocument 和document.documentElement的区别
iframe.contentDocument 和 document.documentElement 是用于访问不同内容的两个不同的对象或属性。 1. iframe.contentDocument 内容: iframe.contentDocument 代表的是 <iframe> 元素所嵌入的文档的 Document 对象。它允许你访问和操作嵌入的文档(即 ifram…...

计算机操作员试题(中篇)
计算机操作员试题(中篇) 335.在 Excel中,把鼠标指向被选中单元格边框,当指变成箭头时,拖动鼠标到目标单 元格时,将完成( )操作。 (A)删除 (B)移动 ©自动填充 (D)复制 336.在 Excel 工作表的单元格中,如想输入数字字符串 070615 (例如学号),则应输 入()。 (A) 0007…...

车规级MCU「换道」竞赛
汽车芯片,尤其是MCU市场正在进入拐点期。 本周,总部位于荷兰的汽车芯片制造商—恩智浦(NXP)半导体总裁兼首席执行官Kurt Sievers在公司第二季度财报电话会议上告诉投资者,由于汽车需求停滞不前,该公司正在努…...

数学生物学-2-离散时间模型(Discrete Time Models)
上一篇介绍了一个指数增长模型。然而,我们也看到,在现实情况下,细菌培养的增长是在离散的时间(在这种情况下是小时)进行测量的,种群并没有无限增长,而是趋于以S形曲线趋于平稳,称为“…...

免费开源!AI视频自动剪辑已成现实!效率提升80%,打工人福音!(附详细教程)
大家好,我是程序员X小鹿,前互联网大厂程序员,自由职业2年,也一名 AIGC 爱好者,持续分享更多前沿的「AI 工具」和「AI副业玩法」,欢迎一起交流~ 想象一下,假设老板给你布置了一项任务:…...

NtripShare全站仪自动化监测之气象改正
最近有幸和自动化监测领域权威专家进行交流,讨论到全站仪气象改正的问题,因为有些观点与专家不太一致,所以再次温习了一下全站仪气象改正的技术细节。 气象改正的概念 全站仪一般利用光波进行测距,首先仪器会处理测距光波的相位漂…...

【人工智能】项目案例分析:使用自动编码器进行信用卡欺诈检测
一、项目背景 信用卡欺诈是金融行业面临的一个重要问题,快速且准确的欺诈检测对于保护消费者和金融机构的利益至关重要。本项目旨在通过利用自动编码器(Autoencoder)这一无监督学习算法,来检测信用卡交易中的欺诈行为,…...

【工控】线扫相机小结
背景简介 我目前接触到的线扫相机有两种形式: 无采集卡,数据通过网线传输。 配备采集卡,使用PCIe接口。 第一种形式的数据通过网线传输,速度较慢,因此扫描和生成图像的速度都较慢,参数设置主要集中在相机本身。第二种形式的相机配备采集卡,通常速度更快,但由于相机和…...

将Web应用部署到Tomcat根目录的三种方法
将应用部署到Tomcat根目录的三种方法 将应用部署到Tomcat根目录的目的是可以通过"http://[ip]:[port]"直接访问应用,而不是使用"http://[ip]:[port]/[appName]"上下文路径进行访问。 方法一:(最简单直接的方法࿰…...