当你编写完自己的黑白棋程序后,是不是想和其他程序比比棋力?GGS(Generic Game Server)服务器为你提供这样的对战平台。GGS不仅是人类棋手下棋的地方,还有不少世界顶级的黑白棋程序也常年挂在上面,如:Logistello、Saio。你一样可以把自己的程序自动连接到GGS上,与其他棋手或程序进行对战。
注册GGS用户
为了使你的程序能登录到GGS服务器,你必须为它注册一个GGS用户。先下载一个GGS的客户端程序,如:Lion或Mimosa,下面以Lion汉化版为例。第一次运行Lion时,当出现登录框后,先点“取消”。再点击菜单: GGS -> 服务器设定,将服务器名称改为:skatgame.net(如果服务器地址有变动,请关注GGS主页的相关公告)。然后再点击菜单 :GGS -> 连接GGS,出现登录框后,填入你喜欢的用户名及密码。登录成功后,还需要找管理员(在线列表窗口上的注册类型栏显示为“a”的人)注册为注册用户。
一般来说,你还需要为自己另外注册一个用户,供你自己登录到GGS服务器用,以便能监视和指挥程序的运行。
ODK开发包
Chris Welty提供了一个黑白棋开发包ODK(Othello Development Kit),它包含了连接GGS服务器所需的完整源代码。你只要稍加修改,就可以很方便地把自己的程序连接到GGS。
先下载ODK源程序,并把它解压到一个目录内。再进行相关配置:
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")) {
将上面的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) << "mson";
flush();
(*this) << "info 4-ply RobotrFrom China(中国)n";
flush();
}
更多关于GGS的内容,请查阅《GGS指南》或GGS主页的相关资讯。