数据结构课程设计-五子棋

2023-11-15

1. 题目描述

五子棋的游戏规则是两人对弈,使用黑白两色棋子,轮流下在棋盘上,当一方先在横线、竖线、斜对角线方向形成五子连线,则取得胜利。

2. 设计要求

⑴ 在内存中,设计数据结构存储游戏需要的数据。

⑵ 满足五子棋游戏的游戏规则。

⑶ 实现简单的人机对战功能。

3. 数据结构设计

在游戏运行过程中,需要在内存中保存游玩的状态,也就是棋盘,可以设计以下结构保存关卡信息。

struct BoardType

{

    // 棋盘,0表示空位,1表示玩家棋子,2表示电脑棋子

       int map[BOARDSIZE][BOARDSIZE]; 

    // AI下棋的权重,权重取对AI最有利和对玩家最有害两者的最大值

       int weight[BOARDSIZE][BOARDSIZE]; 

}

4. 算法设计

对于该款游戏,我们需要以下函数提供基本功能:

InitGame:初始化游戏的游玩状态

PlayerTurn:处理玩家回合的输入

UpdateWeight:根据棋盘局势更新电脑的下棋权重

ComputerTurn:电脑回合的处理

Update:负责根据内存中的游玩状态更新游戏的画面函数

CheckWin:判断是否有一方取得胜利的函数

人机对抗部分主要由UpdateWeight与ComputerTurn函数组成,其中UpdateWeight函数负责计算棋盘上每个空位的权重,便于电脑判断在哪里下棋是较为合理的。这里给出了一种简单的设计思路,UpdateWeight算法设计如下:

其中board数据类型为前文定义的BoardType,保存了棋盘当前的状态以及对应位置上的权重。

UpdateWeight:根据棋盘状态更新权重

输入:棋盘状态board

输出:无

   1. 逐行逐列判断棋盘上该位置是否为空位,记行号为i,列号为j:

          1.1. 若board.map[i][j]!=0,该位置不为空,board.weight[i][j]=-1;

          1.2. 若board.map[i][j]==0,该位置为空:

                 1.2.1. 记对AI有利的权重为aiWeight=0;

                 1.2.2. 记对玩家有害的权重为playerWeight=0;

                 1.2.3. 若该位置放置AI的棋子,分别判断周围8个方向

                         相连的AI棋子个数,每个方向相连的个数记作cnt,

                         令aiWeight+=10^cnt;

                 1.2.4. 若该位置放置玩家的棋子,分别判断周围8个方向

                         相连的玩家棋子个数,每个方向相连的个数记作cnt,

                         令playerWeight+=10^cnt;

                 1.2.5. board.weight[i][j]=max(aiWeight,playerWeight);

ComputerTurn函数负责根据处理电脑的回合,模拟人类与玩家进行对弈,其中出现的UpdateWeight函数为上文中的更新权重算法, board数据类型为前文定义的BoardType,保存了棋盘当前的状态。

ComputerTurn:电脑回合的处理

输入:无

输出:无

   1. 调用UpdateWeight函数,更新权重;

   2. 记maxVal=0,maxX=0,maxY=0,记录最大权重出现的位置;

   3. 逐行逐列比较棋盘上的权值与maxVal的关系,记行为i,列为j:

          3.1. 若board.weight[i][j]>maxVal:

                 3.1.1. maxVal= board.weight[i][j];

                 3.1.2. maxX=i;

                 3.1.3. maxY=j;

   4. 点(maxX,maxY)为权值最大的位置,board.map[maxX][maxY]=2;

CheckWin函数负责判断是否有一方获胜,其中board数据类型为前文定义的BoardType,保存了棋盘当前的状态。

CheckWin:根据棋盘状态更新权重

输入:棋盘状态board

输出:胜利方

   1. 逐行逐列判断棋盘上该位置是否为空位,记行号为i,列号为j:

      若board.map[i][j]!=0,该位置不为空:

          1.1. 分别判断点(i,j)与周围8个方向相连且相同的棋子个数,

                 记作cnt;

          1.2. 若cnt>=5,判断board.map[i][j]的类型:

                 1.2.1. 若board.map[i][j==1,玩家的棋子,返回玩家胜利;

                 1.2.2. 若board.map[i][j==2,电脑的棋子,返回电脑胜利;

   2. 返回无人胜利;

5. 测试样例

无测试样例

6. 代码实现

// 编译器版本 g++8.1.0

// 编译参数 -Weffc++ -Wextra -Wall -std=c++11

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <cmath>

using namespace std;

/**

 * 常量定义

**/

const int BOARDSIZE = 9;

const int DX[8]= {1,1,0,-1,-1,-1,0,1};

const int DY[8]= {0,1,1,1,0,-1,-1,-1};

/**

 * 数据类型定义

**/

// 关卡数据类型

struct BoardType

{

    int map[BOARDSIZE][BOARDSIZE];  // 棋盘,0表示空位,1表示玩家棋子,2表示电脑棋子

    int weight[BOARDSIZE][BOARDSIZE];  // AI下棋的权重,权重取对AI最有利和对玩家最有害两者的最大值

};

/**

 * 全局变量定义

**/

BoardType Gboard;

/**

 * 游戏初始化

**/

void InitGame()

{

    // 初始化显示画面高度和宽度

    system("mode con cols=40 lines=20");

    system("cls");

    // 初始化变量

    memset(Gboard.map,0,sizeof(Gboard.map));

    return;

}

/**

 * 更新画面

**/

void Update()

{

    system("cls");

    printf("     ");

    for(int i=0; i<BOARDSIZE; i++)

        printf("%c ",'a'+i);

    putchar('\n');

    for(int i=0; i<BOARDSIZE; i++)

    {

        printf(" %2d ",i);

        for(int j=0; j<BOARDSIZE; j++)

        {

            if(Gboard.map[i][j]==1)

                printf(" B");

            else if(Gboard.map[i][j]==2)

                printf(" W");

            else

                printf(" .");

        }

        putchar('\n');

    }

    putchar('\n');

    return;

}

/**

 * 胜利画面

**/

void UpdateWin()

{

    printf("YOU WIN!\n");

    system("pause");

    return;

}

/**

 * 失败画面

**/

void UpdateLose()

{

    printf("YOU LOSE!\n");

    system("pause");

    return;

}

/**

 * 用户回合

**/

void PlayerTurn()

{

    char inputStr[255];

    int x,y;

    printf("Input(a 1 / restart / exit):");

    scanf("%s",inputStr);

    if(strcmp(inputStr,"restart")==0)

    {

        InitGame();

        Update();

        PlayerTurn();

        return;

    }

    if(strcmp(inputStr,"exit")==0)

    {

        system("exit");

        return;

    }

    scanf("%d",&x);

    y=inputStr[0]-'a';

    if(Gboard.map[x][y]!=0)

    {

        printf("Error.");

        system("pause");

        Update();

        PlayerTurn();

    }

    Gboard.map[x][y]=1;

    return;

}

/**

 * 更新下棋权重

**/

void UpdateAIWeight()

{

    for(int x=0; x<BOARDSIZE; x++)

        for(int y=0; y<BOARDSIZE; y++)

            if(Gboard.map[x][y]!=0)

                Gboard.weight[x][y]=0;

            else

            {

                int aiWeight=0;

                int playerWeight=0;

                // 计算八个方向上的棋子个数

                for(int i=0; i<8; i++)

                {

                    // 对自己有利

                    int pX=x;

                    int pY=y;

                    int cnt=1;

                    for(int j=0; j<4; j++)

                    {

                        pX+=DX[i];

                        pY+=DY[i];

                        if(pX>=0&&pX<BOARDSIZE&&pY>=0&&pY<BOARDSIZE&&2==Gboard.map[pX][pY])

                            cnt++;

                        else break;

                    }

                    aiWeight+=pow(10,cnt)+1;

                    // 对玩家有害

                    pX=x;

                    pY=y;

                    cnt=1;

                    for(int j=0; j<4; j++)

                    {

                        pX+=DX[i];

                        pY+=DY[i];

                        if(pX>=0&&pX<BOARDSIZE&&pY>=0&&pY<BOARDSIZE&&1==Gboard.map[pX][pY])

                            cnt++;

                        else break;

                    }

                    playerWeight+=pow(10,cnt);

                }

                // 更新权重,对自己最有利或者对玩家最有害,当相同时选择对自己最有利的

                Gboard.weight[x][y]=aiWeight>playerWeight?aiWeight:playerWeight;

            }

    return;

}

/**

 * 电脑回合

**/

void ComputerTurn()

{

    // 计算权重

    UpdateAIWeight();

    // 寻找权重最大的点

    int maxX;

    int maxY;

    int maxWeight=-1;

    for(int i=0; i<BOARDSIZE; i++)

        for(int j=0; j<BOARDSIZE; j++)

            if(Gboard.weight[i][j]>maxWeight)

            {

                maxX=i;

                maxY=j;

                maxWeight=Gboard.weight[i][j];

            }

    // 下棋

    Gboard.map[maxX][maxY]=2;

    return;

}

/**

 * 判断胜利

**/

bool CheckWin(int x,int y)

{

    // 从上方顺时针向八个方向判断

    for(int i=0; i<8; i++)

    {

        int pX=x;

        int pY=y;

        int cnt=1;

        for(int j=0; j<4; j++)

        {

            pX+=DX[i];

            pY+=DY[i];

            if(pX>=0&&pX<BOARDSIZE&&pY>=0&&pY<BOARDSIZE&&Gboard.map[x][y]==Gboard.map[pX][pY])

                cnt++;

        }

        if(cnt==5)

            return true;

    }

    return false;

}

/**

 * 判断玩家胜利

**/

bool CheckPlayerWin()

{

    for(int i=0; i<BOARDSIZE; i++)

        for(int j=0; j<BOARDSIZE; j++)

            if(Gboard.map[i][j]==1&&CheckWin(i,j))

                return true;

    return false;

}

/**

 * 判断电脑胜利

**/

bool CheckPlayerLose()

{

    for(int i=0; i<BOARDSIZE; i++)

        for(int j=0; j<BOARDSIZE; j++)

            if(Gboard.map[i][j]==2&&CheckWin(i,j))

                return true;

    return false;

}

/**

 * 程序入口

**/

int main()

{

    // 初始化游戏

    InitGame();

    Update();

    while(true)

    {

        // 用户输入

        PlayerTurn();

        if(CheckPlayerWin())

        {

            UpdateWin();

            InitGame();

            Update();

        }

        // 电脑输入

        ComputerTurn();

        if(CheckPlayerLose())

        {

            UpdateLose();

            InitGame();

            Update();

        }

        // 更新画面

        Update();

    }

    return 0;

}

7. 思考题

⑴ 在玩家游玩游戏的过程中,可能会出现误操作等需要“悔棋”的情况,需要用到何种数据结构保存玩家的上一步状态,又如何实现呢?

⑵ 上述算法设计中给出了一种简单的人机对抗算法的设计思路,请自行查阅资料,设计一款更加高效合理的权重算法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构课程设计-五子棋 的相关文章

随机推荐

  • Velocity类型转换与运算

    字符串转数字 set str 100 要将str转换为整数 则先定义一个变量 赋值为整数 然后利用Java的类型转转功能 set zhengshu 0 转换为整数 zhengshu parseInt str 若是要转换为其他数字类型 同理定
  • matinal:SAP ABAP FB04清账 POSTING_INTERFACE_CLEARING

    上代码 REPORT ztest INTERNAL TABLE DECLARATION DATA it blntab TYPE TABLE OF blntab WITH HEADER LINE it ftclear TYPE TABLE O
  • JAVA内存管理常识

    大多数 JVM 将内存区域划分为 Method Area Non Heap 方法区 Heap 堆 Program Counter Register 程序计数器 VM Stack 虚拟机栈 也有翻译成JAVA 方法栈的 Native Meth
  • flex布局可能碰到的坑1

    flex布局非常好用 但在开发过程中可能会碰到的一些坑 1 内容超出容器 大致情况是 在一个设置了display flex布局的大容器A中并排放置两个子容器 并且子容器设置flex 1 子容器中都有一个元素包含一段文本 这段文本设置了不换行
  • 服务器终端性能测试之GPU burn压力测试

    GPU burn 测试GPU 1 下载软件 https github com wilicc gpu burn wget https codeload github com wilicc gpu burn zip master 2 解压缩 u
  • win10家庭版启用远程桌面

    此电脑右键属性 gt 远程设置 gt 允许远程协助连接这台计算机 勾选 下载RDP Wrapper 地址 https github com stascorp rdpwrap releases 解压后点击RDPCheck exe 如显示无法连
  • VS2019使用以及UE4的代码调试

    1 会进行代码调试 Ctrl Shift B 编译代码 Ctrl f5 运行 不调试 f5 调试 shift f5 停止调试 f11 逐步执行 f9 切换断点 对于UE4的工程代码的调试仍然需要学习与总结 2 会进行函数查找 不借助番茄插件
  • lattice 包的用法

    1 library lattice 加载包 d lt data frame x seq 0 14 y seq 1 15 z rep c a b c times 5 xyplot y x data d xy的散点图 xyplot y x z
  • 关于贷后的8个专业名词解析

    一 DPD day past due DPD的意思是逾期天数 指的是逾期用户在最早违期日期至目前日期的时间间隔 贷后催收时需要计算用户的逾期天数 并根据逾期的情况采用不同的催收手段 二 Mn M1 Mn的意思是逾期的期数 比如M1表示逾期一
  • 《Autodesk Revit二次开发基础教程》书籍终于上架了

    由Autodesk中国研究院Revit开发团队的几位同事一起编撰的 Autodesk Revit二次开发基础教程 于今天在天猫同济大学出版社旗舰店正式上架 购买链接在这里 https detail tmall com item htm u
  • TensorFlow训练模型的过程中打开tensorboard

    在训练的过程中 想通过tensorboard实时观察训练损失和验证集准确率 一直出错 打开tensorboard后在浏览器查看 然后训练就停止了 提示信息如下 File D ProgramData PycharmProjects tf le
  • Spring源码分析(一):Spring底层核心原理解析

    本节只讲结论 不做验证 后面会专门拉代码讲解验证 Spring的核心是IOC和AOP 大概有这么几个核心知识点 Bean的生命周期底层原理 依赖注入底层原理 初始化底层原理 推断构造方法底层原理 AOP底层原理 Spring事务底层原理 S
  • 大陆医生谈收入

    官网 ZY123 com 中医123 本人今年45岁 84年大本 87年研究生毕业 很正规的医学院校毕业 随后分到中部一家大型医院干了4年临床 91年先到欧洲的实验室混了几年 后来到临床上干了2年后回来了 也算 海龟 吧 先在广东的两家医院
  • 【基础教学】UiBot的下载、安装与使用

    鉴于很多小伙伴 可能刚刚关注UiBot 对这个平台还不是很了解 我们准备系统的讲解UiBot的相关操作 方便您对UiBot的认识与使用 目录 1 UiBot软件简介 2 UiBot能为您做什么 3 系统环境及配置要求 4 下载与安装 5 注
  • 堆排序(几个重点)

    https blog csdn net touch 2011 article details 6767673 几个重点 大小顶堆虽然逻辑形式是完全二叉树 但实际是以数组的形式存储 最后一个非叶子节点 最后一个有孩子的节点 的位置是 n 2
  • linux 获取进程输出流,linux后台进程与标准输出

    一 遇到问题 笔者在测试阶段 把服务拉到服务器上 部署之后 启动服务 但是没有启动成功 也没有报错信息 二 先理解一些概念 1 黑洞 dev null 这个就是黑洞 这是一个文件 这个文件是一个 只写 的文件 从里面读不出信息 为什么要使用
  • Elasticsearch 查询和聚合查询:基本语法和统计数量

    摘要 Elasticsearch是一个强大的分布式搜索和分析引擎 提供了丰富的查询和聚合功能 本文将介绍Elasticsearch的基本查询语法 包括预发查询和聚合查询 以及如何使用聚合功能统计数量 引言 Elasticsearch是一种开
  • C++实现查找字符串中的数字,并输出

    例如输入 dsafjoi3425sfsdjl5435asfkl 3400输出为 3425 5434 3400 include
  • Stata如何快速安装外部命令

    Stata如何快速安装外部命令 来自微信公众号 TidyFridy 1 之前在安装Stata外部命令时 访问外网速度很慢 安装SSC外部命令没有成功 出现过stacktrace not available 的提示 解决办法 Stata的安装
  • 数据结构课程设计-五子棋

    1 题目描述 五子棋的游戏规则是两人对弈 使用黑白两色棋子 轮流下在棋盘上 当一方先在横线 竖线 斜对角线方向形成五子连线 则取得胜利 2 设计要求 在内存中 设计数据结构存储游戏需要的数据 满足五子棋游戏的游戏规则 实现简单的人机对战功能