写点什么

“纯 C”实现——三子棋小游戏

作者:平凡的人
  • 2022 年 8 月 02 日
  • 本文字数:6905 字

    阅读完需:约 23 分钟

“纯C”实现——三子棋小游戏

文章目录

  • ⚓三子棋实现思路

  • ⚓棋盘实现

  • ⚓玩家下棋

  • ⚓电脑下棋

  • ⚓判断输赢

  • ⚓小结语


⚡text.c 文件

⚡game.c 文件

⚡game.h



⚓三子棋实现思路

三子棋游戏就是玩家和电脑之间轮流下棋,首先它的棋盘为 3X3,我们玩家先下棋,然后到电脑,只要其中一方下的棋子连成了一条线即为胜利。 那么要实现游戏的前提首先是得有一个棋盘显示出来,然后把我们每一次下的棋到显示在屏幕上直到一方胜利或者和局。所以

我们第一步是要打印棋盘:

打印棋盘的前提是有数据可以打印,所以我们需要用到一个 3X3 的二维数组,刚开始里面没有数据,我们先初始化成空格,这样打印出来就是空白的形式,为了方便区分玩家和电脑下棋,我们可以规定玩家下棋就用符号" *"填补电脑下棋就用符号"# "填补

那么第二步:

棋盘打印出来屏幕后,就是玩家下棋,那么我们需要一个玩家下棋的独立函数,用来接收玩家输入的坐标,而且要判断坐标是否合法

第三步:

玩家下完棋后,就是电脑下棋,电脑下棋我们可以用随机值进行下棋此时我们又可以实现一个电脑下棋的独立函数

第四步:

玩家和电脑每下一次棋,我们都需要判断一下是否有一方已经胜利了,或者棋盘是否满了,所以我们又需要一个独立函数用来判断

第五步:

把上述的所有函数用循环连接起来即可,下面直接看我操作



⚓棋盘实现

下面的图就是我们要实现的棋盘,首先要创建一个 3X3 的二维数组


#define ROW 3    //行#define COL 3    //列char board[ROW][COL]
复制代码


我们用宏定义来定义行列,方便以后要打印成其他 4X4,10X10 的棋盘而且我们要把它初始化成空格


//初始化成空格void InitBoard(char board[ROW][COL], int row, int col){  int i = 0;  int j = 0;  for (i = 0; i < row; i++)  {    for (j = 0; j < col; j++)    {      board[i][j] = ' ';    }  }}
复制代码


可以看一个下的棋下面都有三个“-”符号,因为我们的“ # ”,和“ * ”都只占一个位置,所以我们打印的时候要用空格隔开



用宏定义的好处,如果把 ROW 和 COL 改成 10,那么它可以直接打印成下面的 10X10 格子 注意我们下面是基于 3X3 格子进行实现的三子棋,只适用于 3x3 格子



下面我们就实现一下打印棋盘的代码实现代码:


//打印棋盘void BoardPrint(char board[ROW][COL], int row, int col){  int i = 0;  int j = 0;  for (i = 0; i < row; i++)  {    for (j = 0; j < col; j++)    {      printf(" %c ",board[i][j]);      //可以看到图片里的最后一列是没有竖杆的,所以这里要判断一下      //如果是最后一列就不必打印竖杆      if(j<col-1)      printf("|");    }    printf("\n");    for (j = 0; j < col; j++)    {      printf("---");      //这里跟上面用一个意思      if (j < col - 1)        printf("|");    }    printf("\n");  }}
复制代码

⚓玩家下棋

玩家下棋的方式是输入坐标 ,所以需要接收玩家输入的数据,我们要考虑到用户友好性


因为我们使用的是二维数组,下标都是从 0 开始的,而玩家不一定是程序员,是不知道的这件事的,所以用户输入的数据时要注意处理



处理用户输入的数据只需将它-1 即可,比如玩家输入坐标"3 3",在后台改变数组的时候就将它减 1 变成“2 2”同时还有判断当前坐标是否已经被占用了,如果被占用了还需要提醒玩家重新输入,直到输入正确为止,所以我们要使用到循环结构 坐标没有被占用,我们就可以将数组对应的位置放字符“ * ”


void PlayerMove(char board[ROW][COL], int row, int col){  while (1)  {    printf("请输入坐标>:");    int x = 0;    int y = 0;    scanf("%d %d", &x, &y);    //判断坐标是否合法    if (x <= 3 && x > 0 && y <= 3 && y > 0)    {      if (board[x - 1][y - 1] == ' ')      {        board[x - 1][y - 1] = '*';        break;      }      else      {        printf("坐标被占用,不能下棋,请选择其他位置\n");      }    }    else    {      printf("坐标非法,请重新输入\n");    }  }}
复制代码

⚓电脑下棋

电脑下棋使用 rand 函数获取随机值,将它坐标按行和列取模,比如随机值是 100,那么取模行 row 之后,就为 1,电脑下棋就不用判断坐标是否合法了,只需要确定它取模后的数字范围在 0~2 之间即可还需要注意获取的坐标是否被占用即可


因为我们是使用随机值的方式让电脑下棋,使用这样的方式下棋,电脑是很“笨”的,不会对我们下的棋进行拦截,简而言之就是不够“智能”,当然如果, 你感兴趣的话,可以对它进行一些算法处理,比如穷举一些玩家可能性下的棋,对它进行预判等等,让它变得更加“智能”一些,但本文章就不做优化了,留给你们自行处理!


//电脑下棋void ComputerMove(char board[ROW][COL], int row, int col){  while (1)  {    int x = rand() % row;    int y = rand() % col;    if (board[x][y] == ' ')    {      board[x][y] = '#';      break;    }  }}
复制代码


⚓判断输赢

判断输赢大概就以下几种,我们可以使用枚举的方式,也可以使用循环的方式去判断,下面我给出两个版本,一个是枚举的,一个是循环的我们规定玩家赢返回“ * ”,电脑赢返回“ # ”,平局返回“Q”,进行游戏返回“0”



枚举


//版本1.0枚举//玩家赢返回"*",电脑赢返回""#,棋盘满了返回"Q"char  DetermineEnd1(char board[ROW][COL]){  //玩家赢的情况  if ((board[0][0] == board[0][1]  && board[0][1] == board[0][2]&&board[0][0]=='*')    || (board[1][0] == board[1][1]  && board[1][1] == board[1][2] && board[1][0] == '*')    || (board[2][0] == board[2][1] && board[2][1] == board[2][2] && board[2][0] == '*')    || (board[0][0] == board[1][0]  && board[1][0] == board[2][0]&&board[0][0] == '*')    || (board[0][1] == board[1][1]  && board[1][1] == board[2][1] && board[0][1] == '*')    || (board[0][2] == board[1][2]  && board[1][2] == board[2][2] && board[0][2] == '*')    || (board[0][0] == board[1][1]  && board[1][1] == board[2][2] && board[0][0] == '*')    || (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] == '*'))  {        return '*';  }  //电脑赢的情况  else if ((board[0][0] == board[0][1] && board[0][1] == board[0][2]&&board[0][0]=='#')    || (board[1][0] == board[1][1] && board[1][1] == board[1][2] && board[1][0] == '#')    || (board[2][0] == board[2][1] && board[2][1] == board[2][2] && board[2][0] == '#')    || (board[0][0] == board[1][0]  && board[1][0] == board[2][0]&&board[0][0] == '#')    || (board[0][1] == board[1][1] && board[1][1] == board[2][1] && board[0][1] == '#')    || (board[0][2] == board[1][2] && board[1][2] == board[2][2] && board[0][2] == '#')    || (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] == '#')    || (board[0][2] == board[1][1]  && board[1][1] == board[2][0] && board[0][02] == '#'))  {        return '#';  }  else  {    //判断棋盘是否满了    for (int i = 0; i < ROW; i++)    {      for (int j = 0; j < COL; j++)      {        if (board[i][j] == ' ')          return '0';      }    }  }  //程序走到这里就说明没有分出胜负,而且棋盘已经满了,故为和局  return 'Q';}
复制代码


其实循环和枚举在这里都是大同小异。


循环


//版本2.0循环// 判断游戏是否结束     玩家赢返回"*",电脑赢返回"#",棋盘满了返回"Q"char  DetermineEnd2(char board[ROW][COL], int row, int col){  int i = 0;  //判断行  for (i = 0; i < row; i++)  {    if ((board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' '))    {      return board[i][1];    }  }  //判断列  for (i = 0; i < row; i++)  {    if ((board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' '))    {      return board[1][i];    }  }  //判断对角  if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')  {    return board[1][1];  }
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ') { return board[1][1]; } //判断棋盘是否满了 for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { //其中有一个空格就说明还没满,返回"0" if (board[i][j] == ' ') return '0'; } } //程序走到这里就说明没有分出胜负,而且棋盘已经满了,故为和局 return 'Q';}
复制代码

⚓小结语

其实不管是什么小游戏,看似代码很大,其实如果理解其中的思想,就知道他们都有一个共同点,都是一些基本操作组装起来。


接下来才是最关键的一步,就是将它们组装起来,形成一个可以运行的程序我把它实现在 text.c 文件中。


⚡text.c 文件

text.c 为工程测试文件


#define _CRT_SECURE_NO_WARNINGS
#include"game.h"void menu(){ printf("******************************\n"); printf("******* 1. play 0. exit*****\n"); printf("******************************\n");}
void game(){ char board[ROW][COL] = { 0 }; //先初始化棋盘 InitBoard(board, ROW, COL); //打印棋盘让玩家下棋 BoardPrint(board, ROW, COL); char ret; while (1) { printf("玩家下棋\n"); PlayerMove(board, ROW, COL); ret= DetermineEnd2(board,ROW,COL); if (ret != '0') { break; } BoardPrint(board, ROW, COL); printf("电脑下棋\n"); ComputerMove(board, ROW, COL); ret = DetermineEnd2(board, ROW, COL); if (ret != '0') { break; } BoardPrint(board, ROW, COL); } if (ret == '*') { printf("玩家赢\n"); } else if (ret == '#') { printf("电脑赢\n"); } else { printf("平局\n"); } BoardPrint(board, ROW, COL);
}int main(){ //初始化随机值 srand((unsigned int)time(NULL)); //接收用户选择开始游戏,或者退出游戏的变量 int input = 0; do { menu(); printf("请选择>:"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出成功\n"); break; default: printf("无此选项,请重新选择\n"); break; }
} while (input); return 0;}
复制代码

⚡game.c 文件

game.c 为工程函数实现文件


#define _CRT_SECURE_NO_WARNINGS
#include"game.h"

//初始化成空格void InitBoard(char board[ROW][COL], int row, int col){ int i = 0; int j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { board[i][j] = ' '; } }}
//打印棋盘void BoardPrint(char board[ROW][COL], int row, int col){ int i = 0; int j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { printf(" %c ",board[i][j]); if(j<col-1) printf("|"); } printf("\n"); for (j = 0; j < col; j++) { printf("---"); if (j < col - 1) printf("|"); } printf("\n"); }}

void PlayerMove(char board[ROW][COL], int row, int col){ while (1) { printf("请输入坐标>:"); int x = 0; int y = 0; scanf("%d %d", &x, &y); //判断坐标是否合法 if (x <= 3 && x > 0 && y <= 3 && y > 0) { if (board[x - 1][y - 1] == ' ') { board[x - 1][y - 1] = '*'; break; } else { printf("坐标被占用,不能下棋,请选择其他位置\n"); } } else { printf("坐标非法,请重新输入\n"); } }}
//电脑下棋void ComputerMove(char board[ROW][COL], int row, int col){ while (1) { int x = rand() % row; int y = rand() % col; if (board[x][y] == ' ') { board[x][y] = '#'; break; } }//}////版本1.0////玩家赢返回"*",电脑赢返回""#,棋盘满了返回"Q"//char DetermineEnd1(char board[ROW][COL])//{// //玩家赢的情况// if ((board[0][0] == board[0][1] && board[0][1] == board[0][2]&&board[0][0]=='*')// || (board[1][0] == board[1][1] && board[1][1] == board[1][2] && board[1][0] == '*')// || (board[2][0] == board[2][1] && board[2][1] == board[2][2] && board[2][0] == '*')// || (board[0][0] == board[1][0] && board[1][0] == board[2][0]&&board[0][0] == '*')// || (board[0][1] == board[1][1] && board[1][1] == board[2][1] && board[0][1] == '*')// || (board[0][2] == board[1][2] && board[1][2] == board[2][2] && board[0][2] == '*')// || (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] == '*')// || (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] == '*'))// {// // return '*';// }// //电脑赢的情况// else if ((board[0][0] == board[0][1] && board[0][1] == board[0][2]&&board[0][0]=='#')// || (board[1][0] == board[1][1] && board[1][1] == board[1][2] && board[1][0] == '#')// || (board[2][0] == board[2][1] && board[2][1] == board[2][2] && board[2][0] == '#')// || (board[0][0] == board[1][0] && board[1][0] == board[2][0]&&board[0][0] == '#')// || (board[0][1] == board[1][1] && board[1][1] == board[2][1] && board[0][1] == '#')// || (board[0][2] == board[1][2] && board[1][2] == board[2][2] && board[0][2] == '#')// || (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] == '#')// || (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][02] == '#'))// {// // return '#';// }// else// {// //判断棋盘是否满了// for (int i = 0; i < ROW; i++)// {// for (int j = 0; j < COL; j++)// {// if (board[i][j] == ' ')// return '0';// }// }// }// //程序走到这里就说明没有分出胜负,而且棋盘已经满了,故为和局// return 'Q';//}
//版本2.0// 判断游戏是否结束 玩家赢返回"*",电脑赢返回"#",棋盘满了返回"Q"char DetermineEnd2(char board[ROW][COL], int row, int col){ int i = 0; //判断行 for (i = 0; i < row; i++) { if ((board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')) { return board[i][1]; } } //判断列 for (i = 0; i < row; i++) { if ((board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')) { return board[1][i]; } } //判断对角 if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ') { return board[1][1]; }
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ') { return board[1][1]; } //判断棋盘是否满了 for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { //其中有一个空格就说明还没满,返回"0" if (board[i][j] == ' ') return '0'; } } //程序走到这里就说明没有分出胜负,而且棋盘已经满了,故为和局 return 'Q';}
复制代码

⚡game.h

game.h 为工程的函数和变量声明的头文件


#pragma once
#include<stdio.h>#include<stdlib.h>#include<time.h>
#define ROW 3 //行#define COL 3 //列
//初始化棋盘void InitBoard(char board[ROW][COL], int row, int col);//打印棋盘void BoardPrint(char board[ROW][COL], int row, int col);//玩家下棋void PlayerMove(char board[ROW][COL], int row, int col);//电脑下棋void ComputerMove(char board[ROW][COL], int row, int col);
// 版本1.0 判断游戏是否结束 玩家赢返回"*",电脑赢返回"#",棋盘满了返回"Q"char DetermineEnd1(char board[ROW][COL]);

//版本2.0 判断游戏是否结束 玩家赢返回"*",电脑赢返回"#",棋盘满了返回"Q"char DetermineEnd2(char board[ROW][COL],int row,int col);
复制代码


用户头像

平凡的人

关注

还未添加个人签名 2022.08.02 加入

还未添加个人简介

评论

发布
暂无评论
“纯C”实现——三子棋小游戏_c_平凡的人_InfoQ写作社区