三子棋(井字棋) 保姆级详解

简介: 三子棋(井字棋) 保姆级详解

              游戏整体思路:

1.编写前的准备

2.游戏的规则

3.游戏的菜单

4.井字棋棋盘的打印

5.玩家下棋与计算机下棋的实现

6.游戏的输赢判断

               编写前的准备:

编写前首先需要准备三个文件:

       1.负责测试游戏逻辑的  .c  文件。 -   test.c

       2.负责头文件包含以及函数声明的  .h  文件。 -   game.h

       3.负责实现游戏函数的  .c  文件。 -   game.c

游戏的规则

       双方依次在九宫格棋盘上摆放棋子,率先将自己的三个棋子走成一条线就视为胜利。

由此可知,在开始游戏后首先会打印棋盘,其次玩家以输入坐标的方式进行下棋;下棋后电脑在一个空余的棋盘上进行下棋,当双方其中一方下完棋后为一条直线后,游戏判断为赢,否则为平局,游戏结束后重新选择选项是否继续游戏,否则退出。

游戏菜单的打印

      1.无论在进入什么游戏,首先应该有一个菜单,负责让玩家做选项,选择游戏时则开始游戏,选择退出时则退出游戏,选择错误则重新作选择,要实现该想法我们应该使用一个     do{ }  while( ); 循环语句;

void menu() 
{
  printf("******************************\n");
  printf("**********   1.play  *********\n");
  printf("******************************\n");
  printf("**********   0.exit  *********\n");
  printf("******************************\n");
}
int main()
{
  int chose = 0;
  do {
    menu();
    printf("Please enter your chose:>");
    scanf("%d",&chose);
    switch (chose) {
    case 1:
      printf("Tic-Tac-Toe\n");
      break;
    case 0:
      printf("Quit the game\n");
      break;
    default:
      printf("Input erro\n");
    }
  } 
  while (chose);
  return 0;
}

       在 test.c(负责测试游戏逻辑的.c文件)中创建一个 menu();函数,用来实现游戏菜单的显现,并在main()函数中的do{ } while()语句中调用,每当打开游戏或者是结束一局游戏后,都会调用该函数,打印一遍菜单;

将游戏菜单的打印实现后,应该实现的就是开始游戏,退出游戏的选择;

      从菜单可知,键盘输入1为开始游戏,0为退出游戏,既然是键盘输入,我们选择的则为getchar( ) \ scanf( ); 在使用getchar()函数时,可能会使下一次输入时读取到 ‘ \n ’ ,所以此处选择用scanf();,并将scanf()读取到的数据值赋给 int类型变量chose内 ;并用 开关语句(多分支语句)swithc( )   case:  语句,并将变量chose作为switch()语句的判断条件进行数据的判断:若键盘输入1,则开始三子棋游戏;若键盘输入0,则提示退出游戏并退出;若键盘输入其他数,则提示输入错误,并提示重新输入选项。

       同时将变量chose作为该处do{ } while( );语句的循环条件,若输入1,则恒为真,并进入游戏;输入0,则为假,跳出循环;输入其他时也恒为真,并提示重新输入。

游戏棋盘的打印

       三子棋游戏中,玩家和电脑分别要在棋盘内进行下棋;所以在下棋之前,首先要实现游戏棋盘的打印和显现;三子棋的游戏是在一个九宫格内进行的,玩家和电脑需要在不同的坐标进行下棋,“九宫格” 相当于是3*3的小方格,从这里我们可以联想到 横为3列为3 的二维数组。

      在switch语句中的 case 1 语句内创建一个game函数,每当玩家选择 1 时,则默认为游戏开始,进入game()函数;

void game()
{
//创建一个二维数组
  char board[ROW][COL] = {0};
  //棋盘的初始化
  Intboard(board, ROW, COL);
  //棋盘的打印
  Display(board, ROW, COL);
}

       在打印出棋盘前,应有相应的一个二维数组,为了加强代码的健壮性,可在负责头文件包含以及函数声明的  .h  文件 game.h 文件中创建一个 #define 定义的标识符常量

                                               #define ROL 3       //行

                                               #define COL 3       //列

       确保当文件需要修改行列时可以一针见血。(应在 test.c 文件中#include "  "来包含此文件)

       

 

       三子棋棋盘的构造由空格、电脑与玩家两种不同的棋子以及每个格子的分界线组成,为了下步棋盘的美观及整齐性,应先将数组初始化为空格;在 game( ) 函数内调用一个Intboard()函数,用来初始化所创建的数组(在头文件中应先声明该函数,并在 game.c 文件中实现该函数),初始化空格有两种方法:

               1.使用 memset( ) 函数初始化数组的内存;

#include<string.h>//在使用memset()函数前应包含string.h头文件
void Intboard(char board[ROW][COL],int row ,int col)
{
    memset(&board[0][0], ' ',row*col*sizeof(board[0][0]));
}

               memset 函数为初始化函数,可以把一段连续的内存初始化某个值。

               2.使用迭代的方式将数组的各个元素遍历一遍,并将每个元素都初始化为 '     '

void Intboard(char board[ROW][COL], int row, int col)
{
  for (int i = 0; i < row; i++)
  {
    for (int j = 0; j < col; j++) {
      board[i][j] = ' ';
    }
  }
}

        此时二维数组内的元素都被初始化为了 ‘    ’ ,但并未出现棋盘该有的样子,再将棋盘的边框以打印的方式显现出来时,才能得到一个较为美观的棋盘。

       在 game( ) 函数内调用的 Intboard( ) 函数后再调用一个Display( )函数,(同样应先在 game.h 文件中先声明 ,并在game.c 文件中实现该函数)。

void Display(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++) {
      printf(" %c ", board[i][j]); //将每个' %c '看为一组
      if (j < col - 1) {
        printf("|");//每打印完一次' %c '打印一次 '|' ,最后一次则不打印
      }
    }
    printf("\n");//每打印完一行后打印换行
    if (i < row - 1) {//每打印一行后打印以下内容,最后一次则不打印
      int j = 0;
      for (j = 0; j < col;j++) {
        printf("---");//与上对应,将'---'看为一组进行打印
        if (j < col - 1) {
          printf("|");//每打印完一组'---',打印一个'|'作为分割线,最后一次不打印
        }
      }
    }
    printf("\n");//打印完一组后的换行
    }
  }

玩家下棋以及计算机下棋的实现

       玩家下棋

       玩家与计算机的下棋机制为回合制,在 game() 函数内玩家与计算机下棋应使用while循环,每下完一次棋判断一次输赢。

char ret = 0;
  while (1) {
    //玩家下棋;
    Playermove(board,ROW,COL);
    //玩家下棋后打印棋盘
    Display(board, ROW, COL);
  }
void Playermove(char board[ROW][COL], int row, int col) 
{
  int x = 0;
  int y = 0;
  while (1) {
    printf("The player continue:>\n");
    printf("Please enter your coordinate:>");
    scanf("%d%d", &x, &y);
    //判断输入坐标的合法性
    if (x > 0 && x <= row && y>0 && y <= col) {
      if (board[x - 1][y - 1] == ' ') {
        board[x - 1][y - 1] = '*';
        break;
      }
      else {
        printf("Coordinates are occupied.\n");
      }
    }
    else { 
      printf("The coordinate is erro;\n");
    }
  }
}

        在while()中调用一个Playermove()函数并在 game.c 文件中实现该函数),当轮到玩家下棋时,打印提示“玩家下棋”以及“请输入坐标",定义两个变量x 与 y,用scanf()从键盘中获取两个数并赋值给x,y的变量

       当x,y获取到两个数据时,应先判断坐标的合法性

       确保输入的坐标范围为 1-3 ,若是输入坐标不合法,则提示“坐标不合法,请重新输入坐标”。

       当坐标合法时则需要判断坐标是否被占用,若输入的坐标被占用时则提示“坐标被占用”。

       如果输入的标既合法且未被占用(字符内为空格),则将 ' * ' 赋值给该坐标;(输入的坐标范围为1-3,数组的下标为0-2,故下棋时的坐标行列都得-1)

       玩家下完棋后应再打印一次棋盘。

计算机下棋

       在while()中调用一个Coputermove()函数并在 game.c 文件中实现该函数),当轮到电脑下棋时,打印提示“计算机下棋”。计算机下棋时应使用随机数,在此之前应创建一个随机数生成器。

int main()
{
srand((unsigned int)time(NULL));
}

       在main()函数中使用 srand( ) 函数以时间戳来创建一个随机数生成器。应包含stdlib.h文件以及time.h文件),并在Computermove()函数中调用rand()函数来生成随机数(计算机生成的数可以以下标来设定,故%3,取出的范围则为0-2,确保坐标的合法性)

#include<time.h>
#include<stdlib.h>
void Computermove(char board[ROW][COL], int row, int col)
{
  printf("Computer move:>\n");
    int x = 0;
    int y = 0;
  while (1) {
    x = rand() % 3;
    y = rand() % 3;
    if (board[x][y] == ' ') {
      board[x][y] = '#';
      break;
    }
  }
}

       当该坐标为 ’   ‘ 时,则代表该处坐标未被占用,计算机则以此坐标落子。当计算机落完子后,应在 game( ) 函数内再打印一次棋盘,并判断输赢。

游戏输赢的判断

       游戏每进行一次则需要判断输赢一次

void game()
{
//创建一个二维数组
  char board[ROW][COL] = {0};
  //棋盘的初始化
  Intboard(board, ROW, COL);
  //棋盘的打印
  Display(board, ROW, COL);
  //玩家下棋以及计算机下棋的实现
  char ret = 0;
  while (1) {
    //玩家下棋;
    Playermove(board,ROW,COL);
    //玩家下棋后打印棋盘
    Display(board, ROW, COL);
    //判断输赢
    ret = Iswin(board, ROW, COL);
    if (ret != 'C') {
      break;
    }
    //计算机下棋
    Computermove(board,ROW,COL);
    Display(board, ROW, COL);
    ret = Iswin(board, ROW, COL);
    if (ret != 'C') {
      break;
    }
  }
  if (ret == '*') {
    printf("Players win!\n");
  }
  else if (ret == '#') {
    printf("Computers win!\n");
  }
  else 
    printf("The game draw.\n");
}

        创建一个char类型的变量ret用来接收 Iswin( ) 函数用来判断输赢的返回值。在此处调用一个名为 Iswin( ) 的char类型的函数

       当电脑赢时,则返回电脑的棋子 - #

       当玩家赢时,则返回玩家的棋子 - *

       当游戏平局时,返回 - 'Q'

       当游戏都不满足上述条件时,则返回 - ’C‘

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][0] != ' ') {
      return board[i][0];
    }
  }
  for (i = 0; i < col; i++) {
    if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ') {
      return board[0][i];
    }
  }
  if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ') {
    return board[0][0];
  }
  if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[2][0] != ' ') {
    return board[2][0];
  }
  if (Isfull(board, row, col)) {
    return 'Q';
  }
  return 'C';
}

       赢的方式为三子连成一线,共有三行、三列、两对点八种结果

       除了判断是否赢还需判断游戏是否平局。

       在该函数内再调用一个int类型的自定义函数 Isfull( )

int Isfull(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++) {
      if (board[i][j] == ' ') {
        return 0;
      }
    }
  }
  return 1;
}

       当未出现任何赢家时则遍历一遍数组,若是数组中其中一个元素为空格,则返回0,if语句条件不成立,返回 ’C‘ ,若是遍历一遍数组内都没有元素为空格,则说明棋盘已满,且未出现赢家,则返回 ’Q‘;

       回到 game( ) 函数

               若是返回不为 ' C ' ,则跳出循环

               若是返回 ' * ',则玩家赢

               若是返回 ' # ',则电脑赢

               若是返回 ' Q ',则平局

代码分享

                                                    test.c

#include<stdio.h>
#include<string.h>
#include"game.h"
void game()
{
//创建一个二维数组
  char board[ROW][COL] = {0};
  //棋盘的初始化
  Intboard(board, ROW, COL);
  //棋盘的打印
  Display(board, ROW, COL);
  //玩家下棋以及计算机下棋的实现
  char ret = 0;
  while (1) {
    //玩家下棋;
    Playermove(board,ROW,COL);
    //玩家下棋后打印棋盘
    Display(board, ROW, COL);
    //判断输赢
    ret = Iswin(board, ROW, COL);
    if (ret != 'C') {
      break;
    }
    //计算机下棋
    Computermove(board,ROW,COL);
    Display(board, ROW, COL);
    ret = Iswin(board, ROW, COL);
    if (ret != 'C') {
      break;
    }
  }
  if (ret == '*') {
    printf("Players win!\n");
  }
  else if (ret == '#') {
    printf("Computers win!\n");
  }
  else 
    printf("The game draw.\n");
}
void menu() 
{
  printf("******************************\n");
  printf("**********   1.play  *********\n");
  printf("******************************\n");
  printf("**********   0.exit  *********\n");
  printf("******************************\n");
}
int main()
{
  srand((unsigned int)time(NULL));
  int chose = 0;
  do {
    menu();
    printf("Please enter your chose:>");
    scanf("%d",&chose);
    switch (chose) {
    case 1:
      game();
      break;
    case 0:
      printf("Quit the game\n");
      break;
    default:
      printf("Input erro\n");
    }
  } 
  while (chose);
  return 0;
}

                                                    game.h

#define ROW 3
#define COL 3
//初始化棋盘
void Intboard(char board[ROW][COL], int row, int col);
//棋盘的打印,显示
void Display(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);
//判断输赢
char Iswin(char board[ROW][COL], int row, int col);

                                                    game.c

#include"game.h"
#include<stdio.h>
int Isfull(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++) {
      if (board[i][j] == ' ') {
        return 0;
      }
    }
  }
  return 1;
}
void Intboard(char board[ROW][COL], int row, int col)
{
  //memset(board, ' ', 9);//使用memset()初始化内存
  for (int i = 0; i < row; i++)
  {
    for (int j = 0; j < col; j++) {
      board[i][j] = ' ';
    }
  }
}
void Display(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++) {
      printf(" %c ", board[i][j]);
      if (j < col - 1) {
        printf("|");
      }
    }
    printf("\n");
    if (i < row - 1) {
      int j = 0;
      for (j = 0; j < col;j++) {
        printf("---");
        if (j < col - 1) {
          printf("|");
        }
      }
    }
    printf("\n");
    }
  }
void Playermove(char board[ROW][COL], int row, int col) 
{
  int x = 0;
  int y = 0;
  while (1) {
    printf("The player continue:>\n");
    printf("Please enter your coordinate:>");
    scanf("%d%d", &x, &y);
    //判断输入坐标的合法性
    if (x > 0 && x <= row && y>0 && y <= col) {
      if (board[x - 1][y - 1] == ' ') {
        board[x - 1][y - 1] = '*';
        break;
      }
      else {
        printf("Coordinates are occupied.\n");
      }
    }
    else { 
      printf("The coordinate is erro;\n");
    }
  }
}
#include<time.h>
#include<stdlib.h>
void Computermove(char board[ROW][COL], int row, int col)
{
  printf("Computer move:>\n");
    int x = 0;
    int y = 0;
  while (1) {
    x = rand() % 3;
    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][0] != ' ') {
      return board[i][0];
    }
  }
  for (i = 0; i < col; i++) {
    if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ') {
      return board[0][i];
    }
  }
  if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ') {
    return board[0][0];
  }
  if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[2][0] != ' ') {
    return board[2][0];
  }
  if (Isfull(board, row, col)) {
    return 'Q';
  }
  return 'C';
}

如有不足之处请兄弟萌批评指正!!!

敲码不易 记得三连!!!

 

相关文章
|
4天前
|
存储
三子棋游戏的实现
三子棋游戏的实现
34 0
|
4天前
|
C语言
三子棋游戏(详解+代码)
三子棋游戏(详解+代码)
27 2
|
10月前
三子棋游戏 2020-12-29
三子棋游戏 2020-12-29
|
9月前
|
小程序 C语言
超详细三子棋(保姆级教学)
三子棋是一款古老的民间传统游戏,又被称为黑白棋、圈圈叉叉棋、井字棋、一条龙、九宫棋等,想必大家都有玩过吧。没玩过的话也可以试着玩一玩,这样对写三子棋这个小游戏的化是会有一个很好的思路的。那么本篇博客就来介绍如何用C语言来实现三子棋小游戏的具体步骤。(编译器用的是VS2019)
|
9月前
|
C语言
从零到一快速学会三子棋
从零到一快速学会三子棋
43 1
|
9月前
|
存储 C语言
智能三子棋——保姆级教学。
智能三子棋——保姆级教学。
智能三子棋——保姆级教学。
|
10月前
三子棋游戏
三子棋游戏
59 0
|
10月前
设计一个三子棋游戏(下)
设计一个三子棋游戏(下)
|
10月前
|
算法
设计一个三子棋游戏(上)
设计一个三子棋游戏(上)
|
10月前
|
小程序 程序员 C++
超简单的三子棋,万字大文(保姆级讲解)上
超简单的三子棋,万字大文(保姆级讲解)
37 0