您的位置:首页 > 移动开发 > Unity3D

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

2017-02-05 21:22 253 查看
首先,新建一个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;
}

}

为了代码简洁方便使用,于是用了这样的代码结构,对于内存方面缺乏研究,故不知道如此使用是否占用过多内存,个人认为代码的简洁易懂应该已经做得不错了,但在内存优化方面还有所欠缺
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息