C语言-三子棋游戏的实现

目录

  • C语言-三子棋游戏的实现
    • 游戏背景介绍
    • 游戏实现思路
      • test模块
      • menu模块
      • **game模块**
        • init_board(初始化棋盘)
        • print_board(打印棋盘)
        • player_move(玩家下棋)
        • computer_move(电脑下棋)
        • is_win(判断输赢)
    • 游戏全部代码

游戏背景介绍

《三子棋》是一款古老的民间传统游戏,又被称为一条龙、九宫棋等。游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子连成一条线的一方则视为胜利者。




游戏实现思路

我们计划创建两个源文件test.c和game.c,一个头文件game.h,来完成对这个游戏的实现。
其中,test.c文件主要用于整体框架的实现,game.c文件主要用来实现游戏中的各个步骤。

游戏的整体模块如下:

下面我们分模块进行实现。


test模块


(上图为从函数的角度的思考)

模块的代码如下:

其中,srand((unsigned int)time(NULL)),主要用来生成随机时间的起点。

void test()
{srand((unsigned int)time(NULL));int input = 0;do {menu();printf("请选择1(开始游戏)或0(退出游戏):>\n");//只能选择一个整数输入,其他类型的输入,均是非法操作scanf("%d",&input);switch (input){case 1:printf("开始游戏\n");game();break;case 0:printf("退出游戏\n");break;default :printf("输入错误,请重新输入\n");break;}} while (input);//可以多次玩耍
}


menu模块


模块的代码如下:

void menu()
{printf("********************\n");printf("***  三子棋游戏  ***\n");printf("****   1.play   ****\n");printf("****   2.exit   ****\n");printf("********************\n");
}


game模块


模块的代码如下:

void game()
{//棋盘为3*3大小,即3*3的数组//为了可以控制棋盘的大小,我们定义一个标识符常量char board[ROW][COL];char ret = 0;//初始化棋盘init_board(board, ROW, COL);//打印棋盘print_board(board, ROW, COL);while (1){//玩家下棋player_move(board, ROW, COL);//打印当前棋盘print_board(board, ROW, COL);//判断输赢ret=is_win(board, ROW, COL);if (ret != 'C'){break;}//电脑下棋computer_move(board, ROW, COL);//打印当前棋盘print_board(board, ROW, COL);ret = is_win(board, ROW, COL);if (ret != 'C'){break;}}if (ret == '*'){printf("玩家赢\n");}else if (ret == '#'){printf("电脑赢\n");}else{printf("平局\n");}}

我们通过is_win函数所带回的返回值来判断是玩家赢,电脑赢,还是说结果为平局。
game模块部分是该游戏实现的核心,下面我们从函数角度来完成各步骤的实现


init_board(初始化棋盘)

初始化棋盘,对于33的棋盘来说,就是要将一个33的数组中的所有元素初始化为空格。

函数实现如下:

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

print_board(打印棋盘)

我们计划打印出如上图所示的棋盘形式,采用化整为零的思路,我们将其分为一行行和一列列,
1 首先打印出全部的元素后,它是没有任何分割线的。
2 先考虑第一行,为了达到图示的目的,我们知道一个下划线占一个位置,因此,在打印元素的同时,要打印出空格和竖线与之对应;再考虑第二行,有三个下划线和一个竖线组成一组,可以打印出来
3 全部打印出来后,发现最后行和最后列的部分多余了出来,为此,我们需要对其调整。
4 我们将第一行和第二行的这种组合为一个整体,那么他们的最后一次打印,都不需要下划线,我们以j(j < col - 1)在每行的最后一次打印时,进行区分。这样右侧的尾部就解决了。
5 在4步骤的基础上,对于下侧部分的尾部,主要是针对第二行的这种元素。我们以外侧for循环的行数i(i < row - 1),进行区分。这样下侧的尾部就解决了。

代码如下:

void print_board(char board[ROW][COL], int row, int col)
{for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){if(j<col-1){printf(" %c |", board[i][j]);}else{printf(" %c ", board[i][j]);}}printf("\n");if(i<row-1){for (int j = 0; j < col; j++){if (j < col - 1){printf("---|");}else{printf("---");}}}printf("\n");}
}

player_move(玩家下棋)

对于下棋,就是要用在特定的位置(33的棋盘,共9个位置),用特定的符号(我们这里采用‘’和‘#’)将空格给替换掉。
玩家下棋,玩家下棋需要考虑到的情况:
1.判断玩家输入坐标的合法性,以三子棋为例,站在玩家的角度,要求坐标值位于1-3之间
2.输入的坐标是否被占用,如果该值不为空,则表示被占用

代码如下:

void player_move(char board[ROW][COL], int row, int col)
{printf("轮到玩家下棋了\n");while (1) {printf("请输入棋子的坐标\n");int x = 0;int y = 0;scanf("%d %d", &x, &y);if ((1 <= x && x <= row) && (1 <= y && y <= row)){if (board[x - 1][y - 1] == ' '){board[x - 1][y - 1] ='*';break;}else{printf("该坐标点已被占用,请重新输入\n");}}else{printf("该坐标点超出棋盘范围,请重新输入\n");}}}

computer_move(电脑下棋)

电脑下棋与玩家下棋类似,只不过对于坐标的合法性,这里采用的是生成随机数来控制坐标的范围。且电脑下棋坐标位置随机,不具有智能性(后续有时间的话,我会回来优化这个地方)。我们也可以从两个方面考虑这里。
1 利用随机生成函数控制,随机数的生成范围为0-2
2 输入的坐标是否被占用,如果该值不为空,则表示被占用

void computer_move(char board[ROW][COL], int row, int col)
{while (1){printf("轮到电脑下棋了\n");int x = rand() % row;int y = rand() % col;if (board[x][y] == ' '){board[x][y] = '#';break;}else{printf("该坐标点已被占用,请重新输入\n");}}
}

is_win(判断输赢)

判断游戏输赢的情况:电脑赢,玩家赢,平局,继续。根据不同的情况我们返回不同的符号。
玩家赢:返回*
电脑赢:返回#
平局:返回Q
游戏继续:返回C
我们可以将玩家赢和电脑赢理解为一种情况:即有一方赢。
于是,有3种不同的情况。

1 一方赢的情况:三行或三列,或两条对角线上为同一个符号,且不为空格

for (int 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 (int 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[2][2] == board[1][1] && board[0][0] != ' ')//判断主对角线{return board[1][1];}if (board[0][2] == board[1][1] && board[2][0] == board[1][1] && board[1][1] != ' ')//判断副对角线{return board[1][1];}

2 平局的情况:不为以上的情况,且棋盘下满(棋盘是否下满,遍历数组看是否有空格)

for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){if (board[i][j] == ' '){return 0;}}}

3 其他情况,为游戏继续

该is_win的全部代码如下:

int is_full_board(char board[ROW][COL], int row, int col)
{for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){if (board[i][j] == ' '){return 0;}}}return 1;
}
char is_win(char board[ROW][COL], int row, int col)
{//先写死程序,后续优化改进for (int 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 (int 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[2][2] == board[1][1] && board[0][0] != ' ')//判断主对角线{return board[1][1];}if (board[0][2] == board[1][1] && board[2][0] == board[1][1] && board[1][1] != ' ')//判断副对角线{return board[1][1];}if (is_full_board(board, ROW, COL)){return 'Q';}return 'C';
}

至此,所有模块功能均已实现。




游戏全部代码

test.c

#define _CRT_SECURE_NO_WARNINGS 1#include "game.h"
//游戏逻辑
//test.c
//
//game.c
//game.h
void game()
{//棋盘为3*3大小,即3*3的数组//为了可以控制棋盘的大小,我们定义一个标识符常量char board[ROW][COL];char ret = 0;//初始化棋盘init_board(board, ROW, COL);//打印棋盘print_board(board, ROW, COL);while (1){//玩家下棋player_move(board, ROW, COL);//打印当前棋盘print_board(board, ROW, COL);//判断输赢ret=is_win(board, ROW, COL);if (ret != 'C'){break;}//电脑下棋computer_move(board, ROW, COL);//打印当前棋盘print_board(board, ROW, COL);ret = is_win(board, ROW, COL);if (ret != 'C'){break;}}if (ret == '*'){printf("玩家赢\n");}else if (ret == '#'){printf("电脑赢\n");}else{printf("平局\n");}}
//判断游戏输赢的情况:电脑赢?玩家赢?平局?继续?
//玩家赢:返回*
//电脑赢:返回#
//平局:返回Q
//游戏继续:返回C
void menu()
{printf("********************\n");printf("***  三子棋游戏  ***\n");printf("****   1.play   ****\n");printf("****   2.exit   ****\n");printf("********************\n");
}
void test()
{srand((unsigned int)time(NULL));int input = 0;do {menu();printf("请选择1(开始游戏)或0(退出游戏):>\n");//只能选择一个整数输入,其他类型的输入,均是非法操作scanf("%d",&input);switch (input){case 1:printf("开始游戏\n");game();break;case 0:printf("退出游戏\n");break;default :printf("输入错误,请重新输入\n");break;}} while (input);//可以多次玩耍
}
int main()
{test();return 0;
}

game.h

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <time.h>
#include <stdlib.h>#define ROW 3
#define COL 3//初始化棋盘
void init_board(char board[ROW][COL], int row, int col);//打印棋盘
void print_board(char board[ROW][COL], int row, int col);//玩家下棋
void player_move(char board[ROW][COL], int row, int col);//电脑下棋
void computer_move(char board[ROW][COL], int row, int col);char is_win(char board[ROW][COL], int row, int col);

game.c

#define _CRT_SECURE_NO_WARNINGS 1#include "game.h"//初始化棋盘,也就是将数组各元素初始化为空格
void init_board(char board[ROW][COL], int row, int col)
{for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){board[i][j] = ' ';}}
}//打印棋盘,也即是打印数组
//分割行和分割列的设计思路:
//1 首先打印出全部的元素
//2 先考虑第一行,为了达到图示的目的,我们知道一个下划线占一个位置,因此,在打印元素的
//同时,要打印出空格和竖线与之对应;
//再考虑第二行,有三个下划线和一个竖线组成一组元素,可以打印出来
//3 全部打印出来后,发现最后行和最后列的部分多余了出来,我们在4和5步骤对其调整/* 4 我们将第一行和第二行的这种组合为一个整体,那么他们的最后一次打印,都不需要下划线,我们以j(j < col - 1)在每行的最后一次打印时,进行区分。这样右侧的尾部就解决了。5  在4步骤的基础上,对于下侧部分的尾部,主要是针对第二行的这种元素。我们以外侧for循环的行数i(i < row - 1),进行区分。这样下侧的尾部就解决了*/
void print_board(char board[ROW][COL], int row, int col)
{for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){if(j<col-1){printf(" %c |", board[i][j]);}else{printf(" %c ", board[i][j]);}}printf("\n");if(i<row-1){for (int j = 0; j < col; j++){if (j < col - 1){printf("---|");}else{printf("---");}}}printf("\n");}
}//玩家下棋,玩家下棋需要考虑到的情况:
//1.判断玩家输入坐标的合法性,以三子棋为例,站在玩家的角度,要求坐标值位于1-3之间
//2.输入的坐标是否被占用,如果该值不为空,则表示被占用
void player_move(char board[ROW][COL], int row, int col)
{printf("轮到玩家下棋了\n");while (1) {printf("请输入棋子的坐标\n");int x = 0;int y = 0;scanf("%d %d", &x, &y);if ((1 <= x && x <= row) && (1 <= y && y <= row)){if (board[x - 1][y - 1] == ' '){board[x - 1][y - 1] ='*';break;}else{printf("该坐标点已被占用,请重新输入\n");}}else{printf("该坐标点超出棋盘范围,请重新输入\n");}}}//电脑下棋
//电脑随机生成坐标,需要考虑到的情况同玩家类似
//1 利用随机生成函数控制,随机数的生成范围为0-2
//2 输入的坐标是否被占用,如果该值不为空,则表示被占用
void computer_move(char board[ROW][COL], int row, int col)
{while (1){printf("轮到电脑下棋了\n");int x = rand() % row;int y = rand() % col;if (board[x][y] == ' '){board[x][y] = '#';break;}else{printf("该坐标点已被占用,请重新输入\n");}}
}//判断游戏是否输赢
//有4种情况:其中一方赢;平局(棋盘下满,且没有出现赢的一方);棋盘未下满
//其中一方赢的情况:三行或三列,或两条对角线上为同一个符号,且不为空格
//平局的情况:不为以上的情况,且棋盘下满(棋盘是否下满,遍历数组看是否有空格)
//其他情况,为游戏继续int is_full_board(char board[ROW][COL], int row, int col)
{for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){if (board[i][j] == ' '){return 0;}}}return 1;
}
char is_win(char board[ROW][COL], int row, int col)
{//先写死程序,后续优化改进for (int 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 (int 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[2][2] == board[1][1] && board[0][0] != ' ')//判断主对角线{return board[1][1];}if (board[0][2] == board[1][1] && board[2][0] == board[1][1] && board[1][1] != ' ')//判断副对角线{return board[1][1];}if (is_full_board(board, ROW, COL)){return 'Q';}return 'C';
}

C语言-三子棋游戏的实现相关推荐

  1. C语言三子棋游戏进阶版详解(包括多子棋)

    目录 一,总体思路介绍 二,具体思路的实现 1.初始化游戏界面 2.初始化棋盘 3.打印棋盘 4.玩家移动 5.电脑移动 6.判断输赢 三,源码展示 game.h game.c test.c 一,总体 ...

  2. C语言三子棋游戏的编程实现

    程序界面实现如图所示的人与电脑互动的三子棋游戏 文件模块 test.c 测试三子棋游戏 game.h 游戏的函数声明(头文件) game.c 游戏函数的实现 代码模块 初始化棋盘 (每个位置初始为空格 ...

  3. C语言三子棋游戏实现(基础版)

    欢迎来到 Claffic 的博客

  4. 用C语言实现三子棋游戏(附上思路+项目展示+源代码)

    文章目录 前言 一.三子棋游戏整体实现思路 二.实现步骤 分模板实现 (以及具体应用实列) 1.test.c 源文件讲解: 2. game.c 源文件讲解: 3.game.h 源文件讲解 三 game ...

  5. c语言程序设计中三子棋游戏,C语言实现简易版三子棋游戏

    本文实例为大家共享了C语言实现三子棋游戏的详细代码,供大家参考,详细内容如下 什么是多文件? 多数大型的工程的头文件和源文件非常多,我们也不可能把所有的代码都写在同一个文件里,这样也不方便代码的阅读与 ...

  6. c语言程序下三子棋,C语言实现三子棋游戏(初级版)

    本文实例为大家分享了C语言实现三子棋游戏的具体代码,供大家参考,具体内容如下 game.h #include #include #include #define ROW 3 #define COL 3 ...

  7. 使用c语言实现三子棋游戏

    下面简介如何使用c语言的基础语法实现三子游戏的底层逻辑: 首先是菜单模块 使用switch语句进入游戏模块 前置交互完成,开始进入游戏函数. 第一部分棋盘创建,使用二维数组实现. a.初始化棋盘 为什 ...

  8. C语言实现三子棋游戏(规范化)

    `在复习了数组之后,对有些经典的数组题进行了,其中一道就是三子棋游戏这一功能的实现.下面附上我的个人理解和代码,希望各位大佬不吝指教. 首先有一个万能游戏模板,在c语言中有好多实现游戏的代码,我总结了 ...

  9. 如何用C语言来实现【三子棋游戏】

    目录 一.前言 二.思维导图 三.思路分析 四.运行结果 五.算法细节 1.游戏相关的测试 2.游戏函数的声明 3.游戏函数的实现 (1)初始化棋盘的函数 (2)打印棋盘的函数 (3)玩家下棋的函数 ...

最新文章

  1. CSS控制字体在一行内显示不换行
  2. 中科院基因组所高远组诚聘生物信息学方向助理/副研及博士
  3. Eclipse中配置Tomcat虚拟路径
  4. 线性表应用之线性表算法设计六大经典案例
  5. 利用栈进行程序的括号匹配
  6. haproxy详细介绍
  7. P2634 [国家集训队]聪聪可可(树形dp)
  8. 前后端分离 与 不分离
  9. sed 、awk用法
  10. JDBC04 PreparedStatement
  11. 手把手玩转win8开发系列课程(13)
  12. Vbs脚本编程简明教程之六
  13. PS1应用之——修改linux终端命令行各字体颜色
  14. excel怎么设置密码?加密文件这么做!
  15. 威联通212-P 安装远程迅雷,docker安装远程迅雷
  16. 调试The Annotated Transformer
  17. SAS:单变量正态性检验
  18. oracle扩充表空间
  19. LattePanda入手笔记
  20. 【计算机视觉】Lecture 11:LoG和DoG滤波算子

热门文章

  1. visualbox ubuntu Linux,共享文件夹的建立
  2. 生活随笔:工作的开心和不开心
  3. 【算法S5-贪心方法】
  4. 企业知识管理实施密码
  5. RGSSAD加密方案分析
  6. linux成功和失败的英文,成功与失败英文作文
  7. Win2012配置软路由
  8. NOIP2014珠心算测验
  9. python输入float_float是什么意思_在python中 float是什么意思?
  10. js点击图片变化成另一个图片