当前位置:网站首页>2021-08-14三子棋
2021-08-14三子棋
2022-07-26 10:36:00 【竹某】
目录
#1 代码
主函数main.cpp
#include <stdio.h>
#include "supportGaming.h"
int main() {
/*chessBoard oneBoard;
piece onePiece; onePiece.player = 1; onePiece.x = 1; onePiece.y = 1;
play(&oneBoard, &onePiece);
printBoard(&oneBoard);
printf("%d\n",isDetermined(&oneBoard));*/
printf("最后的结果是:%d\n",game());
return 0;
}头文件supporGaming.h
//棋盘
struct chessBoard {
int num = 0;//棋盘上棋子的数目
int arr[3][3] = { 0 };//棋盘,0代表没有子,1代表先手下的棋子,2代表后手下的棋子
};
//一次落子
struct piece {
int player = 0;//先手玩家的棋子为1,后手玩家的棋子为2
int x = 0;//棋盘的第x行
int y = 0;//棋盘的第y列
//注意对于棋手,x和y的范围均为[1,3],转化为数组的下标均要减去1
};
int isDetermined(chessBoard* oneBoard);//读入某一棋盘,返回结果.
void play(chessBoard* board, piece* onePiece);//某位棋手对指定棋盘做一次指定修改.
void printBoard(chessBoard* board);//打印棋盘
int game();
int examine(chessBoard* board, piece* onePiece);检查每次落子是否合法:不能落在已经下过的地方源文件supportGaming.cpp
#include <stdio.h>
#include "supportGaming.h"
//棋盘
//进行一次三子棋游戏,返回对弈结果。先手方胜,返回1;后手方胜,返回2;若和棋,则返回0.
int game() {
//生成棋盘和棋子
int result = 0;
chessBoard qipan;
piece player1;
piece player2;
player1.player = 1;
player2.player = 2;
//当没有分出胜负的时候(isDetermined返回值为0)继续一个回合&&棋盘没满
printBoard(&qipan);
while (!(result = isDetermined(&qipan))) {
//没有分出胜负且棋盘满了,直接返回和棋。不能进行下一个回合
if (qipan.num == 9) {
result = 0;
break;
}
do {
printf("请一号棋手下棋\n");
scanf("%d %d", &player1.x, &player1.y);
} while (examine(&qipan, &player1) == 0);
play(&qipan, &player1);
printBoard(&qipan);
if (qipan.num == 9) {
result = 0;
break;
}
if ((result = isDetermined(&qipan)) != 0) {
break;
}
do {
printf("请二号棋手下棋\n");
scanf("%d %d", &player2.x, &player2.y);
} while (examine(&qipan, &player2) == 0);
play(&qipan, &player2);
printBoard(&qipan);
if ((result = isDetermined(&qipan)) != 0) {
break;
}
}
result = isDetermined(&qipan);
return result;
}
//判断某一棋盘的结果,先手方胜,返回1;后手方胜,返回2;若和棋,则返回0.
int isDetermined(chessBoard* oneBoard) {
//arr为棋盘,一个二维数组(的首地址)
int* row1 = (int*)(oneBoard->arr);//第一行的首地址
int* row2 = row1 + 3;//第二行的首地址
int* row3 = row2 + 3;//第三行的首地址
//这几个条件不可能同时发生,所以判断有先后顺序也没有关系。
//第一行和为3/6时分出胜负
if (*row1 * *(row1 + 1) * *(row1 + 2) == 1)
return 1;
if (*row1 * *(row1 + 1) * *(row1 + 2) == 8)
return 2;
//第二行和为3/6时分出胜负
if (*row2 * *(row2 + 1) * *(row2 + 2) == 1)
return 1;
if (*row2 * *(row2 + 1) * *(row2 + 2) == 8)
return 2;
//第三行和为3/6时分出胜负
if (*row3 * *(row3 + 1) * *(row3 + 2) == 1)
return 1;
if (*row3 * *(row3 + 1) * *(row3 + 2) == 8)
return 2;
//第一列和为3/6时分出胜负
if (*row1 * *row2 * *row3 == 1)
return 1;
if (*row1 * *row2 * *row3 == 8)
return 2;
//第二列和为3/6时分出胜负
if (*(row1+1) * *(row2 + 1) * *(row3 + 1) == 1)
return 1;
if (*(row1+1) * *(row2 + 1) * *(row3 + 1) == 8)
return 2;
//第三列和为3/6时分出胜负
if (*(row1 + 2) * *(row2 + 2) * *(row3 + 2) == 1)
return 1;
if (*(row1 + 2) * *(row2 + 2) * *(row3 + 2) == 8)
return 2;
//对角线和为3/6分出胜负
if (*(row1 + 0) * *(row2 + 1) * *(row3 + 2) == 1)
return 1;
if (*(row1 + 0) * *(row2 + 1) * *(row3 + 3) == 8)
return 2;
//反对角线和为3/6分出胜负
if (*(row1 + 2) * *(row2 + 1) * *(row3 + 0) == 1)
return 1;
if (*(row1 + 2) * *(row2 + 1) * *(row3 + 0) == 8)
return 2;
return 0;
}
//某位棋手对指定棋盘做一次指定修改.
void play(chessBoard* board, piece* onePiece ) {
//board:指定的棋盘
//piece:一次落子,含有棋手和落子位置(x,y)
const int x = onePiece->x - 1;
const int y = onePiece->y - 1;
const int player = onePiece->player;
*((int*)(board->arr) + 3 * x + y) = player;
(board->num)++;
return;
}
//打印棋盘
void printBoard(chessBoard* board) {
for (int i = 0; i < 3;i++) {
for (int j = 0; j < 3;j++) {
printf("%d ", *((int*)(board->arr) + 3 * i + j));
//这里的代码如果去掉(int *)的话就会出错.这说明二维数组的数组名的意义可能不是&arr[0][0].
}
printf("\n");
}
return;
}
//检查每次落子是否合法:不能落在已经下过的地方
int examine(chessBoard* board ,piece* onePiece) {
const int x = onePiece->x-1;
const int y = onePiece->y-1;
const int* addr = (int*)(board->arr);
if (*(addr + 3 * x + y) == 0) {
return 1;//落子在0处合法
}
return 0;//否则非法
}
//在编写函数时,创建了很多局部变量用来重新接受函数参数。这在某种程度上浪费了内存(但是时间不长),但是却能简化后续的代码和帮助理清思路。#2 修复的几个bug
1.修复了一号玩家胜利后,二号玩家还需要输入的问题
2.修复了玩家下棋可以下在重复位置的问题
#3积累的经验
1.二维数组的数组名的意义需要明确。从时间来看,int arr[3][3]的arr是不是int*类型的指针,值等于&arr[0][0],但是意义不是二维数组的首地址。如果想要使用二位数组的首地址来进行指针运算,需要使用强制类型转换:arr1=(int* )(arr)。转换之后的指针的意义就相当于二维数组的首地址,可以使用*(arr1+3*i+j)取出[i,j]下标的元素。
2.编写一个工程所使用的多文件编程还不是特别明白,尽管这次是使用对了。
3.分析一个复杂的工程时,还是应该遵循TDD的思路,让测试驱动开发。要仔细考虑程序实际运行时的基本功能和基本对象,以及可能出现的问题。具体来讲,需要先在main函数中搭好程序运行的框架,抽象出基本功能和基本对象,在头文件中声明适当的函数和结构体(对于C语言而言),并且写好注释。最后才是在源文件中加以具体的实现(函数)。
否则,可能会出现一些意想不到的问题。
鹏哥就是先搭建整体的框架,再去实现特定工程的;此外,他特别注意代码的可移植性。
边栏推荐
- Uniapp uses the simple method signalr (only for web debugging, cannot package apps)
- Navicat15连接本地虚拟机的Mysql(Centos7)
- Interview questions and answers for the second company (2)
- 第5期:大学生入职必备技能之二
- Introduction to data analysis | kaggle Titanic mission
- 文案秘籍七步曲至----文献团队协作管理
- 链式方法调用的事务问题剖析
- Simple use of json-c Library -- converting JSON files to struct
- MD5 encryption
- .NET操作Redis Set无序集合
猜你喜欢

Oracle cannot start tnslistener service cannot start

el-table实现可编辑表格

干货likeshop外卖点餐系统开源啦100%开源无加密

【机器学习小记】【人脸识别】deeplearning.ai course4 4th week programming

sigmod 函数与softmax 函数对比

第7期:内卷和躺平,你怎么选

STM32 Alibaba cloud mqtt esp8266 at command

Problems encountered in QRcode QR code (C language)

异常的概念与处理

GIS方法类期刊和论文的综述(Introduction)怎么写?
随机推荐
.NET5WTM(ASP.NET Core) PGSql开箱操作
第4期:大学生提前职业技能准备之一
20210807#1 C语言程序结构
分布式锁解决方案之Redis实现
2022pta平时训练题(1~10题字符串处理问题)
干货likeshop外卖点餐系统开源啦100%开源无加密
超图 影像 如何去除黑边(两种方法)
第8期:云原生—— 大学生职场小白该如何学
Navicat15连接本地虚拟机的Mysql(Centos7)
C language calculation date interval days
.NET操作Redis sorted set有序集合
数据库函数
卸载魅族应用商店
Comparison of packet capturing tools fiddler and Wireshark
L2-005 集合相似度(vector、set求并交集)
【dectectron2】跟着官方demo一起做
【论文下饭】Deep Mining External Imperfect Data for ChestX-ray Disease Screening
json-c库的简单使用——将json文件转换为struct.
SuperMap IClient for Leaflet 加载高斯克吕格投影三度分带CGCS2000大地坐标系WMTS服务
13 以对象管理资源