连接GGS

当你编写完自己的黑白棋程序后,是不是想和其他程序比比棋力?GGS(Generic Game Server)服务器为你提供这样的对战平台。GGS不仅是人类棋手下棋的地方,还有不少世界顶级的黑白棋程序也常年挂在上面,如:Logistello、Saio。你一样可以把自己的程序自动连接到GGS上,与其他棋手或程序进行对战。

注册GGS用户

为了使你的程序能登录到GGS服务器,你必须为它注册一个GGS用户。先下载一个GGS的客户端程序,如:LionMimosa,下面以Lion汉化版为例。第一次运行Lion时,当出现登录框后,先点“取消”。再点击菜单: GGS -> 服务器设定,将服务器名称改为:skatgame.net(如果服务器地址有变动,请关注GGS主页的相关公告)。然后再点击菜单 :GGS -> 连接GGS,出现登录框后,填入你喜欢的用户名及密码。登录成功后,还需要找管理员(在线列表窗口上的注册类型栏显示为“a”的人)注册为注册用户。

一般来说,你还需要为自己另外注册一个用户,供你自己登录到GGS服务器用,以便能监视和指挥程序的运行。

ODK开发包

Chris Welty提供了一个黑白棋开发包ODK(Othello Development Kit),它包含了连接GGS服务器所需的完整源代码。你只要稍加修改,就可以很方便地把自己的程序连接到GGS。

先下载ODK源程序,并把它解压到一个目录内。再进行相关配置:

1、修改main.cpp

int main() {
	……
	if (err = gs.Connect("external.nj.nec.com",5000)) {

将上面的external.nj.nec.com改为当前的GGS服务器,如:skatgame.net。

int main() {
	……
	if (err = gs.Login("greedy", "password")) {

将上面的greedy和password改为你所注册的程序用户名和密码。

2、修改ODKStream.cpp

void CODKStream::HandleGGSTell(const CMsgGGSTell* pmsg) {
	……
	if (pmsg->sFrom == "n2") {

将上面的n2改为你自己的用户名,这样程序就能接受你所发的指令。

然后编译源程序,可使用Borland C++对所有.cpp文件进行编译。如果使用VC++的话,源程序有几个地方可能需要略做修改(主要是涉及const_iterator),同时还必须链接ws2_32.lib(在项目属性中的附加依赖项加上ws2_32.lib,或者在sockbuf.h文件中加上#pragma comment(lib, "ws2_32.lib"))。

运行程序后,程序应能自动连接到GGS。然后你用自己的用户名登录上GGS,就可以在“在线列表”窗口上看到程序用户名。现在你可以试着给程序发指令,点击“在线列表”上的程序用户名(或者在程序用户名上点击右键菜单“与对方交谈”),然后在弹出的“.chat”窗口内输入:ta 8 ant。这样,程序就会去邀请ant下一盘8×8的黑白棋。下完棋后,在“.chat”窗口内输入:quit,程序就退出GGS。

连接自己的程序

现在你可以把自己的程序加入ODK,其核心接口部分是GetMove.cpp的GetMove()函数。

void GetMove(const COsGame& game, COsMoveListItem& mli) {

当轮到你下棋时,GetMove()函数负责从你这里取得一步棋。你应对GetMove()函数做相应修改,使它与你自己的程序对接,并把程序计算出的结果放在mli内。当GetMove()函数返回后,ODK就会将这步棋回传到GGS去下。

game.pos包含了当前局面的情况,其中game.pos.board.sBoard描述了盘面各个位置的棋子分布。由于sBoard是采用包含边界填充子的扩展棋盘表示法,除非你十分清楚这种扩展棋盘的结构,否则一般应使用game.pos.board.GetText()或game.pos.board.Piece()函数取得盘面情况。game.pos.board.fBlackMove表示当前是否轮到黑棋下,而game.pos.cks[game.pos.board.fBlackMove].tCurrent是你所剩余的时间。

mli.mv就是你所要下的棋步,其中mli.mv.fPass表示是否跳步,如果这步棋跳步,设mli.mv.fPass = true;否则设mli.mv.fPass = false,mli.mv.row及mli.mv.col设为下棋点的行、列位置(0-基), 如(row, col) = (0, 1)就表示从左上角起第1行第2列的位置。mli.dEval为c这步棋的估值,这是可选的项。

实现更多的功能

在ODKStream.cpp的CODKStream类中,包含一些处理各种报文的HandleXXX()函数。通过修改这些函数,你可以实现更多的功能。而CODKStream类又是继承于ggsstream.cpp的ggsstream类,它实际上只重载了其中的一部分HandleXXX()函数,你完全可以根据自己的需要,在CODKStream类中重载更多的函数。

1、自动接受对局

当你的程序连线GGS后,你一定希望它能自动接受其他玩家的挑战。但是你的程序可能只支持特定类型的对局,因此对于程序支持的对局请求,程序就自动接受;而对于那些不支持的类型,程序应给予拒绝。CODKStream::HandleOsRequestDelta()就是处理对局请求报文的函数,它可根据所请求对局的类型来决定是否应战。先将该函数去注释,再通过配置if语句中相应的pmsg->RequireXXX()函数,来实现自动接受请求的功能。CMsgOsRequestDelta类提供了各种RequireXXX()函数,用于判别不同的对局类型。例如:

equireBoardSize(int)  用于判别棋盘大小
RequireKomi()              用于判别是否贴目
RequireAnti()              用于判别是否下反棋
RequireRand()              用于判别是否下随机开局棋
RequireSynch()             用于判别是否下同步棋
RequireRated()             用于判别是否为计分棋
RequireMaxOpponentClock()  用于判别判别对方最大时间
RequireMinMyClock()        用于判别己方最小时间

2、悔棋功能

如果你想允许对方悔棋(让程序显得更友好些),那么你可以在CODKStream类中重载HandleOsUndoRequest()函数。当对方发悔棋请求时,程序自动进行回应。值得注意的是,当对方想要悔棋时,他需要撤消前面的两步棋:你所下的一步棋和他自己所下的一步棋。因此,当你回应对方的悔棋请求后,还应再发一个悔棋请求。这里给出一个简单的例子:

void CODKStream::HandleOsUndoRequest (const CMsgOsUndoRequest* pmsg){
	//如果是对方发的请求
	if (pmsg->sLogin != GetLogin()){
		//回应撤消己方的一步棋
		(*this) << "ts undo " << pmsg->idg << "\n";
		flush();
		//请求撤消对方的一步棋
		(*this) << "ts undo " << pmsg->idg << "\n";
		flush();
		//留点时间让对方响应,以免立刻进入自动下棋
		_sleep(5);
	}
}

3、盘后处理

如果你想在下完一盘棋后做点什么,比如对刚下完的这盘棋进行学习,那么你可以在CODKStream类中重载HandleOsGameOver()函数,就象下面这样:

void CODKStream::HandleOsGameOver(const CMsgOsMatchDelta* pmsg,const string& idg) {
	if (pmsg->match.IsPlaying(GetLogin())) {
		LearnGame(idToGame[idg]);
	}
	BaseOsGameOver(pmsg, idg);
}

这里LearnGame()是你自己设计的对局学习函数。

4、增添资料信息

你可以在用户信息的info一栏里增添一些关于程序的说明,就象这样:

void CODKStream::HandleGGSLogin() {
	BaseGGSLogin();
	(*this) << "mso\n";
	flush();
	(*this) << "info 4-ply Robot\rFrom China(中国)\n";
	flush();
}

更多关于GGS的内容,请查阅《GGS指南》或GGS主页的相关资讯。