Unity中国象棋(三)——各类棋子规则

首先,新建一个RuleManager脚本,写下各类棋子的走棋规则,先贴上脚本


public class RuleManager : MonoBehaviour
{
    /// <summary>
    /// 将的走棋规则
    /// </summary>
    /// <returns></returns>
    public static bool moveJiang(int selectedId, int row, int col, int destoryId)
    {
        /*
         * 1.首先目标位置在九宫格内
         * 2.移动的步长是一个格子
         * 3.将和帅不准在同一直线上直接对面(中间无棋子),如一方已先占据位置,则另一方必须回避,否则就算输了
        */
        if (destoryId != -1 && StoneManager.s[destoryId]._type == StoneManager.Stone.TYPE.JIANG)
            return moveChe(selectedId, row, col, destoryId);

        if (col < 3 || col > 5) return false;
        if (ToolManager.IsBottomSide(selectedId))
        {
            if (row < 7) return false;
        }
        else
        {
            if (row > 2) return false;
        }

        int row1 = ToolManager.yToRow(StoneManager.s[selectedId]._y);
        int col1 = ToolManager.xToCol(StoneManager.s[selectedId]._x);
        int d = ToolManager.Relation(row1, col1, row, col);
        if (d != 1 && d != 10) return false;

        return true;
    }


    /// <summary>
    /// 士的走棋规则
    /// </summary>
    /// <returns></returns>
    public static bool moveShi(int selectedId, int row, int col, int destoryId)
    {
        /*
         * 1.目标位置在九宫格内
         * 2.只许沿着九宫中的斜线行走一步(方格的对角线)
        */
        if (ToolManager.IsBottomSide(selectedId))
        {
            if (row < 7) return false;
        }
        else
        {
            if (row > 2) return false;
        }
        if (col < 3 || col > 5) return false;

        int row1 = ToolManager.yToRow(StoneManager.s[selectedId]._y);
        int col1 = ToolManager.xToCol(StoneManager.s[selectedId]._x);
        int d = ToolManager.Relation(row1, col1, row, col);
        if (d != 11) return false;

        return true;
    }


    /// <summary>
    /// 相的走棋规则
    /// </summary>
    /// <returns></returns>
    public static bool moveXiang(int selectedId, int row, int col, int destoryId)
    {
        /*
         * 1.目标位置不能越过河界走入对方的领地
         * 2.只能斜走(两步),可以使用汉字中的田字形象地表述:田字格的对角线,即俗称象(相)走田字
         * 3.当象(相)行走的路线中,及田字中心有棋子时(无论己方或者是对方的棋子),则不允许走过去,俗称:塞象(相)眼。
        */
        int row1 = ToolManager.yToRow(StoneManager.s[selectedId]._y);
        int col1 = ToolManager.xToCol(StoneManager.s[selectedId]._x);
        int d = ToolManager.Relation(row1, col1, row, col);
        if (d != 22) return false;

        int rEye = (row + row1) / 2;
        int cEye = (col + col1) / 2;

        if (ToolManager.GetStoneId(rEye, cEye) != -1) return false;

        if (ToolManager.IsBottomSide(selectedId))
        {
            if (row < 4) return false;
        }
        else
        {
            if (row > 5) return false;
        }

        return true;
    }


    /// <summary>
    /// 车的走棋规则
    /// </summary>
    /// <returns></returns>
    public static bool moveChe(int selectedId, int row, int col, int destoryId)
    {
        /*
         * 1.每行一步棋可以上、下直线行走(进、退);左、右横走
         * 2.中间不能隔棋子
         * 3.行棋步数不限
         */
        int row1 = ToolManager.yToRow(StoneManager.s[selectedId]._y);
        int col1 = ToolManager.xToCol(StoneManager.s[selectedId]._x);
        int ret = ToolManager.GetStoneCountAtLine(row1, col1, row, col);
        if (ret == 0) return true;
        return false;
    }


    /// <summary>
    /// 马的走棋规则
    /// </summary>
    /// <returns></returns>
    public static bool moveMa(int selectedId, int row, int col, int destoryId)
    {
        /*
         * 1.马走日字(斜对角线)
         * 2.可以将马走日分解为:先一步直走(或一横)再一步斜走
         * 3.如果在要去的方向,第一步直行处(或者横行)有别的棋子挡住,则不许走过去(俗称:蹩马腿)
         */
        int row1 = ToolManager.yToRow(StoneManager.s[selectedId]._y);
        int col1 = ToolManager.xToCol(StoneManager.s[selectedId]._x);
        int d = ToolManager.Relation(row1, col1, row, col);
        if (d != 12 && d != 21) return false;

        if (d == 12)
        {
            if (ToolManager.GetStoneId(row1, (col + col1) / 2) != -1)
                return false;
        }
        else
        {
            if (ToolManager.GetStoneId((row + row1) / 2, col1) != -1)
                return false;
        }

        return true;
    }


    /// <summary>
    /// 炮的走棋规则
    /// </summary>
    /// <returns></returns>
    public static bool movePao(int selectedId, int row, int col, int destoryId)
    {
        /*
         * 1.此棋的行棋规则和车(車)类似,横平、竖直,只要前方没有棋子的地方都能行走
         * 2.但是,它的吃棋规则很特别,必须跳过一个棋子(无论是己方的还是对方的)去吃掉对方的一个棋子。俗称:隔山打炮
         */
        int row1 = ToolManager.yToRow(StoneManager.s[selectedId]._y);
        int col1 = ToolManager.xToCol(StoneManager.s[selectedId]._x);
        int ret = ToolManager.GetStoneCountAtLine(row1, col1, row, col);
        if (destoryId != -1)
        {
            if (ret == 1)
                return true;
        }
        else
        {
            if (ret == 0)
                return true;
        }
        return false;
    }


    /// <summary>
    /// 兵的走棋规则
    /// </summary>
    /// <returns></returns>
    public static bool moveBing(int selectedId, int row, int col, int destoryId)
    {
        /*
         * 1.在没有过河界前,此棋每走一步棋只许向前直走一步(不能后退)
         * 2.过了河界之后,每行一步棋可以向前直走,或者横走(左、右)一步,但也是不能后退的
         */
        int row1 = ToolManager.yToRow(StoneManager.s[selectedId]._y);
        int col1 = ToolManager.xToCol(StoneManager.s[selectedId]._x);
        int d = ToolManager.Relation(row1, col1, row, col);
        if (d != 1 && d != 10) return false;

        if (ToolManager.IsBottomSide(selectedId))
        {
            if (row > row1) return false;
            if (row1 >= 5 && row == row1) return false;
        }
        else
        {
            if (row < row1) return false;
            if (row1 <= 4 && row == row1) return false;
        }
        return true;
    }

}


先说明下传入的四个参数

selectedId是当前选择的棋子的ID,col和row分别是棋子要移动到的目标位置的行列坐标,destoryId是当前选择的棋子所可能会吃掉的棋子的ID,即目标位置上的棋子,若目标位置上没有棋子则destoryId为-1;


拿“将”的规则举个例子:

1.若将帅之间没有隔着棋子,则可以像“车”一样直接过去杀死对方的将;

2.将只能待在九宫格内,故他所移动的目标位置只能在第3列与第5列之间;若将在下方,则不能小于第7行,若在上放,则不能大于第2行(列:0-8;行:0-9);

3.因为他只能上下左右移动一个格子,设他左右移动dr,上下移动dc,则可以设d=dr*10+dc;那么将所移动的d只能是10或者1;


另外,在象棋中还有“拐马脚”、“塞象眼,可以通过GetStoneId求“马脚”、“象眼”处是否有棋子;而车和炮的直行,可以通过GetStoneCountAtLine求两个位置之间有多少个棋子,若车或炮与目标位置之间没有棋子,则可以直行,特别的,若目标位置有一个棋子,并且炮到这个棋子之间的位置上刚好有一个棋子,那么就可以“隔山打炮”了


再附上ToolsManager的代码


public class ToolManager : MonoBehaviour {

    /// <summary>
    /// 工具类:将坐标x的值转换为所在的列数
    /// </summary>
    /// <param name="x"></param>
    /// <returns></returns>
    public static int xToCol(float x)
    {
        int col = (int)(x / 0.51f);
        col = col + 4;
        return col;       
    }

    /// <summary>
    /// 工具类:将坐标y的值转换为所在的行数
    /// </summary>
    /// <param name="y"></param>
    /// <returns></returns>
    public static int yToRow(float y)
    {
        int row;
        if (y > 0)
        {
            row=(int)(y/0.51f);
            row = 4 - row;
        }
        else
        {
            row = (int)(y / 0.51f);
            row = 5 - row;
        }
            return row;
    }

    /// <summary>
    /// 工具类:将所在的列数转换为对应的x坐标
    /// </summary>
    /// <param name="col"></param>
    /// <returns></returns>
    public static float colToX(int col)
    {
        float x;
        col = col - 4;
        x = col * 0.51f;
        return x;
    }

    /// <summary>
    /// 工具类:将所在的行数转换为对应的y坐标(因浮点数计算存在不精确问题,故先乘100,计算后再除100)
    /// </summary>
    /// <param name="row"></param>
    /// <returns></returns>
    public static float rowToY(int row)
    {
        float y;
        if (row < 5)
        {
            row = 4 - row;
            y =(float) (row * 51 + 21);
            y = y / 100;
        }
        else
        {
            row = 5 - row;
            y = (float)(row * 51 - 31);
            y = y / 100;
        }
        return y;
    }


    /// <summary>
    /// 计算选中的棋子的位置和要移动的位置之间的位置关系
    /// </summary>
    /// <param name="row1"></param>
    /// <param name="col1"></param>
    /// <param name="row"></param>
    /// <param name="col"></param>
    /// <returns></returns>
    public static int Relation(int row1, int col1, int row, int col)
    {
        return Mathf.Abs(row1 - row) * 10 + Mathf.Abs(col1 - col);
    }

    /// <summary>
    /// 工具类:通过行列数来判断该位置上是否有棋子,若有则返回棋子的ID,若没有则返回-1
    /// </summary>
    /// <param name="rEye"></param>
    /// <param name="cEye"></param>
    /// <returns></returns>
    public static int GetStoneId(int rEye, int cEye)
    {
        float x = ToolManager.colToX(cEye);
        float y = ToolManager.rowToY(rEye);

        for (int i = 0; i < 32; ++i)
        {
            if (x == StoneManager.s[i]._x && y == StoneManager.s[i]._y && StoneManager.s[i]._dead == false)
                return i;
        }
        return -1;
    }

    /// <summary>
    /// 工具类:通过x、y的坐标来判断该位置上是否有棋子,若有则返回棋子的ID,若没有则返回-1
    /// </summary>
    /// <param name="rEye"></param>
    /// <param name="cEye"></param>
    /// <returns></returns>
    public static int GetStoneId(float x, float y)
    {
        for (int i = 0; i < 32; ++i)
        {
            if (x == StoneManager.s[i]._x && y == StoneManager.s[i]._y && StoneManager.s[i]._dead == false)
                return i;
        }
        return -1;
    }

    /// <summary>
    /// 工具类:判断要移动的棋子是否在棋盘的下方
    /// </summary>
    /// <param name="selectedId"></param>
    /// <returns></returns>
    public static bool IsBottomSide(int selectedId)
    {
        if (StoneManager.s[selectedId]._initY < 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    /// <summary>
    /// 工具类:判断两个位置所连成的一条直线上有多少个棋子
    /// </summary>
    /// <param name="row1"></param>
    /// <param name="col1"></param>
    /// <param name="row"></param>
    /// <param name="col"></param>
    /// <returns></returns>
    public static int GetStoneCountAtLine(int row1, int col1, int row2, int col2)
    {
        int ret = 0;
        if (row1 != row2 && col1 != col2) return -1;
        if (row1 == row2 && col1 == col2) return -1;

        if (row1 == row2)
        {
            int min = col1 < col2 ? col1 : col2;
            int max = col1 < col2 ? col2 : col1;
            for (int col = min + 1; col < max; ++col)
            {
                if (GetStoneId(row1, col) != -1) ++ret;
            }
        }
        else
        {
            int min = row1 < row2 ? row1 : row2;
            int max = row1 < row2 ? row2 : row1;
            for (int row = min + 1; row < max; ++row)
            {
                if (GetStoneId(row, col1) != -1) ++ret;
            }
        }
        return ret;
    }

}


为了代码简洁方便使用,于是用了这样的代码结构,对于内存方面缺乏研究,故不知道如此使用是否占用过多内存,个人认为代码的简洁易懂应该已经做得不错了,但在内存优化方面还有所欠缺

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值