x01.Weiqi.5 位置寻找
电脑下棋的关键在于寻找落子点。为此,我作了点尝试,旨在抛砖引玉,但也不排除抛砖引砖的可能。
为了确定子之间的关系和批量处理子的位置,我首先写了一个结构 Vector 和方法扩展类 PosListExtensions。实践证明,这个决定是英明的。代码改了又改,但这两个却始终未动,且发挥着重要的作用。
代码文件 ForPosSearch.cs 内容如下:
ForPosSearch
namespace x01.Weiqi.WhereHelper
{
struct Vector
{
int m_X;
int m_Y;
public Vector(int x, int y)
{
m_X = x;
m_Y = y;
}
publicdouble Length
{
get { return Math.Round(Math.Sqrt(m_X * m_X + m_Y * m_Y), 2); }
}
}
staticclass PosListExtensions
{
publicstatic List<Pos> AddDifferentPos(this List<Pos> self, List<Pos> other)
{
List<Pos> result =new List<Pos>();
foreach (var item in self)
{
result.Add(item);
}
foreach (var item in other)
{
if (!result.Contains(item))
{
result.Add(item);
}
}
return result;
}
publicstatic List<Pos> RemoveSamePos(this List<Pos> self, List<Pos> other)
{
List<Pos> result =new List<Pos>();
foreach (var item in self)
{
result.Add(item);
}
foreach (var p in self)
{
if (other.Contains(p))
{
result.Remove(p);
}
}
return result;
}
publicstaticint CountSamePos(this List<Pos> self, List<Pos> other)
{
int count =0;
foreach (var item in other)
{
if (self.Contains(item))
{
count++;
}
}
return count;
}
publicstatic List<Pos> GetSamePos(this List<Pos> self, List<Pos> other)
{
List<Pos> result =new List<Pos>();
foreach (var item in other)
{
if (self.Contains(item))
{
result.Add(item);
}
}
return result;
}
publicstaticbool Include(this List<Pos> self, List<Pos> other)
{
foreach (var item in other)
{
if (!self.Contains(item))
{
returnfalse;
}
}
returntrue;
}
}
}
{
struct Vector
{
int m_X;
int m_Y;
public Vector(int x, int y)
{
m_X = x;
m_Y = y;
}
publicdouble Length
{
get { return Math.Round(Math.Sqrt(m_X * m_X + m_Y * m_Y), 2); }
}
}
staticclass PosListExtensions
{
publicstatic List<Pos> AddDifferentPos(this List<Pos> self, List<Pos> other)
{
List<Pos> result =new List<Pos>();
foreach (var item in self)
{
result.Add(item);
}
foreach (var item in other)
{
if (!result.Contains(item))
{
result.Add(item);
}
}
return result;
}
publicstatic List<Pos> RemoveSamePos(this List<Pos> self, List<Pos> other)
{
List<Pos> result =new List<Pos>();
foreach (var item in self)
{
result.Add(item);
}
foreach (var p in self)
{
if (other.Contains(p))
{
result.Remove(p);
}
}
return result;
}
publicstaticint CountSamePos(this List<Pos> self, List<Pos> other)
{
int count =0;
foreach (var item in other)
{
if (self.Contains(item))
{
count++;
}
}
return count;
}
publicstatic List<Pos> GetSamePos(this List<Pos> self, List<Pos> other)
{
List<Pos> result =new List<Pos>();
foreach (var item in other)
{
if (self.Contains(item))
{
result.Add(item);
}
}
return result;
}
publicstaticbool Include(this List<Pos> self, List<Pos> other)
{
foreach (var item in other)
{
if (!self.Contains(item))
{
returnfalse;
}
}
returntrue;
}
}
}
主代码大体分三部分:Ready,Think,Next。
Ready 部分采取了全面撒网的方针,定义了一大堆诸如 m_BlackPos,m_WhitePos,m_EmptyPos,LineOne(),LineTwo() 之类的东西,且大部分无用,如 AreaTop() 之类。但我还是予以保留,主要是为了展示这种冗余设计的思想。
Think 部分主要处理了“碰、尖、飞、跳”四型。逻辑比较简单,例如“飞、跳”二型的代码如下:
FlySkip
Pos SkipOne(Pos white) // 跳
{
foreach (var item in RoundTwo(white))
{
if (IsElephantStep(item, CurrentPos) &&!LineOneTwoEmpties().Contains(item)
&& m_EmptyPos.Contains(item) && HasEightEmpty(item))
{
return item;
}
}
return m_InvalidPos;
}
Pos FlyOne(Pos white) // 飞
{
if (HasEightEmpty(white) && HasFourLinkEmpty(CurrentPos))
{
Pos pos = m_InvalidPos;
int x = white.X - CurrentPos.X;
int y = white.Y - CurrentPos.Y;
if (x ==1|| x ==-1)
{
pos =new Pos(CurrentPos.X + x, CurrentPos.Y);
}
elseif (y ==1|| y ==-1)
{
pos =new Pos(CurrentPos.X, CurrentPos.Y + y);
}
if (HasTwoEmpty(pos))
{
return pos;
}
}
return m_InvalidPos;
}
{
foreach (var item in RoundTwo(white))
{
if (IsElephantStep(item, CurrentPos) &&!LineOneTwoEmpties().Contains(item)
&& m_EmptyPos.Contains(item) && HasEightEmpty(item))
{
return item;
}
}
return m_InvalidPos;
}
Pos FlyOne(Pos white) // 飞
{
if (HasEightEmpty(white) && HasFourLinkEmpty(CurrentPos))
{
Pos pos = m_InvalidPos;
int x = white.X - CurrentPos.X;
int y = white.Y - CurrentPos.Y;
if (x ==1|| x ==-1)
{
pos =new Pos(CurrentPos.X + x, CurrentPos.Y);
}
elseif (y ==1|| y ==-1)
{
pos =new Pos(CurrentPos.X, CurrentPos.Y + y);
}
if (HasTwoEmpty(pos))
{
return pos;
}
}
return m_InvalidPos;
}
为检验,特和建立在 GnuGo 基础上的 JaGo 下了几局,基本上布局 50 步不算太落后,终局还能活个五、六十子。这是未想到的,因为我尚未写一句关于形势判断和死活做眼的代码。GnuGo 的作者们看到,只怕会怄得吐血!要知道,那可是历时二十余年的著名的围棋开源项目。其 50 步和终局图示如下:
因为 NotIn() 规定空点的四个连接点皆为白时禁入,所以事实上白已活!
Next 部分是为点目和做活作准备,未完待续。
源代码可从 http://www.cnblogs.com/china_x01 的 download\code\x01.Weiqi 获取。
Copyright (c) 2011 by x01(china_x01@qq.com).