三子棋游戏设计与实现(C 语言版)

一、需求分析

目标:实现一个简单的人机对战三子棋,支持以下功能:

  1. 初始化空棋盘,清晰展示落子状态。
  2. 玩家通过坐标落子(X 代表玩家),电脑随机落子(O 代表电脑)。
  3. 实时判断胜负:某方三子连成一线(行 / 列 / 对角线)则获胜;棋盘填满则平局。

二、设计思路

1. 模块拆分(高内聚低耦合)

  • 数据层:用二维数组 char board[ROW][COL] 存储棋盘状态。
  • 逻辑层:封装独立函数,如初始化棋盘、打印棋盘、落子、胜负判断。
  • 交互层:通过菜单让用户选择 “开始游戏” 或 “退出”,控制游戏流程。

三、代码结构解析

1. 多文件组织(工程化思路)

  • Noughts and crosses.h:声明函数、定义常量(如 ROW=3),解耦接口与实现。
  • Noughts and crosses.c:实现具体逻辑(初始化、打印、落子等),隐藏细节。
  • test.c:负责游戏流程控制(菜单、主循环),专注交互。

2. 关键函数拆解

1.头文件:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include<windows.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 CheckWin(char board[ROW][COL], int row, int col);
//设置控制台文本颜色
void SetColor(int color);

2.关键函数分析(每个函数的定义写在Noughts and crosses.c):

  1.         
    void InitBoard(char board[ROW][COL], int row, int col)
    {for (int i = 0;i < row;i++){for (int j = 0;j < col;j++){board[i][j] = ' ';}}//随机种字(用于电脑下棋随机化)srand((unsigned int)time(NULL));
    }

    初始化,通过二维数组来实现棋盘,将9个格子初始化为空。  srand((unsigned      int)time(NULL));这段代码:是指time(NULL)以获取当前系统时间(从 1970 年 1 月 1 日到现在的秒数,也叫 “时间戳” )。将其类型转换为unsigned  int 来适配srand 这样就会生成随机数的 “起点,生成真随机值。如果是一个常数,那么会生成固定的序列。

  2. void DisplayBoard(char board[ROW][COL], int row, int col)
    {system("cls");//清空printf("=== 三子棋游戏 ===\n");for (int i = 0;i < row;i++){for (int j = 0;j < col;j++){if (board[i][j] == 'X'){SetColor(12);//红色}else if (board[i][j] == 'O'){SetColor(9);//蓝色}else{SetColor(7);//白色默认色}printf(" %c ", board[i][j]);if (j < col - 1){SetColor(7);printf("|");}}printf("\n");if (i < row - 1){SetColor(7);for (int j = 0; j < col; j++){printf("---");if (j < col - 1){printf("|");}}printf("\n");}}
    }
    

    打印棋盘,棋盘里会有X ,O ,“  ” ,---,|,等元素。通过循环遍历用判断语句来给这其中的元素附加颜色,然后再打印二维数组的元数 ,再通过判断语句的条件,让 | 这个放在其该在的位置(这里的是落子后面的 | )再“\n"换行 。这就第一行完事了接着我们要想第二行,在第一个循环里加上行的判断条件,先给它附上颜色 ,再加嵌套循环,来给--- 加上 ,再加上 | 。再换行。

  3. //玩家下棋
    void PlayerMove(char board[ROW][COL], int row, int col)
    {int x, y;printf("玩家下棋(输入坐标,如:1 1)");while (1){scanf_s("%d %d", &x, &y);if (x >= 0 && x < row && y >= 0 && y < col){if (board[x][y] == ' '){board[x][y] = 'X';break;}else{printf("该位置已占有,请重新输入");}}else{printf("坐标无效,请重新输入(l~%d, l~%d):",row,col);}}}

    玩家下棋,我这里是通过坐标落子,要定义两个变量下x,y。因为我要将每次下完棋更新再同一棋盘,而不是每下完其再在小黑屏里加一个棋盘,所以我加了个while循环。首先我们要判断x,y的定义域。接着还在注意落子的地方是不是空的。还有下棋时,你填的坐标是否正确。不正确如何处理。这些可以用判断语句来实现。

  4. // 策略AI:优先封堵玩家即将获胜的位置,否则随机落子
    void ComputerMove(char board[ROW][COL], int row, int col)
    {printf("电脑思考中...\n");// 1. 先检查电脑自己能否获胜,有机会则直接落子for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){if (board[i][j] == ' ') {// 尝试在此处落子board[i][j] = 'O';// 检查是否能赢if (CheckWin(board, row, col) == 'O') {return; // 能赢则直接落子}// 不能赢则撤销尝试board[i][j] = ' ';}}}// 2. 再检查玩家是否即将获胜,若有则封堵for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){if (board[i][j] == ' '){// 模拟玩家在此处落子board[i][j] = 'X';// 检查玩家是否能赢if (CheckWin(board, row, col) == 'X') {// 封堵这个位置board[i][j] = 'O';return;}// 玩家不能赢则撤销尝试board[i][j] = ' ';}}}// 3. 若没有需要封堵的位置,随机落子while (1) {int x = rand() % row;int y = rand() % col;if (board[x][y] == ' ') {board[x][y] = 'O';break;}}
    }

    为了让游戏不是那么简单。我加了电脑3种下棋方式:1.判断电脑下棋是否能赢,就下。2.判断玩家下棋能赢就阻断。3.最后是没有前几种情况就随机下。这里要用到CheckWin函数。其实这里的逻辑就是:预测。你们可以分析代码就知道了。这样也能找到些许乐趣。

  5. // 判断胜负
    // 返回值:
    // 'X' -> 玩家胜
    // 'O' -> 电脑胜
    // 'Q' -> 平局
    // 'C' -> 继续
    char CheckWin(char board[ROW][COL], int row, int col)
    {// 1. 检查行for (int 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];}}// 2. 检查列for (int j = 0; j < col; j++){if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ') {return board[0][j];}}// 3. 检查对角线if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ') {return board[0][0];}if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ') {return board[0][2];}// 4. 检查是否平局(无空格)int full = 1;for (int i = 0; i < row; i++){for (int j = 0; j < col; j++) {if (board[i][j] == ' '){full = 0;break;}}if (!full) break;}if (full) {return 'Q';}// 5. 继续游戏return 'C';
    }

    检查输赢:我们首先要給输赢 ,平or 继续游戏用一些符号表示作为返回值。'X' -> 玩家胜  'O' -> 电脑胜  'Q' -> 平局   'C' -> 继续。X 与 O是玩家和电脑的子的形式,可以通过行,列,对角线的检查 方便返回二维数组里的元素。平局:我们要定义一个变量full来查看棋盘是否满了。最后只剩:C 继续游戏了。

  6. void SetColor(int color) {HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleTextAttribute(hConsole, color);
    }

    这是我们要改颜色用的函数。

3.菜单

  1. 代码:
     char board[ROW][COL];char result = 'C';int player_wins = 0;int computer_wins = 0;int ties = 0;char play_again = 'Y';while (play_again == 'Y' || play_again == 'y') {// 初始化棋盘InitBoard(board, ROW, COL);printf("=== 三子棋游戏 ===\n");printf("总战绩:玩家 %d 胜 | 电脑 %d 胜 | 平局 %d 次\n",player_wins, computer_wins, ties);DisplayBoard(board, ROW, COL);// 游戏循环while (1) {// 玩家回合PlayerMove(board, ROW, COL);DisplayBoard(board, ROW, COL);// 判断结果result = CheckWin(board, ROW, COL);if (result == 'X') {printf("=== 玩家获胜! ===\n");player_wins++;break;}else if (result == 'Q') {printf("=== 平局! ===\n");ties++;break;}// 电脑回合ComputerMove(board, ROW, COL);DisplayBoard(board, ROW, COL);// 判断结果result = CheckWin(board, ROW, COL);if (result == 'O') {printf("=== 电脑获胜! ===\n");computer_wins++;break;}else if (result == 'Q') {printf("=== 平局! ===\n");ties++;break;}}// 询问是否再来一局printf("是否再来一局?(Y/N): ");scanf_s(" %c", &play_again);while (play_again != 'Y' && play_again != 'y' &&play_again != 'N' && play_again != 'n') {printf("输入无效,请输入 Y 或 N: ");scanf_s(" %c", &play_again);}}// 显示最终战绩printf("\n=== 游戏结束 ===\n");printf("最终战绩:玩家 %d 胜 | 电脑 %d 胜 | 平局 %d 次\n",player_wins, computer_wins, ties);if (player_wins > computer_wins) {printf("玩家胜率更高!\n");}else if (player_wins < computer_wins){printf("电脑胜率更高!\n");}else {printf("双方势均力敌!\n");}return 0;

    2.分析:这里我们要有  游戏继续还是结束  记录比赛比分   胜率的分析等的功能 。因此我们要定义 6个变量 。第一个while决定着游戏的继续还是结束,如果继续就初始化以及打印棋盘。第二个while来按照先玩家再电脑的顺序落子,里面要有打印棋盘刷新棋盘。还有判断落子后的效果。第三个while则是与第一个while呼应判断玩家是否玩还是不玩,以及玩家输入错误信息的处理方式。最后结束游戏打印结果胜率的分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/88711.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/88711.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GD32 CAN1和TIMER0同时开启问题

背景&#xff1a;今天在一个项目调试的时候发现了一些问题&#xff0c;由此贴记录一下问题解决的过程。使用的芯片是GD32F305VE。使用到了CAN1和TIMER0。在使用这连个外设的时候发送了一些问题。单独使用CAN1。功能正常。单独使用TIMER0。配置为输出模式。功能正常。但是当两个…

剑指offer56_数组中唯一只出现一次的数字

数组中唯一只出现一次的数字在一个数组中除了一个数字只出现一次之外&#xff0c;其他数字都出现了三次。 请找出那个只出现一次的数字。 你可以假设满足条件的数字一定存在。 思考题&#xff1a; 如果要求只使用 O(n) 的时间和额外 O(1) 的空间&#xff0c;该怎么做呢&#xf…

从语音识别到智能助手:Voice Agent 的技术进化与交互变革丨Voice Agent 学习笔记

From Research AI&#xff1a; 最近看到 Andrew Ng 的一句话让我印象深刻&#xff1a;“While some things in AI are overhyped, voice applications seem underhyped right now.”&#xff08;尽管 AI 中有些领域被过度炒作&#xff0c;语音应用却似乎被低估了&#xff09;。…

什么是Jaccard 相似度(Jaccard Similarity)

文章目录✅ 定义&#xff1a;&#x1f4cc; 取值范围&#xff1a;&#x1f50d; 举例说明&#xff1a;&#x1f9e0; 应用场景&#xff1a;⚠️ 局限性&#xff1a;&#x1f4a1; 扩展概念&#xff1a;Jaccard 相似度&#xff08;Jaccard Similarity&#xff09; 是一种用于衡量…

ragflow_多模态文档解析与正文提取策略

多模态文档解析与正文提取策略 RAGflow的文档解析系统位于deepdoc/parser/目录下,实现了对多种文档格式的统一解析处理。该系统采用模块化设计,针对不同文档格式提供专门的解析器,并通过视觉识别技术增强解析能力。本文将深入探讨RAGflow的文档解析系统的设计原理、实现细节…

数据结构栈的实现(C语言)

栈的基本概念栈是一种特殊的线性存储结构&#xff0c;是一种操作受到限制的线性表&#xff0c;特殊体现在两个地方&#xff1a;1、元素进栈出栈的操作只能从同一端完成&#xff0c;另一端是封闭的&#xff0c;通常将数据进栈叫做入栈&#xff0c;压栈等&#xff0c;出栈叫做弹栈…

【springboot】IDEA手动创建SpringBoot简单工程(无插件)

大致步骤 创建Maven工程 引入依赖 提供启动类 详细教程 创建Maven工程 修改pom.xml文件 添加父节点 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.5.3</…

独立开发第二周:构建、执行、规划

一 第二周的独立开发旅程落下帷幕。相较于第一周的适应&#xff0c;本周的核心词是“聚焦”与“执行”。 目标非常明确&#xff1a;在产品开发上取得进展&#xff1b;在个人工作节奏上&#xff0c;将上周初步形成的框架进行实践与固化。 同时&#xff0c;为至关重要的自媒体运营…

在YOLO-World中集成DeformConv、CBAM和Cross-Modal Attention模块的技术报告

在YOLO-World中集成DeformConv、CBAM和Cross-Modal Attention模块的技术报告 1. 引言 1.1 项目背景 目标检测是计算机视觉领域的核心任务之一,而YOLO(You Only Look Once)系列算法因其出色的速度和精度平衡而广受欢迎。YOLO-World是YOLO系列的最新发展,专注于开放词汇目标…

从UI设计到数字孪生实战应用:构建智慧金融的风险评估与预警平台

hello宝子们...我们是艾斯视觉擅长ui设计、前端开发、数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩!一、引言&#xff1a;传统金融风控的 “滞后困境” 与数字孪生的破局之道金融风险的隐蔽性、突…

【Linux】权限相关指令

前言&#xff1a; 上两篇文章我们讲到了&#xff0c;关于Linux中的基础指令。 【Linux】初见&#xff0c;基础指令-CSDN博客【Linux】初见&#xff0c;基础指令&#xff08;续&#xff09;-CSDN博客 本文我们来讲Linux中关于权限中的一些指令 shell命令 Linux严格来说是一个操…

前端学习3--position定位(relative+absolute+sticky)

继上一篇&#xff0c;做下拉菜单的时候&#xff0c;涉及到了position&#xff0c;这篇就来学习下~先看下position在下拉菜单中的应用&#xff1a;一、关键代码回顾&#xff1a;/* 下拉菜单容器 */ .dropdown {position: relative; /* ➊ 关键父级 */ }/* 下拉内容&#xff08;默…

APP Inventor使用指南

APP Inventor使用指南一、组件介绍二、逻辑设计设计方法&#xff1a;设计实例&#xff08;参考嘉立创教程&#xff09;点击跳转 &#xff1a; app inventor&#xff08;点不开的话需要&#x1fa84;&#x1fa84;&#x1fa84;&#x1fa84;&#x1fa84;&#xff09; 一、组…

SpringAI实现保存聊天记录到redis中

redis相关准备添加依赖我利用redission来实现<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.37.0</version> </dependency>添加配置文件spring:redis:database: 5host: 127.0.0.1…

Unity中使用EzySlice实现模型切割与UV控制完全指南

引言 在Unity中实现3D模型的动态切割是一个常见的需求&#xff0c;无论是用于游戏特效、建筑可视化还是医疗模拟。本文将全面介绍如何使用EzySlice插件实现高效的模型切割&#xff0c;并深入探讨如何通过Shader Graph精确控制切割面的UV映射。 第一部分&#xff1a;EzySlice基…

【c++学习记录】状态模式,实现一个登陆功能

状态模式建议为对象的所有可能状态新建一个类&#xff0c; 然后将所有状态的对应行为抽取到这些类中。 原始对象被称为上下文 &#xff08;context&#xff09;&#xff0c; 它并不会自行实现所有行为&#xff0c; 而是会保存一个指向表示当前状态的状态对象的引用&#xff0c;…

Docker 搭建 Harbor 私有仓库

1 部署 Harbor 注意&#xff1a;docker、docker-compose、Harbor的版本是否适配&#xff0c;这里使用的版本如下表&#xff1a; Docker版本Docker Compose版本Harbor版本v19.09.8v1.29.2v2.8.2 1.1 安装 docker-compose # 下载 docker-compose 1.29.2 版本 curl -L "h…

C++类模板继承部分知识及测试代码

目录 0.前言 1.类模板基本使用 2.类模板继承 2.1类模板继承过程中的模板参数 情况1&#xff1a;父类非模板&#xff0c;子类为模板 情况2&#xff1a;父类模板&#xff0c;子类为非模板 情况3&#xff1a;父类模板&#xff0c;子类为模板 3.STL中的模板类分析 3.1STL中…

Laravel + Python 图片水印系统:实现与调试指南

前言 本系统通过 Laravel 作为前端框架接收用户上传的图片&#xff0c;调用 Python 脚本处理水印添加&#xff0c;最终返回处理后的图片。这种架构充分利用了 Laravel 的便捷性和 Python 图像处理库的强大功能。 一、Python 水印处理脚本 from PIL import Image, ImageEnhance …

【速通RAG实战:企业应用】25、从数智化场景看RAG:是临时方案,还是终局架构?

引言&#xff1a;RAG为何成为数智化场景的"必争之地"&#xff1f; 当ChatGPT在2023年掀起生成式AI浪潮时&#xff0c;一个矛盾逐渐凸显&#xff1a;大语言模型&#xff08;LLM&#xff09;能生成流畅文本&#xff0c;却常陷入"幻觉"&#xff08;虚构事实&a…