【C语言项目】三子棋

文章目录

  • 项目思路
  • 一、分文件进行创建
  • 二、进入游戏前的目录
    • 2.1 目录的功能:
    • 2.2 目录界面:
    • 2.3 选择进入或退出游戏
    • 2.4 多次重玩功能
  • 三、画出棋盘
    • 3.1 写出棋子
    • 3.2 初始化棋盘
    • 3.2 画出棋盘的框架
    • 3.3 代码实现
  • 四、玩家落子
    • 4.1 落子逻辑
    • 4.2具体情况分类讨论
    • 4.3代码示范
  • 五、电脑落子
    • 5.1 电脑落子的逻辑
    • 5.2分类讨论
    • 5.3 代码示范
  • 六、输赢判断
    • 6.1 分类讨论
    • 6.2 行和列的三字成线
    • 6.3 对角线的三字成线
    • 6.4平局
    • 6.5 代码实现:
      • 6.5.1 判断输赢
      • 6.5.2 判断棋盘是否满
  • 七、完整代码示范(无注释)
    • 7.1 test.c
    • 7.2 game.h
    • 7.3 game.c
    • 7.4 运行图片(示例)
  • 写在最后

项目思路

  1. 分文件进行创建
  2. 进入游戏前的目录
  3. 画出棋盘
  4. 玩家落子
  5. 电脑落子
  6. 输赢判断

接下来,我们分步骤进行详细的解释说明。

一、分文件进行创建

在具体的项目实施中,我们需要分成不同的文件进行创建和书写,以此来保证项目的模块化。
那么在三子棋的实际书写中,

  • 源文件:
    • 测试游戏用的代码文件用test.c或者日期.c来作为文件名
    • 游戏实现的底层代码用game.c文件作为文件名
  • 头文件
    • 游戏实现中使用的各个函数的声明,以及包含的其他库函数的头文件需要写在头文件game.h文件里
      如图所示:
      image.png

二、进入游戏前的目录

2.1 目录的功能:

  1. 在游戏开始时,给玩家视觉上的帮助和提示
  2. 让玩家可以选择进入游戏或者退出游戏
  3. 将游戏形成一个可以不断重玩的循环
    接下来,我们分步骤进行书写:

2.2 目录界面:

void menu()
{printf("***********************\n");printf("*****1、开始游戏*******\n");printf("*****0、退出游戏*******\n");printf("***********************\n");
}

接着,在main函数里面进行调用:

int main()
{menu();return 0;
}

这样目录的表面就写好了,接下来需要写玩家选择进入游戏和退出游戏的功能了

2.3 选择进入或退出游戏

选择功能的逻辑:1进入游戏,0退出游戏。
显然,必备的库函数有scanf,switch、case和default。为了让游戏的体验更加良好,可以再加一个printf增加视觉上的游玩帮助,用户友好。
那么根据刚刚的逻辑,可以写出如下的选择结构,其中game函数虽然还没有写出来,但是整体的框架可以先确定下来,之后再往里慢慢写内容。

int main()
{int input = 0;menu();printf("请选择:--->");scanf("%d", &input);switch (input){case 1:game();break;case 0:printf("退出游戏\n");break;default:printf("非法输入!请重试\n");break;}return 0;
}

2.4 多次重玩功能

多次重玩功能需要一个循环结构。由于开游戏的时候菜单页面必定会打印,所以菜单页面至少会运行一次,故可以使用 do while 循环结构
使用这个结构的同时,判断停止的条件就可以直接填写输入项,因为输入0是退出,而while为非0数字运行,所以刚好可以填写输入项,逻辑自洽。

int main()
{int input = 0;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;
}

三、画出棋盘

3.1 写出棋子

在画棋盘的框架之前,需要找一个容器把棋子容纳进去,而3x3的棋盘,很明显用二维数组来进行盛放最为合适。
故可以写一个二维数组,当做棋盘,下棋就下在二维数组里面。

	char board[ROW][COL] = { 0 };

直接写到void game函数里面就行了。

3.2 初始化棋盘

棋盘应该是全部空的,而不是初始化那样全部是0,所以可以写一个函数把数组里面的数据全部初始化成空格。
逻辑:遍历数组并赋值
代码示范:

int i,j = 0 ;
void InitBoard(char board[ROW][COL], int row, int col)
{for (i = 0; i < row; i++){for (j = 0; j < col; j++){board[i][j] = ' ';}}
}

3.2 画出棋盘的框架

棋盘虽然也可以不画,直接9个字符位置下棋,但是太不美观,所以可以画一个美观一些的棋盘。
参考已经画出来的:
image.png
这个棋盘显然就比9个字符位美观多了,接下来就分步骤拆解它的输出:
image.png
第一行看起来是三个空格一个竖杠,但实际上,这里需要打印的不仅是棋盘的线,还要打印数组里面的棋子。
同时,代码不能写死,可以在game.h里面定义一个ROW(行)COL(列),这样的话,想要十乘十的棋盘,直接在game.h里面改数字就可以直接改全部的行列了。
game.h内:

#define ROW 3
#define COL 3

这样,接下来的棋盘打印就可以用ROW和COL代替原来的3了。

3.3 代码实现

为了打印棋盘这个功能,我们需要声明并定义一个函数,使用函数进行各个不同模块的功能实现是在项目中十分有必要的。
game.h内:
接受的数据:棋盘数组、行、列

void DisplayBoard(char board[ROW][COL], int row, int col);

而函数的定义则放在game.c内:

void DisplayBoard(char board[ROW][COL], int row, int col)
{for (i = 0; i < row; i++){//打印数据行for (j = 0; j < col; j++)//使用for循环打印每一格的数据{printf(" %c ", board[i][j]);if (j < COL - 1)//因为棋盘边缘没有边界线,所以少打印一个“|”printf("|");}printf("\n");//这里的换行需要留意别漏了//打印分割线行if (i < row - 1) {for (j = 0; j < row; j++)//使用for循环打印每一格的分割线{printf("---");if (j < row - 1)//打印“|”printf("|");//同理}}printf("\n");//这里的换行需要留意别漏了}
}

四、玩家落子

4.1 落子逻辑

玩家落子的逻辑是输入几行几列的坐标,然后棋盘在对应的位置上出现一个符号,相当于是落子。

4.2具体情况分类讨论

  • 当玩家落子正确
    • 将“ * ”放入数组
  • 当玩家落子不在棋盘内
    • 打印提示,让玩家重新输入
  • 当玩家落子时棋盘已经有子
    • 打印提示,让玩家重新输入

这三种情况需要不同的代码来实现

  1. 判断是否在棋盘内,可以用坐标是否在棋盘的范围内的if语句判断
  2. 判断是否已经落子,可以用数组里的数据是否为空格来判断,若不是空格,即有子,不能下
  3. 如果都可以,就放入数组一个 * 号,然后break跳出循环

经过分析,不难发现,这里的循环是直到下到正确的棋才会跳出循环,所以只需使用while循环,条件里填1或者其他非0数字,就可以一直循环了。

4.3代码示范

头文件中声明函数

void PlayerMove(char board[ROW][COL], int row, int col);

源文件中定义函数

void PlayerMove(char board[ROW][COL], int row, int col)
{printf("请输入棋子坐标:");while (1){scanf("%d %d", &i, &j);//输入坐标if (i > 0 && i<= row && j>0 && j <= col)//判断是否在棋盘内{if (board[i - 1][j - 1] == ' ')//判断是否有子{board[i - 1][j - 1] = '*';break;}elseprintf("已经落子,请重新输入\n");}elseprintf("非法输入\n");}
}

五、电脑落子

5.1 电脑落子的逻辑

首先电脑落子是需要一个随机性的,那么就可以使用srand和rand函数(伪随机数),加上时间戳构成一个真随机数,再利用这个真随机数取一个模,就可以在棋盘里下棋了。

5.2分类讨论

至于实际上的分类逻辑,和玩家下棋不太一样,只有两种情况:

  • 当电脑落子正确
    • 将“ # ”放入数组
  • 当电脑落子时棋盘已经有子
    • 电脑重新落子

5.3 代码示范

函数声明

void ComputerMove(char board[ROW][COL], int row, int col);

函数定义

void ComputerMove(char board[ROW][COL], int row, int col)
{while (1) //和玩家落子同理,不下对棋就继续下,故while(1){i = rand() % row;//行的随机数取模j = rand() % col;//列的随机数取模if (board[i][j] == ' ')//判断是否是空位{board[i][j] = '#';//落子break;//跳出循环}}
}

这里注意rand需要和srand函数配合使用
在main函数中:

	srand((unsigned int)time(NULL));

在头文件中:

#include <stdio.h> //printf和scanf函数需要
#include <stdlib.h> //随机数需要用
#include <time.h> //时间戳需要用

六、输赢判断

输赢判断这里,由于规则是三字成线,且存在平局的情况,故需要分类讨论。

6.1 分类讨论

  1. 行三字成线
  2. 列三字成线
  3. 对角线三字成线
  4. 平局

6.2 行和列的三字成线

  1. 直接判断第一个棋子和第二个棋子是否相等,再并上第二个棋子与第三个棋子是否相等。
  2. 在判断相等的同时,需要判断是否是空格,如果是空格那就是没人赢。所以需要并上一个不等于空格的条件。

6.3 对角线的三字成线

  1. 同行列的判断条件,只是数位坐标需要换成对角线的。

6.4平局

  1. 当棋盘落满,且没有人胜出的时候,就可以判定为平局。
  2. 棋盘是否落满的逻辑:遍历二维数组,并判断是否为空格。如果每一个位置都不是空格,那就是落满了,返回1,如果有任何一个位置是空格,立马返回0。

6.5 代码实现:

6.5.1 判断输赢

char IsWin(char board[ROW][COL], int row, int col)
{//行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[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];//平局if (IsFull(board,row,col) == 1)return 'D';return 'C';
}

6.5.2 判断棋盘是否满

int IsFull(char board[ROW][COL], int row, int col)
{for (i = 0; i < row; i++){for (j = 0; j < col; j++){if (board[i][j] == ' ')//遍历并判断是否是空格return 0;}}return 1;
}

注:以上的函数都需要在头文件中进行声明,声明格式同上

七、完整代码示范(无注释)

7.1 test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{printf("***********************\n");printf("*****1、开始游戏*******\n");printf("*****0、退出游戏*******\n");printf("***********************\n");
}void game()
{char ret = 0;char board[ROW][COL] = { 0 };InitBoard(board,ROW,COL);DisplayBoard(board,ROW,COL);while (1){PlayerMove(board, ROW, COL);DisplayBoard(board, ROW, COL);ret=IsWin(board, ROW, COL);if (ret != 'C')break;ComputerMove(board, ROW, COL);DisplayBoard(board, ROW, COL);ret = IsWin(board, ROW, COL);if (ret != 'C')break;}//平局Drawif (ret == '#')printf("电脑赢\n");else if (ret == '*')printf("玩家赢\n");else if (ret == 'D')printf("平局\n");elseprintf("程序出错\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;
}

7.2 game.h

#pragma once
#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);
int IsFull(char board[ROW][COL], int row, int col);

7.3 game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//初始化棋盘为空格
int i, j = 0;
void InitBoard(char board[ROW][COL], int row, int col)
{for (i = 0; i < row; i++){for (j = 0; j < col; j++){board[i][j] = ' ';}}
}//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{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 < row; j++){printf("---");if (j < row - 1)printf("|");}}printf("\n");}}//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{printf("请输入棋子坐标:");while (1){scanf("%d %d", &i, &j);if (i > 0 && i<= row && j>0 && j <= col){if (board[i - 1][j - 1] == ' '){board[i - 1][j - 1] = '*';break;}elseprintf("已经落子,请重新输入\n");}elseprintf("非法输入\n");}
}//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{while (1){i = rand() % row;j = rand() % col;if (board[i][j] == ' '){board[i][j] = '#';break;}}
}int IsFull(char board[ROW][COL], int row, int col)
{for (i = 0; i < row; i++){for (j = 0; j < col; j++){if (board[i][j] == ' ')return 0;}}return 1;
}char IsWin(char board[ROW][COL], int row, int col)
{//行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[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];//平局if (IsFull(board,row,col) == 1)return 'D';return 'C';
}

7.4 运行图片(示例)

c7167e9826357219cd4173cd75d5eae.png

写在最后

如果本文对您有帮助,可不可以给我一个小小的点赞呀❤~您的支持是我最大的动力。

博主小白一枚,才疏学浅,难免有所纰漏,欢迎大家讨论和提出问题,博主一定第一时间改正。

谢谢观看嘿嘿(๑•̀ㅂ•́)و✧~!

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

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

相关文章

抖斗音直播间评论引流助手,支持直播间喊话+视频评论区喊话=到指定直播间引流精准粉丝【永久脚本+详细教程】

如果你觉得直播间发言手动太麻烦了&#xff0c;或许这个自动工具能帮到你&#xff01; 1.开始运行前&#xff0c;需要手动去打开打开直播间或者视频评论区&#xff0c;再运行脚本。 2.脚本就是模拟人工操作&#xff0c;在相应的APP里进行评论&#xff0c;无突破APP限制功能。…

【Kubernetes运维篇】ingress-nginx实现业务灰度发布详解

文章目录 一、理论&#xff1a;实现灰度发布的几种场景1、场景一&#xff1a;将新版本灰度给部分用户2、场景二&#xff1a;按照比例流程给新版本3、实现灰度发布字段解释 二、实践&#xff1a;1、实验前提环境2、基于Request Header(请求头)进行流量分割3、基于Cookie进行流量…

93.qt qml-自定义Table优化(新增:水平拖拽/缩放自适应/选择使能/自定义委托)

之前我们更新了90.qt qml-Table表格组件(支持表头表尾固定/自定义颜色/自定义操作按钮/排序)_qml 表格_诺谦的博客-CSDN博客 但是一直没出源码,是因为该demo还存在问题,那就是表头表尾固定下,如果是半透明状态下,会看到表头表尾固定后的内容,所以只能重构代码,不能使用重…

Vue3组合式API+TypeScript写法入门

文章目录 前言1.reactive2.ref3.props4.computed5.emit6.watch总结 前言 参考Vue3官网. 本篇以组合式API为例, 但不包含setup语法糖式写法. 原本打算结合class-component, Vue3不推荐就不用了: OverView|Vue Class Component. 而且是不再推荐基于类的组件写法, 推荐单文件组件…

Android App 持续集成性能测试:启动流量

目录 前言&#xff1a; get app UID 获取流量数据 获得启动流量数据 总结 前言&#xff1a; Jenkins 是一种开源的持续集成工具&#xff0c;可以帮助我们更加方便地进行软件开发和测试工作。通过 API 远程管理 Jenkins 可以帮助我们更加方便地进行 Jenkins 的配置和管理工…

react实现路由跳转动画

下载插件 npm i react-transition-group 配置路由 import { createBrowserRouter as ReactRouter,Navigate } from "react-router-dom";import App from ../App.js import Login from "../view/login.js"; import Home from "../home.js"; co…

了解 3DS MAX 3D摄像机跟踪设置:第 4 部分

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 项目设置 步骤 1 打开“后效”。 打开后效果 步骤 2 转到合成>新合成以创建新合成。 将“宽度”和“高度”值分别设置为 1280 和 720。将帧速率设置为 25&#xff0c;将持续时间设置为 12 秒。单…

Flask 文件上传,删除上传的文件

目录结构 app.py from flask import Flask, request, render_template, redirect, url_for import osapp Flask(__name__) BASE_DIR os.getcwd() UPLOAD_FOLDER os.path.join(BASE_DIR, testfile)app.route(/) def home():files os.listdir(UPLOAD_FOLDER)return render_t…

欧盟新规,燃油噩梦?2025年起,高速公路每60公里设立一处快充站

根据外媒The Verge报道&#xff0c;欧洲电动汽车用户将获得更多便捷的待遇&#xff0c;同时还能减少有害温室气体排放&#xff0c;这得益于欧盟理事会最新通过的法规。 根据欧盟的法规要求&#xff0c;自2025年起&#xff0c;TEN-T高速公路系统在欧洲将需要每隔60公里设立一座高…

Langchain 和 Chroma 的集成

Langchain 和 Chroma 的集成 1. Chroma2. 基本示例​3. 基本示例(包括保存到磁盘)4. 将 Chroma Client 传递到 Langchain ​5. 基本示例(使用 Docker 容器)6. 更新和删除7. 带分数的相似性搜索​ 1. Chroma Chroma 是一个人工智能原生开源矢量数据库&#xff0c;专注于开发人员…

ES6基础知识六:你是怎么理解ES6中 Promise的?使用场景?

一、介绍 Promise&#xff0c;译为承诺&#xff0c;是异步编程的一种解决方案&#xff0c;比传统的解决方案&#xff08;回调函数&#xff09;更加合理和更加强大 在以往我们如果处理多层异步操作&#xff0c;我们往往会像下面那样编写我们的代码 doSomething(function(resu…

[ 容器 ] Harbor 私有仓库的部署与管理

目录 一、什么是Harbor二、Harbor的特性三、Harbor的构成四、Harbor 部署五、关于 Harbor.cfg 配置文件中有两类参数&#xff1a;所需参数和可选参数六、维护管理Harbor 一、什么是Harbor Harbor 是 VMware 公司开源的企业级 Docker Registry 项目&#xff0c;其目标是帮助用户…

jQuery的DOM操作之笔记总结

jQuery的DOM操作之笔记总结 首先我们来介绍一下什么是DOM 简述&#xff1a; 1.DOM全称Document Object Model&#xff08;文档对象模型&#xff09;。 2.每个文档都是一棵DOM结构的树&#xff0c;文档里的很多元素&#xff0c;就像树上的很多节点&#xff0c;或是分叉的树枝…

知识库数据导出为excel-使用JavaScript实现在浏览器中导出Excel文件

我们智能客服知识库机器人已经开发完成&#xff0c;后端数据库是使用的qdrant向量数据库&#xff0c;但是该数据库并没有导出备份功能&#xff0c;所以我按简单的纯前端实现知识库导出excel数据 使用第三方库(如SheetJS) SheetJS是一个流行的JavaScript库&#xff0c;可帮助处理…

腾讯云 Cloud Studio 实战训练营——快速构建React完成点餐H5页面

目录 ​编辑 一、前言 1、什么是腾讯云 Cloud Studio 2、本文实验介绍 二、前期准备工作 1、注册 Cloud Studio 2、初始化工作空间 三、开发一个简版的点餐系统页面 1、安装依赖 1.1、安装 antd-mobile 1.2、安装 less 和 less-loader 1.3、暴露 webpack 配置文件 …

OpenCV实现照片换底色处理

目录 1.导言 2.引言 3.代码分析 4.优化改进 5.总结 1.导言 在图像处理领域&#xff0c;OpenCV是一款强大而广泛应用的开源库&#xff0c;能够提供丰富的图像处理和计算机视觉功能。本篇博客将介绍如何利用Qt 编辑器调用OpenCV库对照片进行换底色处理&#xff0c;实现更加…

Stable Diffusion生成艺术二维码

Stable Diffusion生成艺术二维码 文章会有浏览问题&#xff0c;点击此处查看原文 首先需要一个Stable Diffusion服务环境&#xff0c;《Stable Diffusion服务环境搭建&#xff08;远程服务版&#xff09;》如果你已经有了那就忽略 一、准备一个比较好的二维码底图 首先解析二…

STC12C5A系列单片机片内看门狗的应用

wdt.c #include "wdt.h"void wdt_init(void) {WDT_CONTR 0x24; // 0010 0100 - 1.1377s }void wdt_feed(void) {WDT_CONTR | 0x10; // 喂狗 }wdt.h #ifndef _WDT_H_ #define _WDT_H_#include "stc12c5a60s2.h"// 函数声明 extern void wdt_init(void); …

uniapp开启消息通知/提示(使用uniPush)

亲爱的小伙伴们&#xff0c;最近对uniPush有稍许研究&#xff0c;当前研究出一些心得&#xff0c;现在分项给大家&#xff0c;希望对大家的uniapp之路有所帮助。本次的教程是教会你如何使用uniPush自定义消息通知&#xff0c;同时还附带添加消息提示音添加&#xff0c;多的不说…

【Spring MVC学习】连接 接收请求参数 响应返回参数

目录 前言&#xff1a;认识Spring MVC &#x1f337;1、什么是MVC&#xff1f; 一、建立连接&#xff08;5个注解&#xff09; &#x1f337;1、RequestMapping注解:注册接⼝的路由映射&#xff08;默认返回页面&#xff09; &#x1f337;2、ResponseBody注解&#xff1a…