三子棋游戏

简介: 之前我们学过函数,数组,循环选择分支等等,我们今天就用现有的C语言知识编写一个小游戏——三子棋游戏。

    之前我们学过函数,数组,循环选择分支等等,我们今天就用现有的C语言知识编写一个小游戏——三子棋游戏。


说到三子棋,我们应该先构思所需要的内容。


1.构建一个游戏菜单界面


2.创建一个3*3的棋盘


3.选择先手(玩家先还是电脑先)


4.轮流下棋流程


5.判断胜负


此次游戏代码将分为两大模块,主函数体系和游戏函数体系


创建三子棋主函数


我们在主函数中,可以使用do while 函数进行游戏的循环在用switch嵌套创建一个主观菜单,让玩家进行循环游戏,也可以直接推出游戏,增加了游戏的开始界面。


代码如下:


void Screenmeau(void)
{
  printf("*******************\n");
  printf("******1.Play******\n");
  printf("******0.Exit*******\n");
  printf("*******************\n");
}
int main(void)
{ 
  int input = 0;
  do {
    Screenmeau();
    printf("请输入数字:");
    scanf("%d", &input);
    switch (input)
    {
      case 1:
        game();
        break;
      case 0:
        break;
      default:
        printf("请从新输入数字,0或1\n");
        break;
    }
  } while (input != 0);
  return 0;
}


通过选择0,可以结束游戏,选择1,进入游戏,选择其他数字则属于非法输入,需要重新选择。当我们选择1时,就可以进入游戏函数体系了。


游戏函数的创建与完善

当我们在主函数中的菜单进行选择进入游戏后,我们的第一大任务就是创建一个棋盘。棋盘的本质就是一个二维数组。那我们这次的目标为三子棋,我们可以使用#define ROW 3  和#define COL 3进行常量定义,在以后的使用或游戏的修改都提供了便利。


首先创建一个未被初始化的二维数组arr,将arr传入Array函数中进行初始化,arr中放入空格可以当作空棋盘。但如果打印棋盘给予玩家的视觉冲击很差,什么也看不见。所以我们应该将棋盘进行美化。


美化棋盘其实非常简单,可以在打印棋盘的同时,打印一些符号将棋盘鲜明化,利用for循环和一些if语句的镶嵌组合就可以将其完成。


见代码:


void Array(char arr[ROW][COL], int row, int col)//arr数组的初始化
{
  for (int i = 0; i < row; i++)
  {
    for (int j = 0; j < col; j++)
    {
      arr[i][j] = ' ';
    }
  }
}
void Ddfinarray(char arr[ROW][COL], int row, int col)//棋盘的美化
{
  for (int i = 0; i < row; i++)
  {
    for (int j = 0; j < col; j++)
    {
      printf(" %c ", arr[i][j]);
      if(j < col-1)
      printf("|");
    }
    printf("\n");
    for (int j = 0; j < col; j++)
    {
      printf("---");
      if (j < col - 1)
        printf("|");
    }
    printf("\n");
  }
}


美化的棋盘:


ec333c900c4441b6b8ebe04773b0e7ed.png


之前的代码只是准备工作,与玩家没有多大的关系,接下来我们将正式进入游戏的环节。


创建一个choosemeau函数让玩家选择,是玩家先下棋还是让电脑先。与主函数的思路一样,使用do while循环中间嵌套一个switch提供菜单让玩家进行选择。然后创建两个函数,一个是玩家的下棋函数,一个是电脑下棋函数,无论在上一环节选择1或者2,只是两个函数的先后执行顺序不同。所以先完成两个函数体。


int  Choosemeau(void)//选择先手菜单程序
{
  int a = 0;
  do {
    printf("********************\n");
    printf("*****1.玩家先 ******\n");
    printf("*****2.电脑先 ******\n");
    printf("********************\n");
    printf("请选择:");
    scanf("%d", &a);
    switch (a)
    {
      case 1:
        return 1;
      case 2:
        return 2;
      default:
        printf("只能输入1或2,请重新输入\n");
    }
  } while (1);
}


首先完成玩家下棋函数,提供两个变量,分别对应下棋的坐标,因为这个棋盘实质是一个二维数组,数组的第一个元素的下标为0,但是玩家不都是程序员,所以我们应当在玩家输入对应坐标后将其坐标减1,得到的值才是玩家心里所想的值。


为了使游戏更加严谨,作为程序员我们应该考虑更加全面。有两个问题,输入的值超出数组下标,玩家下棋的位置已有棋子。我们应该让程序避免出现这两个问题,我们可以用分支语句进行判断,如果玩家选择的位置的值是空格并且输入的坐标没有超出数组的下标,既可以落子,玩家的棋子用‘*’表示。反正系统将提醒非法落子,需要玩家重新输入两个合法的坐标。


void Playermove(char arr[ROW][COL], int row, int col)
{
  int x = 0;
  int y = 0;
  while (1)
  {
    printf("请输入要下棋的横竖坐标:");
    scanf("%d %d", &x, &y);
    if (arr[x - 1][y - 1] == ' ')
    {
      if (x > 0 && x <= row && y >0 && y <= col)
      {
        arr[x - 1][y - 1] = '*';
        break;
      }
    }
    else
    {
      printf("非法落子\n");
    }
  }
}


完成玩家下棋函数后,我们将进行电脑下棋。


下棋的本质就是在arr二维数组中选择合适的两个下标,所以我们可以使用随机数函数rand()和srand()的函数,我们在主函数中引入srand((unsigned int )time(NULL));,就可以在函数中使用rand()函数中产生随机数(使用rand(),srand(),time()函数得引用头文件#include<stdlib.h>和#include<time.h>),将随机数%3就可以得到0-3的随机数。在电脑输入时我们只需要考虑落子位置是空位置(空格)即可,使用if判断即可,电脑落子用‘#’表示。


void Displayermove(char arr[ROW][COL], int row, int col)
{
  printf("电脑下棋\n");
  while (1)
  {
    int x = rand() % row;
    int y = rand() % col;
    if (arr[x][y] == ' ')
    {
      arr[x][y] = '#';
      break;
    }
  }
}


当玩家和电脑都走了一步之后,我们将棋盘展现出来,可以更直观的看出棋子的位置。所以我们可以在两个下棋函数运行完成后运行一下Ddfinarray函数,将棋盘打印出来即可。


如果现在运行程序,当棋盘被占满后会进入死循环,就相当于递归没退出程序。而退出的程序就是判断棋的输赢。


下棋的结果一般有四个:玩家赢,电脑赢,和棋,继续游戏。


无论玩家赢电脑赢还是和棋就可以结束循环了,但是继续游戏还需要我们继续下棋。所以我们现需要一个函数来判断棋盘是否下满,利用for循环对数组中的每个元素进行检测判断,如果没有空格就是棋盘下满了。这个函数需要有返回值供以后选择,如果棋盘满了则返回值为1,反之返回0。


三子棋的获胜条件就是让横竖斜都是同一种棋子,创建一个判断胜负的函数,在玩家和电脑每下一步棋后进行判断,并且这个函数有返回值char,返回的值在if判断条件时可以使用,如果返回的是’*‘时,则玩家赢,返回是‘#’电脑赢,返回是‘h’为和棋,返回c为继续。


这里就有一个非常巧妙的点,用数组判断横竖斜是否相等并且不等于空格,返回值为数组的符号,这样就可以用一个函数来提取其中的返回值即可,使用if就可以知道游戏的输赢。如果前面的横竖斜都没有相同的时,使用上一个函数的返回值当作下一个if的判断条件,即可判断出是否和棋。如果棋盘没有满将使用continue退出此次循环进行下一次两两下棋,直到得到结果,游戏结束。


游戏函数的主体:


void game(void)
{
  char arr[ROW][COL] = { 0 };
  Array(arr, ROW, COL);
  Ddfinarray(arr, ROW, COL);
  int a = Choosemeau();
  if (a == 1)
  { 
    while (1)
    {
      Playermove(arr, ROW, COL);
      Ddfinarray(arr, ROW, COL);
      Displayermove(arr, ROW, COL);
      Ddfinarray(arr, ROW, COL);
      char ch =Iswin(arr, ROW, COL);
      if (ch == '*')
      {
        printf("玩家赢\n");
        break;
      }
      else if (ch == '#')
      {
        printf("电脑赢\n");
        break;
      }
      else if (ch == 'c')
      {
        continue;
      }
      else
      {
        printf("和棋\n");
        break;
      }
    }
  }
  else
  {
    while (1)
    {
      Displayermove(arr, ROW, COL);
      Ddfinarray(arr, ROW, COL);
      Playermove(arr, ROW, COL);
      Ddfinarray(arr, ROW, COL);
      char ch = Iswin(arr, ROW, COL);
      if (ch == '*')
      {
        printf("玩家赢\n");
        break;
      }
      else if (ch == '#')
      {
        printf("电脑赢\n");
        break;
      }
      else if (ch == 'c')
      {
        continue;
      }
      else 
      {
        printf("和棋\n");
        break;
      }
    }
  }
}


判断棋盘是否下满函数:


int Isfull(char arr[ROW][COL], int row, int col)
{
  for (int i = 0; i < row; i++)
    for (int j = 0; j < col; j++)
    {
      if (arr[i][j] == ' ')
        return 1;
    }
}


判断输赢的函数:


char Iswin(char arr[ROW][COL], int row, int col)
{
  for(int i = 0; i < row; i++)
  {
    if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][0] != ' ')
      return arr[i][0];
  }
  for(int j = 0; j < col; j++)
  {
    if (arr[0][j] == arr[1][j] && arr[1][j] == arr[2][j] && arr[2][j] == '*')
      return arr[0][j];
  }
  if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[2][2] == '*')
  {
    return arr[0][0];
  }
   if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[2][2] == '#')
  {
     return arr[0][0];
  }
   if (Isfull(arr, ROW, COL))
     return 'c';
   else
     return 'h';
}


将这些函数结合一起就可以得到三子棋小游戏,现在让我们玩一下。


92a89acd1d7a4d97aaf53cf4a344a674.png

baf25301ff6e46f2b37827eeb0d170e9.png


小伙伴们快来实践一下吧!!!

目录
相关文章
|
传感器 编解码 数据处理
毕业设计|基于STM32单片机的水位浑浊度检测设计
毕业设计|基于STM32单片机的水位浑浊度检测设计
1599 0
|
存储 安全 数据安全/隐私保护
Windows部署WebDAV服务并映射到本地盘符实现公网访问本地存储文件
Windows部署WebDAV服务并映射到本地盘符实现公网访问本地存储文件
1322 0
|
Web App开发 iOS开发 Windows
ios获取原生系统应用的包名
ios获取原生系统应用的包名
2925 0
|
存储 自然语言处理 PyTorch
Transformers 4.37 中文文档(二十一)(1)
Transformers 4.37 中文文档(二十一)
235 0
|
存储 弹性计算 数据库
阿里云服务器租用收费价格参考,弹性裸金属服务器架构云服务器收费价格表
弹性裸金属服务器架构阿里云服务器有计算型弹性裸金属服务器ebmc7、内存型弹性裸金属服务器ebmr7、AMD计算型弹性裸金属服务器ebmc7a、通用型弹性裸金属服务器ebmg6等实例规格可选,不同实例规格的租用收费价格是不一样的,本文为大家汇总了目前基于弹性裸金属服务器架构下的各个实例规格的阿里云服务器收费标准,以供参考。
阿里云服务器租用收费价格参考,弹性裸金属服务器架构云服务器收费价格表
|
存储 Oracle 关系型数据库
oracle 过滤字段中的中文,不再洋不洋土不土
oracle 过滤字段中的中文,不再洋不洋土不土
354 0
|
BI 数据安全/隐私保护
二维码活码和静态码有什么区别?
作为万物互联时代的“数字身份证”,二维码在物流运输、生产制造、会议签到、移动支付等行业广泛应用,极大地改变了人们的日常生活。接下来,我们就为大家科普一下二维码的类型,也就是活码和静态码。
539 0
|
SQL 弹性计算 自然语言处理
AIGC-知识库-LLM:从0开始搭建智能问答钉钉机器人
本文描述在阿里云上从0开始构建个人/企业专属,具备私域知识库+LLM智能问答钉钉机器人。知识库技术方案使用了Lindorm AI数据服务平台知识库能力,LLM使用了开源ChatGLM2-6B。
1604 1
|
传感器 数据采集 安全
|
算法 网络协议 API
彻底明白如何设计一个良好的 API
现在软件开发流程都是协同合作的,前后端分离,那么我们如何实现对 API 的统一认知?又该如何设计一个良好的 API 接口?随着业务的演进,如何设计一个有兼容性的API?面对多种客户端,如何设计一个处处适用的 API 呢? RESTful 是目前最流行的 API 设计规范,通过一定的规范可以解决上面这些问题,对于 RESTful 架构的理解可以参考阮一峰老师的这篇文章(http://www.ruanyifeng.com/blog/2011/09/restful.html),简单一句话就是对服务器端资源进行操作,实现"表现层状态转化"。URI(统一资源定位符)代表一种资源,它可以是一段文本、一张图