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

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. 思考题

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

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

  • 5
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
线性表 某软件公司大约有30名员工,每名员工有姓名、工号、职务等属性,每年都有员工离职和入职。 把所有员工按照顺序存储结构建立一个线性表,建立离职和入职函数,当有员工离职或入职时,修改线性表,并且打印最新的员工名单。 约瑟夫(Josephus)环问题:编号为1,2,3,…,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数的上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止。报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一人开始重新从1报数,如此下去,直到所有人全部出列为止。 建立n个人的单循环链表存储结构,运行结束后,输出依次出队的人的序号。 栈和队列 某商场有一个100个车位的停车场,当车位未满时,等待的车辆可以进入并计时;当车位已满时,必须有车辆离开,等待的车辆才能进入;当车辆离开时计算停留的的时间,并且按照每小时1元收费。 汽车的输入信息格式可以是(进入/离开,车牌号,进入/离开时间),要求可以随时显示停车场内的车辆信息以及收费历史记录。 某银行营业厅共有6个营业窗口,设有排队系统广播叫号,该银行的业务分为公积金、银行卡、理财卡等三种。公积金业务指定1号窗口,银行卡业务指定2、3、4号窗口,理财卡业务指定5、6号窗口。但如果5、6号窗口全忙,而2、3、4号窗口有空闲时,理财卡业务也可以在空闲的2、3、4号窗口之一办理。 客户领号、业务完成可以作为输入信息,要求可以随时显示6个营业窗口的状态。 5、4阶斐波那契序列如下:f0=f1=f2=0, f3=1,…,fi=fi-1+fi-2+fi-3+fi-4, 利用容量为k=4的循环队列,构造序列的前n+1项(f0, f1 , f2 ,… fn ),要求满足fn ≤200而fn+1 >200。 6、八皇后问题:设8皇后问题的解为 (x1, x2, x3, …,x8), 约束条件为:在8x8的棋盘上,其中任意两个xi 和xj不能位于棋盘的同行、同列及同对角线。要求用一位数组进行存储,输出所有可能的排列。 7、迷宫求解:用二维矩阵表示迷宫,自动生成或者直接输入迷宫的格局,确定迷宫是否能走通,如果能走通,输出行走路线。 8、英国人格思里于1852年提出四色问题(four colour problem,亦称四色猜想),即在为一平面或一球面的地图着色时,假定每一个国家在地图上是一个连通域,并且有相邻边界线的两个国家必须用不同的颜色,问是否只要四种颜色就可完成着色。现在给定一张地图,要求对这张地图上的国家用不超过四种的颜色进行染色。 要求建立地图的邻接矩阵存储结构,输入国家的个数和相邻情况,输出每个国家的颜色代码。 9、以下问题要求统一在一个大程序里解决。 从原四则表达式求得后缀式,后缀表达式求值,从原四则表达式求得中缀表达式,从原四则表达式求得前缀表达式,前缀表达式求值。 数组与广义表 鞍点问题: 若矩阵A中的某一元素A[i,j]是第i行中的最小值,而又是第j列中的最大值,则称A[i,j]是矩阵A中的一个鞍点。写出一个可以确定鞍点位置的程序。 稀疏矩阵转置: 输入稀疏矩阵中每个元素的行号、列号、值,建立稀疏矩阵的三元组存储结构,并将此矩阵转置,显示转置前后的三元组结构。 用头尾链表存储表示法建立广义表,输出广义表,求广义表的表头、广义表的表尾和广义表的深度。 树和二叉树 以下问题要求统一在一个大程序里解决。 按先序遍历的扩展序列建立二叉树的存储结构 二叉树先序、中序、后序遍历的递归算法 二叉树中序遍历的非递归算法 二叉树层次遍历的非递归算法 求二叉树的深度(后序遍历) 建立树的存储结构 求树的深度 图 输入任意的一个网,用普里姆(Prim)算法构造最小生成树。 要求建立图的存储结构(邻接表或邻接矩阵),输入任意的一个图,显示图的深度优先搜索遍历路径。 要求建立图的存储结构(邻接表或邻接矩阵),输入任意的一个图,显示图的广度优先搜索遍历路径。 查找 设计一个读入一串整数构成一颗二叉排序树的程序,从二叉排序树中删除一个结点,使该二叉树仍保持二叉排序树的特性。 24、设定哈希函数 H(key) = key MOD 11 ( 表长=11 ),输入一组关键字序列,根据线性探测再散列解决冲突的方法建立哈希表的存储结构,显示哈希表,任意输入关键字,判断是否在哈希表中。 排序 以下问题要求统一在一个大程序里解决。 25、折半插入排序 26、冒泡排序 27、快速排序 28、简单选择排序 29、归并排序 30、堆排序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值