• C语言实现《三子棋》游戏


    目录

    前言:

    (一)菜单的创建和使用:

    1.编写菜单:

    2.创建变量以供选择菜单:

    (二)实现game()函数:

    1.创建并包含源文件和头文件:

    (1)创建头文件game.h

    (2)创建源文件game.c

    2.初始化棋盘:

    (1)定义行和列:

    (2)遍历数组进行初始化:

    3.打印棋盘:

    4.玩家下棋:

    5.电脑下棋:

    6.判断输赢还是平局

    (三)代码演示:

    总结:


    前言:

    三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子走成一条线就视为胜利,而对方就算输了,但是三子棋在很多时候会出现和棋的局面。

    (一)菜单的创建和使用:

    1.编写菜单:

    1. void menu()
    2. {
    3. printf("*******************\n");
    4. printf("*******1.Play******\n");
    5. printf("*******0.Exit******\n");
    6. printf("*******************\n");
    7. }
    8. int main()
    9. {
    10. menu();
    11. return 0;
    12. }

    运用以上代码可以实现简易菜单的编写以及打印,如下图:

    2.创建变量以供选择菜单:

    在这里我们有两个选择,选1就进入游戏,选0就直接退出游戏。那么我们不妨定义一个变量input。这样当input == 1时开始游戏,当input == 2时自动退出游戏。综上,我们可以考虑使用do....while循环和switch分支选择语句来实现选择的底层的逻辑代码。

    代码如下:

    1. void menu()
    2. {
    3. printf("*******************\n");
    4. printf("*******1.Play******\n");
    5. printf("*******0.Exit******\n");
    6. printf("*******************\n");
    7. }
    8. int main()
    9. {
    10. menu();
    11. int input = 0;
    12. do
    13. {
    14. printf("请选择:");
    15. scanf("%d", &input);
    16. switch (input)
    17. {
    18. case 0:
    19. printf("正在退出游戏...\n");
    20. break;
    21. case 1:
    22. printf("开始游戏\n");
    23. break;
    24. default:
    25. printf("请重新输入\n");
    26. }
    27. } while (input);
    28. return 0;
    29. }

    运行之后如下图所示:

    如此一来,菜单的创建和选择就已经编写完毕了,接下来就是实现游戏内容的代码编写操作 。

    (二)实现game()函数:

    1.创建并包含源文件和头文件:

    (1)创建头文件game.h

    对于一个程序来说,在未来,如果我们要编写一个程序或者简单的小游戏,一定会需要使用库函数,使用库函数就需要包含相对应的头文件,例如#include、#include、#include等等。那么对于只有一个源文件的程序来说,在代码的前半部分就会含有大量的对头文件声明的语句。不仅仅有对库函数的声明,还会包含自定义函数的声明,这样就会使得文件程序冗长,在未来调试测试的效率低下,使人难以查找到bug,然后进行修复。

    因此我们设置一个头文件game.h

    再在第一个源文件test.c以及随后创建的源文件game.c里包含#include"game.h"

    在头文件内部我们就可以包含以下代码

    #include

    #include

    #include

    这些头文件的库函数随后就会用到。

    (2)创建源文件game.c

    在game.c的源文件里,我们则可以在此文件里实现函数的定义和实现操作,这样我们在test.c源文件里只需要调用函数,而具体的实现函数则就会在game.c函数中实现,这样可以大大缩短test.c源文件的代码量,使得读者在阅读代码的时候一目了然。

    2.初始化棋盘:

    在这里,我们需要知道的是,下棋,则必须要使用棋盘,并且还需要打印出来。

    棋盘是一个具有行和列的二维图形,所以我们只需要在二维空间上面进行操作。

    因此我们将使用二维数组board[][]来实现关于棋盘的一系列操作。

    (1)定义行和列:

    我们在此项目只需要实现三子棋棋盘的应,因此棋盘的行和列都应该为3,即3行3列。

    那如果以后我们需要实现五子棋棋盘初始化和打印呢?

    那这样我们就会需要在原代码中出现的3全部改为5,这样会十分麻烦,而且极容易造成错误致使存在偏差。

    所以我们可以在头文件game.h中定义关于行和列的

    1. #define ROW 3
    2. #define COL 3

    这样,在未来,我们想要实现五子棋的时候,我们就可以将3改为5。

    (2)遍历数组进行初始化:

    如今,我们已经将行和列定义完毕,我们现在可以使用如下函数实现对二维数组的初始化了

    1. void InitBoard(char board[ROW][COL], int row, int col)
    2. {
    3. }

    这里要注意的是,我们应当将如上的函数定义和实现写在game.c的源文件里,而对于如下的函数调用应写在test.c的源文件里并在void game()函数里面。

    InitBoard(board,ROW,COL)

    这里给予例子:

    别忘了还要在头文件game.h里对函数进行声明,注意要加上分号

    void InitBoard(char board[ROW][COL], int row, int col);

    对遍历数组初始化的具体操作如下:

    1. //1初始化棋盘
    2. void InitBoard(char board[ROW][COL], int row, int col)
    3. {
    4. int i = 0;
    5. int j = 0;
    6. for (i = 0; i < row; i++)
    7. {
    8. for (j = 0; j < col; j++)
    9. {
    10. board[i][j] = ' ';
    11. }
    12. }
    13. }

    3.打印棋盘:

    打印棋盘为本次游戏实现的一个重难点,想要彻底学会实现棋盘的打印,光是听一遍是远远不够的,还需要多次练习,多次默写,并且多多总结回顾。

    1. | |
    2. ---|---|---
    3. | |
    4. ---|---|---
    5. | |

    以上就是一个最简单的棋盘形状

    以下是代码的实现

    1. //2.打印棋盘
    2. void DisplayBoard(char board[ROW][COL], int row, int col)
    3. {
    4. int i = 0;
    5. int j = 0;
    6. for (i = 0; i < row; i++)
    7. {
    8. for (j = 0; j < col; j++)
    9. {
    10. printf(" %c ", board[i][j]);
    11. if (j < col - 1)
    12. {
    13. printf("|");
    14. }
    15. }
    16. printf("\n");
    17. if (i < col - 1)
    18. {
    19. for (j = 0; j < col; j++)
    20. {
    21. printf("---");
    22. if (j < col - 1)
    23. {
    24. printf("|");
    25. }
    26. }
    27. printf("\n");
    28. }
    29. }
    30. }

    这一串代码的底层逻辑,翻译过来就是如下

    “空格——数据——空格       |      空格——数据——空格     |    空格——数据——空格 ”

    “------------------------------------|---------------------------------------|----------------------------------”

    “空格——数据——空格       |      空格——数据——空格     |    空格——数据——空格 ”

    “------------------------------------|---------------------------------------|----------------------------------”

    “空格——数据——空格       |      空格——数据——空格     |    空格——数据——空格 ”

    此次循环主要以一行一行打印,先打印关于数据和下划线,再换行打印关于数据和下划线。

    以此类推

    并且运用循环,告诉编译器当我的棋盘

    在(i < row - 1)的时候打印“---”

    在(j < col - 1)的时候打印“|”

    如此棋盘的打印就实现完毕了。

    4.玩家下棋:

    对于玩家下棋,需要注意的是,玩家不会如同程序员甚至是计算机一样,从0开始进行数数,而是从1开始数数。

    因此我们在赋值的时候

    实际上对board[x-1][y-1]进行赋值,这是需要注意的!

    以下为代码的实现

    1. //3.玩家下棋
    2. void PlayerMove(char board[ROW][COL], int row, int col)
    3. {
    4. int x = 0;
    5. int y = 0;
    6. while (1)
    7. {
    8. scnaf("%d %d", &x, &y);
    9. if (x >= 1 && x <= row&&y >= 1 && y <= col)
    10. {
    11. if (board[x - 1][y - 1] == ' ')
    12. {
    13. board[x - 1][y - 1] = '*';
    14. break;
    15. }
    16. else
    17. {
    18. printf("坐标已被占用\n");
    19. }
    20. }
    21. else
    22. {
    23. printf("坐标非法\n");
    24. }
    25. }
    26. }

    5.电脑下棋:

    对于电脑下棋,我们采取的是随机生成坐标,并进行判断,

    该坐标是否被占用?

    改坐标是否在棋盘范围内?

    如果判断无误,将把生成的随机坐标赋值‘#’。

    如此一来电脑下棋就实现完毕了。,

    以下为代码:

    1. //4.电脑下棋
    2. void ComputerMove(char board[ROW][COL], int row, int col)
    3. {
    4. int x = 0;
    5. int y = 0;
    6. while (1)
    7. {
    8. x = rand() % row;
    9. y = rand() % col;
    10. if (x >= 0 && x < row&&y >= 0 && y < col)
    11. {
    12. if (board[x][y] == ' ')
    13. {
    14. board[x][y] = '#';
    15. break;
    16. }
    17. }
    18. }
    19. }

    6.判断输赢还是平局

    我们对于下棋是回合制的,因此我们需要用到循环,我们可以在game()函数里实现while循环

    如:

    1. while (1)
    2. {
    3. //3.玩家下棋
    4. PlayerMove(board, ROW, COL);
    5. //4.电脑下棋
    6. ComputerMove(board, ROW, COL);
    7. DisplayBoard(board, ROW, COL);

    这样就代表一回合一回合的下棋

    对于输赢的判断,会出现的情况就两种,要么电脑赢要么玩家赢,所以我们应当分别在PlayerMove和ComputerMove后面加上IsWin函数来判断输赢还是平局

    我们在这里可以定义对于的返回值:

    玩家赢----"   *   "

    电脑赢----"   #  "

    平局-------"  'Q' "

    继续-------"  'C' "

    如果

    电脑赢返回#

    玩家赢返回*

    平局返回Q

    继续返回C

    所以我们可以进行以下操作:

    1. while (1)
    2. {
    3. //3.玩家下棋
    4. PlayerMove(board, ROW, COL);
    5. ret = IsWin(board, ROW, COL);
    6. if (ret != 'C')
    7. {
    8. break;
    9. }
    10. //4.电脑下棋
    11. ComputerMove(board, ROW, COL);
    12. ret = IsWin(board, ROW, COL);
    13. if (ret != 'C')
    14. {
    15. break;
    16. }
    17. DisplayBoard(board, ROW, COL);
    18. }
    19. if (ret == '*')
    20. {
    21. printf("玩家赢\n");
    22. }
    23. else if (ret == '#')
    24. {
    25. printf("电脑赢\n");
    26. }
    27. else
    28. {
    29. printf("平局\n");
    30. }

    接下来就是实现IsWin函数的具体操作:

    1. //5.判断输赢还是平局
    2. char IsWin(char board[ROW][COL], int row, int col)
    3. {
    4. int i = 0;
    5. int j = 0;
    6. for (i = 0; i < row; i++)
    7. {
    8. for (j = 0; j < col; j++)
    9. {
    10. if (board[i][0] == board[i][1] && board[i][1] == board[i][2]
    11. && board[i][0] != ' ')
    12. {
    13. return board[i][0];
    14. }
    15. else if (board[0][i] == board[1][i] && board[1][i] == board[2][i]
    16. && board[0][i] != ' ')
    17. {
    18. return board[0][i];
    19. }
    20. }
    21. }
    22. if (board[0][0] == board[1][1] && board[1][1] == board[2][2]
    23. && board[1][1] != ' ')
    24. {
    25. return board[1][1];
    26. }
    27. else if (board[2][0] == board[1][1] && board[1][1] == board[0][2]
    28. && board[1][1] != ' ')
    29. {
    30. return board[1][1];
    31. }
    32. else if (IsFull(board, ROW, COL))
    33. {
    34. return 'C';
    35. }
    36. return 'Q';
    37. }

     在函数内部,我们又创建了个函数,叫IsFull,这个函数是用来判断当前棋盘有没有装满,也就是可以用它遍历数组,

    来查找“空格”。

    如果有空格我就返回1,然后IsWin函数就返回‘C’,代表继续,循环继续。

    如果没有空格了就返回0,然后IsWIn函数就返回‘Q’,代表平局,循环结束,并且打印“平局”。

    对于IsFul函数的实现如下:

    1. static int IsFull(char board[ROW][COL], int row, int col)
    2. {
    3. int i = 0;
    4. int j = 0;
    5. for (i = 0; i < row; i++)
    6. {
    7. for (j = 0; j < col; j++)
    8. {
    9. if (board[i][j] == ' ')
    10. {
    11. return 1;
    12. }
    13. }
    14. }
    15. return 0;
    16. }

    因为IsFull函数只出现在game.c的源文件中,因此可以运用static进行修饰。

    (三)代码演示:

    总结:

    对于以上代码的实现和设置,重难点的地方应当是棋盘的打印还有返回值的底层逻辑关系,这些是我们必须要去练习,去钻研的。

    第一次可能会有难度,但是写久了以后对代码的理解就会更深刻,对循环和返回值的逻辑思维会有显著的思维提升。

    完整代码在我的gitee里:Game with C: 这一仓库全部以c语言编写的小游戏 - Gitee.com

  • 相关阅读:
    设计模式-原则篇-01.开闭原则
    「uniapp」 H5高德地图定位、周边、路线规划、多点绘制、位置点击监听
    聊一聊我的第一个开源项目
    查找算法【哈希表】 - 处理冲突的方法:开放地址法-线性探测法
    RFID固定资产盘点系统给企业带来哪些便利?
    Java高手的30k之路|面试宝典|精通Map篇
    【AIGC专题】Stable Diffusion 从入门到企业级实战0403
    JS有哪些基本数据类型,它们的区别?
    掌握雅思写作:任务 2(在 7 小时内达到 7+ 级)
    Redis内存模型讲解
  • 原文地址:https://blog.csdn.net/weixin_72917087/article/details/132695363