文章目录

  • 前言
  • 一、问题描述
  • 二、基本流程
  • 三、步骤
    • 1.构建程序主体框架以及菜单的实现
    • 2.良好的宏定义增强代码可读性
    • 3.构建游戏入口PlayersGame()函数
    • 4.实现打印棋盘函数
    • 5.实现玩家落子函数
    • 6.实现判断输赢函数
  • 四、结果演示
  • 五、具体代码
  • 总结

前言

初学C语言,了解基本语法后,可以用来练练手。


一、问题描述

用C语言实现玩家对战的五子棋。

二、基本流程

在具体写代码之前,先来确定要实现的功能和实现流程。

  1. 创建菜单界面并选择开始或者退出游戏——菜单函数。
  2. 若选择开始则打印棋盘——打印棋盘的函数。
  3. 玩家1,玩家2,分别开始落子——落子函数。
  4. 每当玩家落子就需要判断输赢——判断是否结束的函数。
  5. 若结束则再次打印棋盘,并输出结果。
  6. 否则返回第二步开始循环。

三、步骤

为了让代码和逻辑更清晰,方便修改,调试。我们采用多文件协作,可创建三个文件,gobang.h用来存放头文件,函数以及宏定义的声明;gobang.c用于存放函数的定义;gobang_test.c用来测试程序。分别在gobang.c和gobang_test.c中引入头文件gobang.h即可。

1.构建程序主体框架以及菜单的实现

此步骤放在gobang_test.c中,是程序的开始。
包括菜单的实现,以及使用一个do while循环控制是否退出游戏,其中用switch语句实现用户的选择。

#include"gobang.h"//游戏菜单
void Menu()
{printf("****************************************\n");printf("****          1.玩家PK              ****\n");printf("****          0.退出                ****\n");printf("****************************************\n");
}int main()
{int choice;do{Menu();printf("请选择:>");scanf("%d", &choice);switch (choice){case 1:PlayersGame();break;case 0:break;default:printf("输入错误,请重新选择:>");break;}} while (choice);return 0;
}

2.良好的宏定义增强代码可读性

宏定义可以让代码的可读性更好,也可以让我们写代码时减少出错。

#define ROW 20        //数组行数,可以按照需求调整
#define COL 20        //数组列数,可以按照需求调整
#define PLAYER1 1     //玩家1编号,默认棋盘数据是0,玩家1落子,该位置被改成1
#define PLAYER2 2     //玩家2编号,默认棋盘数据是0,玩家2落子,该位置被改成2//落子之后的四种情况
#define NEXT 0        //游戏继续
#define PLAYER1_WIN 1 //玩家1赢了
#define PLAYER2_WIN 2 //玩家2赢了
#define DRAW 3        //平局

这些定义后面都会用到。

3.构建游戏入口PlayersGame()函数

这是主要的五子棋框架,将会调用具体函数来完成。
可以将需要的函数先写出来,再具体去实现。
这里需要落一次子,就判断一次。

//玩家对战
void PlayersGame()
{int board[ROW][COL];//创建一个二维数组作为棋盘memset(board, 0, sizeof(board));//将数组每个元素赋值为零    int row = ROW;int  col = COL;int ret = NEXT;//获取每落一个子的结果while (1){ShowBoard(board, row, col);PlayerMove(board, row, col, PLAYER1);ret = IsOver(board, row, col);    //判断是否结束//如果不是继续就退出if (ret != NEXT)break;//玩家2ShowBoard(board, row, col);PlayerMove(board, row, col, PLAYER2);ret = IsOver(board, row, col);    //判断是否结束//如果不是继续就退出if (ret != NEXT)break;}ShowBoard(board, row, col);switch (ret){case PLAYER1_WIN:printf("恭喜玩家1获胜\n");break;case PLAYER2_WIN:printf("恭喜玩家1获胜\n");break;case DRAW:printf("平局,再战一次?\n");break;default:break;}
}

4.实现打印棋盘函数

为了更加真实的模拟棋盘,我们可以选取一些特殊符号。
默认符号比较丑陋,我们需要使用美化后的符号,下面链接里面可找到所需的黑白棋,复制粘贴即可。
特殊符号大全.

打印的时候,要根据元素的内容分别打印对印的符号。
我这里的列和行都是以0开始。

//打印棋盘
void ShowBoard(int board[][COL], int row, int col)
{system("cls");printf("   ");for (int i = 0; i < col; i++){printf("%-3d", i);//打印列号}printf("\n");for (int i = 0; i < row; i++){printf("%2d", i);//打印行号for (int j = 0; j < col; j++){if (board[i][j] == PLAYER1){//玩家1落子printf(" ●");}else{if (board[i][j] == PLAYER2)printf(" ○"); //玩家2落子elseprintf(" ·"); //没有玩家落子}         }printf("\n");}printf("\n");
}

5.实现玩家落子函数

要实现玩家落子,可以用两个整形变量来获取坐标,但是后面判断输赢函数我们也会用到输入的坐标,所以需要设置成全局变量。

//因为判定结果时需要用到玩家落子的坐标,所有设置两个全局变量方便使用
int x, y;//x是行,y是列

参数player是用来记录当前是哪个玩家落子。


//玩家下棋
void PlayerMove(int board[][COL], int row, int col , int player)
{printf("请玩家%d输入落子的坐标:>",player);while (1){scanf("%d%d", &x, &y);if (x<0 || x>=row || y<0 || y>=col){printf("输入坐标错误,请重新输入:>");}else{if (board[x][y] != 0){printf("此坐标已有子,请重新输入:>");}else{board[x][y] = player;//将该点改为落子的玩家的值break;}}}}

6.实现判断输赢函数

这是实现五子棋的难点。

任何落子位置都有八个方向,所以判定五子连珠,本质是判定1,5方向之和,2,6方向之和,3,7方向之和,4,8方向之和,其中任意一个出现相同的连续五个棋子,即游戏结束

为了记录这八个方向我们采用枚举来记录,增加代码的可读性

// 枚举八个方向
//左右,上下,左上,左下,右上,右下
enum Dir {LEFT,RIGHT,UP,DOWN,LEFT_UP,RIGHT_DOWN,RIGHT_UP,LEFT_DOWN
};

这里就需要再创建一个用来记录相连棋子数量的函数。
参数enum dir d是传入的方向。
我们采用while循环和switch语句,用来循环笔记该方向上相同棋子的数量。
我们可以参照上图,对不同的方向,坐标做相应的加减。

//棋子计数
int ChessCount(int board[][COL], int row, int col, enum dir d)
{int count = 0;int _x = x;int _y = y;while (1){switch (d){case UP://上方_x--;break;case DOWN:_x++;break;case LEFT:_y--;break;case RIGHT:_y++;break;case LEFT_UP:_x--; _y--;break;case LEFT_DOWN:_x++; _y--;break;case RIGHT_UP:_x--; _y++;break;case RIGHT_DOWN:_x++; _y++;break;default:break;}//移动坐标之后,保证是否越界if (x < 0 || x >= row || y < 0 || y >= col){break;}//判断是否和原棋子相同,相同则计数if (board[_x][_y] == board[x][y]){count++;}else{break;}}return count;
}

四、结果演示

这里使用了清屏函数system(“cls”)使得界面更加整洁。

五、具体代码

gobang.h

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>#define ROW 20        //数组行数,可以按照需求调整
#define COL 20        //数组列数,可以按照需求调整
#define PLAYER1 1     //玩家1编号,默认棋盘数据是0,玩家1落子,该位置被改成1
#define PLAYER2 2     //玩家2编号,默认棋盘数据是0,玩家2落子,该位置被改成2//落子之后的四种情况
#define NEXT 0        //游戏继续
#define PLAYER1_WIN 1 //玩家1赢了
#define PLAYER2_WIN 2 //玩家2赢了
#define DRAW 3        //平局//因为判定结果时需要用到玩家落子的坐标,所有设置两个全局变量方便使用
int x, y;//x是行,y是列// 枚举八个方向
//左右,上下,左上,左下,右上,右下
enum Dir {LEFT,RIGHT,UP,DOWN,LEFT_UP,RIGHT_DOWN,RIGHT_UP,LEFT_DOWN
};//玩家对战
void PlayersGame();//打印棋盘
void ShowBoard(int board[][COL], int row, int col);//玩家下棋
void PlayerMove(int board[][COL], int row, int col, int player);//判断是否结束
int IsOver(int board[][COL], int row, int col);//棋子计数
int ChessCount(int board[][COL], int row, int col, enum dir d);//判断棋盘是否满
//满了返回1
int BoardIsFull(int board[][COL], int row, int col);

gobang.c


#include"gobang.h"//玩家对战
void PlayersGame()
{int board[ROW][COL];//创建一个二维数组作为棋盘memset(board, 0, sizeof(board));//将数组每个元素赋值为零    int row = ROW;int  col = COL;int ret = NEXT;//获取每落一个子的结果while (1){ShowBoard(board, row, col);PlayerMove(board, row, col, PLAYER1);ret = IsOver(board, row, col);    //判断是否结束//如果不是继续就退出if (ret != NEXT)break;//玩家2ShowBoard(board, row, col);PlayerMove(board, row, col, PLAYER2);ret = IsOver(board, row, col);    //判断是否结束//如果不是继续就退出if (ret != NEXT)break;}ShowBoard(board, row, col);switch (ret){case PLAYER1_WIN:printf("恭喜玩家1获胜\n");break;case PLAYER2_WIN:printf("恭喜玩家1获胜\n");break;case DRAW:printf("平局,再战一次?\n");break;default:break;}
}//打印棋盘
void ShowBoard(int board[][COL], int row, int col)
{system("cls");printf("   ");for (int i = 0; i < col; i++){printf("%-3d", i);//打印列号}printf("\n");for (int i = 0; i < row; i++){printf("%2d", i);//打印行号for (int j = 0; j < col; j++){if (board[i][j] == PLAYER1){//玩家1落子printf(" ●");}else{if (board[i][j] == PLAYER2)printf(" ○"); //玩家2落子elseprintf(" ·"); //没有玩家落子}}printf("\n");}printf("\n");
}//玩家下棋
void PlayerMove(int board[][COL], int row, int col , int player)
{printf("请玩家%d输入落子的坐标:>",player);while (1){scanf("%d%d", &x, &y);if (x<0 || x>=row || y<0 || y>=col){printf("输入坐标错误,请重新输入:>");}else{if (board[x][y] != 0){printf("此坐标已有子,请重新输入:>");}else{board[x][y] = player;//将该点改为落子的玩家的值break;}}}}//判断是否结束
int IsOver(int board[][COL], int row, int col)
{//分别统计四条线上的棋子数量int count1 = ChessCount(board, row, col, LEFT) + ChessCount(board, row, col, RIGHT) + 1;int count2 = ChessCount(board, row, col, UP) + ChessCount(board, row, col, DOWN) + 1;int count3 = ChessCount(board, row, col, LEFT_UP) + ChessCount(board, row, col, RIGHT_DOWN) + 1;int count4 = ChessCount(board, row, col, LEFT_DOWN) + ChessCount(board, row, col, RIGHT_UP) + 1;if (count1 >= 5 || count2 >= 5 || count3 >= 5 || count4 >= 5){if (board[x][y] == PLAYER1)return PLAYER1_WIN;elsereturn PLAYER2_WIN;}//如果没有五子联珠,判断棋盘是否满了if (BoardIsFull(board, row, col))return DRAW;//满了就平局elsereturn NEXT;//没满就继续}//棋子计数
int ChessCount(int board[][COL], int row, int col, enum dir d)
{int count = 0;int _x = x;int _y = y;while (1){switch (d){case UP://上方_x--;break;case DOWN:_x++;break;case LEFT:_y--;break;case RIGHT:_y++;break;case LEFT_UP:_x--; _y--;break;case LEFT_DOWN:_x++; _y--;break;case RIGHT_UP:_x--; _y++;break;case RIGHT_DOWN:_x++; _y++;break;default:break;}//移动坐标之后,保证是否越界if (x < 0 || x >= row || y < 0 || y >= col){break;}//判断是否和原棋子相同,相同则计数if (board[_x][_y] == board[x][y]){count++;}else{break;}}return count;
}//判断棋盘是否满
//满了返回1
int BoardIsFull(int board[][COL], int row, int col)
{for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){if (board[i][j] == 0)return 0;}}return 1;
}

gobang_test.c

#define _CRT_SECURE_NO_WARNINGS 1#include"gobang.h"
//游戏菜单
void Menu()
{printf("****************************************\n");printf("****          1.玩家PK              ****\n");printf("****          0.退出                ****\n");printf("****************************************\n");
}int main()
{int choice;do{Menu();printf("请选择:>");scanf("%d", &choice);switch (choice){case 1:PlayersGame();break;case 0:break;default:printf("输入错误,请重新选择:>");break;}} while (choice);return 0;
}

总结

这里仅仅是玩家之间的对战,欢迎各位大佬实现在线对战,以及人机对战。

【C语言实现】玩家互战五子棋(具体步骤和代码)相关推荐

  1. C语言实现双人对战五子棋游戏

    在编写五子棋游戏前首先对整个项目进行分析: 1.五子棋的界面绘制及显示 2.对输入的数据进行写入 3.判断输入的数据多对应的位置上是否可以下棋其中包括检测此位置是否为空及是否超出下棋的有效位置(越界超 ...

  2. C语言五子棋进阶版(增设玩家对战,大棋盘,棋盘行列对应等)

    经过好多天的编写,五子棋项目总算完成,作为一个学了不是很久的小白,我在编写过程中也遇到了许多困难,但还好都解决了,在此我建议和我一样,是小白的都应该写写五子棋或三子棋,当然,不是全抄别人的代码,也不是 ...

  3. 五子棋程序设计(C语言、人机对战、禁手)

    五子棋程序设计(C语言.人机对战.禁手) 一.程序需求分析 1.1五子棋简介 五子棋是全国智力运动会竞技项目之一,是一种两人对弈的纯策略型棋类游戏. 五子棋有两种玩法.玩法一:双方分别使用黑白两色的棋 ...

  4. Java用最少代码实现五子棋-玩家对战模式-人机对战模式-电脑策略对战

    Java用最少代码实现五子棋-玩家对战模式-人机对战模式-电脑策略对战 玩家对战模式 背景说明 代码实现 人机对战模式 背景说明 完整代码实现 电脑根据优势分数对战 背景说明 完整代码实现 小结 玩家 ...

  5. java人际对战五子棋_系统框图如下 java实现五子棋程序 可以实现人人对战 人机对战 简单功能 悔棋 认输...

    展开全部 一.实验题目 五子棋游戏e69da5e6ba9062616964757a686964616f31333365633835. 二.问题分析 五子棋是双人博弈棋类益智游戏,由围棋演变而来,属纯策 ...

  6. java双人对战五子棋(socket通信)

    学习java的时候一直想要做出一个像样的小游戏,所以就动手做了一个远程联网对战的java五子棋小游戏.这个程序我前前后后也是改动了几次,这次发出来的是最终版本了,虽然还是有很多不足,但本人已经没有精力 ...

  7. 借助实时数据推送快速制作在线对战五子棋小游戏丨实战

    1 项目概述 游戏开发,尤其是微信小游戏开发,是最近几年比较热门的话题. 本次「云开发」公开课,将通过实战「在线对战五子棋」,一步步带领大家,在不借助后端的情况下,利用「小程序 ✖ 云开发」,独立完成 ...

  8. 网络对战五子棋(来一起PK鸭)

    网络对战五子棋(来一起PK鸭) 一.本地调用和RPC调用的区别 首先了解一下RPC~ RPC主要是解决了两个问题: 解决了分布式系统中,服务之间的调用问题 尤其是在远程调用的时候,可以让调用者感受不到 ...

  9. C语言学会数组就可以做五子棋 - 快来试试吧(包含源码)

    本篇给大家带来两个玩家对战的五子棋,棋盘大小和玩家所下的棋子可以很容易的更改,读者如有更好的建议,可以在评论区留言 代码模块将分为3个部分,游戏页面逻辑书写写在 test.c 文件中,游戏所需要的声明 ...

最新文章

  1. 武汉约100所中小学将试点人工智能课,现面向社会遴选教材
  2. Activity启动过程
  3. css3的新特性transform,transition,animation
  4. torch.unsqueeze()和torch.unsqueeze()
  5. MAP的get与containskey
  6. Ionic 2.0 相关资料
  7. java utf8 简繁转换 类库_Java封装简体繁体(香港台湾)转换工具
  8. python 代码命令大全-Linux命令大全
  9. 圆方树(bzoj 2125: 最短路)
  10. websocket没准备好如何解决_惠普打印机打印没反应如何解决 惠普打印机打印没反应解决方法【详解】...
  11. VB中输入函数InputBox的用法
  12. 2010.11.18 关于向窗口发送消息
  13. 芯片国产化进程提速 赶超洋品牌核心技术尚欠火候
  14. lg相乘公式_lg函数(log的公式大全)
  15. 世界性能服务器图片欣赏,AMD发布全球最强服务器显卡-AMD,全球最强,服务器显卡,FirePro,S9000,S7000,虚拟机 ——快科技(驱动之家旗下媒体)--科技改变未来...
  16. lambda的peek(流元素操作),filter(过滤),map(映射),limit(截断),skip(跳过),collect,distinct(去重)函数使用
  17. 22种设计模式——原型模型
  18. Maven依赖版本号不生效
  19. C#安装Newtonsoft.Json并调用
  20. ESP32 ESP-IDF安装教程(windows 64位)

热门文章

  1. UE4绝地求生双开门向量逻辑
  2. 三国杀里面有哪些是和历史吻合的比较好的例子?
  3. 计算机相关的迎新标语,创意新颖的迎新标语
  4. 请柬(双向SPFA及SLF LLL优化法模板题)
  5. 【BZOJ2325】[ZJOI2011]道馆之战 线段树+树链剖分
  6. DCD、DTR、DSR、RTS及CTS状态指示的意义
  7. 20170303找女朋友之路思考总结
  8. MongoDB 架构
  9. 写一个把控件丢到垃圾桶的动画其实很简单
  10. 使用 Git 生成 GPCC 的配置文件(cfg)【图文详细教程、含百度网盘资源】