【C】三子棋 VS 扫雷

简介: 【C】三子棋 VS 扫雷

💻前言

🍁在这里总结用C语言实现三子棋和扫雷俩个小游戏,实现这俩个游戏的思路其实是差不多的,都是对二维数组进行操作 ,把功能通过分支和循环等实现;只要能想到,就可以通过顺序,选择,循环这三种结构实现!

💻三子棋

实现思路

(main.c)是主函数测试部分

(game.c)是游戏功能具体实现部分

(game.h)是函数声明部分

首先将棋盘初始化,也就是将二维数组中的元素都放空格。

7fc11c8a2de144ff835420d6c2508286.png

然后我们这里实现打印上面这样一个三子棋盘,有人想为啥不在 上下左右加上 —— 呢,其实是可以实现的,不过实现打印图中的棋盘可以锻炼编程的逻辑,下面分析一下:

7fc11c8a2de144ff835420d6c2508286.png

这里可以通过for循环打印棋盘的行和列,棋盘的每一行可以拆分为数据部分和分割线部分 ,通过if语句限制条件使分割部分比数据部分少打印一次;而每一行数据部分的拆成每一列循环打印,每一列的格式是空格 %c 空格 | ,同样的做法把每一行最后一个 | 过滤掉,打印分割线(- - -| )也是一样的。

棋盘打印好后,实现下棋部分,每次玩家/电脑下过棋后,都要进行判断输赢的过程。

game.h

#pragma once
#include <stdio.h>
#include<time.h>
#include<stdlib.h>
//可自由更改棋盘大小
#define ROW 3
#define COL 3
//初始化棋盘
void initboard(char board[ROW][COL],int row,int col);
//显示棋盘
void displayboard(char board[ROW][COL], int row, int col);
//玩家下棋
void palyermove(char board[ROW][COL], int row, int col);
//电脑下棋
void computermove(char board[ROW][COL], int row, int col);
//判断输赢
//玩家赢返回'*'
//电脑赢返回'#'
//平局返回'q'
//继续返回'c'
char iswin(char board[ROW][COL], int row, int col);

main.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
  printf("*********************\n");
  printf("***** 1.开始游戏*****\n");
  printf("***** 0.退出游戏*****\n");
  printf("*********************\n");
}
void game()
{
  char ret = '0';
  char board[ROW][COL] = { 0 };
  //初始化棋盘
  initboard(board,ROW,COL);
  //显示棋盘
  displayboard(board, ROW, COL);
  while (1)
  {
    //玩家下棋
    palyermove(board, ROW, COL);
    //判断输赢
    ret = iswin(board, ROW, COL);
    if (ret != 'c')//当返回'*'/'#'/'q'时跳出循环判断谁赢了
    {
      break;
    }
    displayboard(board, ROW, COL);
    //电脑下棋
    computermove(board, ROW, COL);
    ret = iswin(board, ROW, COL);
    if (ret != 'c')
    {
      break;
    }
    displayboard(board, ROW, COL);
  }
  if (ret == '*')
  {
    printf("玩家胜出\n");
  }
  if (ret == '#')
  {
    printf("电脑胜出\n");
  }
  if (ret == 'q')
  {
    printf("你和电脑旗鼓相当\n");
  }
}
int main()
{
  srand((unsigned)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

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//满了返回1
//不满返回0
int isfull(char board[ROW][COL], int row, int col)
{
  int i = 0, j = 0;
  for (i = 0; i < row; i++)
  {
    for (j = 0; j < col; j++)
    {
      if (board[i][j] == ' ')
        return 0;
    }
  }
  return 1;
}
void initboard(char board[ROW][COL], int row, int col)
{//未下棋,数组中放的是空格
  int i = 0;
  for (i = 0; i < row; i++)
  {
    int j = 0;
    for (j = 0; j < col; j++)
    {
      board[i][j] = ' ';
    }
  }
}
void displayboard(char board[ROW][COL], int row, int col)
{//将棋盘分行打印,每一行再分列打印
  int i = 0;
  for (i = 0; i < row; i++)
  {
    //打印数据
    //printf(" %c | %c | %c \n",board[i][0],board[i][1],board[i][2]);不推荐,把棋盘规格写死了
    int j = 0;
    for (j = 0; j < col; j++)
    {
      printf(" %c ",board[i][j]);
      if(j < col - 1)
           printf("|");//过滤掉最后一个竖杠
    }
    printf("\n");
    if(i < row - 1)//过滤掉最后一行分割线
    {   //打印分割线 
      //printf("---|---|---\n");
      for (j = 0; j < col; j++)
      {
        printf("---");
        if (j < col - 1)//过滤掉最后一个竖杠
          printf("|");
      }
      printf("\n");
    }
  } 
}
void palyermove(char board[ROW][COL], int row, int col)
{
  while(1)
  { 
    int x = 0;
    int y = 0;
    printf("玩家下棋\n");
    printf("请输入坐标:>");
    scanf("%d%d", &x, &y);
    if (x > 0 && x <= 3 && y > 0 && y <= 3)
    {
      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)
{
  printf("电脑下棋\n");
  while (1)
  {
    int x = rand() % 3;
    int y = rand() % 3;
    if (board[x][y] == ' ')
    {
      board[x][y] = '#';
      break;
    }
  }
}
char iswin(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];
    }
  }
  //列相同
  int j = 0;
  for (j = 0; j < col; j++)
  {
    if (board[0][j] == board[1][j]&&board[1][j] == board[2][j] && board[1][j] != ' ')
    {
      return board[1][j];
    }
  }
  //对角线相同
  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];
  }
  //棋盘满了,在上面几个判断完后
  //到这里说明平局了
  if (isfull(board, row, col))
  {
    return 'q';
  }
  //上面都不满足游戏继续
  return 'c'; 
}

实现效果,待改进,不足之处

7fc11c8a2de144ff835420d6c2508286.png

1.电脑下棋是生成随机数随机落子,也就是它不会根据玩家的棋子位置进行堵棋,电脑赢不了玩家。。。

2.判断输赢部分写死了,只适合三子棋盘上进行三子棋的判断输赢,后面有时间我再研究改进更新!


💻扫雷

实现思路

(main.c)是主函数测试部分

(game.c)是游戏功能具体实现部分

(game.h)是函数声明部分

7fc11c8a2de144ff835420d6c2508286.png

这是一个扫雷的棋盘,同样的这样的棋盘实质上是一个数组,比如我们扫雷的棋盘是9 * 9的大小,如果我们要排雷的位置位置不是雷的话,这个位置就显示周围一圈雷的个数,而如果将数组大小设置为9行9列的话,而排雷位置在边界,在计算周围雷的个数时就会出现数组越界的情况,所以为了方便这里我们将数组扩大一圈数组大小设置为11行11列,而布雷和排雷只在中间9 * 9的范围内进行!

7fc11c8a2de144ff835420d6c2508286.png

我们定义俩个不同的数组,一个是布雷的数组,一个是玩家排雷的数组,排雷过程中会计算布雷数组中的信息反馈到排雷数组中显示,屏幕上只显示排雷数组中的内容。

7fc11c8a2de144ff835420d6c2508286.png

定义好俩个数组后,对俩个数组中的内容进行初始化,未布雷的棋盘中初始化为字符’0’,而未排雷的棋盘中初始为字符’#'。

然后用rand函数随机布置雷(字符’1’),雷的个数不必写死,利用宏定义的标识符常量可以做到灵活设置雷的个数。

最后一部分实现排雷过程,排雷位置是雷则被炸死,游戏失败,不是雷则通过计算布置雷数组中位置周围雷的个数存放到排雷数组中并显示排雷数组中的内容;

这里介绍如何计算周维雷的个数:布雷的数组中’0’不是雷,'1’是雷,这里的0和1是字符,1 + ‘0’ = ‘1’,0 + ‘0’= ‘0’;所以我们将排雷坐标周围的八个字符加起来然后减去8 * ‘0’得到的就是这个坐标周围雷的个数,这个结果再加上字符’0’,得到字符数字放到排雷的数组中。

还可以实现俩个功能,对位置进行标记 ‘-’ ,把没有雷的地方展开一片,这里展开一片功能的实现要用到递归,递归要满足3个条件:

  1. 该坐标不是雷
  2. 该坐标周围没有雷
  3. 该坐标没有被排查过

否则就会死递归!

判断输赢我这里这样实现:棋盘大小-雷的个数-不是 ‘*’ 和 ‘-’ 等于0,就表明扫雷成功了!

game.h

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
//雷盘范围,可灵活更改
#define ROW 9
#define COL 9
//防止计算周围雷的个数时数组越界
#define ROWS ROW+2
#define COLS COL+2
//可更改雷的个数
#define EASY_COUNT 10
void initboard(char board[ROWS][COLS], int rows, int cols,char set);
void displayboard(char board[ROWS][COLS], int row, int col);
void setMine(char board[ROWS][COLS], int row, int col);
void findMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);

main.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
  printf("|------1.play------|\n");
  printf("|------0.exit------|\n");
}
void game()
{
  //布置雷的棋盘
  char mine[ROWS][COLS] = { 0 };
  //玩家进行排雷的棋盘
  char show[ROWS][COLS] = { 0 };
  //初始化雷盘'0'
  initboard(mine,ROWS,COLS,'0');
  //初始化排雷棋盘'*'
  initboard(show, ROWS, COLS, '*');
  //布置雷
  setMine(mine, ROW, COL);
  //显示雷盘,只显示排雷的棋盘
  /*displayboard(mine,ROW,COL);*/
  displayboard(show, ROW, COL);
  //实现排雷过程
  findMine(mine,show, ROW, COL);
}
int main()
{
  srand((unsigned)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

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void initboard(char board[ROWS][COLS], int rows, int cols, char set)
{
  int i = 0;
  int j = 0;
  for (i = 0; i < rows; i++)
  {
    for (j = 0; j < cols; j++)
    {
      board[i][j] = set;
    }
  }
}
void displayboard(char board[ROWS][COLS], int row, int col)
{
  printf("------扫雷游戏------\n");
  int i = 0;
  int j = 0;
  for (j = 0; j <= col; j++)
  {
    printf("%d ", j);
  }
  printf("\n");
  for (i = 1;i <= row;i++)
  {
    printf("%d ", i);
    for (j = 1; j <= col; j++)
    {
      printf("%c ", board[i][j]);
    }
    printf("\n");
  }
  printf("------扫雷游戏------\n");
}
void setMine(char board[ROWS][COLS], int row, int col)
{
  int count = EASY_COUNT;
  while (count)
  {
    int x = rand() % row + 1;
    int y = rand() % col + 1;
    if (board[x][y] == '0')
    {
      board[x][y] = '1';
      count--;
    }
  }
}
int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
  return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1]
    + mine[x][y - 1] + mine[x][y + 1]
    + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';
}
void sign(char show[ROWS][COLS])
{
  int i = 0;
  int x = 0, y = 0;
  do
  {
    printf("标记坐标?(1:是/0:否/2:对已标记的坐标取消)>");
    scanf("%d", &i);
    switch (i)
    {
    case 0:
      break;
    case 1:
      printf("输入标记位置坐标:>");
      scanf("%d%d", &x, &y);
      if (show[x][y] != '*'||show[x][y] == '-')
      {
        printf("这个位置再不能进行标记了\n");
      }
      else
      {
        if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
        {
          show[x][y] = '-';
          displayboard(show, ROW, COL);
        }
        else
        {
          printf("坐标非法,请重新输入!\n");
        }
      }
      break;
        case 2:
        printf("输入要取消标记的坐标:>");
        scanf("%d%d", &x, &y);
        if (show[x][y] != '-')
        {
          printf("这个坐标未进行标记,重新输入\n");
        }
        else
        {
          show[x][y] = '*';
          displayboard(show, ROW, COL);
        }
        break;
    default:
      printf("选择错误,请重新输入\n");
      break;
    }
  } while (i);
}
void unfold1(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
  if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
  {
    int i = 0;
    int j = 0;
    int flag = 1;
    if (show[x][y] == '*' || show[x][y] == '-' )
    {
      flag = 0;//没有被排查过的
    }
    int count = get_mine_count(mine, x, y);
        if (count == 0)
        {
      show[x][y] = ' '; 
        }
        else
        {
         show[x][y] = count + '0';
        }
    if (flag == 0 && count == 0)
    {
      for (i = -1; i <= 1; i++)
      {
        for (j = -1; j <= 1; j++)//对其周围的8个坐标递归
        {
          unfold1(mine, show, x + i, y + j);
        }
      }
    }
  }
}
int is_win(char show[ROWS][COLS], int row, int col)
{
  int i = 0;
  int j = 0;
  int ret = 0;
  for (i = 1; i <= ROW; i++)
  {
    for (j = 1; j <= COL; j++)
    {
      if (show[i][j] != '*' && show[i][j] != '-')
        ret++;
    }
  }
  return ret;
}
void findMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
  int x = 0;
  int y = 0;
  while (row * col - EASY_COUNT - is_win(show, row, col))
  {
    printf("输入要排雷的坐标:>");
    scanf("%d%d", &x, &y);
    if (show[x][y] != '*'&& show[x][y] != '-')
    {
      printf("这个位置已经排过了,不能重复进行\n");
    }
    else
    {
      if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
      {
        //是雷
        if (mine[x][y] == '1')
        {
          printf("游戏失败,你被炸死了!\n");
          displayboard(mine, ROW, COL);
          break;
        }
        else
        {
          int count = get_mine_count(mine, x, y);
          if (count == 0)
          {
            unfold1(mine, show, x, y);//展开一片的功能
            displayboard(show, ROW, COL);
            sign(show);//标记功能
          }
          else
          {
                show[x][y] = count + '0';
            displayboard(show, ROW, COL);
            sign(show);//标记功能
          }
        }
      }
      else
      {
        printf("坐标非法,请重新输入!\n");
      }
    }
    if (row * col - EASY_COUNT - is_win(show, row, col) == 0)
    {
      printf("恭喜你,扫雷成功!\n");
      displayboard(mine, ROW, COL);
    }
}

实现效果,待改进,不足之处

7fc11c8a2de144ff835420d6c2508286.png

标记功能每一次排雷后都要进行选择,感觉步骤太过繁琐,有待改进,以后有时间研究更新!

目录
相关文章
|
8月前
|
C语言
简易三子棋的实现
简易三子棋的实现
|
8月前
|
存储
三子棋游戏的实现
三子棋游戏的实现
69 0
|
8月前
|
C语言
三子棋游戏(详解+代码)
三子棋游戏(详解+代码)
86 2
|
8月前
|
人工智能 C语言
三子棋小游戏
三子棋小游戏
|
8月前
|
算法 C语言
三子棋小游戏(可改棋盘大小)
三子棋小游戏(可改棋盘大小)
84 0
简单三子棋的实现
简单三子棋的实现
72 0
|
程序员 C语言
三子棋(超详细)
三子棋(超详细)
|
C语言
【小朋友的三子棋】
【小朋友的三子棋】
75 0
|
C语言
C/【三子棋】
C/【三子棋】