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

c语言实战:从零开始实现三子棋游戏 | 来自我的第一个完整c语言项目

经过两天时间,我完成了我的小项目--三子棋,终于能成功运行啦!!!

在之前的时间里,我一直仅仅是在完成一些c语言课后小作业,并没有真正自己动手完成一个完整的项目。后来在一本有关编程的书里面,说真正动手才能提升自己的编程能力,所以我也动手想完成这个已经有很多人写过的三子棋项目。经过不断调试,我终终终终终于完成!

今天记录一下我整个开发过程,希望想入手这个小项目的小伙伴们能少走一些弯路/

一、为什么想写三子棋呢

我在最近一段时间,学习完了c语言,c语言的很多知识点很散,不知道给怎么串起来。

而三子棋这个项目犹如一个大篓子,用到的知识点不多,但很全面,像什么二维数组、随机数生成、文件调用等等它都有,它规则并不算难,还挺适合新手的,完整完成还非常有成就感。

二、项目的整体思路

三子棋游戏规则很简单:3x3的棋盘,玩家下*,电脑下#,谁能先完成横、竖、斜连成线,就算谁赢,如果棋盘下满没人赢就算是平局。

整个流程也是非常清晰明了:

1.打印一个游戏菜单,选择开始游戏或者退出。

2.定义一个空棋盘。

3.初始化棋盘,将所有位置设为空格。

4.打印这个空棋盘。

5.玩家输入坐标下棋->打印棋盘->判断是否胜利。

6.电脑随机下棋->打印棋盘->判断是否胜利。

7.循环往复,直到分出胜负亦或是平局。

三、多文件结构

我进行编译时把代码分成了三个文件,这样思路清晰多了。

game.h:头文件,放函数声明、宏定义以及一些特定头文件。

game.c:游戏功能函数的实现。

test.c:主函数,游戏主要逻辑,菜单。

这样一来,代码可读性大大提升,分类好,后续修改也更加方便。

四、核心功能实现

1.棋盘定义以及初始化

我使用char sand[3][3]这个二维数组作为棋盘,将所有位置初始化为' ' ,这样一来,打印的就是空棋盘。

2.打印棋盘

我用 _ 和 | 来画棋盘,要注意的是最后一行不要再打印下划线_,不好看。

3.玩家下棋

玩家输入坐标如 1 2 ,中间用空格隔开,同时有两个判断:

·坐标要在1~3范围内,防止越界。

·输入坐标是否被占用。

均合法,'*' 代替 ' ' 。

4.电脑下棋

用rand()生成随机坐标,并且判断此位置是否为空格,若是空,就下 #。

5.判断胜负

· 三行是否有相同的非空格字符(本人在此犯过严重错误,后面会提到)。

· 三列是否有相同的非空格字符。

· 两条对角线是否有相同的非空格字符。

· 棋盘满了没人胜利则为平局。

五、编译时踩的坑

1.char类型,我却使用了双引号 “ ” 。

2.判断相等 == 写成了赋值 = ,逻辑全错,运行时我一脸懵。

3.我判断胜负时,没加 != ' ',导致我三个空格也被判赢,游戏一开始就结束了。

六、运行效果展示

1.选择1开始游戏,选择0结束游戏。

2.玩家下棋、电脑下棋(交替落子)

3.玩家胜利/电脑胜利/平局

·玩家胜利

·电脑胜利

·平局

七、总结

说起来,编译时最重要的就是严谨 严谨 再严谨,每一个细小的东西都不能放过。

在未来严谨我将会一直贯彻。

做完这个项目,我释然的笑了,像是看见自己的“孩子”一样,也许从这个时候开始,我不再仅仅是“hello wold”,更是在“change wold”

八、完整源码

我使用的是vs2022这个编译器。

· game.h

#pragma once
#include<stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 3
#define COL 3

void insand(char sand[ROW][COL], int row, int col);
void dysand(char sand[ROW][COL], int row, int col);
void player_move(char sand[ROW][COL], int row, int col);
void dy_move(char sand[ROW][COL], int row, int col);
char pd_move(char sand[ROW][COL], int row, int col);
void pc_move(char sand[ROW][COL], int row, int col);

· game.c

#define _CRT_SECURE_NO_WARNINGS
#include"game.h"

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

void dysand(char sand[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf(" %c ", sand[i][j]);
if (j != 2)
{
printf("|");
}
}
printf("\n");
if (i != 2)
{
printf("-----------");
}
printf("\n");
}
}
int m, n;
void player_move(char sand[ROW][COL], int row, int col)
{

while (1)
{
printf("请玩家移动:");
scanf("%d %d", &m, &n);
if ((m <= ROW && m > 0) && (n <= COL && n > 0))
{
if (sand[m - 1][n - 1] == ' ')
{
sand[m - 1][n - 1] = '*';
break;
}
else
{
printf("当前坐标已经有棋子了\n");
}
}
else
{
printf("输入有误");
}
}
}

void dy_move(char sand[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf(" %c ", sand[i][j]);
if (j != 2)
{
printf("|");
}
}
printf("\n");
if (i != 2)
{
printf("-----------");
}
printf("\n");
}
}


static int ml(char sand[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (sand[i][j] == ' ')
{
return 0;//没满
}
}
}
return 1;//满了
}

char pd_move(char sand[ROW][COL], int row, int col)
{
//玩家赢了
//平局?
//玩家没有赢 继续玩
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (sand[0][j]!=' ' && sand[0][j] == sand[1][j] && sand[1][j] == sand[2][j])
{
return sand[0][j];
}
else if(sand[i][0] != ' ' && sand[i][0] == sand[i][1] && sand[i][1] == sand[i][2] )
{
return sand[i][0];
}
else if(sand[0][0] != ' ' && sand[0][0] == sand[1][1] && sand[1][1] == sand[2][2])
{
return sand[0][0];
}
else if (sand[0][2] != ' ' && sand[0][2] == sand[1][1] && sand[1][1] == sand[2][0])
{
return sand[0][2];
}
}
}
if (ml(sand, ROW, COL) == 1)
{
return 'A';//平局
}
return 'B';//没满继续玩
}

void pc_move(char sand[ROW][COL], int row, int col)
{
printf("电脑移动:\n");
while (1)
{
int x = rand() % row;//[0-row)
int y = rand() % col;//[0-col)
if (sand[x][y] == ' ')
{
sand[x][y] = '#';
break;
}
}
}

· test.c

#define _CRT_SECURE_NO_WARNINGS
#include"game.h"

//菜单
void menu()
{
printf("------------------------------\n");
printf("----------1.开始游戏----------\n");
printf("----------0.结束游戏----------\n");
printf("------------------------------\n");
}

void game()
{
//1.定义一个空棋盘
char sand[ROW][COL];

//2.初始化棋盘
insand(sand, ROW, COL);

//3.打印这个棋盘
dysand(sand, ROW, COL);

char ret = 0;
//4.进入游戏
while (1)
{
//5.玩家移动
player_move(sand, ROW, COL);

//6.移动完后打印棋盘
dy_move(sand, ROW, COL);

//7.判断玩家是否胜利
ret = pd_move(sand, ROW, COL);
if (ret != 'B')
{
break;
}
//8.电脑移动
pc_move(sand, ROW, COL);

//9.移动完后打印棋盘
dy_move(sand, ROW, COL);

//10.判断电脑是否胜利
ret = pd_move(sand, ROW, COL);
if (ret != 'B')
{
break;
}

}
if (ret == 'A')
{
printf("平局\n");

}
else if (ret == '*')
{
printf("玩家胜利\n");
}
else if (ret == '#')
{
printf("电脑胜利\n");
}


}


int main()
{

srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("请输入你的操作:");
scanf("%d", &input);
switch (input)
{
case 0:
printf("游戏结束\n");
break;
case 1:
game();
break;
default:
printf("输入有误,请输入1或者0\n");
break;
}

} while (input != 0);
return 0;
}

http://www.cnnetsun.cn/news/1697694.html

相关文章:

  • OpenClaw版本升级:Qwen3-32B镜像无缝迁移与配置保留方案
  • mujoco无人机实战建模(一)
  • 嵌入式开发者的编程能力提升与重构实践
  • Hyperf方案 多环境配置管理
  • Windows下OpenClaw安装避坑指南:Qwen3.5-9B-AWQ-4bit联调实录
  • OpenClaw长期运行优化:Qwen3.5-9B-AWQ-4bit内存泄漏排查
  • 《贾子科学真理主权理论(Truth Sovereignty of Science)》
  • M5Unit-DigiClock模块:基于I²C的即插即用数字时钟解决方案
  • RTOS任务切换机制与触发时机详解
  • 迷彩伪装目标检测数据集VOC+YOLO格式839张1类别
  • PicoBricks-for-ESP32库详解:面向教育的ESP32硬件抽象封装
  • 2006 Text 3
  • OpenClaw深度学习:千问3.5-9B模型微调实战
  • M2LOrder轻量级部署教程:Miniconda torch28环境隔离与依赖冲突解决
  • Go语言的接口与多态
  • NDefs:嵌入式C语言零开销宏库与类型安全实践
  • 嵌入式差分升级技术解析与实践指南
  • (复现)基于自适应滑模控制(ASMC)和神经网络容错控制的主从式无人机编队控制研究(Matlab代码实现)
  • C语言结构体详解:从基础到高级应用
  • Linux系统调用原理与性能优化实践
  • python unique
  • TomServo:嵌入式低功耗多路舵机串行控制库
  • 嵌入式RTP协议栈:面向实时音频的低延迟传输设计
  • STM32异步Web服务器:零拷贝HTTP/WS工业网关实战
  • 【CPP 深度学习】PyTorch On CPP 系列课程 第一章 01 :入门与环境搭建 【Ai Infra 3.0】[PyTorch CPP LibTorch 硕士研一课程]
  • 基于FPGA的TCP乱序重排算法的实战实现与解析:自创算法的Verilog编码及性能验证
  • 深入解析seamless-immutable:特殊对象处理的终极指南
  • LLMLingua未来展望:AI推理加速技术的终极发展趋势
  • VirtualAPK插件监控告警终极指南:钉钉/企业微信通知配置
  • GeoIP2-CN的IP段合并工具开发:命令行参数详解