0


C语言一个小时实现简易三子棋,看看你能不能让愚蠢的电脑获胜,还不快上手试试吗(无AI算法)

今天利用空余时间,便写一写三子棋的实现,可能很多人第一次看到这些东西,心里就会有一点害怕,可能会说:天呐,这得多难啊,我真的能写吗?那么回答是肯定的:能写,并且还很简单。那就让我们来看一看吧!

我们首先要分析整个三子棋的需要,因为我们不是做人机对战(过段时间博主会发一篇五子棋人机对战的实现),主要实现人机对战需要考虑的情况比较多,比较复杂,在这里既然是新手向,大家有一个制作的思路就可以了。

三子棋下棋,首先我们需要做到能看到棋盘,之后是下棋,电脑会在我们之后。每一步我们都想屏幕能清空,同时保留一次棋盘。然后是判断输赢的问题。(如果书写对战AI,还需要考虑棋盘情况来书写对应的算法)。

为了实现功能,我们将其拆解成三部分:一个test.c,一个game.c和一个game.h。

其中test.c用来书写实现逻辑,game.c用来书写函数定义,game.h则用来声明。

第一部分:

测试文件test.c,这个文件我们需要一个目录指引,也就是玩还是不玩,这个很容易实现,之后是定制棋盘,我们在头文件里会声明这些东西(ROW和COL一类的量)。首先肯定是初始化一个棋盘,这个很简单,直接给棋盘赋值空格就好(InitBoard),之后我们需要让它变成我们想要的样子(也就是DisplayBoard),这需要一点点的功夫,比如我们期望如下:

那么我们就需要在书写定义的时候用点心了,不过我们现在看的是主函数,所以我们先关注主函数:

有了棋盘我们就还需要能下子,就需要玩家下子(PlayerMove)和电脑下子(ComputerMove),之后我们需要判断输赢平局的情况(IfWin),这时候就基本上思路齐全了。

那么我们的主函数的逻辑已经比较清晰了,就直接上代码吧:

  1. #include"game.h"
  2. void menu()
  3. {
  4. printf("===========================\n");
  5. printf("======= 1.play =======\n");
  6. printf("======= 0.exit =======\n");
  7. printf("===========================\n");
  8. }
  9. void game()
  10. {
  11. //存储数据-二维数组
  12. char board[ROW][COL];
  13. //初始化棋盘-初始化空格
  14. InitBoard(board, ROW, COL);
  15. //打印棋盘-本质打印数组的内容
  16. DisplayBoard(board, ROW, COL);
  17. char ret = 0;//用来获取游戏状态
  18. while (1)
  19. {
  20. //玩家走
  21. PlayerMove(board, ROW, COL);
  22. DisplayBoard(board, ROW, COL);
  23. //判断输赢
  24. ret = IfWin(board, ROW, COL);
  25. if (ret != 'C')
  26. break;
  27. //电脑走
  28. ComputerMove(board, ROW, COL);
  29. system("cls");
  30. DisplayBoard(board, ROW, COL);
  31. //判断输赢
  32. ret = IfWin(board, ROW, COL);
  33. if (ret != 'C')
  34. break;
  35. }
  36. if (ret == '*')
  37. {
  38. printf("玩家胜利\n");
  39. }
  40. else if (ret == '#')
  41. {
  42. printf("电脑胜利\n");
  43. }
  44. else
  45. {
  46. printf("平局\n");
  47. }
  48. DisplayBoard(board, ROW, COL);
  49. }
  50. int main()
  51. {
  52. int input = 0;
  53. srand((unsigned int)time(NULL));
  54. do
  55. {
  56. menu();
  57. printf("请选择:\n");
  58. scanf_s("%d", &input);
  59. system("cls");
  60. switch (input)
  61. {
  62. case 1:
  63. printf("三子棋游戏:\n");
  64. game();
  65. break;
  66. case 0:
  67. printf("退出游戏\n");
  68. break;
  69. default:
  70. printf("选择错误,请重新选择:\n");
  71. break;
  72. }
  73. } while (input != 0);
  74. }

在这里需要注意IfWin返回值的情况,这个是需要提前考虑的,由于玩家使用 “ * ”我们就认为返回了“ * ”就是玩家胜利,返回“ # ”就是电脑胜利,返回“ C ”(continue)就继续,返回 “ Q "就是平局

这样会使代码更加简单,至于为什么,我们马上就能知道。

第二步,书写头文件,主函数已经有了需求,接下来在头文件里书写一下声明就OK了:

  1. #pragma once
  2. //头文件的包含
  3. #include<time.h>
  4. #include<stdio.h>
  5. #include<stdlib.h>
  6. #include<Windows.h>
  7. //符号的定义
  8. #define ROW 3
  9. #define COL 3
  10. //函数的声明
  11. //初始化棋盘
  12. void InitBoard(char board[ROW][COL], int row, int col);
  13. //打印棋盘的函数
  14. void DisplayBoard(char board[ROW][COL], int row, int col);
  15. //玩家下棋
  16. void PlayerMove(char board[ROW][COL], int row, int col);
  17. //电脑下棋
  18. void ComputerMove(char board[ROW][COL], int row, int col);
  19. //判断输赢
  20. //1.玩家赢 '*' 2.电脑赢 '#' 3.平局 'Q' 4.游戏继续 'C'
  21. char IfWin(char board[ROW][COL], int row, int col);

第三步就是很关键的一步了,这一步决定了程序能否正常运行,因为它书写的是我们的函数定义,

我们已经知道,我们需要初始化一个棋盘(InitBoard),展示棋盘情况(DisplayBoard)玩家下子(PlayerMove)和电脑下子(ComputerMove),之后我们需要判断输赢平局的情况(IfWin)

那么就分开看看这些函数模块(不要忘记包含头文件#include"game.h"):

1、InitBoard

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

这个就是妥妥的二维数组赋值,全赋值空格,没什么好说的。

2、DisplayBoard

  1. void DisplayBoard(char board[ROW][COL], int row, int col)
  2. {
  3. int i = 0, j = 0;
  4. for (i = 0; i < row; i++)
  5. {
  6. //打印每一行的数据 空格+数据+空格+竖杠作为一个大元素
  7. for (j = 0; j < col; j++)
  8. {
  9. printf(" %c ", board[i][j]);
  10. if (j < col - 1)
  11. printf("|");
  12. }
  13. printf("\n");
  14. //打印每一行的分割线
  15. if (i < row - 1)
  16. {
  17. int j = 0;
  18. for (j = 0; j < col; j++)
  19. {
  20. printf("---");
  21. if (j < col - 1)
  22. printf("|");
  23. }
  24. printf("\n");
  25. }
  26. }
  27. }

因为我们有所期望,就是上图的那个棋盘,所以需要思考怎么制作棋盘,也很简单,打印每一行的数据,用 空格+数据+空格+竖杠 作为一个大元素 ,同时最后一个大元素没有竖杠。用代码解释的话,就是上面所示。

之后是每一行的区别,我们需要隔离棋盘的行,那么打印分隔线即可,道理如同打印“大元素”。

这个期望,我们就解决了。

3、PlayerMove

玩家落子,我们需要思考合法性,并给出反馈,所以需要需要if条件,首先判断输入的数据是否合理,之后是判断所输入的空间是否被占用。(由于我们习惯性的把横坐标在前,纵坐标灾后,同时注意数组的下标从0开始数,所以接收的时候为了符合判断,我们颠倒一下x和y,同时3-y,x-1)

注意思考为什么是3-y(或者说是ROW - y),因为我们打印出来的列是从上向下数的,而我们看棋盘是从下向上看,所以只能倒过来接受数据才符合我们的思路。

  1. void PlayerMove(char board[ROW][COL], int row, int col)
  2. {
  3. int x = 0, y = 0;
  4. printf("玩家走:\n");
  5. printf("请输入下棋的坐标:");
  6. //判断坐标合法性
  7. while (1)
  8. {
  9. scanf_s("%d %d", &x, &y);
  10. if (x >= 1 && x <= row && y >= 1 && y <= col)
  11. {
  12. //下棋
  13. //判断坐标是否呗被占用
  14. if (board[3 - y][x - 1] == ' ')
  15. {
  16. board[3 - y][x - 1] = '*';
  17. break;
  18. }
  19. else
  20. {
  21. printf("坐标被占用,请重新输入\n");
  22. }
  23. }
  24. else
  25. {
  26. printf("坐标非法,请重新输入\n");
  27. }
  28. }
  29. }

如果二者都没有,那么我们就给原来的空格赋值为 “ * ”

4、ComputerMove

电脑下子就简单的多了,只需要随机赋值就可以,同时判断一下占用情况。

  1. void ComputerMove(char board[ROW][COL], int row, int col)
  2. {
  3. printf("电脑走:\n");
  4. while (1)
  5. {
  6. int x = rand() % row;
  7. int y = rand() % col;
  8. //判断占用
  9. if (board[x - 1][y - 1] == ' ')
  10. {
  11. board[x - 1][y - 1] = '#';
  12. break;
  13. }
  14. }
  15. }

5、IfWin

整个代码,唯一有一点难度的应该就是这里了,这里笔者写的比较简陋,我们首先判断每一行是否相等,再每一列,再对角线,如果都没有,就进入棋盘是否满了的判断,如果棋盘满了,那么就是平局,没满,就继续下棋,这里我们需要返回值。同时注意,我们的返回值就是棋盘存储的数据,这一点简化了我们的代码。

  1. //判断游戏是否有输赢
  2. char IfWin(char board[ROW][COL], int row, int col)
  3. {
  4. int i = 0;
  5. //判断行
  6. for (i = 0; i < row; i++)
  7. {
  8. if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
  9. {
  10. return board[i][1];
  11. }
  12. }
  13. //判断列
  14. for (i = 0; i < col; i++)
  15. {
  16. if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
  17. {
  18. return board[1][i];
  19. }
  20. }
  21. //判断对角线
  22. if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
  23. {
  24. return board[1][1];
  25. }
  26. if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
  27. {
  28. return board[1][1];
  29. }
  30. //判断平局
  31. //满了返回1,没满返回0
  32. if (IsFull(board, ROW, COL) == 1)
  33. {
  34. return 'Q';
  35. }
  36. //都没有,继续游戏
  37. return 'C';
  38. }

那么我们自然需要一个判断棋盘是否已满的函数,由于笔者的电脑出了一点小问题,只能书写这种非常原始的判断方式,有能力的伙伴可以自行改善代码哦

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

到这里,我们的简化版三子棋就实现了,想要书写AI算法的也可以根据棋盘判断的情况进行思考,思考电脑应该如何围堵,落子,然后代码实现。

这里就不多说了,那么感谢看到这里啦!

标签: c语言 算法

本文转载自: https://blog.csdn.net/awaitxm/article/details/122628275
版权归原作者 芝士工具猿 所有, 如有侵权,请联系我们删除。

“C语言一个小时实现简易三子棋,看看你能不能让愚蠢的电脑获胜,还不快上手试试吗(无AI算法)”的评论:

还没有评论