三子棋游戏的实现

简介: 三子棋游戏的实现

     前言:相信大家都玩过三子棋吧,曾想经常和同学在考试之后玩一个晚自习的三子棋。那么,如何自己编写一个三子棋游戏呢,请看下面的分析;

1.游戏设计思路

       在写任何代码时,最好先有个大致的框架,然后在大的框架下不断填充相应的细节,来实现相应的功能。

那么,想设计一个三子棋游戏需要哪些步骤呢

           1.主函数部分

           2.初始化棋盘

           3.打印棋盘

           4.下棋(玩家和电脑)

           5.判断输赢

2.设计之前的一些准备

       可以预见到,在编写游戏时需要使用到大量的函数,如果在同一文件下既声明函数,又定义函数,最后再调用函数,会使得整个程序杂乱无章,难以阅读。这时,利用分模块编程的方法就可以很好的避免代码混乱,冗长的问题。

       所谓分模块编程,即创立三个文件,一个头文件game.h(用来存放函数的声明,包括各种库函数以及使用到的标识符常量),两个源文件game.c(用来存放函数的定义)和test.c(函数的实现以及主函数);

       同时,为了使代码的灵活性大大提高,我们可以使用#define 定义的标识符常量来定义棋盘的大小;

3.具体实现过程

       1.主函数部分

               主函数是整个游戏过程的初始化,要根据用户的要求来判断是否进行游戏;

               代码如下:

int main()
{
  srand((unsigned int)time(NULL));
  int input = 0;
  do            //不管三七二十一先打印一次菜单              
  {
    menu();//打印菜单
    scanf("%d",&input);
    //根据用户输入的内容来判断是否进行后续操作
    switch (input)
    {
      case 1:
        game();
        break;
      case 0:
        printf("退出游戏\n");
        break;
      default:
        printf("输入错误,请重新输入");
    }
  } while (input); //只有输入0的时候才会推出循环(0为假)
  return 0;
}

       2.初始化棋盘

               分析:棋盘是用来落子的,在程序中其实就是存放数据的,又因为棋盘的形状,很自然的想到使用二维数组存放数据,所以我们可以使用二维数组来初始化棋盘

//game.h      //函数声明,定义都要指明各参数的类型
void InitBoard (char board [ROW][COL], int row, int col);
//game.c
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] = ' ';//将棋盘全部初始化为空格
    }                     //注意这里不是"",是'',因为空格本质上是个字符,引用字符要使用单引号
  }                         //若使用" ",本质上是一个字符串引用,实际上存储了两个字符,还有\0
}
//1.test.c  存放数据 ->二维数组      先用空格代替
  InitBoard(board, ROW, COL);

       3.打印棋盘

               在初始化棋盘后,发现棋盘仅仅只能存储数据(而且数据是空格),并没有边界线,所以我们要想办法打印棋盘

#define ROW 3      //这个刚开始犯了一个巨低级的错误,在最后添加了;
#define COL 3      //只有语句的结束才需要添加分号
//game.h
//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col);
//game.c
//打印棋盘
//版本1    只有空格  并没有分割线,不符合棋盘样式
//void DisplayBoard(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]);
//    }
//    printf("\n");
//  }
//}
//版本2      行数和列数都限制死了
//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
//  int i = 0;
//  //一行一行打印
//  for (i = 0; i < row; i++)
//  {
//  //1.打印数据
//    printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
//  //2.打印分割线
//    if(i<row -1 )//最后一行不打印分割线
//      printf("---|---|---\n");
//  }
//
//}
//版本3       利用大事化小的思维,利用循环打印数据和分割线
void DisplayBoard(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");
    }
  }
}
//test.c
//2.打印棋盘
  DisplayBoard(board, ROW, COL);

      4.模拟下棋(玩家and电脑)

               下棋整个过程是游戏实现的重要步骤,在下棋的过程中要模拟真实世界的下棋过程(循环一直对垒,直到分出输赢),下面请看代码实现

game.h
//下棋
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);
//电脑下棋
void ComMove(char board[ROW][COL], int row, int col);
game.c
//下棋
//玩家先步
void PlayerMove(char board[ROW][COL], int row, int col)
{
  //给用户提供坐标
  int x = 0;
  int y = 0;
  printf("玩家下棋->");
  while (1)
  {
    printf("请输入要下的坐标,中间请用空格分隔->\n");
    scanf("%d %d", &x, &y);//用户输入之后,要判断用户输入的行数和列数是否合法
    if (x >= 1 && x <= row && y >= 1 && y <= col)//输入合法
    {
      //要注意从用户的角度去思考,用户只认为的第一行是从1开始的,而不是0
      if (board[x - 1][y - 1] == ' ')//分两种情况讨论
      {                              //1.位置没有被占用
        board[x - 1][y - 1] = '*';
        break;//输入正确就挑出玩家下棋的过程
      }
      else                           //2.位置已被占用
      {
        printf("该位置已被占用,请重新输入");
      }
    }
    else   //也要考虑到玩家有可能输入的坐标值越界
    {
      printf("坐标非法,请重新输入:");
    }
  }
}
//电脑下棋
void ComMove(char board[ROW][COL], int row, int col)//可见,电脑和用户还是有很大区别的
{
  int x = 0;
  int y = 0;
  printf("电脑下棋->\n");
  //电脑下的位置是随机的  但是也要满足下棋的规则
  while (1)
  {
    x = rand() % row;//产生0-row-1的随机数
    y = rand() % col;
    if ( board[x][y] ==' ')
    {
      board[x][y] = '#';
      break;
    }
  }
}
test.c
while (1)//用循环模拟对战环节    下一步棋,打印一次
  {
    PlayerMove(board, ROW, COL);//玩家下棋
    DisplayBoard(board, ROW, COL);}

       5.判断输赢

               在每一次玩家或者电脑落子之后都要进行一次输赢的判断;这里的难点在于要对输赢的条件进行充分的考虑。比赛的结果分为三种,玩家赢,电脑赢,平局;

               比如获胜的情况大致可分为四种,主对角线,次对角线,横着一排,竖着一排,每种情况都要设置相应的代码;

下面请看代码实现过程

game.h
//判断输赢  在玩家或者用户每一次落子之后进行判断
//玩家赢  '*'
//电脑赢  '#'
//平局    'Q'//这样设置返回值的原因是:除了继续这种条件外,其他的都属于游戏结束的范畴
//继续    'C' //那我只需接受返回值,不是c,就跳出对垒,游戏结束        
int IsWin(char board[ROW][COL], int row, int col);
game.c
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;//找不到空格,证明满了,就是平局
}
int IsWin(char board[ROW][COL], int row, int col)
{
  //先判断赢
  //1.连成一行
  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];
  }
  //2.连成一列
  int j = 0;
  for (j = 0; j < col; j++)
  {
    if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j]!=' ')
      return board[0][j];
  }
  //3.对角线
  //主对角线
  if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0]!=' ')
    return board[0][0];
  //次对角线
  if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2]!=' ')
    return board[0][2];
  //平局
  if (IsFull(board,row,col) == 1)
    return 'Q';
  //继续
  return 'C';
}
test.c
while (1)//用循环模拟对战环节    下一步棋,打印一次
  {
    PlayerMove(board, ROW, COL);//玩家下棋
    DisplayBoard(board, ROW, COL);
    //判断输赢
    ret = IsWin(board, ROW, COL);
    if (ret != 'C')
      break;
    ComMove(board, ROW, COL);
    DisplayBoard(board, ROW, COL);
    //判断输赢
        ret = IsWin(board, ROW, COL);
    if (ret != 'C') //只要不是c继续这种条件,其余情况都属于游戏结束的范畴
      break;
  }
  if (ret == '*')
    printf("玩家赢\n");
  else if (ret == '#')
    printf("电脑赢\n");
  else
    printf("平局\n");
}

4.总结部分

       通过编写三子棋游戏,很锻炼我们的思维,我们不能仅仅认为代码能跑就行,而是应该不断去优化我们的代码,这样我们的代码能力才可以提高。在编写代码时,我们要有自己的大致框架,路线是个纲,沿着正确的路线不断填充我们的细节;

最后附上各文件完整程序代码

game.h

#pragma once
//此文件是与游戏有关函数声明部分 
//有一个细节,将所有的可能用到的头文件都写到game.h之中,在其余的文件中只需引用此文件即可
#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 DisplayBoard(char board[ROW][COL], int row, int col);
//下棋
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);
//电脑下棋
void ComMove(char board[ROW][COL], int row, int col);
//判断输赢  在玩家或者用户每一次落子之后进行判断
//玩家赢  '*'
//电脑赢  '#'
//平局    'Q'//这样设置返回值的原因是:除了继续这种条件外,其他的都属于游戏结束的范畴
//继续    'C' //那我只需接受返回值,不是c,就跳出对垒,游戏结束        
int IsWin(char board[ROW][COL], int row, int col);

game.c

//此文件是和游戏有关函数的定义部分
#define _CRT_SECURE_NO_WARNINGS 1
#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] = ' ';//将棋盘全部初始化为空格
    }                     //注意这里不是"",是'',因为空格本质上是个字符,引用字符要使用单引号
  }                         //若使用" ",本质上是一个字符串引用,实际上存储了两个字符,还有\0
}
//打印棋盘
//版本1    只有空格  并没有分割线,不符合棋盘样式
//void DisplayBoard(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]);
//    }
//    printf("\n");
//  }
//}
//版本2      行数和列数都限制死了
//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
//  int i = 0;
//  //一行一行打印
//  for (i = 0; i < row; i++)
//  {
//  //1.打印数据
//    printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
//  //2.打印分割线
//    if(i<row -1 )//最后一行不打印分割线
//      printf("---|---|---\n");
//  }
//
//}
//版本3       利用大事化小的思维,利用循环打印数据和分割线
void DisplayBoard(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;
  printf("玩家下棋->");
  while (1)
  {
    printf("请输入要下的坐标,中间请用空格分隔->\n");
    scanf("%d %d", &x, &y);//用户输入之后,要判断用户输入的行数和列数是否合法
    if (x >= 1 && x <= row && y >= 1 && y <= col)//输入合法
    {
      //要注意从用户的角度去思考,用户只认为的第一行是从1开始的,而不是0
      if (board[x - 1][y - 1] == ' ')//分两种情况讨论
      {                              //1.位置没有被占用
        board[x - 1][y - 1] = '*';
        break;//输入正确就挑出玩家下棋的过程
      }
      else                           //2.位置已被占用
      {
        printf("该位置已被占用,请重新输入");
      }
    }
    else
    {
      printf("坐标非法,请重新输入:");
    }
  }
}
//电脑下棋
void ComMove(char board[ROW][COL], int row, int col)//可见,电脑和用户还是有很大区别的
{
  int x = 0;
  int y = 0;
  printf("电脑下棋->\n");
  //电脑下的位置是随机的  但是也要满足下棋的规则
  while (1)
  {
    x = rand() % row;//产生0-row-1的随机数
    y = rand() % col;
    if ( board[x][y] ==' ')
    {
      board[x][y] = '#';
      break;
    }
  }
}
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;//找不到空格,证明满了,就是平局
}
int IsWin(char board[ROW][COL], int row, int col)
{
  //先判断赢
  //1.连成一行
  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];
  }
  //2.连成一列
  int j = 0;
  for (j = 0; j < col; j++)
  {
    if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j]!=' ')
      return board[0][j];
  }
  //3.对角线
  //主对角线
  if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0]!=' ')
    return board[0][0];
  //次对角线
  if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2]!=' ')
    return board[0][2];
  //平局
  if (IsFull(board,row,col) == 1)
    return 'Q';
  //继续
  return 'C';
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
//这个文件是游戏实现的文件
//三子棋游戏的实现
#include "game.h"
//菜单函数
void menu()
{
  printf("******************\n");
  printf("*****1 ->play*****\n");
  printf("*****0 ->exit*****\n");
}
//游戏函数
void game()
{
  char board[ROW][COL] = {0};
  //1.初始化棋盘  存放数据 ->二维数组      先用空格代替
  InitBoard(board, ROW, COL);
  //2.打印棋盘
  DisplayBoard(board, ROW, COL);
  //3.下棋并判断胜负
  char ret = ' ';
  while (1)//用循环模拟对战环节    下一步棋,打印一次
  {
    PlayerMove(board, ROW, COL);//玩家下棋
    DisplayBoard(board, ROW, COL);
    //判断输赢
    ret = IsWin(board, ROW, COL);
    if (ret != 'C')
      break;
    ComMove(board, ROW, COL);
    DisplayBoard(board, ROW, COL);
    //判断输赢
        ret = IsWin(board, ROW, COL);
    if (ret != 'C')
      break;
  }
  if (ret == '*')
    printf("玩家赢\n");
  else if (ret == '#')
    printf("电脑赢\n");
  else
    printf("平局\n");
}
int main()
{
  srand((unsigned int)time(NULL));
  int input = 0;
  do              
  {
    menu();//打印菜单
    scanf("%d",&input);
    //根据用户输入的内容来判断是否进行后续操作
    switch (input)
    {
      case 1:
        game();
        break;
      case 0:
        printf("退出游戏\n");
        break;
      default:
        printf("输入错误,请重新输入");
    }
  } while (input); //只有输入0的时候才会推出循环(0为假)
  return 0;
}

游戏截图(让电脑一盘!)

 

目录
相关文章
|
6月前
|
C语言
简易三子棋的实现
简易三子棋的实现
|
6月前
|
C语言
三子棋游戏(详解+代码)
三子棋游戏(详解+代码)
71 2
|
6月前
|
人工智能 C语言
三子棋小游戏
三子棋小游戏
|
6月前
|
算法 C语言
三子棋小游戏(可改棋盘大小)
三子棋小游戏(可改棋盘大小)
76 0
|
11月前
简单三子棋的实现
简单三子棋的实现
64 0
|
程序员 C语言
三子棋(超详细)
三子棋(超详细)
三子棋游戏
三子棋游戏
73 0
|
C语言
C/【三子棋】
C/【三子棋】
三子棋的实现有这么简单吗?
三子棋的实现有这么简单吗?
50 0