VV游戏

 找回密码
 立即注册
查看: 130|回复: 0

C++实现双人中国象棋(一)——算法篇(附完整代码)

[复制链接]

1

主题

5

帖子

8

积分

新手上路

Rank: 1

积分
8
发表于 2022-12-2 16:29:02 | 显示全部楼层 |阅读模式
一、简介

最近突发奇想,要使用C++做一个双人象棋的程序,昨天肝了一天,终于把算法部分完成了,下面把开发过程中的经验分享一下。
开发环境:Visual Studio 2019
语言标准:C++11及以上
纠错:暂无
二、准备工作

知识要求:

  • 熟练掌握C++语言面向对象编程的知识(继承,多态)
  • 掌握STL的基本操作
  • 了解中国象棋基本规则(不会还有人不知道中国象棋规则吧!)



  • 既然都知道了,下面说一个大家可能没注意过的点:
  • 象棋棋盘尺寸为9×10,9列10行
象棋摆法


三、程序框架

由于这是双人象棋,所以算法主要就是判断胜负(容易实现)和判断棋子能否走到某个地方(难点)。这篇博客主要就介绍这两个问题。
程序主要由以下几个类组成:

  • Point
  • 记录棋盘中的一个坐标,并且附带基本功能,如判断在红方区域还是黑方区域,是否在九宫格中等。
  • ChessBoard
  • 棋盘类,管理所有棋子。
  • ChessPiece
  • 所有棋子的基类,也是一个抽象类。
  • 七种棋子分别对应的七个类
  • 都是ChessPiece的子类。
  • ChessGame
  • 象棋游戏类,管理先后手、胜负等。
关于棋子的存储

关于棋子的存储,有两种方式,一是把红黑双方棋子分别存储到两个容器中,优点是便于知道一方还有哪些棋子,但知道坐标查找棋子则很困难。
还有一种方法,是用一个10×9的数组(0-4红方区域,5-9黑方区域,这一点很重要),分别存储每个棋子或空(nullptr),优点是知道坐标便于查找棋子,但知道一方还有哪些棋子则很困难。
为了方便,我们把这两种方法综合起来,既分别存储双方棋子,又存储棋盘状态。
四、代码实现

以下代码均在Chess.h中。
一些常量的声明

const bool BLACK = 0, RED = 1;
const uint8_t NONE = 2;Point

最简单的一个类,无脑写就行。
class Point
{
public:
        int8_t x, y;
        Point(int8_t nx, int8_t ny) :x(nx), y(ny) {}
        bool ColorOfTheArea()const//判断在红方区域还是黑方区域
        {
                if (y <= 4)
                        return RED;
                return BLACK;
        }
        bool IsInNinePalaces()const//是否在九宫格中
        {
                return x >= 3 && x <= 5 && (y <= 2 || y >= 7);
        }
};
bool operator==(const Point& a, const Point& b)
{
        return a.x == b.x && a.y == b.y;
}ChessPiece

class ChessPiece
{
protected:
        Point pt;
        ChessBoard& board;
public:
        const bool cl;
        ChessPiece(const Point& point, bool color, ChessBoard& chessboard) :pt(point), cl(color), board(chessboard)
        {
                if (cl == BLACK)
                        board.black.push_back(this);
                else
                        board.red.push_back(this);
                board.GetChess(pt) = this;
        }
        const Point& GetPoint()const
        {
                return pt;
        }
        virtual bool CanMoveTo(const Point& point)const = 0;
        virtual const char* GetName()const = 0;
        virtual const bool CanCrossTheRiver()const = 0;
        bool MoveTo(const Point& point)
        {
                if (CanMoveTo(point))
                {
                        board.GetChess(pt) = nullptr;
                        pt.x = point.x;
                        pt.y = point.y;
                        board.RemoveChess(point);//删除目的地棋子(如果有)
                        board.GetChess(point) = this;
                        return true;
                }
                return false;
        }
};成员变量

棋盘和棋子紧密相关,棋子必须依附于棋盘而存在,棋盘也必须包含棋子。所以,在ChessPiece类中包含一个棋盘的引用是非常有必要的。当然,每个棋子都有自己的坐标,所以也必须有一个坐标属性。但这里有一个需要注意的地方:


上面说的两个属性必须是protected,不能是private,因为子类不能访问父类的private属性!
成员函数

构造函数

构造函数非常简单,就是初始化一些变量,并且把自己添加到ChessBoard类里。
CanMoveTo

判断能否移动到指定地点(但不移动),纯虚函数。
GetName

获取棋子名称,纯虚函数。
CanCrossTheRiver

棋子能否过河,纯虚函数。
MoveTo

移动到指定地点,并返回是否成功。因为所有棋子移动的流程都是判断是否可以移动->移到指定位置->吃掉原有棋子(如果有),所以这个函数没必要是虚函数,直接按照流程来即可。注意,在非虚函数中调用虚函数是可行且有效的。
在开始下面的之前,我先说一下,为了查找各个中国象棋棋子的英文名称,我焦头烂额地百度了大半天,才找到一个比较靠谱的。下面的英文名称就是按照这个来的。
車(Rook)



車大概是象棋中最厉害的棋子,所以很多新手都喜欢用,并且流传下了“新手玩车,熟手玩炮,老手玩马”的谚语(这么看我是熟手 )。下面我们就先来实现車。
class Rook :public ChessPiece//車
{
public:
        Rook(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
        virtual bool CanMoveTo(const Point& point)const override
        {
                if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl)//目的地没有棋子或有对方棋子
                {
                        if (point.x == pt.x)
                        {
                                if (point.y < pt.y)
                                {
                                        for (uint8_t i = point.y + 1; i < pt.y; ++i)
                                        {
                                                if (board.GetChess(Point(point.x, i)))//中间有棋子
                                                        return false;
                                        }
                                }
                                else
                                {
                                        for (uint8_t i = pt.y + 1; i < point.y; ++i)
                                        {
                                                if (board.GetChess(Point(point.x, i)))//中间有棋子
                                                        return false;
                                        }
                                }

                        }
                        else if (point.y == pt.y)
                        {
                                if (point.x < pt.x)
                                {
                                        for (uint8_t i = point.x + 1; i < pt.x; ++i)
                                        {
                                                if (board.GetChess(Point(i, point.y)))//中间有棋子
                                                        return false;
                                        }
                                }
                                else
                                {
                                        for (uint8_t i = pt.x + 1; i < point.x; ++i)
                                        {
                                                if (board.GetChess(Point(i, point.y)))//中间有棋子
                                                        return false;
                                        }
                                }
                        }
                        return true;
                }
                return false;
        }
        virtual const char* GetName()const
        {
                return "車";
        }
        virtual const bool CanCrossTheRiver()const
        {
                return true;
        }
}; CanMoveTo
車的走法是直线行走任意格,所在位置和目的地中间不能有棋子,可行处可吃敌子。根据这条规则就可以写出以上代码,挺好理解的。
馬(Horse)



class Horse :public ChessPiece//馬
{
public:
        Horse(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
        virtual bool CanMoveTo(const Point& point)const override
        {
                static const Point s[8] = { {2,1},{2,-1},{-2,1},{-2,-1},{1,2},{-1,2},{1,-2},{-1,-2} },
                        u[8] = { {1,0},{1,0},{-1,0},{-1,0},{0,1},{0,1},{0,-1},{0,-1} };//马可以到达的八个点和蹩马腿的八个点
                if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl)
                {
                        for (size_t i = 0; i < 8; i++)
                        {
                                if (point == Point(pt.x + s.x, pt.y + s.y) && board.GetChess(Point(pt.x + u.x, pt.y + u.y)) == nullptr)
                                {
                                        return true;
                                }
                        }
                }
                return false;
        }
        virtual const char* GetName()const
        {
                return "馬";
        }
        virtual const bool CanCrossTheRiver()const
        {
                return true;
        }
};CanMoveTo

馬的走法是走“日”字,可行处即可吃子。因为馬最多有八个可以到达的地点,所以最简单的方法是一一列举出来,判断是否与参数相同即可。当然,还有一个条件,就是相应的“蹩马腿”的点必须没有棋子。
炮(Cannon)



class Cannon :public ChessPiece//炮
{
public:
        Cannon(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
        virtual bool CanMoveTo(const Point& point)const override
        {
                //第一种走法:直线移动,不吃子
                if (board.GetChess(point) == nullptr)//目的地没有棋子
                {
                        if (point.x == pt.x)
                        {
                                if (point.y < pt.y)
                                {
                                        for (uint8_t i = point.y + 1; i < pt.y; ++i)
                                        {
                                                if (board.GetChess(Point(point.x, i)))//中间有棋子
                                                        return false;
                                        }
                                }
                                else
                                {
                                        for (uint8_t i = pt.y + 1; i < point.y; ++i)
                                        {
                                                if (board.GetChess(Point(point.x, i)))//中间有棋子
                                                        return false;
                                        }
                                }
                        }
                        else if (point.y == pt.y)
                        {
                                if (point.x < pt.x)
                                {
                                        for (uint8_t i = point.x + 1; i < pt.x; ++i)
                                        {
                                                if (board.GetChess(Point(i, point.y)))//中间有棋子
                                                        return false;
                                        }
                                }
                                else if (pt.x < point.x)
                                {
                                        for (uint8_t i = pt.x + 1; i < point.x; ++i)
                                        {
                                                if (board.GetChess(Point(i, point.y)))//中间有棋子
                                                        return false;
                                        }
                                }
                        }
                        return true;
                }
                else if (board.GetChess(point)->cl != this->cl)//第二种走法:吃子
                {
                        uint8_t count = 0;
                        if (point.x == pt.x)
                        {
                                if (point.y < pt.y)
                                {
                                        for (uint8_t i = point.y + 1; i < pt.y; ++i)
                                        {
                                                if (board.GetChess(Point(point.x, i)))//中间有棋子
                                                        count++;
                                        }
                                }
                                else
                                {
                                        for (uint8_t i = pt.y + 1; i < point.y; ++i)
                                        {
                                                if (board.GetChess(Point(point.x, i)))//中间有棋子
                                                        count++;
                                        }
                                }
                        }
                        else if (point.y == pt.y)
                        {
                                if (point.x < pt.x)
                                {
                                        for (uint8_t i = point.x + 1; i < pt.x; ++i)
                                        {
                                                if (board.GetChess(Point(i, point.y)))//中间有棋子
                                                        count++;
                                        }
                                }
                                else if (pt.x < point.x)
                                {
                                        for (uint8_t i = pt.x + 1; i < point.x; ++i)
                                        {
                                                if (board.GetChess(Point(i, point.y)))//中间有棋子
                                                        count++;
                                        }
                                }
                        }
                        if (count == 1)
                                return true;
                }
                return false;
        }
        virtual const char* GetName()const
        {
                return "炮";
        }
        virtual const bool CanCrossTheRiver()const
        {
                return true;
        }
};CanMoveTo

炮有两种基本走法:一是像車一样直线行走任意格,所在位置和目的地中间不能有棋子,但不能吃子;一是直线行走任意格,所在位置和目的地中间有且只有一个棋子(敌方我方均可),必须吃子。第一种走法的代码和車的基本一样;第二种也基本相同,只是“中间没有棋子”的条件改成了“棋子个数为1”。
相/象(Elephant)



(别问我为什么只贴黑方的象,我就是喜欢黑方)
class Elephant :public ChessPiece//相/象
{
public:
        Elephant(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
        virtual bool CanMoveTo(const Point& point)const override
        {
                static const Point s[4] = { {2,2},{2,-2},{-2,2},{-2,-2} }, u[4] = { {1,1},{1,-1},{-1,1},{-1,-1} };//象可以到达的四个点和蹩象眼的八个点
                if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl)
                {
                        if (cl == point.ColorOfTheArea())//在我方范围内
                        {
                                for (size_t i = 0; i < 4; i++)
                                {
                                        if (point == Point(pt.x + s.x, pt.y + s.y) && board.GetChess(Point(pt.x + u.x, pt.y + u.y)) == nullptr)
                                        {
                                                return true;
                                        }
                                }
                        }
                }
                return false;
        }
        virtual const char* GetName()const
        {
                return cl == BLACK ? "象" : "相";
        }
        virtual const bool CanCrossTheRiver()const
        {
                return false;
        }
};CanMoveTo

和馬的原理完全一样,不过要注意象不能过河。
士(Adviser)



class Adviser :public ChessPiece//士
{
public:
        Adviser(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
        virtual bool CanMoveTo(const Point& point)const override
        {
                static const Point s[4] = { {1,1},{1,-1},{-1,1},{-1,-1} };//士可以到达的四个点
                if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl)
                {
                        if (cl == point.ColorOfTheArea() && point.IsInNinePalaces())
                        {
                                for (size_t i = 0; i < 4; i++)
                                {
                                        if (point == Point(pt.x + s.x, pt.y + s.y))
                                        {
                                                return true;
                                        }
                                }
                        }
                }
                return false;
        }
        virtual const char* GetName()const
        {
                return "士";
        }
        virtual const bool CanCrossTheRiver()const
        {
                return false;
        }
};CanMoveTo

没啥说头,和象的基本一样,只不过去掉了蹩象眼的限制,而且不能出九宫格。
兵/卒(Pawn)



class Pawn :public ChessPiece//兵/卒
{
public:
        Pawn(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
        virtual bool CanMoveTo(const Point& point)const override
        {
                if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl)
                {
                        int8_t front = (cl == RED ? 1 : -1);
                        if (cl == pt.ColorOfTheArea())//没过河
                                return point == Point(pt.x, pt.y + front);
                        static const Point s[3] = { {0,front},{1,0},{-1,0} };
                        for (size_t i = 0; i < 4; i++)
                        {
                                if (point == Point(pt.x + s.x, pt.y + s.y))
                                {
                                        return true;
                                }
                        }
                }
                return false;
        }
        virtual const char* GetName()const
        {
                return cl == BLACK ? "卒" : "兵";
        }
        virtual const bool CanCrossTheRiver()const
        {
                return true;
        }
};CanMoveTo

兵的移动分两种情况:没过河只能前进一格,过河后可以前进或向左、向右一格,所以需要分类讨论。还要注意,红黑双方前进的方向是不同的,红方纵坐标+1,黑方-1。
帥/將



class King :public ChessPiece//將/帥
{
public:
        King(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
        virtual bool CanMoveTo(const Point& point)const override
        {
                static const Point s[4] = { {0,1},{0,-1},{1,0},{-1,0} };//將可以到达的四个点
                if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl)
                {
                        if (point.IsInNinePalaces() && point.ColorOfTheArea() == cl)//在我方九宫格内
                        {
                                for (size_t i = 0; i < 4; i++)
                                {
                                        if (point == Point(pt.x + s.x, pt.y + s.y))
                                        {
                                                return true;
                                        }
                                }
                        }
                }
                return false;
        }
        virtual const char* GetName()const
        {
                return cl == BLACK ? "將" : "帥";
        }
        virtual const bool CanCrossTheRiver()const
        {
                return false;
        }
}; CanMoveTo
和士的又雷同了,只是把可以到的几个点数值变了变。
ChessBoard

棋子类都写完了,就可以添加棋盘类了。
class ChessBoard
{
private:
        friend class ChessPiece;
        ChessPiece* board[10][9];//红方纵坐标0-4,黑方纵坐标5-9,很重要!
        std::list<ChessPiece*> red, black;
public:
        ChessBoard();
        const std::list<ChessPiece*>& GetRedPieces()const
        {
                return red;
        }
        const std::list<ChessPiece*>& GetBlackPieces()const
        {
                return black;
        }
        ChessPiece*& GetChess(const Point& point)
        {
                return board[point.y][point.x];
        }
        ChessPiece* const& GetChess(const Point& point)const
        {
                return board[point.y][point.x];
        }
        void RemoveChess(const Point& point);
        bool KingsFaceToFace()const;
        ~ChessBoard();
};
ChessBoard::ChessBoard()
{
        memset(board, 0, sizeof(board));
        new King(Point(4, 0), RED, *this);
        new King(Point(4, 9), BLACK, *this);
        new Adviser(Point(3, 0), RED, *this);
        new Adviser(Point(5, 0), RED, *this);
        new Adviser(Point(3, 9), BLACK, *this);
        new Adviser(Point(5, 9), BLACK, *this);
        new Elephant(Point(2, 0), RED, *this);
        new Elephant(Point(6, 0), RED, *this);
        new Elephant(Point(2, 9), BLACK, *this);
        new Elephant(Point(6, 9), BLACK, *this);
        new Horse(Point(1, 0), RED, *this);
        new Horse(Point(7, 0), RED, *this);
        new Horse(Point(1, 9), BLACK, *this);
        new Horse(Point(7, 9), BLACK, *this);
        new Rook(Point(0, 0), RED, *this);
        new Rook(Point(8, 0), RED, *this);
        new Rook(Point(0, 9), BLACK, *this);
        new Rook(Point(8, 9), BLACK, *this);
        new Cannon(Point(1, 2), RED, *this);
        new Cannon(Point(7, 2), RED, *this);
        new Cannon(Point(1, 7), BLACK, *this);
        new Cannon(Point(7, 7), BLACK, *this);
        new Pawn(Point(0, 3), RED, *this);
        new Pawn(Point(2, 3), RED, *this);
        new Pawn(Point(4, 3), RED, *this);
        new Pawn(Point(6, 3), RED, *this);
        new Pawn(Point(8, 3), RED, *this);
        new Pawn(Point(0, 6), BLACK, *this);
        new Pawn(Point(2, 6), BLACK, *this);
        new Pawn(Point(4, 6), BLACK, *this);
        new Pawn(Point(6, 6), BLACK, *this);
        new Pawn(Point(8, 6), BLACK, *this);
}
bool ChessBoard::KingsFaceToFace() const
{
        auto r = std::find_if(red.begin(), red.end(), [](ChessPiece* p) {return !strcmp(p->GetName(), "帥"); }),
                b = std::find_if(black.begin(), black.end(), [](ChessPiece* p) {return !strcmp(p->GetName(), "將"); });
        if (r != red.end() && b != black.end())
        {
                if ((*r)->GetPoint().x == (*b)->GetPoint().x)
                {
                        for (uint8_t i = (*r)->GetPoint().y + 1; i < (*b)->GetPoint().y; i++)
                        {
                                if (GetChess(Point((*r)->GetPoint().x, i)))
                                        return false;
                        }
                        return true;
                }
        }
        return false;
}
ChessBoard::~ChessBoard()
{
        for (ChessPiece* p : red)
                delete p;
        for (ChessPiece* p : black)
                delete p;
} 成员变量
根据之前说的棋子存储方法,开一个数组和两个list(方便增删)。
成员函数

构造函数



如图是象棋各个棋子开局时的位置,左上角的車坐标为(0,0),往右横坐标递增,往下纵坐标递增(和平面直角坐标系不太一样)。构造函数就是用来初始化这些位置的。
首先清空棋盘,然后分别new出双方每一个棋子。由于ChessPiece类的构造函数中会把棋子自动添加到ChessBoard类相关容器里面,所以直接new就可以。
GetChess

返回对应坐标的棋子。由于数组下标是先行后列的,与平时习惯不符,所以要转换一下。
RemoveChess

删除指定坐标的棋子。先从所在的list中删除,然后释放内存(delete),最后把board对应元素设为nullptr(没有棋子)。
KingsFaceToFace

用来判断是否老将对脸。首先寻找双方的将帅,然后判断是否在同一列,中间是否没有棋子。
析构函数

释放所有棋子内存。
ChessGame

class ChessGame
{
private:
        bool nextPlayer;
        ChessBoard board;
public:
        ChessGame() :nextPlayer(RED) {}
        const ChessBoard& GetBoard()const
        {
                return board;
        }
        bool Move(const Point& a, const Point& b)
        {
                if (board.GetChess(a) && board.GetChess(a)->cl == nextPlayer)
                {
                        if (board.GetChess(a)->MoveTo(b))
                        {
                                nextPlayer = !nextPlayer;
                                return true;
                        }
                        return false;
                }
                return false;
        }
        uint8_t GetWinner()const
        {
                if (board.KingsFaceToFace())
                        return nextPlayer;
                if (std::find_if(board.GetRedPieces().begin(), board.GetRedPieces().end(), [](ChessPiece* p) {return !strcmp(p->GetName(), "帥"); }) == board.GetRedPieces().end())//红方帅被吃
                        return BLACK;
                if (std::find_if(board.GetBlackPieces().begin(), board.GetBlackPieces().end(), [](ChessPiece* p) {return !strcmp(p->GetName(), "將"); }) == board.GetBlackPieces().end())//黑方帅被吃
                        return RED;
                return NONE;
        }
        bool GetNextPlayer()const
        {
                return nextPlayer;
        }
};成员变量

nextPlayer用来记录下一手是红方还是黑方,board就是棋盘。
成员函数

Move

和ChessPiece类的MoveTo基本相同,不过这里限制了轮到哪一方只能走哪一方的棋子。
GetWinner

首先判断是否老将对脸,如果是,下一手玩家胜利。然后搜索双方将帅,如果其中一方没有,对方胜利。如果双方都没有可以过河的棋子,平局。否则还没决出胜负。
五、测试代码

用上述代码写了个简单的象棋程序测试,但这样输入坐标很不方便,所以下期会用MFC做个鼠标操作的。
#include <iostream>
#include "Chess.h"
#include<Windows.h>
using namespace std;
using namespace ChineseChess;
void put(const ChessBoard& b)
{
        for (int y = 0; y < 10; y++)
        {
                for (int x = 0; x < 9; x++)
                {
                        cout.width(3);
                        ChessPiece* p = b.GetChess(Point(x, y));
                        if(p)
                        {
                                if (p->cl == BLACK)
                                {
                                                SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), BACKGROUND_INTENSITY
                                                        | FOREGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
                                }
                                else
                                {
                                        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), BACKGROUND_INTENSITY
                                                | FOREGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
                                                | FOREGROUND_RED);
                                }
                                cout << p->GetName();
                        }
                        else
                        {
                                SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), BACKGROUND_INTENSITY
                                        | FOREGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
                                        | FOREGROUND_GREEN);
                                cout << "十";
                        }
                }
                SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY
                        | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
                cout << endl;
        }
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY
                | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}
int main()
{
        ChessGame game;
        put(game.GetBoard());
        while (1)
        {
                int x, y, i, j;
                cout <<"轮到"<<(game.GetNextPlayer()== RED?"红":"黑") << "方了,请输入要移动棋子的坐标和目标点坐标(输入-1退出):";
                cin >> x >> y >> i >> j;
                if (x == -1)
                        break;
                while (!game.Move(Point(x,y),Point(i,j)))
                {
                        cout << "输入有误,请重新输入:";
                        cin >> x >> y >> i >> j;
                }

                put(game.GetBoard());
                auto winner = game.GetWinner();
                if (winner == BLACK)
                {
                        cout << "黑方获胜!";
                        break;
                }
                else if (winner == RED)
                {
                        cout << "红方获胜!";
                        break;
                }
                else if (winner == DRAW)
                {
                        cout << "平局!";
                        break;
                }
        }
        return 0;
}随便贴张截图吧


附录:Chess.h完整代码

#pragma once
#include<cstdint>
#include<list>
#include<stdexcept>
#include<algorithm>
namespace ChineseChess
{
        const bool BLACK = 0, RED = 1;
        const uint8_t DRAW = 2, NONE = 3;
        class Point
        {
        public:
                int8_t x, y;
                Point(int8_t nx, int8_t ny) :x(nx), y(ny) {}
                bool ColorOfTheArea()const//判断在红方区域还是黑方区域
                {
                        if (y <= 4)
                                return RED;
                        return BLACK;
                }
                bool IsInNinePalaces()const//是否在九宫格中
                {
                        return x >= 3 && x <= 5 && (y <= 2 || y >= 7);
                }
        };
        bool operator==(const Point& a, const Point& b)
        {
                return a.x == b.x && a.y == b.y;
        }
        class ChessPiece;
        class ChessBoard
        {
        private:
                friend class ChessPiece;
                ChessPiece* board[10][9];//红方纵坐标0-4,黑方纵坐标5-9,很重要!
                std::list<ChessPiece*> red, black;
        public:
                ChessBoard();
                const std::list<ChessPiece*>& GetRedPieces()const
                {
                        return red;
                }
                const std::list<ChessPiece*>& GetBlackPieces()const
                {
                        return black;
                }
                ChessPiece*& GetChess(const Point& point)
                {
                        return board[point.y][point.x];
                }
                ChessPiece* const& GetChess(const Point& point)const
                {
                        return board[point.y][point.x];
                }
                void RemoveChess(const Point& point);
                bool KingsFaceToFace()const;
                ~ChessBoard();
        };
        class ChessPiece
        {
        protected:
                Point pt;
                ChessBoard& board;
        public:
                const bool cl;
                ChessPiece(const Point& point, bool color, ChessBoard& chessboard) :pt(point), cl(color), board(chessboard)
                {
                        if (cl == BLACK)
                                board.black.push_back(this);
                        else
                                board.red.push_back(this);
                        board.GetChess(pt) = this;
                }
                const Point& GetPoint()const
                {
                        return pt;
                }
                virtual bool CanMoveTo(const Point& point)const = 0;
                virtual const char* GetName()const = 0;
                virtual const bool CanCrossTheRiver()const = 0;
                bool MoveTo(const Point& point)
                {
                        if (CanMoveTo(point))
                        {
                                board.GetChess(pt) = nullptr;
                                pt.x = point.x;
                                pt.y = point.y;
                                board.RemoveChess(point);//删除目的地棋子(如果有)
                                board.GetChess(point) = this;
                                return true;
                        }
                        return false;
                }
        };
        void ChessBoard::RemoveChess(const Point& point)
        {
                if (GetChess(point))
                {
                        if (GetChess(point)->cl == RED)
                        {
                                red.erase(std::find(red.begin(), red.end(), GetChess(point)));
                        }
                        else
                        {
                                black.erase(std::find(black.begin(), black.end(), GetChess(point)));
                        }
                        delete GetChess(point);
                        GetChess(point) = nullptr;
                }
        }
        class Rook :public ChessPiece//車
        {
        public:
                Rook(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
                virtual bool CanMoveTo(const Point& point)const override
                {
                        if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl)//目的地没有棋子或有对方棋子
                        {
                                if (point.x == pt.x)
                                {
                                        if (point.y < pt.y)
                                        {
                                                for (uint8_t i = point.y + 1; i < pt.y; ++i)
                                                {
                                                        if (board.GetChess(Point(point.x, i)))//中间有棋子
                                                                return false;
                                                }
                                        }
                                        else
                                        {
                                                for (uint8_t i = pt.y + 1; i < point.y; ++i)
                                                {
                                                        if (board.GetChess(Point(point.x, i)))//中间有棋子
                                                                return false;
                                                }
                                        }
                                        return true;
                                }
                                else if (point.y == pt.y)
                                {
                                        if (point.x < pt.x)
                                        {
                                                for (uint8_t i = point.x + 1; i < pt.x; ++i)
                                                {
                                                        if (board.GetChess(Point(i, point.y)))//中间有棋子
                                                                return false;
                                                }
                                        }
                                        else
                                        {
                                                for (uint8_t i = pt.x + 1; i < point.x; ++i)
                                                {
                                                        if (board.GetChess(Point(i, point.y)))//中间有棋子
                                                                return false;
                                                }
                                        }
                                        return true;
                                }
                        }
                        return false;
                }
                virtual const char* GetName()const
                {
                        return "車";
                }
                virtual const bool CanCrossTheRiver()const
                {
                        return true;
                }
        };
        class Horse :public ChessPiece//馬
        {
        public:
                Horse(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
                virtual bool CanMoveTo(const Point& point)const override
                {
                        static const Point s[8] = { {2,1},{2,-1},{-2,1},{-2,-1},{1,2},{-1,2},{1,-2},{-1,-2} },
                                u[8] = { {1,0},{1,0},{-1,0},{-1,0},{0,1},{0,1},{0,-1},{0,-1} };//马可以到达的八个点和蹩马腿的八个点
                        if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl)
                        {
                                for (size_t i = 0; i < 8; i++)
                                {
                                        if (point == Point(pt.x + s.x, pt.y + s.y) && board.GetChess(Point(pt.x + u.x, pt.y + u.y)) == nullptr)
                                        {
                                                return true;
                                        }
                                }
                        }
                        return false;
                }
                virtual const char* GetName()const
                {
                        return "馬";
                }
                virtual const bool CanCrossTheRiver()const
                {
                        return true;
                }
        };
        class Cannon :public ChessPiece//炮
        {
        public:
                Cannon(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
                virtual bool CanMoveTo(const Point& point)const override
                {
                        //第一种走法:直线移动,不吃子
                        if (board.GetChess(point) == nullptr)//目的地没有棋子
                        {
                                if (point.x == pt.x)
                                {
                                        if (point.y < pt.y)
                                        {
                                                for (uint8_t i = point.y + 1; i < pt.y; ++i)
                                                {
                                                        if (board.GetChess(Point(point.x, i)))//中间有棋子
                                                                return false;
                                                }
                                        }
                                        else
                                        {
                                                for (uint8_t i = pt.y + 1; i < point.y; ++i)
                                                {
                                                        if (board.GetChess(Point(point.x, i)))//中间有棋子
                                                                return false;
                                                }
                                        }
                                        return true;
                                }
                                else if (point.y == pt.y)
                                {
                                        if (point.x < pt.x)
                                        {
                                                for (uint8_t i = point.x + 1; i < pt.x; ++i)
                                                {
                                                        if (board.GetChess(Point(i, point.y)))//中间有棋子
                                                                return false;
                                                }
                                        }
                                        else if (pt.x < point.x)
                                        {
                                                for (uint8_t i = pt.x + 1; i < point.x; ++i)
                                                {
                                                        if (board.GetChess(Point(i, point.y)))//中间有棋子
                                                                return false;
                                                }
                                        }
                                        return true;
                                }
                        }
                        else if (board.GetChess(point)->cl != this->cl)//第二种走法:吃子
                        {
                                uint8_t count = 0;
                                if (point.x == pt.x)
                                {
                                        if (point.y < pt.y)
                                        {
                                                for (uint8_t i = point.y + 1; i < pt.y; ++i)
                                                {
                                                        if (board.GetChess(Point(point.x, i)))//中间有棋子
                                                                count++;
                                                }
                                        }
                                        else
                                        {
                                                for (uint8_t i = pt.y + 1; i < point.y; ++i)
                                                {
                                                        if (board.GetChess(Point(point.x, i)))//中间有棋子
                                                                count++;
                                                }
                                        }
                                }
                                else if (point.y == pt.y)
                                {
                                        if (point.x < pt.x)
                                        {
                                                for (uint8_t i = point.x + 1; i < pt.x; ++i)
                                                {
                                                        if (board.GetChess(Point(i, point.y)))//中间有棋子
                                                                count++;
                                                }
                                        }
                                        else if (pt.x < point.x)
                                        {
                                                for (uint8_t i = pt.x + 1; i < point.x; ++i)
                                                {
                                                        if (board.GetChess(Point(i, point.y)))//中间有棋子
                                                                count++;
                                                }
                                        }
                                }
                                if (count == 1)
                                        return true;
                        }
                        return false;
                }
                virtual const char* GetName()const
                {
                        return "炮";
                }
                virtual const bool CanCrossTheRiver()const
                {
                        return true;
                }
        };
        class Elephant :public ChessPiece//相/象
        {
        public:
                Elephant(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
                virtual bool CanMoveTo(const Point& point)const override
                {
                        static const Point s[4] = { {2,2},{2,-2},{-2,2},{-2,-2} }, u[4] = { {1,1},{1,-1},{-1,1},{-1,-1} };//象可以到达的四个点和蹩象眼的八个点
                        if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl)
                        {
                                if (cl == point.ColorOfTheArea())//在我方范围内
                                {
                                        for (size_t i = 0; i < 4; i++)
                                        {
                                                if (point == Point(pt.x + s.x, pt.y + s.y) && board.GetChess(Point(pt.x + u.x, pt.y + u.y)) == nullptr)
                                                {
                                                        return true;
                                                }
                                        }
                                }
                        }
                        return false;
                }
                virtual const char* GetName()const
                {
                        return cl == BLACK ? "象" : "相";
                }
                virtual const bool CanCrossTheRiver()const
                {
                        return false;
                }
        };
        class Adviser :public ChessPiece//士
        {
        public:
                Adviser(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
                virtual bool CanMoveTo(const Point& point)const override
                {
                        static const Point s[4] = { {1,1},{1,-1},{-1,1},{-1,-1} };//士可以到达的四个点
                        if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl)
                        {
                                if (cl == point.ColorOfTheArea() && point.IsInNinePalaces())
                                {
                                        for (size_t i = 0; i < 4; i++)
                                        {
                                                if (point == Point(pt.x + s.x, pt.y + s.y))
                                                {
                                                        return true;
                                                }
                                        }
                                }
                        }
                        return false;
                }
                virtual const char* GetName()const
                {
                        return "士";
                }
                virtual const bool CanCrossTheRiver()const
                {
                        return false;
                }
        };
        class Pawn :public ChessPiece//兵/卒
        {
        public:
                Pawn(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
                virtual bool CanMoveTo(const Point& point)const override
                {
                        if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl)
                        {
                                int8_t front = (cl == RED ? 1 : -1);
                                if (cl == pt.ColorOfTheArea())//没过河
                                        return point == Point(pt.x, pt.y + front);
                                const Point s[3] = { {0,front},{1,0},{-1,0} };
                                for (size_t i = 0; i < 3; i++)
                                {
                                        if (point == Point(pt.x + s.x, pt.y + s.y))
                                        {
                                                return true;
                                        }
                                }
                        }
                        return false;
                }
                virtual const char* GetName()const
                {
                        return cl == BLACK ? "卒" : "兵";
                }
                virtual const bool CanCrossTheRiver()const
                {
                        return true;
                }
        };
        class King :public ChessPiece//將/帥
        {
        public:
                King(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
                virtual bool CanMoveTo(const Point& point)const override
                {
                        static const Point s[4] = { {0,1},{0,-1},{1,0},{-1,0} };//將可以到达的四个点
                        if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl)
                        {
                                if (point.IsInNinePalaces() && point.ColorOfTheArea() == cl)//在我方九宫格内
                                {
                                        for (size_t i = 0; i < 4; i++)
                                        {
                                                if (point == Point(pt.x + s.x, pt.y + s.y))
                                                {
                                                        return true;
                                                }
                                        }
                                }
                        }
                        return false;
                }
                virtual const char* GetName()const
                {
                        return cl == BLACK ? "將" : "帥";
                }
                virtual const bool CanCrossTheRiver()const
                {
                        return false;
                }
        };

        ChessBoard::ChessBoard()
        {
                memset(board, 0, sizeof(board));
                new King(Point(4, 0), RED, *this);
                new King(Point(4, 9), BLACK, *this);
                new Adviser(Point(3, 0), RED, *this);
                new Adviser(Point(5, 0), RED, *this);
                new Adviser(Point(3, 9), BLACK, *this);
                new Adviser(Point(5, 9), BLACK, *this);
                new Elephant(Point(2, 0), RED, *this);
                new Elephant(Point(6, 0), RED, *this);
                new Elephant(Point(2, 9), BLACK, *this);
                new Elephant(Point(6, 9), BLACK, *this);
                new Horse(Point(1, 0), RED, *this);
                new Horse(Point(7, 0), RED, *this);
                new Horse(Point(1, 9), BLACK, *this);
                new Horse(Point(7, 9), BLACK, *this);
                new Rook(Point(0, 0), RED, *this);
                new Rook(Point(8, 0), RED, *this);
                new Rook(Point(0, 9), BLACK, *this);
                new Rook(Point(8, 9), BLACK, *this);
                new Cannon(Point(1, 2), RED, *this);
                new Cannon(Point(7, 2), RED, *this);
                new Cannon(Point(1, 7), BLACK, *this);
                new Cannon(Point(7, 7), BLACK, *this);
                new Pawn(Point(0, 3), RED, *this);
                new Pawn(Point(2, 3), RED, *this);
                new Pawn(Point(4, 3), RED, *this);
                new Pawn(Point(6, 3), RED, *this);
                new Pawn(Point(8, 3), RED, *this);
                new Pawn(Point(0, 6), BLACK, *this);
                new Pawn(Point(2, 6), BLACK, *this);
                new Pawn(Point(4, 6), BLACK, *this);
                new Pawn(Point(6, 6), BLACK, *this);
                new Pawn(Point(8, 6), BLACK, *this);
        }
        bool ChessBoard::KingsFaceToFace() const
        {
                auto r = std::find_if(red.begin(), red.end(), [](ChessPiece* p) {return !strcmp(p->GetName(), "帥"); }),
                        b = std::find_if(black.begin(), black.end(), [](ChessPiece* p) {return !strcmp(p->GetName(), "將"); });
                if (r != red.end() && b != black.end())
                {
                        if ((*r)->GetPoint().x == (*b)->GetPoint().x)
                        {
                                for (uint8_t i = (*r)->GetPoint().y + 1; i < (*b)->GetPoint().y; i++)
                                {
                                        if (GetChess(Point((*r)->GetPoint().x, i)))
                                                return false;
                                }
                                return true;
                        }
                }
                return false;
        }
        ChessBoard::~ChessBoard()
        {
                for (ChessPiece* p : red)
                        delete p;
                for (ChessPiece* p : black)
                        delete p;
        }
        class ChessGame
        {
        private:
                bool nextPlayer;
                ChessBoard board;
        public:
                ChessGame() :nextPlayer(RED) {}
                const ChessBoard& GetBoard()const
                {
                        return board;
                }
                bool Move(const Point& a, const Point& b)
                {
                        if (board.GetChess(a) && board.GetChess(a)->cl == nextPlayer)
                        {
                                if (board.GetChess(a)->MoveTo(b))
                                {
                                        nextPlayer = !nextPlayer;
                                        return true;
                                }
                                return false;
                        }
                        return false;
                }
                uint8_t GetWinner()const
                {
                        if (board.KingsFaceToFace())
                                return nextPlayer;
                        if (std::find_if(board.GetRedPieces().begin(), board.GetRedPieces().end(), [](ChessPiece* p) {return !strcmp(p->GetName(), "帥"); }) == board.GetRedPieces().end())//红方帅被吃
                                return BLACK;
                        if (std::find_if(board.GetBlackPieces().begin(), board.GetBlackPieces().end(), [](ChessPiece* p) {return !strcmp(p->GetName(), "將"); }) == board.GetBlackPieces().end())//黑方帅被吃
                                return RED;
                        if (std::count_if(board.GetRedPieces().begin(), board.GetRedPieces().end(), [](ChessPiece* p) {return p->CanCrossTheRiver(); }) +
                                std::count_if(board.GetBlackPieces().begin(), board.GetBlackPieces().end(), [](ChessPiece* p) {return p->CanCrossTheRiver(); }) == 0)
                                return DRAW;//双方都不能过河,平局
                        return NONE;
                }
                bool GetNextPlayer()const
                {
                        return nextPlayer;
                }
        };
}
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|VV游戏

GMT+8, 2025-4-8 11:49 , Processed in 0.091647 second(s), 22 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表