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;
}
