本篇主要讲解的是黑白棋的逻辑实现机理,当然这个机理应该是很简单的,所以熟悉的人可以大致浏览之后跳过。而且这个是本人第一次写这样的代码时所用的自己想的逻辑,很搓轻喷。
既然是逻辑处理那么我们将会给COthelloLogic类进行添加功能并实现,首先先描述清楚我们的算法实现原理:
我们将8*8的棋盘定义为一个二维的数组,用int color存储当前所下棋子的颜色。
private:
int Color[8][8]; //代表棋盘每个位置上是什么,0为没有,1为黑子,2为白子
int color;
这样做的好处是通过一个数组可以存储棋盘所有的信息,简化数据量。
然后我们给初始化的函数添加初始棋盘的数据,在黑白棋里面初始数据是两黑两白交差排在棋盘中心,并且黑棋先手,那么初始化应该是这样的:
//初始化棋局,四颗子,两黑两白,交错放置在棋盘中间
bool COthelloLogic::InitLogic(void)
{
for(int i=0;i<8;i++)
for(int j=0;j<8;j++)
Color[i][j]=0;
Color[3][3] = Color[4][4] = 1;
Color[3][4] = Color[4][3] = 2;
color=1;
return false;
}
下面我们考虑下棋的时候的逻辑,黑白棋中是每当落下一颗子时检查8个方向上(横竖4个方向斜线4个方向)有没有自己的两颗棋子夹住对方棋子并且中间没有空隙和自己的棋子,如果是的话,那么将夹住的对方棋子翻转为自己的棋子,反之则不能落子,如果棋盘上任何地方都不能落子,则落子权交换给对方下。下面我们捋顺一下逻辑:
1)某一方玩家开始下子
2)检查是否可以落子,如果不能则跳到5),如果可以则继续
3)落子
4)将符合条件的夹住的对方的棋子翻转为自己的棋子
5)交换棋权并跳至1)
那么我们需要提供一个函数来处理一下所谓的条件
int COthelloLogic::Judgement(int x, int y)
检查翻转条件需要检查8个方向,那么我们需要从落子的地方开始向着8个方向上面延伸到棋盘边沿,于是我们需要一些变量储存这些坐标数据:
int x1 , x2 ,x3, x4 , x6 , x7 , x8 , x9 ,
y1 , y2 ,y3, y4 , y6 , y7 , y8 , y9 ;//保存从所下棋子位置八个方向上面延伸到棋盘边缘的坐标
x1 = x2 = x3 = x4 = x6 = x7 = x8 = x9 = y1 = y2 = y3 = y4 = y6 = y7 = y8 = y9 =0;
然后要给出准备动作,即假装棋子无论如何都下在棋盘上面了,那么需要一个变量作为状态变量:
int judge = 0;//状态变量,0为未记数,1则允许记数
Color [x][y]=color; //暂时当作是棋子下下来了
成员变量建立一个int Change[8]来存储8个方向上面应该翻转的棋子个数。
下面给出第一个方向上面检测翻转的代码:
int Temp_X = x, Temp_Y = y;//用临时变量读入坐标的原始数据
while(true)
{
if( (Temp_X == 0) || (Temp_Y == 0) )
{
x1 = Temp_X;
y1 = Temp_Y;
break;
}
Temp_X--;
Temp_Y--;
};
Temp_X = x, Temp_Y = y;//读入坐标的原始数据
for( int temp = 0/*计数变量*/;; x1++,y1++)//遍历制定方向上的所有可下棋子的位置知道碰到所下的棋子
{
if( (x1 == Temp_X) && (y1 == Temp_Y))//如果循环到所下的棋子位置那么返回计数值到对应的成员变量里面并跳出循环
{
Change_1 = temp;
judge = 0;
break;//返回后跳出循环
}
if( (Color[x1][y1] == color) || (Color[x1][y1] == 0) )//如果遇上的是跟自己一样的颜色的棋子或者是没有棋子则计数清零
{
temp=0;
if (Color[x1][y1] == color)
{
judge=1;
}
if (Color[x1][y1] == 0)
{
judge=0;
}
}
else //遇到相反颜色的棋子就计数一次
{
if(judge==1)
temp++;
}
}
然后从边缘坐标开始回溯,遇到对方棋子便记数,遇到自己的棋子或者空白则记数清零,指导回溯到落子的坐标。原理其实很简单,翻转的棋子必然从落子坐标出发,所以我们所需要记录的仅仅只要一个当前方向上面需要翻转的棋子个数。
不过比较无奈的是我的算法需要把代码复制8遍然后根据每个方向修改一点点。于是代码长度就有点长了,现在人也懒得改了,大家多多包涵。
8个方向上面都检查完成之后,那么我们需要做的是判断该位置是否可以落子,当然这个函数的前提是落子的地方不能有子,于是我们只需要检验所有的翻转变量是否都为0.
if( (Change_1 == 0) && (Change_2 == 0) && (Change_3 == 0) && (Change_4 == 0) && (Change_6 == 0) && (Change_7 == 0) && (Change_8 == 0) && (Change_9 == 0) )
{
Color [x][y] = 0;//如果无解则需要撤销假设落下的棋子
return 2;
}
else
return 1;
这样这个函数的所有功能就写完了。那么有了检查函数我们需要的则是翻转函数,有了上面的原理翻转函数也是很简单的了:
void COthelloLogic::Change(void)
private:
int Pos_X;
int Pos_Y;
是根据外部鼠标响应算出的点击坐标,其实也就是当前下子的坐标。
那么一个方向上面翻转功能的代码如下:
int Temp_X = Pos_X-1, Temp_Y = Pos_Y-1;//使用临时变量进行操作
for(int i=0;i
翻转完成后需要交换棋权,那么要加入一个对应的函数:
//将所下棋子的颜色反转过来
void COthelloLogic::Changecolor(void)
{
if(color==1)
color=2;
else
color=1;
}
将这个函数加入到翻转函数代码的最后。
下棋的逻辑中还有一个让子的功能,也就是棋盘并没有满但是无棋可下,则需要将棋权无条件转让给对手,这个我就不多讲了,也就是重复调用检查函数。
//判断是否是让子情况
bool COthelloLogic::GiveIn(void)
{
int xxx = 1; //默认的情况是让子成立
for(int i=0;i<8;i++)
for(int j=0;j<8;j++)
{
if( Color[i][j]==0 )//遍历所有没有下子的地方
{
if((Judgement(i,j))!= 2) //如果有解则把判断变量赋值为0,即无须让步
{
xxx = 0;
}
Color[i][j]=0;
Change_1=Change_2=Change_3=Change_4=Change_6=Change_7=Change_8=Change_9=0;
Pos_X = Pos_Y= -1;
}
}
if(xxx == 0)
return false;
else
return true;
}
敬请期待~