从零到一快速学会三子棋

简介: 从零到一快速学会三子棋

前言


三子棋又名井字棋,是个老少皆宜的小游戏,相信大家都玩过吧,游戏规则就不多说了。今天我们就来用 C 语言来简单的实现它。


一、游戏预期画面


1138b28d3db44342999d4ccaccb6946c.png


二、游戏的实现


1、菜单


void menu()
{
  printf("*****************************\n"); 
  printf("*******    1.play    ********\n"); 
  printf("*******    0.exit    ********\n");
  printf("*****************************\n");
}


2、主函数


首先,我们思考一下,本次游戏应该最少执行一次,在执行过程中进行选择,那么我们应该使用 do while 循环来实现。根据选择不同,来执行相应的程序,那么应该使用 switch 语句。

下面我们来看代码:


int main()
{
  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);                //根据输入的值来确定是否进行循环,0则退出循环
  return 0;                       //这也是为什么在菜单中把退出游戏设置成0的原因
}


3、game函数的实现


3.1、棋盘的打印


我们根据预期画面可以判断出,我们应该创建一个 3*3 的数组来存放我们下的棋,并且得把数组初始化为空格。我们可以根据坐标来选择落子位置。那么问题来了,我们应该如何打印棋盘呢?


首先,我们来仔细观察一下画面中的棋盘,我们可以发现每次下棋都是下在格子的中间也就是下图:


a2257f825294463788c6037c06111cab.png


也就是说每个数的旁边有空格,同行的数之间用 | 隔开,第二行是由 - 和 | 组成。

我们把第一行和第二行看成一组,我们就可以得到三组(假设第三组第二行存在)

也就是像下图:


647f7520fd794845b092d3a941ddafba.png


这样我们便能写出如下代码:


#define M 3
#define N 3
void game()
{
  char arr[M][N] = { 0 };
  chu_shi(arr, M, N);                 //初始化数组
  da_yin(arr, M, N);                  //打印棋盘
}
void chu_shi(char arr[M][N], int m, int n)
{
  int i = 0;
  int j = 0;
  for(i=0;i<m;i++)
  for (j = 0; j < n; j++)
  {
    arr[i][j] = ' ';           //将数组初始化为空格
  }
}
void da_yin(char arr[M][N], int m, int n)         
{
  int i = 0;
  for (i; i < m; i++)
  {
  printf(" %c | %c | %c \n", arr[i][0], arr[i][1], arr[i][2]);
  }
  if (i < m - 1)                  //若没有 if 则打印了第三组的第二行,if 起限制作用
  printf("---|---|---\n");
}


这样,我们就打印出来了整个棋盘。但有个小问题,就是如果我改动棋盘大小呢,现在是 3 * 3 ,我改动 M N 的值为 5 5 呢,我们可以看出,行是没错的,但是列只打印了三列,这明显不行,于是我们按照打印行的思路来打印列,同样的将列分成三组,如图:


326afa22febe43a7855e2a8a7088f237.png


我们可以将打印棋盘代码优化成如下:


void da_yin(char arr[M][N], int m, int n)
{
  int i = 0;
  for (i; i < m; i++)
  {
  int j = 0;
  for (j = 0; j < n; j++)
  {
    printf(" %c ", arr[i][j]);
    if (j < n - 1)               //限制 | 的打印
    printf("|");
  }
  printf("\n");
  if (i < m - 1)
  {
    for (j = 0; j < n; j++)
    {
    printf("---");
    if (j < n - 1)           //限制 | 的打印
      printf("|");
    }
  }
  printf("\n");
  }
}


3.2、玩家下棋


接下来我们应该开始正式玩游戏了,也就是输入落子位置。

我们思考一下,我们坐标应该合理,不能超过数组规定,还有我们下的子应该保存下来,下次该点不能被选择。


我们可以写出如下代码:


void wan_jia(char arr[M][N], int m, int n)
{                              
  int x = 0;
  int y = 0;                  //因为玩家可能不知道是从0行0列开始的
  printf("玩家下棋:>\n");     //所以我们就设置成从第1行1列到第3行3列
  while (1)              
  {
  printf("请输入要下棋的坐标:>\n");
  scanf("%d %d", &x, &y);
  if (x >= 1 && x <= m && y >= 1 && y <= n)
  {                        
  if (arr[x - 1][y - 1] == ' ')
    {
    arr[x - 1][y - 1] = '*';
    break;
    }
    else
    {
    printf("该坐标被占用,请重新输入\n");
    }
  }
  else
  {
    printf("坐标非法请重新输入");
  }
  }
}


3.3、电脑下棋


现在我们下了一步,接下来轮到电脑了,同样的逻辑,但电脑下棋是随机的,因此我们要生成随机数,随机数的生成代码如下:


#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
  srand((unsigned int)time(NULL));    //设置一个随机数的生成器 
  int m = rand()%3;                 //因为 rand 生成随机数范围0~32767
  printf("%d",m);                   //所以 %3 使随机数生成范围为0~2
}


因此电脑下棋代码我们就可以写出了:


srand((unsigned int)time(NULL));     //我们需要放在 main 函数中
void dian_nao(char arr[M][N], int m, int n)
{
  printf("电脑下棋:>\n");
  while (1)
  {
  int x = rand() % m;
  int y = rand() % n;
  if (arr[x][y] == ' ')
  {
    arr[x][y] = '#';
    break;
  }
  }
}


这样我们就能完成玩家与电脑之间的对弈,但是美中不足的是,我们无法判断输赢。因此,下一步我们判断输赢。


3.4、判断输赢


我们知道游戏过程中会有几种状态:玩家赢、电脑赢、平局、游戏继续,这四种状态我们就分别用 ’ * ’ 、’ # ‘、’ Q ‘、’ C '来作为返回值。

我们知道三子棋的无非就是横或竖或斜三子成线,这样我们便能写出如下代码:


char is_win(char arr[M][N], int m, int n)      
{                                //我们要返回字符所以用 char 
  int i = 0;
  for (i = 0; i < m; i++)     //判断行
  {
  if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][0] != ' ')
  {
    return arr[i][0];
  }
  }
  for (i = 0; i < n; i++)     //判断列
  {                         
  if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[0][i] != ' ')
  {
    return arr[0][i];
  }
  }
        //以下判断两条斜线
  if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[0][0] != ' ')                      
  {
  return arr[0][0];
  }
  if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' ')
  {
  return arr[1][1];
  }
}


我们已经可以判断输赢了,但是我们似乎还漏了平局。

我们便再创建个函数来遍历 arr 数组,来判断是否还有空格,如果有也就是棋盘没满,返回0,反之返回1。

由此,我们可以写出如下代码:


//如果棋盘满了,返回1
//不满返回0
int is_full(char arr[M][N], int m, int n)
{
  int i = 0;
  for (i = 0; i < m; i++)
  {
  int j = 0;
  for (j = 0; j < n; j++)
  {
    if (' ' == arr[i][j])
    return 0;
  }
  }
  return 1;
}


我们再将其插入判断属于代码中,可以得到:


char is_win(char arr[M][N], int m, int n)
{
  int i = 0;
  for (i = 0; i < m; i++)
  {
  if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][0] != ' ')
  {
    return arr[i][0];
  }
  }
  for (i = 0; i < n; i++)
  {
  if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[0][i] != ' ')
  {
    return arr[0][i];
  }
  }
  if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[0][0] != ' ')
  {
  return arr[0][0];
  }
  if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' ')
  {
  return arr[1][1];
  }
  //判断平局
  if (is_full(arr, m, n) == 1)
  {
  return 'Q';
  }
  //继续
  return 'C';
}


3.5、game函数的真正实现


我们将 game 函数的实现过程,调用的函数了解了一遍,下面我们来看具体 game 函数如何实现:


void game()
{
  char ret = 0;
  char arr[M][N] = { 0 };
  chu_shi(arr, M, N);       //首先,我们初始化数组
  da_yin(arr, M, N);        //打印一下棋盘
  while (1)                 //进行游戏
  {
  wan_jia(arr, M, N);         //玩家先下
  ret = is_win(arr, M, N);    //每下一步进行判断
  if (ret != 'C')             //判断是否出现输赢或平局·
    break;             //'C'代表继续,不等于 'C' 即出现了结果
                              //break 跳出循环
  da_yin(arr, M, N);         //每下完一步再打印一下棋盘
  dian_nao(arr, M, N);       //电脑下
  ret = is_win(arr, M, N);
  if (ret != 'C')
    break;
  da_yin(arr, M, N);
  }
  if (ret == '*')
  printf("玩家赢\n");
  else if (ret == '#')
  printf("电脑赢\n");
  else
  printf("平局\n");
  da_yin(arr, M, N);        //最后游戏结束打印棋盘
}


注意,我们判断输赢都是在打印棋盘之前的,如果出了结果,我们是不知道棋盘状况的,所以游戏最后应该打印棋盘。


4、游戏具体代码


像这样一个游戏,我们应该分三个部分:game.h、test.c、game.c。

game.h : 包含头文件和对 test.c 中函数的声明。

test.c :将函数封装起来,使得 game.c 尽量简洁

game.c : 游戏主体,包含 main 函数。


4.1、game.h


#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define M 3
#define N 3
//初始化数组
void chu_shi(char arr[M][N], int m, int n);
//打印棋盘
void da_yin(char arr[M][N], int m, int n);
//玩家下棋
void wan_jia(char arr[M][N], int m, int n);
//电脑下棋
void dian_nao(char arr[M][N], int m, int n);
//判断游戏状态
char is_win(char arr[M][N], int m, int n);


4.2、test.c


#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
     //初始化数组
void chu_shi(char arr[M][N], int m, int n)
{
  int i = 0;
  int j = 0;
  for(i=0;i<m;i++)
  for (j = 0; j < n; j++)
  {
    arr[i][j] = ' ';
  }
}
       //打印棋盘
void da_yin(char arr[M][N], int m, int n)
{
  int i = 0;
  for (i; i < m; i++)
  {
  int j = 0;
  for (j = 0; j < n; j++)
  {
    printf(" %c ", arr[i][j]);
    if (j < n - 1)
    printf("|");
  }
  printf("\n");
  if (i < m - 1)
  {
    for (j = 0; j < n; j++)
    {
    printf("---");
    if (j < n - 1)
      printf("|");
    }
  }
  printf("\n");
  }
}
        //玩家下棋
void wan_jia(char arr[M][N], int m, int n)
{
  int x = 0;
  int y = 0;
  printf("玩家下棋:>\n");
  while (1)
  {
  printf("请输入要下棋的坐标:>\n");
  scanf("%d %d", &x, &y);
  if (x >= 1 && x <= m && y >= 1 && y <= n)
  {
    if (arr[x - 1][y - 1] == ' ')
    {
    arr[x - 1][y - 1] = '*';
    break;
    }
    else
    {
    printf("该坐标被占用,请重新输入\n");
    }
  }
  else
  {
    printf("坐标非法请重新输入");
  }
  }
}
      //电脑下棋
void dian_nao(char arr[M][N], int m, int n)
{
  printf("电脑下棋:>\n");
  while (1)
  {
  int x = rand() % m;
  int y = rand() % n;
  if (arr[x][y] == ' ')
  {
    arr[x][y] = '#';
    break;
  }
  }
}
   //如果棋盘满了,返回1
   //不满返回0
int is_full(char arr[M][N], int m, int n)
{
  int i = 0;
  for (i = 0; i < m; i++)
  {
  int j = 0;
  for (j = 0; j < n; j++)
  {
    if (' ' == arr[i][j])
    return 0;
  }
  }
  return 1;
}
         //判断输赢
char is_win(char arr[M][N], int m, int n)
{
  int i = 0;
  for (i = 0; i < m; i++)
  {
  if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][0] != ' ')
  {
    return arr[i][0];
  }
  }
  for (i = 0; i < n; i++)
  {
  if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[0][i] != ' ')
  {
    return arr[0][i];
  }
  }
  if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[0][0] != ' ')
  {
  return arr[0][0];
  }
  if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' ')
  {
  return arr[1][1];
  }
  //判断平局
  if (is_full(arr, m, n) == 1)
  {
  return 'Q';
  }
  //继续
  return 'C';
}


4.3、game.c


#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"        //包含 game.h
void menu()
{
  printf("*****************************\n"); 
  printf("*******    1.play    ********\n"); 
  printf("*******    0.exit    ********\n");
  printf("*****************************\n ");
}
void game()
{
  char ret = 0;
  char arr[M][N] = { 0 };
  chu_shi(arr, M, N);
  da_yin(arr, M, N);
  while (1)
  {
  wan_jia(arr, M, N);
  ret = is_win(arr, M, N);
  if (ret != 'C')
    break;
  da_yin(arr, M, N);
  dian_nao(arr, M, N);
  ret = is_win(arr, M, N);
  if (ret != 'C')
    break;
  da_yin(arr, M, N);
  }
  if (ret == '*')
  printf("玩家赢\n");
  else if (ret == '#')
  printf("电脑赢\n");
  else
  printf("平局\n");
  da_yin(arr, M, N);
}
int main()
{
  int input = 0;
  srand((unsigned int)time(NULL));     //设置一个随机数的生成器
  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;
}


这样我们的三子棋游戏就圆满完成了,咋一看似乎头晕晕的,但是如果我们把它拆分为一个个小步骤,我们便能轻易理解并写出来。

学习就是这样,碰到难题往往拆分步骤就变简单了。


以后会给大家带来惊险又刺激的扫雷游戏。

那我们下期见了~


相关文章
|
7月前
|
C语言
简易三子棋的实现
简易三子棋的实现
简单三子棋的实现
简单三子棋的实现
68 0
|
C语言
C项目(三子棋BUG解决)
C项目(三子棋BUG解决)
61 0
|
程序员 C语言
三子棋(超详细)
三子棋(超详细)
|
C语言
C/【三子棋】
C/【三子棋】
三子棋的实现有这么简单吗?
三子棋的实现有这么简单吗?
56 0
自创三子棋,拓展版
自创三子棋,拓展版
44 0
|
存储 C语言
简单的三子棋实现代码
简单的三子棋实现代码
72 0